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