1 /*
2  * Copyright (c) 1996, 2013, 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 
26 package java.sql;
27 
28 import java.util.Iterator;
29 import java.util.NoSuchElementException;
30 import 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  */
53 public 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      */
SQLException(String reason, String SQLState, int vendorCode)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      */
SQLException(String reason, String SQLState)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 vendor 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      */
SQLException(String reason)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      *
138      */
SQLException()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      */
SQLException(Throwable cause)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      */
SQLException(String reason, Throwable cause)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      */
SQLException(String reason, String sqlState, Throwable cause)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      */
SQLException(String reason, String sqlState, int vendorCode, Throwable cause)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      */
getSQLState()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      */
getErrorCode()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      */
getNextException()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      */
setNextException(SQLException ex)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      */
iterator()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