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