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      *
  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      *
  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 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      *
 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      *
 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      *
 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      *
 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      *
 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      *
 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 }