1/*
2 * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.sql;
27
28import java.util.Iterator;
29import java.util.NoSuchElementException;
30import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
31
32/**
33 * <P>An exception that provides information on a database access
34 * error or other errors.
35 *
36 * <P>Each <code>SQLException</code> provides several kinds of information:
37 * <UL>
38 *   <LI> a string describing the error.  This is used as the Java Exception
39 *       message, available via the method <code>getMesasge</code>.
40 *   <LI> a "SQLstate" string, which follows either the XOPEN SQLstate conventions
41 *        or the SQL:2003 conventions.
42 *       The values of the SQLState string are described in the appropriate spec.
43 *       The <code>DatabaseMetaData</code> method <code>getSQLStateType</code>
44 *       can be used to discover whether the driver returns the XOPEN type or
45 *       the SQL:2003 type.
46 *   <LI> an integer error code that is specific to each vendor.  Normally this will
47 *       be the actual error code returned by the underlying database.
48 *   <LI> a chain to a next Exception.  This can be used to provide additional
49 *       error information.
50 *   <LI> the causal relationship, if any for this <code>SQLException</code>.
51 * </UL>
52 */
53public class SQLException extends java.lang.Exception
54                          implements Iterable<Throwable> {
55
56    /**
57     *  Constructs a <code>SQLException</code> object with a given
58     * <code>reason</code>, <code>SQLState</code>  and
59     * <code>vendorCode</code>.
60     *
61     * The <code>cause</code> is not initialized, and may subsequently be
62     * initialized by a call to the
63     * {@link Throwable#initCause(java.lang.Throwable)} method.
64     * <p>
65     * @param reason a description of the exception
66     * @param SQLState an XOPEN or SQL:2003 code identifying the exception
67     * @param vendorCode a database vendor-specific exception code
68     */
69    public SQLException(String reason, String SQLState, int vendorCode) {
70        super(reason);
71        this.SQLState = SQLState;
72        this.vendorCode = vendorCode;
73        if (!(this instanceof SQLWarning)) {
74            if (DriverManager.getLogWriter() != null) {
75                DriverManager.println("SQLState(" + SQLState +
76                                                ") vendor code(" + vendorCode + ")");
77                printStackTrace(DriverManager.getLogWriter());
78            }
79        }
80    }
81
82
83    /**
84     * Constructs a <code>SQLException</code> object with a given
85     * <code>reason</code> and <code>SQLState</code>.
86     *
87     * The <code>cause</code> is not initialized, and may subsequently be
88     * initialized by a call to the
89     * {@link Throwable#initCause(java.lang.Throwable)} method. The vendor code
90     * is initialized to 0.
91     * <p>
92     * @param reason a description of the exception
93     * @param SQLState an XOPEN or SQL:2003 code identifying the exception
94     */
95    public SQLException(String reason, String SQLState) {
96        super(reason);
97        this.SQLState = SQLState;
98        this.vendorCode = 0;
99        if (!(this instanceof SQLWarning)) {
100            if (DriverManager.getLogWriter() != null) {
101                printStackTrace(DriverManager.getLogWriter());
102                DriverManager.println("SQLException: SQLState(" + SQLState + ")");
103            }
104        }
105    }
106
107    /**
108     *  Constructs a <code>SQLException</code> object with a given
109     * <code>reason</code>. The  <code>SQLState</code>  is initialized to
110     * <code>null</code> and the vender code is initialized to 0.
111     *
112     * The <code>cause</code> is not initialized, and may subsequently be
113     * initialized by a call to the
114     * {@link Throwable#initCause(java.lang.Throwable)} method.
115     * <p>
116     * @param reason a description of the exception
117     */
118    public SQLException(String reason) {
119        super(reason);
120        this.SQLState = null;
121        this.vendorCode = 0;
122        if (!(this instanceof SQLWarning)) {
123            if (DriverManager.getLogWriter() != null) {
124                printStackTrace(DriverManager.getLogWriter());
125            }
126        }
127    }
128
129    /**
130     * Constructs a <code>SQLException</code> object.
131     * The <code>reason</code>, <code>SQLState</code> are initialized
132     * to <code>null</code> and the vendor code is initialized to 0.
133     *
134     * The <code>cause</code> is not initialized, and may subsequently be
135     * initialized by a call to the
136     * {@link Throwable#initCause(java.lang.Throwable)} method.
137     * <p>
138     */
139    public SQLException() {
140        super();
141        this.SQLState = null;
142        this.vendorCode = 0;
143        if (!(this instanceof SQLWarning)) {
144            if (DriverManager.getLogWriter() != null) {
145                printStackTrace(DriverManager.getLogWriter());
146            }
147        }
148    }
149
150    /**
151     *  Constructs a <code>SQLException</code> object with a given
152     * <code>cause</code>.
153     * The <code>SQLState</code> is initialized
154     * to <code>null</code> and the vendor code is initialized to 0.
155     * The <code>reason</code>  is initialized to <code>null</code> if
156     * <code>cause==null</code> or to <code>cause.toString()</code> if
157     * <code>cause!=null</code>.
158     * <p>
159     * @param cause the underlying reason for this <code>SQLException</code>
160     * (which is saved for later retrieval by the <code>getCause()</code> method);
161     * may be null indicating the cause is non-existent or unknown.
162     * @since 1.6
163     */
164    public SQLException(Throwable cause) {
165        super(cause);
166
167        if (!(this instanceof SQLWarning)) {
168            if (DriverManager.getLogWriter() != null) {
169                printStackTrace(DriverManager.getLogWriter());
170            }
171        }
172    }
173
174    /**
175     * Constructs a <code>SQLException</code> object with a given
176     * <code>reason</code> and  <code>cause</code>.
177     * The <code>SQLState</code> is  initialized to <code>null</code>
178     * and the vendor code is initialized to 0.
179     * <p>
180     * @param reason a description of the exception.
181     * @param cause the underlying reason for this <code>SQLException</code>
182     * (which is saved for later retrieval by the <code>getCause()</code> method);
183     * may be null indicating the cause is non-existent or unknown.
184     * @since 1.6
185     */
186    public SQLException(String reason, Throwable cause) {
187        super(reason,cause);
188
189        if (!(this instanceof SQLWarning)) {
190            if (DriverManager.getLogWriter() != null) {
191                    printStackTrace(DriverManager.getLogWriter());
192            }
193        }
194    }
195
196    /**
197     * Constructs a <code>SQLException</code> object with a given
198     * <code>reason</code>, <code>SQLState</code> and  <code>cause</code>.
199     * The vendor code is initialized to 0.
200     * <p>
201     * @param reason a description of the exception.
202     * @param sqlState an XOPEN or SQL:2003 code identifying the exception
203     * @param cause the underlying reason for this <code>SQLException</code>
204     * (which is saved for later retrieval by the
205     * <code>getCause()</code> method); may be null indicating
206     *     the cause is non-existent or unknown.
207     * @since 1.6
208     */
209    public SQLException(String reason, String sqlState, Throwable cause) {
210        super(reason,cause);
211
212        this.SQLState = sqlState;
213        this.vendorCode = 0;
214        if (!(this instanceof SQLWarning)) {
215            if (DriverManager.getLogWriter() != null) {
216                printStackTrace(DriverManager.getLogWriter());
217                DriverManager.println("SQLState(" + SQLState + ")");
218            }
219        }
220    }
221
222    /**
223     * Constructs a <code>SQLException</code> object with a given
224     * <code>reason</code>, <code>SQLState</code>, <code>vendorCode</code>
225     * and  <code>cause</code>.
226     * <p>
227     * @param reason a description of the exception
228     * @param sqlState an XOPEN or SQL:2003 code identifying the exception
229     * @param vendorCode a database vendor-specific exception code
230     * @param cause the underlying reason for this <code>SQLException</code>
231     * (which is saved for later retrieval by the <code>getCause()</code> method);
232     * may be null indicating the cause is non-existent or unknown.
233     * @since 1.6
234     */
235    public SQLException(String reason, String sqlState, int vendorCode, Throwable cause) {
236        super(reason,cause);
237
238        this.SQLState = sqlState;
239        this.vendorCode = vendorCode;
240        if (!(this instanceof SQLWarning)) {
241            if (DriverManager.getLogWriter() != null) {
242                DriverManager.println("SQLState(" + SQLState +
243                                                ") vendor code(" + vendorCode + ")");
244                printStackTrace(DriverManager.getLogWriter());
245            }
246        }
247    }
248
249    /**
250     * Retrieves the SQLState for this <code>SQLException</code> object.
251     *
252     * @return the SQLState value
253     */
254    public String getSQLState() {
255        return (SQLState);
256    }
257
258    /**
259     * Retrieves the vendor-specific exception code
260     * for this <code>SQLException</code> object.
261     *
262     * @return the vendor's error code
263     */
264    public int getErrorCode() {
265        return (vendorCode);
266    }
267
268    /**
269     * Retrieves the exception chained to this
270     * <code>SQLException</code> object by setNextException(SQLException ex).
271     *
272     * @return the next <code>SQLException</code> object in the chain;
273     *         <code>null</code> if there are none
274     * @see #setNextException
275     */
276    public SQLException getNextException() {
277        return (next);
278    }
279
280    /**
281     * Adds an <code>SQLException</code> object to the end of the chain.
282     *
283     * @param ex the new exception that will be added to the end of
284     *            the <code>SQLException</code> chain
285     * @see #getNextException
286     */
287    public void setNextException(SQLException ex) {
288
289        SQLException current = this;
290        for(;;) {
291            SQLException next=current.next;
292            if (next != null) {
293                current = next;
294                continue;
295            }
296
297            if (nextUpdater.compareAndSet(current,null,ex)) {
298                return;
299            }
300            current=current.next;
301        }
302    }
303
304    /**
305     * Returns an iterator over the chained SQLExceptions.  The iterator will
306     * be used to iterate over each SQLException and its underlying cause
307     * (if any).
308     *
309     * @return an iterator over the chained SQLExceptions and causes in the proper
310     * order
311     *
312     * @since 1.6
313     */
314    public Iterator<Throwable> iterator() {
315
316       return new Iterator<Throwable>() {
317
318           SQLException firstException = SQLException.this;
319           SQLException nextException = firstException.getNextException();
320           Throwable cause = firstException.getCause();
321
322           public boolean hasNext() {
323               if(firstException != null || nextException != null || cause != null)
324                   return true;
325               return false;
326           }
327
328           public Throwable next() {
329               Throwable throwable = null;
330               if(firstException != null){
331                   throwable = firstException;
332                   firstException = null;
333               }
334               else if(cause != null){
335                   throwable = cause;
336                   cause = cause.getCause();
337               }
338               else if(nextException != null){
339                   throwable = nextException;
340                   cause = nextException.getCause();
341                   nextException = nextException.getNextException();
342               }
343               else
344                   throw new NoSuchElementException();
345               return throwable;
346           }
347
348           public void remove() {
349               throw new UnsupportedOperationException();
350           }
351
352       };
353
354    }
355
356    /**
357         * @serial
358         */
359    private String SQLState;
360
361        /**
362         * @serial
363         */
364    private int vendorCode;
365
366        /**
367         * @serial
368         */
369    private volatile SQLException next;
370
371    private static final AtomicReferenceFieldUpdater<SQLException,SQLException> nextUpdater =
372            AtomicReferenceFieldUpdater.newUpdater(SQLException.class,SQLException.class,"next");
373
374    private static final long serialVersionUID = 2135244094396331484L;
375}
376