Home | History | Annotate | Download | only in sql
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package java.sql;
     19 
     20 import dalvik.system.VMStack;
     21 import java.io.PrintStream;
     22 import java.io.PrintWriter;
     23 import java.util.ArrayList;
     24 import java.util.Collections;
     25 import java.util.Enumeration;
     26 import java.util.Iterator;
     27 import java.util.List;
     28 import java.util.Properties;
     29 
     30 /**
     31  * Provides facilities for managing JDBC drivers.
     32  * <p>
     33  * The {@code DriverManager} class loads JDBC drivers during its initialization,
     34  * from the list of drivers referenced by the system property {@code
     35  * "jdbc.drivers"}.
     36  */
     37 public class DriverManager {
     38 
     39     /*
     40      * Facilities for logging. The Print Stream is deprecated but is maintained
     41      * here for compatibility.
     42      */
     43     private static PrintStream thePrintStream;
     44 
     45     private static PrintWriter thePrintWriter;
     46 
     47     // Login timeout value - by default set to 0 -> "wait forever"
     48     private static int loginTimeout = 0;
     49 
     50     /*
     51      * Set to hold Registered Drivers - initial capacity 10 drivers (will expand
     52      * automatically if necessary.
     53      */
     54     private static final List<Driver> theDrivers = new ArrayList<Driver>(10);
     55 
     56     // Permission for setting log
     57     private static final SQLPermission logPermission = new SQLPermission("setLog");
     58 
     59     /*
     60      * Load drivers on initialization
     61      */
     62     static {
     63         loadInitialDrivers();
     64     }
     65 
     66     /*
     67      * Loads the set of JDBC drivers defined by the Property "jdbc.drivers" if
     68      * it is defined.
     69      */
     70     private static void loadInitialDrivers() {
     71         String theDriverList = System.getProperty("jdbc.drivers", null);
     72         if (theDriverList == null) {
     73             return;
     74         }
     75 
     76         /*
     77          * Get the names of the drivers as an array of Strings from the system
     78          * property by splitting the property at the separator character ':'
     79          */
     80         String[] theDriverNames = theDriverList.split(":");
     81 
     82         for (String element : theDriverNames) {
     83             try {
     84                 // Load the driver class
     85                 Class
     86                         .forName(element, true, ClassLoader
     87                                 .getSystemClassLoader());
     88             } catch (Throwable t) {
     89                 // Ignored
     90             }
     91         }
     92     }
     93 
     94     /*
     95      * A private constructor to prevent allocation
     96      */
     97     private DriverManager() {
     98     }
     99 
    100     /**
    101      * Removes a driver from the {@code DriverManager}'s registered driver list.
    102      * This will only succeed when the caller's class loader loaded the driver
    103      * that is to be removed. If the driver was loaded by a different class
    104      * loader, the removal of the driver fails silently.
    105      * <p>
    106      * If the removal succeeds, the {@code DriverManager} will not use this
    107      * driver in the future when asked to get a {@code Connection}.
    108      *
    109      * @param driver
    110      *            the JDBC driver to remove.
    111      * @throws SQLException
    112      *             if there is a problem interfering with accessing the
    113      *             database.
    114      */
    115     public static void deregisterDriver(Driver driver) throws SQLException {
    116         if (driver == null) {
    117             return;
    118         }
    119         ClassLoader callerClassLoader = VMStack.getCallingClassLoader();
    120         if (!DriverManager.isClassFromClassLoader(driver, callerClassLoader)) {
    121             throw new SecurityException("calling class not authorized to deregister JDBC driver");
    122         }
    123         synchronized (theDrivers) {
    124             theDrivers.remove(driver);
    125         }
    126     }
    127 
    128     /**
    129      * Attempts to establish a connection to the given database URL.
    130      *
    131      * @param url
    132      *            a URL string representing the database target to connect with.
    133      * @return a {@code Connection} to the database identified by the URL.
    134      *         {@code null} if no connection can be established.
    135      * @throws SQLException
    136      *             if there is an error while attempting to connect to the
    137      *             database identified by the URL.
    138      */
    139     public static Connection getConnection(String url) throws SQLException {
    140         return getConnection(url, new Properties());
    141     }
    142 
    143     /**
    144      * Attempts to establish a connection to the given database URL.
    145      *
    146      * @param url
    147      *            a URL string representing the database target to connect with
    148      * @param info
    149      *            a set of properties to use as arguments to set up the
    150      *            connection. Properties are arbitrary string/value pairs.
    151      *            Normally, at least the properties {@code "user"} and {@code
    152      *            "password"} should be passed, with appropriate settings for
    153      *            the user ID and its corresponding password to get access to
    154      *            the corresponding database.
    155      * @return a {@code Connection} to the database identified by the URL.
    156      *         {@code null} if no connection can be established.
    157      * @throws SQLException
    158      *             if there is an error while attempting to connect to the
    159      *             database identified by the URL.
    160      */
    161     public static Connection getConnection(String url, Properties info) throws SQLException {
    162         // 08 - connection exception
    163         // 001 - SQL-client unable to establish SQL-connection
    164         String sqlState = "08001";
    165         if (url == null) {
    166             throw new SQLException("The url cannot be null", sqlState);
    167         }
    168         synchronized (theDrivers) {
    169             /*
    170              * Loop over the drivers in the DriverSet checking to see if one can
    171              * open a connection to the supplied URL - return the first
    172              * connection which is returned
    173              */
    174             for (Driver theDriver : theDrivers) {
    175                 Connection theConnection = theDriver.connect(url, info);
    176                 if (theConnection != null) {
    177                     return theConnection;
    178                 }
    179             }
    180         }
    181         // If we get here, none of the drivers are able to resolve the URL
    182         throw new SQLException("No suitable driver", sqlState);
    183     }
    184 
    185     /**
    186      * Attempts to establish a connection to the given database URL.
    187      *
    188      * @param url
    189      *            a URL string representing the database target to connect with.
    190      * @param user
    191      *            a user ID used to login to the database.
    192      * @param password
    193      *            a password for the user ID to login to the database.
    194      * @return a {@code Connection} to the database identified by the URL.
    195      *         {@code null} if no connection can be established.
    196      * @throws SQLException
    197      *             if there is an error while attempting to connect to the
    198      *             database identified by the URL.
    199      */
    200     public static Connection getConnection(String url, String user, String password)
    201             throws SQLException {
    202         Properties theProperties = new Properties();
    203         if (user != null) {
    204             theProperties.setProperty("user", user);
    205         }
    206         if (password != null) {
    207             theProperties.setProperty("password", password);
    208         }
    209         return getConnection(url, theProperties);
    210     }
    211 
    212     /**
    213      * Tries to find a driver that can interpret the supplied URL.
    214      *
    215      * @param url
    216      *            the URL of a database.
    217      * @return a {@code Driver} that matches the provided URL. {@code null} if
    218      *         no {@code Driver} understands the URL
    219      * @throws SQLException
    220      *             if there is any kind of problem accessing the database.
    221      */
    222     public static Driver getDriver(String url) throws SQLException {
    223         ClassLoader callerClassLoader = VMStack.getCallingClassLoader();
    224         synchronized (theDrivers) {
    225             /*
    226              * Loop over the drivers in the DriverSet checking to see if one
    227              * does understand the supplied URL - return the first driver which
    228              * does understand the URL
    229              */
    230             for (Driver driver : theDrivers) {
    231                 if (driver.acceptsURL(url) &&
    232                         DriverManager.isClassFromClassLoader(driver, callerClassLoader)) {
    233                     return driver;
    234                 }
    235             }
    236         }
    237         // If no drivers understand the URL, throw an SQLException
    238         // SQLState: 08 - connection exception
    239         // 001 - SQL-client unable to establish SQL-connection
    240         throw new SQLException("No suitable driver", "08001");
    241     }
    242 
    243     /**
    244      * Returns an {@code Enumeration} that contains all of the loaded JDBC
    245      * drivers that the current caller can access.
    246      *
    247      * @return An {@code Enumeration} containing all the currently loaded JDBC
    248      *         {@code Drivers}.
    249      */
    250     public static Enumeration<Driver> getDrivers() {
    251         /*
    252          * Synchronize to avoid clashes with additions and removals of drivers
    253          * in the DriverSet
    254          */
    255         ClassLoader callerClassLoader = VMStack.getCallingClassLoader();
    256         synchronized (theDrivers) {
    257             ArrayList<Driver> result = new ArrayList<Driver>();
    258             for (Driver driver : theDrivers) {
    259                 if (DriverManager.isClassFromClassLoader(driver, callerClassLoader)) {
    260                     result.add(driver);
    261                 }
    262             }
    263             return Collections.enumeration(result);
    264         }
    265     }
    266 
    267     /**
    268      * Returns the login timeout when connecting to a database in seconds.
    269      *
    270      * @return the login timeout in seconds.
    271      */
    272     public static int getLoginTimeout() {
    273         return loginTimeout;
    274     }
    275 
    276     /**
    277      * Gets the log {@code PrintStream} used by the {@code DriverManager} and
    278      * all the JDBC Drivers.
    279      *
    280      * @deprecated use {@link #getLogWriter()} instead.
    281      * @return the {@code PrintStream} used for logging activities.
    282      */
    283     @Deprecated
    284     public static PrintStream getLogStream() {
    285         return thePrintStream;
    286     }
    287 
    288     /**
    289      * Retrieves the log writer.
    290      *
    291      * @return A {@code PrintWriter} object used as the log writer. {@code null}
    292      *         if no log writer is set.
    293      */
    294     public static PrintWriter getLogWriter() {
    295         return thePrintWriter;
    296     }
    297 
    298     /**
    299      * Prints a message to the current JDBC log stream. This is either the
    300      * {@code PrintWriter} or (deprecated) the {@code PrintStream}, if set.
    301      *
    302      * @param message
    303      *            the message to print to the JDBC log stream.
    304      */
    305     public static void println(String message) {
    306         if (thePrintWriter != null) {
    307             thePrintWriter.println(message);
    308             thePrintWriter.flush();
    309         } else if (thePrintStream != null) {
    310             thePrintStream.println(message);
    311             thePrintStream.flush();
    312         }
    313         /*
    314          * If neither the PrintWriter not the PrintStream are set, then silently
    315          * do nothing the message is not recorded and no exception is generated.
    316          */
    317     }
    318 
    319     /**
    320      * Registers a given JDBC driver with the {@code DriverManager}.
    321      * <p>
    322      * A newly loaded JDBC driver class should register itself with the
    323      * {@code DriverManager} by calling this method.
    324      *
    325      * @param driver
    326      *            the {@code Driver} to register with the {@code DriverManager}.
    327      * @throws SQLException
    328      *             if a database access error occurs.
    329      */
    330     public static void registerDriver(Driver driver) throws SQLException {
    331         if (driver == null) {
    332             throw new NullPointerException("driver == null");
    333         }
    334         synchronized (theDrivers) {
    335             theDrivers.add(driver);
    336         }
    337     }
    338 
    339     /**
    340      * Sets the login timeout when connecting to a database in seconds.
    341      *
    342      * @param seconds
    343      *            seconds until timeout. 0 indicates wait forever.
    344      */
    345     public static void setLoginTimeout(int seconds) {
    346         loginTimeout = seconds;
    347     }
    348 
    349     /**
    350      * Sets the print stream to use for logging data from the {@code
    351      * DriverManager} and the JDBC drivers.
    352      *
    353      * @deprecated Use {@link #setLogWriter} instead.
    354      * @param out
    355      *            the {@code PrintStream} to use for logging.
    356      */
    357     @Deprecated
    358     public static void setLogStream(PrintStream out) {
    359         thePrintStream = out;
    360     }
    361 
    362     /**
    363      * Sets the {@code PrintWriter} that is used by all loaded drivers, and also
    364      * the {@code DriverManager}.
    365      *
    366      * @param out
    367      *            the {@code PrintWriter} to be used.
    368      */
    369     public static void setLogWriter(PrintWriter out) {
    370         thePrintWriter = out;
    371     }
    372 
    373     /**
    374      * Determines whether the supplied object was loaded by the given {@code ClassLoader}.
    375      *
    376      * @param theObject
    377      *            the object to check.
    378      * @param theClassLoader
    379      *            the {@code ClassLoader}.
    380      * @return {@code true} if the Object does belong to the {@code ClassLoader}
    381      *         , {@code false} otherwise
    382      */
    383     private static boolean isClassFromClassLoader(Object theObject,
    384             ClassLoader theClassLoader) {
    385 
    386         if ((theObject == null) || (theClassLoader == null)) {
    387             return false;
    388         }
    389 
    390         Class<?> objectClass = theObject.getClass();
    391 
    392         try {
    393             Class<?> checkClass = Class.forName(objectClass.getName(), true,
    394                     theClassLoader);
    395             if (checkClass == objectClass) {
    396                 return true;
    397             }
    398         } catch (Throwable t) {
    399             // Empty
    400         }
    401         return false;
    402     }
    403 }
    404