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