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.ServiceLoader;
  30 import java.security.AccessController;
  31 import java.security.PrivilegedAction;
  32 import java.util.concurrent.CopyOnWriteArrayList;
  33 import sun.reflect.CallerSensitive;
  34 import sun.reflect.Reflection;
  35 
  36 
  37 /**
  38  * <P>The basic service for managing a set of JDBC drivers.<br>
  39  * <B>NOTE:</B> The {@link javax.sql.DataSource} interface, new in the
  40  * JDBC 2.0 API, provides another way to connect to a data source.
  41  * The use of a <code>DataSource</code> object is the preferred means of
  42  * connecting to a data source.
  43  *
  44  * <P>As part of its initialization, the <code>DriverManager</code> class will
  45  * attempt to load the driver classes referenced in the "jdbc.drivers"
  46  * system property. This allows a user to customize the JDBC Drivers
  47  * used by their applications. For example in your
  48  * ~/.hotjava/properties file you might specify:
  49  * <pre>
  50  * <CODE>jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver</CODE>
  51  * </pre>
  52  *<P> The <code>DriverManager</code> methods <code>getConnection</code> and
  53  * <code>getDrivers</code> have been enhanced to support the Java Standard Edition
  54  * <a href="../../../technotes/guides/jar/jar.html#Service%20Provider">Service Provider</a> mechanism. JDBC 4.0 Drivers must
  55  * include the file <code>META-INF/services/java.sql.Driver</code>. This file contains the name of the JDBC drivers
  56  * implementation of <code>java.sql.Driver</code>.  For example, to load the <code>my.sql.Driver</code> class,
  57  * the <code>META-INF/services/java.sql.Driver</code> file would contain the entry:
  58  * <pre>
  59  * <code>my.sql.Driver</code>
  60  * </pre>
  61  *
  62  * <P>Applications no longer need to explictly load JDBC drivers using <code>Class.forName()</code>. Existing programs
  63  * which currently load JDBC drivers using <code>Class.forName()</code> will continue to work without
  64  * modification.
  65  *
  66  * <P>When the method <code>getConnection</code> is called,
  67  * the <code>DriverManager</code> will attempt to
  68  * locate a suitable driver from amongst those loaded at
  69  * initialization and those loaded explicitly using the same classloader
  70  * as the current applet or application.
  71  *
  72  * <P>
  73  * Starting with the Java 2 SDK, Standard Edition, version 1.3, a
  74  * logging stream can be set only if the proper
  75  * permission has been granted.  Normally this will be done with
  76  * the tool PolicyTool, which can be used to grant <code>permission
  77  * java.sql.SQLPermission "setLog"</code>.
  78  * @see Driver
  79  * @see Connection
  80  */
  81 public class DriverManager {
  82 
  83 
  84     // List of registered JDBC drivers
  85     private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
  86     private static volatile int loginTimeout = 0;
  87     private static volatile java.io.PrintWriter logWriter = null;
  88     private static volatile java.io.PrintStream logStream = null;
  89     // Used in println() to synchronize logWriter
  90     private final static  Object logSync = new Object();
  91 
  92     /* Prevent the DriverManager class from being instantiated. */
  93     private DriverManager(){}
  94 
  95 
  96     /**
  97      * Load the initial JDBC drivers by checking the System property
  98      * jdbc.properties and then use the {@code ServiceLoader} mechanism
  99      */
 100     static {
 101         loadInitialDrivers();
 102         println("JDBC DriverManager initialized");
 103     }
 104 
 105     /**
 106      * The <code>SQLPermission</code> constant that allows the
 107      * setting of the logging stream.
 108      * @since 1.3
 109      */
 110     final static SQLPermission SET_LOG_PERMISSION =
 111         new SQLPermission("setLog");
 112 
 113     /**
 114      * The {@code SQLPermission} constant that allows the
 115      * un-register a registered JDBC driver.
 116      * @since 1.8
 117      */
 118     final static SQLPermission DEREGISTER_DRIVER_PERMISSION =
 119         new SQLPermission("deregisterDriver");
 120 
 121     //--------------------------JDBC 2.0-----------------------------
 122 
 123     /**
 124      * Retrieves the log writer.
 125      *
 126      * The <code>getLogWriter</code> and <code>setLogWriter</code>
 127      * methods should be used instead
 128      * of the <code>get/setlogStream</code> methods, which are deprecated.
 129      * @return a <code>java.io.PrintWriter</code> object
 130      * @see #setLogWriter
 131      * @since 1.2
 132      */
 133     public static java.io.PrintWriter getLogWriter() {
 134             return logWriter;
 135     }
 136 
 137     /**
 138      * Sets the logging/tracing <code>PrintWriter</code> object
 139      * that is used by the <code>DriverManager</code> and all drivers.
 140      * <P>
 141      * There is a minor versioning problem created by the introduction
 142      * of the method <code>setLogWriter</code>.  The
 143      * method <code>setLogWriter</code> cannot create a <code>PrintStream</code> object
 144      * that will be returned by <code>getLogStream</code>---the Java platform does
 145      * not provide a backward conversion.  As a result, a new application
 146      * that uses <code>setLogWriter</code> and also uses a JDBC 1.0 driver that uses
 147      * <code>getLogStream</code> will likely not see debugging information written
 148      * by that driver.
 149      *<P>
 150      * Starting with the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
 151      * to see that there is an <code>SQLPermission</code> object before setting
 152      * the logging stream.  If a <code>SecurityManager</code> exists and its
 153      * <code>checkPermission</code> method denies setting the log writer, this
 154      * method throws a <code>java.lang.SecurityException</code>.
 155      *
 156      * @param out the new logging/tracing <code>PrintStream</code> object;
 157      *      <code>null</code> to disable logging and tracing
 158      * @throws SecurityException
 159      *    if a security manager exists and its
 160      *    <code>checkPermission</code> method denies
 161      *    setting the log writer
 162      *
 163      * @see SecurityManager#checkPermission
 164      * @see #getLogWriter
 165      * @since 1.2
 166      */
 167     public static void setLogWriter(java.io.PrintWriter out) {
 168 
 169         SecurityManager sec = System.getSecurityManager();
 170         if (sec != null) {
 171             sec.checkPermission(SET_LOG_PERMISSION);
 172         }
 173             logStream = null;
 174             logWriter = out;
 175     }
 176 
 177 
 178     //---------------------------------------------------------------
 179 
 180     /**
 181      * Attempts to establish a connection to the given database URL.
 182      * The <code>DriverManager</code> attempts to select an appropriate driver from
 183      * the set of registered JDBC drivers.
 184      *<p>
 185      * <B>Note:</B> If a property is specified as part of the {@code url} and
 186      * is also specified in the {@code Properties} object, it is
 187      * implementation-defined as to which value will take precedence.
 188      * For maximum portability, an application should only specify a
 189      * property once.
 190      *
 191      * @param url a database url of the form
 192      * <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
 193      * @param info a list of arbitrary string tag/value pairs as
 194      * connection arguments; normally at least a "user" and
 195      * "password" property should be included
 196      * @return a Connection to the URL
 197      * @exception SQLException if a database access error occurs or the url is
 198      * {@code null}
 199      * @throws SQLTimeoutException  when the driver has determined that the
 200      * timeout value specified by the {@code setLoginTimeout} method
 201      * has been exceeded and has at least tried to cancel the
 202      * current database connection attempt
 203      */
 204     @CallerSensitive
 205     public static Connection getConnection(String url,
 206         java.util.Properties info) throws SQLException {
 207 
 208         return (getConnection(url, info, Reflection.getCallerClass()));
 209     }
 210 
 211     /**
 212      * Attempts to establish a connection to the given database URL.
 213      * The <code>DriverManager</code> attempts to select an appropriate driver from
 214      * the set of registered JDBC drivers.
 215      *<p>
 216      * <B>Note:</B> If a property is specified as part of the {@code url} and
 217      * is also specified in the {@code Properties} object, it is
 218      * implementation-defined as to which value will take precedence.
 219      * For maximum portability, an application should only specify a
 220      * property once.
 221      *
 222      * @param url a database url of the form
 223      * <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
 224      * @param user the database user on whose behalf the connection is being
 225      *   made
 226      * @param password the user's password
 227      * @return a connection to the URL
 228      * @exception SQLException if a database access error occurs or the url is
 229      * {@code null}
 230      * @throws SQLTimeoutException  when the driver has determined that the
 231      * timeout value specified by the {@code setLoginTimeout} method
 232      * has been exceeded and has at least tried to cancel the
 233      * current database connection attempt
 234      */
 235     @CallerSensitive
 236     public static Connection getConnection(String url,
 237         String user, String password) throws SQLException {
 238         java.util.Properties info = new java.util.Properties();
 239 
 240         if (user != null) {
 241             info.put("user", user);
 242         }
 243         if (password != null) {
 244             info.put("password", password);
 245         }
 246 
 247         return (getConnection(url, info, Reflection.getCallerClass()));
 248     }
 249 
 250     /**
 251      * Attempts to establish a connection to the given database URL.
 252      * The <code>DriverManager</code> attempts to select an appropriate driver from
 253      * the set of registered JDBC drivers.
 254      *
 255      * @param url a database url of the form
 256      *  <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
 257      * @return a connection to the URL
 258      * @exception SQLException if a database access error occurs or the url is
 259      * {@code null}
 260      * @throws SQLTimeoutException  when the driver has determined that the
 261      * timeout value specified by the {@code setLoginTimeout} method
 262      * has been exceeded and has at least tried to cancel the
 263      * current database connection attempt
 264      */
 265     @CallerSensitive
 266     public static Connection getConnection(String url)
 267         throws SQLException {
 268 
 269         java.util.Properties info = new java.util.Properties();
 270         return (getConnection(url, info, Reflection.getCallerClass()));
 271     }
 272 
 273     /**
 274      * Attempts to locate a driver that understands the given URL.
 275      * The <code>DriverManager</code> attempts to select an appropriate driver from
 276      * the set of registered JDBC drivers.
 277      *
 278      * @param url a database URL of the form
 279      *     <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
 280      * @return a <code>Driver</code> object representing a driver
 281      * that can connect to the given URL
 282      * @exception SQLException if a database access error occurs
 283      */
 284     @CallerSensitive
 285     public static Driver getDriver(String url)
 286         throws SQLException {
 287 
 288         println("DriverManager.getDriver(\"" + url + "\")");
 289 
 290         Class<?> callerClass = Reflection.getCallerClass();
 291 
 292         // Walk through the loaded registeredDrivers attempting to locate someone
 293         // who understands the given URL.
 294         for (DriverInfo aDriver : registeredDrivers) {
 295             // If the caller does not have permission to load the driver then
 296             // skip it.
 297             if(isDriverAllowed(aDriver.driver, callerClass)) {
 298                 try {
 299                     if(aDriver.driver.acceptsURL(url)) {
 300                         // Success!
 301                         println("getDriver returning " + aDriver.driver.getClass().getName());
 302                     return (aDriver.driver);
 303                     }
 304 
 305                 } catch(SQLException sqe) {
 306                     // Drop through and try the next driver.
 307                 }
 308             } else {
 309                 println("    skipping: " + aDriver.driver.getClass().getName());
 310             }
 311 
 312         }
 313 
 314         println("getDriver: no suitable driver");
 315         throw new SQLException("No suitable driver", "08001");
 316     }
 317 
 318 
 319     /**
 320      * Registers the given driver with the {@code DriverManager}.
 321      * A newly-loaded driver class should call
 322      * the method {@code registerDriver} to make itself
 323      * known to the {@code DriverManager}. If the driver had previously been
 324      * registered, no action is taken.
 325      *
 326      * @param driver the new JDBC Driver that is to be registered with the
 327      *               {@code DriverManager}
 328      * @exception SQLException if a database access error occurs
 329      */
 330     public static synchronized void registerDriver(java.sql.Driver driver)
 331         throws SQLException {
 332 
 333         registerDriver(driver, null);
 334     }
 335 
 336     /**
 337      * Registers the given driver with the {@code DriverManager}.
 338      * A newly-loaded driver class should call
 339      * the method {@code registerDriver} to make itself
 340      * known to the {@code DriverManager}. If the driver had previously been
 341      * registered, no action is taken.
 342      *
 343      * @param driver the new JDBC Driver that is to be registered with the
 344      *               {@code DriverManager}
 345      * @param da     the {@code DriverAction} implementation to be used when
 346      *               {@code DriverManager#deregisterDriver} is called
 347      * @exception SQLException if a database access error occurs
 348      */
 349     public static synchronized void registerDriver(java.sql.Driver driver,
 350             DriverAction da)
 351         throws SQLException {
 352 
 353         /* Register the driver if it has not already been added to our list */
 354         if(driver != null) {
 355             registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
 356         } else {
 357             // This is for compatibility with the original DriverManager
 358             throw new NullPointerException();
 359         }
 360 
 361         println("registerDriver: " + driver);
 362 
 363     }
 364 
 365     /**
 366      * Removes the specified driver from the {@code DriverManager}'s list of
 367      * registered drivers.
 368      * <p>
 369      * If a {@code null} value is specified for the driver to be removed, then no
 370      * action is taken.
 371      * <p>
 372      * If a security manager exists and its {@code checkPermission} denies
 373      * permission, then a {@code SecurityException} will be thrown.
 374      * <p>
 375      * If the specified driver is not found in the list of registered drivers,
 376      * then no action is taken.  If the driver was found, it will be removed
 377      * from the list of registered drivers.
 378      * <p>
 379      * If a {@code DriverAction} instance was specified when the JDBC driver was
 380      * registered, its deregister method will be called
 381      * prior to the driver being removed from the list of registered drivers.
 382      *
 383      * @param driver the JDBC Driver to remove
 384      * @exception SQLException if a database access error occurs
 385      * @throws SecurityException if a security manager exists and its
 386      * {@code checkPermission} method denies permission to deregister a driver.
 387      *
 388      * @see SecurityManager#checkPermission
 389      */
 390     @CallerSensitive
 391     public static synchronized void deregisterDriver(Driver driver)
 392         throws SQLException {
 393         if (driver == null) {
 394             return;
 395         }
 396 
 397         SecurityManager sec = System.getSecurityManager();
 398         if (sec != null) {
 399             sec.checkPermission(DEREGISTER_DRIVER_PERMISSION);
 400         }
 401 
 402         println("DriverManager.deregisterDriver: " + driver);
 403 
 404         DriverInfo aDriver = new DriverInfo(driver, null);
 405         if(registeredDrivers.contains(aDriver)) {
 406             if (isDriverAllowed(driver, Reflection.getCallerClass())) {
 407                 DriverInfo di = registeredDrivers.get(registeredDrivers.indexOf(aDriver));
 408                  // If a DriverAction was specified, Call it to notify the
 409                  // driver that it has been deregistered
 410                  if(di.action() != null) {
 411                      di.action().deregister();
 412                  }
 413                  registeredDrivers.remove(aDriver);
 414             } else {
 415                 // If the caller does not have permission to load the driver then
 416                 // throw a SecurityException.
 417                 throw new SecurityException();
 418             }
 419         } else {
 420             println("    couldn't find driver to unload");
 421         }
 422     }
 423 
 424     /**
 425      * Retrieves an Enumeration with all of the currently loaded JDBC drivers
 426      * to which the current caller has access.
 427      *
 428      * <P><B>Note:</B> The classname of a driver can be found using
 429      * <CODE>d.getClass().getName()</CODE>
 430      *
 431      * @return the list of JDBC Drivers loaded by the caller's class loader
 432      */
 433     @CallerSensitive
 434     public static java.util.Enumeration<Driver> getDrivers() {
 435         java.util.Vector<Driver> result = new java.util.Vector<>();
 436 
 437         Class<?> callerClass = Reflection.getCallerClass();
 438 
 439         // Walk through the loaded registeredDrivers.
 440         for(DriverInfo aDriver : registeredDrivers) {
 441             // If the caller does not have permission to load the driver then
 442             // skip it.
 443             if(isDriverAllowed(aDriver.driver, callerClass)) {
 444                 result.addElement(aDriver.driver);
 445             } else {
 446                 println("    skipping: " + aDriver.getClass().getName());
 447             }
 448         }
 449         return (result.elements());
 450     }
 451 
 452 
 453     /**
 454      * Sets the maximum time in seconds that a driver will wait
 455      * while attempting to connect to a database once the driver has
 456      * been identified.
 457      *
 458      * @param seconds the login time limit in seconds; zero means there is no limit
 459      * @see #getLoginTimeout
 460      */
 461     public static void setLoginTimeout(int seconds) {
 462         loginTimeout = seconds;
 463     }
 464 
 465     /**
 466      * Gets the maximum time in seconds that a driver can wait
 467      * when attempting to log in to a database.
 468      *
 469      * @return the driver login time limit in seconds
 470      * @see #setLoginTimeout
 471      */
 472     public static int getLoginTimeout() {
 473         return (loginTimeout);
 474     }
 475 
 476     /**
 477      * Sets the logging/tracing PrintStream that is used
 478      * by the <code>DriverManager</code>
 479      * and all drivers.
 480      *<P>
 481      * In the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
 482      * to see that there is an <code>SQLPermission</code> object before setting
 483      * the logging stream.  If a <code>SecurityManager</code> exists and its
 484      * <code>checkPermission</code> method denies setting the log writer, this
 485      * method throws a <code>java.lang.SecurityException</code>.
 486      *
 487      * @param out the new logging/tracing PrintStream; to disable, set to <code>null</code>
 488      * @deprecated Use {@code setLogWriter}
 489      * @throws SecurityException if a security manager exists and its
 490      *    <code>checkPermission</code> method denies setting the log stream
 491      *
 492      * @see SecurityManager#checkPermission
 493      * @see #getLogStream
 494      */
 495     @Deprecated
 496     public static void setLogStream(java.io.PrintStream out) {
 497 
 498         SecurityManager sec = System.getSecurityManager();
 499         if (sec != null) {
 500             sec.checkPermission(SET_LOG_PERMISSION);
 501         }
 502 
 503         logStream = out;
 504         if ( out != null )
 505             logWriter = new java.io.PrintWriter(out);
 506         else
 507             logWriter = null;
 508     }
 509 
 510     /**
 511      * Retrieves the logging/tracing PrintStream that is used by the <code>DriverManager</code>
 512      * and all drivers.
 513      *
 514      * @return the logging/tracing PrintStream; if disabled, is <code>null</code>
 515      * @deprecated  Use {@code getLogWriter}
 516      * @see #setLogStream
 517      */
 518     @Deprecated
 519     public static java.io.PrintStream getLogStream() {
 520         return logStream;
 521     }
 522 
 523     /**
 524      * Prints a message to the current JDBC log stream.
 525      *
 526      * @param message a log or tracing message
 527      */
 528     public static void println(String message) {
 529         synchronized (logSync) {
 530             if (logWriter != null) {
 531                 logWriter.println(message);
 532 
 533                 // automatic flushing is never enabled, so we must do it ourselves
 534                 logWriter.flush();
 535             }
 536         }
 537     }
 538 
 539     //------------------------------------------------------------------------
 540 
 541     // Indicates whether the class object that would be created if the code calling
 542     // DriverManager is accessible.
 543     private static boolean isDriverAllowed(Driver driver, Class<?> caller) {
 544         ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
 545         return isDriverAllowed(driver, callerCL);
 546     }
 547 
 548     private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
 549         boolean result = false;
 550         if(driver != null) {
 551             Class<?> aClass = null;
 552             try {
 553                 aClass =  Class.forName(driver.getClass().getName(), true, classLoader);
 554             } catch (Exception ex) {
 555                 result = false;
 556             }
 557 
 558              result = ( aClass == driver.getClass() ) ? true : false;
 559         }
 560 
 561         return result;
 562     }
 563 
 564     private static void loadInitialDrivers() {
 565         String drivers;
 566         try {
 567             drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
 568                 public String run() {
 569                     return System.getProperty("jdbc.drivers");
 570                 }
 571             });
 572         } catch (Exception ex) {
 573             drivers = null;
 574         }
 575         // If the driver is packaged as a Service Provider, load it.
 576         // Get all the drivers through the classloader
 577         // exposed as a java.sql.Driver.class service.
 578         // ServiceLoader.load() replaces the sun.misc.Providers()
 579 
 580         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 581             public Void run() {
 582 
 583                 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
 584                 Iterator<Driver> driversIterator = loadedDrivers.iterator();
 585 
 586                 /* Load these drivers, so that they can be instantiated.
 587                  * It may be the case that the driver class may not be there
 588                  * i.e. there may be a packaged driver with the service class
 589                  * as implementation of java.sql.Driver but the actual class
 590                  * may be missing. In that case a java.util.ServiceConfigurationError
 591                  * will be thrown at runtime by the VM trying to locate
 592                  * and load the service.
 593                  *
 594                  * Adding a try catch block to catch those runtime errors
 595                  * if driver not available in classpath but it's
 596                  * packaged as service and that service is there in classpath.
 597                  */
 598                 try{
 599                     while(driversIterator.hasNext()) {
 600                         driversIterator.next();
 601                     }
 602                 } catch(Throwable t) {
 603                 // Do nothing
 604                 }
 605                 return null;
 606             }
 607         });
 608 
 609         println("DriverManager.initialize: jdbc.drivers = " + drivers);
 610 
 611         if (drivers == null || drivers.equals("")) {
 612             return;
 613         }
 614         String[] driversList = drivers.split(":");
 615         println("number of Drivers:" + driversList.length);
 616         for (String aDriver : driversList) {
 617             try {
 618                 println("DriverManager.Initialize: loading " + aDriver);
 619                 Class.forName(aDriver, true,
 620                         ClassLoader.getSystemClassLoader());
 621             } catch (Exception ex) {
 622                 println("DriverManager.Initialize: load failed: " + ex);
 623             }
 624         }
 625     }
 626 
 627 
 628     //  Worker method called by the public getConnection() methods.
 629     private static Connection getConnection(
 630         String url, java.util.Properties info, Class<?> caller) throws SQLException {
 631         /*
 632          * When callerCl is null, we should check the application's
 633          * (which is invoking this class indirectly)
 634          * classloader, so that the JDBC driver class outside rt.jar
 635          * can be loaded from here.
 636          */
 637         ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
 638         synchronized(DriverManager.class) {
 639             // synchronize loading of the correct classloader.
 640             if (callerCL == null) {
 641                 callerCL = Thread.currentThread().getContextClassLoader();
 642             }
 643         }
 644 
 645         if(url == null) {
 646             throw new SQLException("The url cannot be null", "08001");
 647         }
 648 
 649         println("DriverManager.getConnection(\"" + url + "\")");
 650 
 651         // Walk through the loaded registeredDrivers attempting to make a connection.
 652         // Remember the first exception that gets raised so we can reraise it.
 653         SQLException reason = null;
 654 
 655         for(DriverInfo aDriver : registeredDrivers) {
 656             // If the caller does not have permission to load the driver then
 657             // skip it.
 658             if(isDriverAllowed(aDriver.driver, callerCL)) {
 659                 try {
 660                     println("    trying " + aDriver.driver.getClass().getName());
 661                     Connection con = aDriver.driver.connect(url, info);
 662                     if (con != null) {
 663                         // Success!
 664                         println("getConnection returning " + aDriver.driver.getClass().getName());
 665                         return (con);
 666                     }
 667                 } catch (SQLException ex) {
 668                     if (reason == null) {
 669                         reason = ex;
 670                     }
 671                 }
 672 
 673             } else {
 674                 println("    skipping: " + aDriver.getClass().getName());
 675             }
 676 
 677         }
 678 
 679         // if we got here nobody could connect.
 680         if (reason != null)    {
 681             println("getConnection failed: " + reason);
 682             throw reason;
 683         }
 684 
 685         println("getConnection: no suitable driver found for "+ url);
 686         throw new SQLException("No suitable driver found for "+ url, "08001");
 687     }
 688 
 689 
 690 }
 691 
 692 /*
 693  * Wrapper class for registered Drivers in order to not expose Driver.equals()
 694  * to avoid the capture of the Driver it being compared to as it might not
 695  * normally have access.
 696  */
 697 class DriverInfo {
 698 
 699     final Driver driver;
 700     DriverAction da;
 701     DriverInfo(Driver driver, DriverAction action) {
 702         this.driver = driver;
 703         da = action;
 704     }
 705 
 706     @Override
 707     public boolean equals(Object other) {
 708         return (other instanceof DriverInfo)
 709                 && this.driver == ((DriverInfo) other).driver;
 710     }
 711 
 712     @Override
 713     public int hashCode() {
 714         return driver.hashCode();
 715     }
 716 
 717     @Override
 718     public String toString() {
 719         return ("driver[className="  + driver + "]");
 720     }
 721 
 722     DriverAction action() {
 723         return da;
 724     }
 725 }