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