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