Save This Page
Home » openjdk-7 » java » sql » [javadoc | source]
    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<DriverInfo>();
   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<Driver>();
  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       public boolean equals(Object other) {
  625           return (other instanceof DriverInfo)
  626                   && this.driver == ((DriverInfo) other).driver;
  627       }
  628   
  629       public int hashCode() {
  630           return driver.hashCode();
  631       }
  632   
  633       public String toString() {
  634           return ("driver[className="  + driver + "]");
  635       }
  636   }

Save This Page
Home » openjdk-7 » java » sql » [javadoc | source]