/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.sql; import dalvik.system.VMStack; import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Properties; /** * Provides facilities for managing JDBC drivers. * <p> * The {@code DriverManager} class loads JDBC drivers during its initialization, * from the list of drivers referenced by the system property {@code * "jdbc.drivers"}. */ public class DriverManager { /* * Facilities for logging. The Print Stream is deprecated but is maintained * here for compatibility. */ private static PrintStream thePrintStream; private static PrintWriter thePrintWriter; // Login timeout value - by default set to 0 -> "wait forever" private static int loginTimeout = 0; /* * Set to hold Registered Drivers - initial capacity 10 drivers (will expand * automatically if necessary. */ private static final List<Driver> theDrivers = new ArrayList<Driver>(10); // Permission for setting log private static final SQLPermission logPermission = new SQLPermission("setLog"); /* * Load drivers on initialization */ static { loadInitialDrivers(); } /* * Loads the set of JDBC drivers defined by the Property "jdbc.drivers" if * it is defined. */ private static void loadInitialDrivers() { String theDriverList = System.getProperty("jdbc.drivers", null); if (theDriverList == null) { return; } /* * Get the names of the drivers as an array of Strings from the system * property by splitting the property at the separator character ':' */ String[] theDriverNames = theDriverList.split(":"); for (String element : theDriverNames) { try { // Load the driver class Class .forName(element, true, ClassLoader .getSystemClassLoader()); } catch (Throwable t) { // Ignored } } } /* * A private constructor to prevent allocation */ private DriverManager() { } /** * Removes a driver from the {@code DriverManager}'s registered driver list. * This will only succeed when the caller's class loader loaded the driver * that is to be removed. If the driver was loaded by a different class * loader, the removal of the driver fails silently. * <p> * If the removal succeeds, the {@code DriverManager} will not use this * driver in the future when asked to get a {@code Connection}. * * @param driver * the JDBC driver to remove. * @throws SQLException * if there is a problem interfering with accessing the * database. */ public static void deregisterDriver(Driver driver) throws SQLException { if (driver == null) { return; } ClassLoader callerClassLoader = VMStack.getCallingClassLoader(); if (!DriverManager.isClassFromClassLoader(driver, callerClassLoader)) { throw new SecurityException("calling class not authorized to deregister JDBC driver"); } synchronized (theDrivers) { theDrivers.remove(driver); } } /** * Attempts to establish a connection to the given database URL. * * @param url * a URL string representing the database target to connect with. * @return a {@code Connection} to the database identified by the URL. * {@code null} if no connection can be established. * @throws SQLException * if there is an error while attempting to connect to the * database identified by the URL. */ public static Connection getConnection(String url) throws SQLException { return getConnection(url, new Properties()); } /** * Attempts to establish a connection to the given database URL. * * @param url * a URL string representing the database target to connect with * @param info * a set of properties to use as arguments to set up the * connection. Properties are arbitrary string/value pairs. * Normally, at least the properties {@code "user"} and {@code * "password"} should be passed, with appropriate settings for * the user ID and its corresponding password to get access to * the corresponding database. * @return a {@code Connection} to the database identified by the URL. * {@code null} if no connection can be established. * @throws SQLException * if there is an error while attempting to connect to the * database identified by the URL. */ public static Connection getConnection(String url, Properties info) throws SQLException { // 08 - connection exception // 001 - SQL-client unable to establish SQL-connection String sqlState = "08001"; if (url == null) { throw new SQLException("The url cannot be null", sqlState); } synchronized (theDrivers) { /* * Loop over the drivers in the DriverSet checking to see if one can * open a connection to the supplied URL - return the first * connection which is returned */ for (Driver theDriver : theDrivers) { Connection theConnection = theDriver.connect(url, info); if (theConnection != null) { return theConnection; } } } // If we get here, none of the drivers are able to resolve the URL throw new SQLException("No suitable driver", sqlState); } /** * Attempts to establish a connection to the given database URL. * * @param url * a URL string representing the database target to connect with. * @param user * a user ID used to login to the database. * @param password * a password for the user ID to login to the database. * @return a {@code Connection} to the database identified by the URL. * {@code null} if no connection can be established. * @throws SQLException * if there is an error while attempting to connect to the * database identified by the URL. */ public static Connection getConnection(String url, String user, String password) throws SQLException { Properties theProperties = new Properties(); if (user != null) { theProperties.setProperty("user", user); } if (password != null) { theProperties.setProperty("password", password); } return getConnection(url, theProperties); } /** * Tries to find a driver that can interpret the supplied URL. * * @param url * the URL of a database. * @return a {@code Driver} that matches the provided URL. {@code null} if * no {@code Driver} understands the URL * @throws SQLException * if there is any kind of problem accessing the database. */ public static Driver getDriver(String url) throws SQLException { ClassLoader callerClassLoader = VMStack.getCallingClassLoader(); synchronized (theDrivers) { /* * Loop over the drivers in the DriverSet checking to see if one * does understand the supplied URL - return the first driver which * does understand the URL */ for (Driver driver : theDrivers) { if (driver.acceptsURL(url) && DriverManager.isClassFromClassLoader(driver, callerClassLoader)) { return driver; } } } // If no drivers understand the URL, throw an SQLException // SQLState: 08 - connection exception // 001 - SQL-client unable to establish SQL-connection throw new SQLException("No suitable driver", "08001"); } /** * Returns an {@code Enumeration} that contains all of the loaded JDBC * drivers that the current caller can access. * * @return An {@code Enumeration} containing all the currently loaded JDBC * {@code Drivers}. */ public static Enumeration<Driver> getDrivers() { /* * Synchronize to avoid clashes with additions and removals of drivers * in the DriverSet */ ClassLoader callerClassLoader = VMStack.getCallingClassLoader(); synchronized (theDrivers) { ArrayList<Driver> result = new ArrayList<Driver>(); for (Driver driver : theDrivers) { if (DriverManager.isClassFromClassLoader(driver, callerClassLoader)) { result.add(driver); } } return Collections.enumeration(result); } } /** * Returns the login timeout when connecting to a database in seconds. * * @return the login timeout in seconds. */ public static int getLoginTimeout() { return loginTimeout; } /** * Gets the log {@code PrintStream} used by the {@code DriverManager} and * all the JDBC Drivers. * * @deprecated use {@link #getLogWriter()} instead. * @return the {@code PrintStream} used for logging activities. */ @Deprecated public static PrintStream getLogStream() { return thePrintStream; } /** * Retrieves the log writer. * * @return A {@code PrintWriter} object used as the log writer. {@code null} * if no log writer is set. */ public static PrintWriter getLogWriter() { return thePrintWriter; } /** * Prints a message to the current JDBC log stream. This is either the * {@code PrintWriter} or (deprecated) the {@code PrintStream}, if set. * * @param message * the message to print to the JDBC log stream. */ public static void println(String message) { if (thePrintWriter != null) { thePrintWriter.println(message); thePrintWriter.flush(); } else if (thePrintStream != null) { thePrintStream.println(message); thePrintStream.flush(); } /* * If neither the PrintWriter not the PrintStream are set, then silently * do nothing the message is not recorded and no exception is generated. */ } /** * Registers a given JDBC driver with the {@code DriverManager}. * <p> * A newly loaded JDBC driver class should register itself with the * {@code DriverManager} by calling this method. * * @param driver * the {@code Driver} to register with the {@code DriverManager}. * @throws SQLException * if a database access error occurs. */ public static void registerDriver(Driver driver) throws SQLException { if (driver == null) { throw new NullPointerException(); } synchronized (theDrivers) { theDrivers.add(driver); } } /** * Sets the login timeout when connecting to a database in seconds. * * @param seconds * seconds until timeout. 0 indicates wait forever. */ public static void setLoginTimeout(int seconds) { loginTimeout = seconds; } /** * Sets the print stream to use for logging data from the {@code * DriverManager} and the JDBC drivers. * * @deprecated Use {@link #setLogWriter} instead. * @param out * the {@code PrintStream} to use for logging. */ @Deprecated public static void setLogStream(PrintStream out) { thePrintStream = out; } /** * Sets the {@code PrintWriter} that is used by all loaded drivers, and also * the {@code DriverManager}. * * @param out * the {@code PrintWriter} to be used. */ public static void setLogWriter(PrintWriter out) { thePrintWriter = out; } /** * Determines whether the supplied object was loaded by the given {@code ClassLoader}. * * @param theObject * the object to check. * @param theClassLoader * the {@code ClassLoader}. * @return {@code true} if the Object does belong to the {@code ClassLoader} * , {@code false} otherwise */ private static boolean isClassFromClassLoader(Object theObject, ClassLoader theClassLoader) { if ((theObject == null) || (theClassLoader == null)) { return false; } Class<?> objectClass = theObject.getClass(); try { Class<?> checkClass = Class.forName(objectClass.getName(), true, theClassLoader); if (checkClass == objectClass) { return true; } } catch (Throwable t) { // Empty } return false; } }