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