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