blob: a06425b001d4e8e604f5888544d128fcae4e8c88 [file] [log] [blame]
/*
* 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 java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Enumeration;
import java.util.Iterator;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Vector;
import java.security.AccessController;
import org.apache.harmony.luni.util.PriviAction;
import org.apache.harmony.sql.internal.nls.Messages;
import org.apache.harmony.kernel.vm.VM;
/**
* 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"); //$NON-NLS-1$
/*
* 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 = AccessController
.doPrivileged(new PriviAction<String>("jdbc.drivers", null)); //$NON-NLS-1$
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(":"); //$NON-NLS-1$
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() {
super();
}
/**
* 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 = VM.callerClassLoader();
if (!DriverManager.isClassFromClassLoader(driver, callerClassLoader)) {
// sql.1=DriverManager: calling class not authorized to deregister
// JDBC driver
throw new SecurityException(Messages.getString("sql.1")); //$NON-NLS-1$
} // end if
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"; //$NON-NLS-1$
if (url == null) {
// sql.5=The url cannot be null
throw new SQLException(Messages.getString("sql.5"), sqlState); //$NON-NLS-1$
}
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
// sql.6=No suitable driver
throw new SQLException(Messages.getString("sql.6"), sqlState); //$NON-NLS-1$
}
/**
* 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 (null != user) {
theProperties.setProperty("user", user); //$NON-NLS-1$
}
if (null != password) {
theProperties.setProperty("password", password); //$NON-NLS-1$
}
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 = VM.callerClassLoader();
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
*/
Iterator<Driver> theIterator = theDrivers.iterator();
while (theIterator.hasNext()) {
Driver theDriver = theIterator.next();
if (theDriver.acceptsURL(url)
&& DriverManager.isClassFromClassLoader(theDriver,
callerClassLoader)) {
return theDriver;
}
}
}
// If no drivers understand the URL, throw an SQLException
// sql.6=No suitable driver
// SQLState: 08 - connection exception
// 001 - SQL-client unable to establish SQL-connection
throw new SQLException(Messages.getString("sql.6"), "08001"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* 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() {
ClassLoader callerClassLoader = VM.callerClassLoader();
/*
* Synchronize to avoid clashes with additions and removals of drivers
* in the DriverSet
*/
synchronized (theDrivers) {
/*
* Create the Enumeration by building a Vector from the elements of
* the DriverSet
*/
Vector<Driver> theVector = new Vector<Driver>();
Iterator<Driver> theIterator = theDrivers.iterator();
while (theIterator.hasNext()) {
Driver theDriver = theIterator.next();
if (DriverManager.isClassFromClassLoader(theDriver,
callerClassLoader)) {
theVector.add(theDriver);
}
}
return theVector.elements();
}
}
/**
* 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.
*/
return;
}
/**
* 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;
return;
}
/**
* 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) {
checkLogSecurity();
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) {
checkLogSecurity();
thePrintWriter = out;
}
/*
* Method which checks to see if setting a logging stream is allowed by the
* Security manager
*/
private static void checkLogSecurity() {
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
// Throws a SecurityException if setting the log is not permitted
securityManager.checkPermission(logPermission);
}
}
/**
* 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;
}
}