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