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