/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.sql;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.CopyOnWriteArrayList;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
The basic service for managing a set of JDBC drivers.
NOTE: The DataSource
interface, new in the JDBC 2.0 API, provides another way to connect to a data source. The use of a DataSource
object is the preferred means of
connecting to a data source.
As part of its initialization, the DriverManager
class will
attempt to load the driver classes referenced in the "jdbc.drivers"
system property. This allows a user to customize the JDBC Drivers
used by their applications. For example in your
~/.hotjava/properties file you might specify:
jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver
The DriverManager
methods getConnection
and
getDrivers
have been enhanced to support the Java Standard Edition
Service Provider mechanism. JDBC 4.0 Drivers must
include the file META-INF/services/java.sql.Driver
. This file contains the name of the JDBC drivers
implementation of java.sql.Driver
. For example, to load the my.sql.Driver
class,
the META-INF/services/java.sql.Driver
file would contain the entry:
my.sql.Driver
Applications no longer need to explicitly load JDBC drivers using Class.forName()
. Existing programs
which currently load JDBC drivers using Class.forName()
will continue to work without
modification.
When the method getConnection
is called,
the DriverManager
will attempt to
locate a suitable driver from amongst those loaded at
initialization and those loaded explicitly using the same classloader
as the current applet or application.
Starting with the Java 2 SDK, Standard Edition, version 1.3, a
logging stream can be set only if the proper
permission has been granted. Normally this will be done with
the tool PolicyTool, which can be used to grant permission
java.sql.SQLPermission "setLog"
.
See Also:
/**
* <P>The basic service for managing a set of JDBC drivers.<br>
* <B>NOTE:</B> The {@link javax.sql.DataSource} interface, new in the
* JDBC 2.0 API, provides another way to connect to a data source.
* The use of a <code>DataSource</code> object is the preferred means of
* connecting to a data source.
*
* <P>As part of its initialization, the <code>DriverManager</code> class will
* attempt to load the driver classes referenced in the "jdbc.drivers"
* system property. This allows a user to customize the JDBC Drivers
* used by their applications. For example in your
* ~/.hotjava/properties file you might specify:
* <pre>
* <CODE>jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver</CODE>
* </pre>
*<P> The <code>DriverManager</code> methods <code>getConnection</code> and
* <code>getDrivers</code> have been enhanced to support the Java Standard Edition
* <a href="../../../technotes/guides/jar/jar.html#Service%20Provider">Service Provider</a> mechanism. JDBC 4.0 Drivers must
* include the file <code>META-INF/services/java.sql.Driver</code>. This file contains the name of the JDBC drivers
* implementation of <code>java.sql.Driver</code>. For example, to load the <code>my.sql.Driver</code> class,
* the <code>META-INF/services/java.sql.Driver</code> file would contain the entry:
* <pre>
* <code>my.sql.Driver</code>
* </pre>
*
* <P>Applications no longer need to explicitly load JDBC drivers using <code>Class.forName()</code>. Existing programs
* which currently load JDBC drivers using <code>Class.forName()</code> will continue to work without
* modification.
*
* <P>When the method <code>getConnection</code> is called,
* the <code>DriverManager</code> will attempt to
* locate a suitable driver from amongst those loaded at
* initialization and those loaded explicitly using the same classloader
* as the current applet or application.
*
* <P>
* Starting with the Java 2 SDK, Standard Edition, version 1.3, a
* logging stream can be set only if the proper
* permission has been granted. Normally this will be done with
* the tool PolicyTool, which can be used to grant <code>permission
* java.sql.SQLPermission "setLog"</code>.
* @see Driver
* @see Connection
*/
public class DriverManager {
// List of registered JDBC drivers
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
private static volatile int loginTimeout = 0;
private static volatile java.io.PrintWriter logWriter = null;
private static volatile java.io.PrintStream logStream = null;
// Used in println() to synchronize logWriter
private final static Object logSync = new Object();
/* Prevent the DriverManager class from being instantiated. */
private DriverManager(){}
Load the initial JDBC drivers by checking the System property jdbc.properties and then use the ServiceLoader
mechanism /**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
The SQLPermission
constant that allows the
setting of the logging stream.
Since: 1.3
/**
* The <code>SQLPermission</code> constant that allows the
* setting of the logging stream.
* @since 1.3
*/
final static SQLPermission SET_LOG_PERMISSION =
new SQLPermission("setLog");
//--------------------------JDBC 2.0-----------------------------
Retrieves the log writer.
The getLogWriter
and setLogWriter
methods should be used instead
of the get/setlogStream
methods, which are deprecated.
See Also: Returns: a java.io.PrintWriter
object Since: 1.2
/**
* Retrieves the log writer.
*
* The <code>getLogWriter</code> and <code>setLogWriter</code>
* methods should be used instead
* of the <code>get/setlogStream</code> methods, which are deprecated.
* @return a <code>java.io.PrintWriter</code> object
* @see #setLogWriter
* @since 1.2
*/
public static java.io.PrintWriter getLogWriter() {
return logWriter;
}
Sets the logging/tracing PrintWriter
object
that is used by the DriverManager
and all drivers.
There is a minor versioning problem created by the introduction
of the method setLogWriter
. The
method setLogWriter
cannot create a PrintStream
object
that will be returned by getLogStream
---the Java platform does
not provide a backward conversion. As a result, a new application
that uses setLogWriter
and also uses a JDBC 1.0 driver that uses
getLogStream
will likely not see debugging information written
by that driver.
Starting with the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
to see that there is an SQLPermission
object before setting
the logging stream. If a SecurityManager
exists and its
checkPermission
method denies setting the log writer, this
method throws a java.lang.SecurityException
.
Params: - out – the new logging/tracing
PrintStream
object;
null
to disable logging and tracing
Throws: - SecurityException –
if a security manager exists and its
checkPermission
method denies
setting the log writer
See Also: Since: 1.2
/**
* Sets the logging/tracing <code>PrintWriter</code> object
* that is used by the <code>DriverManager</code> and all drivers.
* <P>
* There is a minor versioning problem created by the introduction
* of the method <code>setLogWriter</code>. The
* method <code>setLogWriter</code> cannot create a <code>PrintStream</code> object
* that will be returned by <code>getLogStream</code>---the Java platform does
* not provide a backward conversion. As a result, a new application
* that uses <code>setLogWriter</code> and also uses a JDBC 1.0 driver that uses
* <code>getLogStream</code> will likely not see debugging information written
* by that driver.
*<P>
* Starting with the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
* to see that there is an <code>SQLPermission</code> object before setting
* the logging stream. If a <code>SecurityManager</code> exists and its
* <code>checkPermission</code> method denies setting the log writer, this
* method throws a <code>java.lang.SecurityException</code>.
*
* @param out the new logging/tracing <code>PrintStream</code> object;
* <code>null</code> to disable logging and tracing
* @throws SecurityException
* if a security manager exists and its
* <code>checkPermission</code> method denies
* setting the log writer
*
* @see SecurityManager#checkPermission
* @see #getLogWriter
* @since 1.2
*/
public static void setLogWriter(java.io.PrintWriter out) {
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
sec.checkPermission(SET_LOG_PERMISSION);
}
logStream = null;
logWriter = out;
}
//---------------------------------------------------------------
Attempts to establish a connection to the given database URL.
The DriverManager
attempts to select an appropriate driver from
the set of registered JDBC drivers.
Params: - url – a database url of the form
jdbc:subprotocol:subname
- info – a list of arbitrary string tag/value pairs as
connection arguments; normally at least a "user" and
"password" property should be included
Throws: - SQLException – if a database access error occurs
Returns: a Connection to the URL
/**
* Attempts to establish a connection to the given database URL.
* The <code>DriverManager</code> attempts to select an appropriate driver from
* the set of registered JDBC drivers.
*
* @param url a database url of the form
* <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @param info a list of arbitrary string tag/value pairs as
* connection arguments; normally at least a "user" and
* "password" property should be included
* @return a Connection to the URL
* @exception SQLException if a database access error occurs
*/
@CallerSensitive
public static Connection getConnection(String url,
java.util.Properties info) throws SQLException {
return (getConnection(url, info, Reflection.getCallerClass()));
}
Attempts to establish a connection to the given database URL.
The DriverManager
attempts to select an appropriate driver from
the set of registered JDBC drivers.
Params: - url – a database url of the form
jdbc:subprotocol:subname
- user – the database user on whose behalf the connection is being
made
- password – the user's password
Throws: - SQLException – if a database access error occurs
Returns: a connection to the URL
/**
* Attempts to establish a connection to the given database URL.
* The <code>DriverManager</code> attempts to select an appropriate driver from
* the set of registered JDBC drivers.
*
* @param url a database url of the form
* <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @param user the database user on whose behalf the connection is being
* made
* @param password the user's password
* @return a connection to the URL
* @exception SQLException if a database access error occurs
*/
@CallerSensitive
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
Attempts to establish a connection to the given database URL.
The DriverManager
attempts to select an appropriate driver from
the set of registered JDBC drivers.
Params: - url – a database url of the form
jdbc:subprotocol:subname
Throws: - SQLException – if a database access error occurs
Returns: a connection to the URL
/**
* Attempts to establish a connection to the given database URL.
* The <code>DriverManager</code> attempts to select an appropriate driver from
* the set of registered JDBC drivers.
*
* @param url a database url of the form
* <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @return a connection to the URL
* @exception SQLException if a database access error occurs
*/
@CallerSensitive
public static Connection getConnection(String url)
throws SQLException {
java.util.Properties info = new java.util.Properties();
return (getConnection(url, info, Reflection.getCallerClass()));
}
Attempts to locate a driver that understands the given URL.
The DriverManager
attempts to select an appropriate driver from
the set of registered JDBC drivers.
Params: - url – a database URL of the form
jdbc:subprotocol:subname
Throws: - SQLException – if a database access error occurs
Returns: a Driver
object representing a driver
that can connect to the given URL
/**
* Attempts to locate a driver that understands the given URL.
* The <code>DriverManager</code> attempts to select an appropriate driver from
* the set of registered JDBC drivers.
*
* @param url a database URL of the form
* <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @return a <code>Driver</code> object representing a driver
* that can connect to the given URL
* @exception SQLException if a database access error occurs
*/
@CallerSensitive
public static Driver getDriver(String url)
throws SQLException {
println("DriverManager.getDriver(\"" + url + "\")");
Class<?> callerClass = Reflection.getCallerClass();
// Walk through the loaded registeredDrivers attempting to locate someone
// who understands the given URL.
for (DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerClass)) {
try {
if(aDriver.driver.acceptsURL(url)) {
// Success!
println("getDriver returning " + aDriver.driver.getClass().getName());
return (aDriver.driver);
}
} catch(SQLException sqe) {
// Drop through and try the next driver.
}
} else {
println(" skipping: " + aDriver.driver.getClass().getName());
}
}
println("getDriver: no suitable driver");
throw new SQLException("No suitable driver", "08001");
}
Registers the given driver with the DriverManager
.
A newly-loaded driver class should call
the method registerDriver
to make itself
known to the DriverManager
.
Params: - driver – the new JDBC Driver that is to be registered with the
DriverManager
Throws: - SQLException – if a database access error occurs
/**
* Registers the given driver with the <code>DriverManager</code>.
* A newly-loaded driver class should call
* the method <code>registerDriver</code> to make itself
* known to the <code>DriverManager</code>.
*
* @param driver the new JDBC Driver that is to be registered with the
* <code>DriverManager</code>
* @exception SQLException if a database access error occurs
*/
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
/* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
Drops a driver from the DriverManager
's list.
Applets can only deregister drivers from their own classloaders.
Params: - driver – the JDBC Driver to drop
Throws: - SQLException – if a database access error occurs
/**
* Drops a driver from the <code>DriverManager</code>'s list.
* Applets can only deregister drivers from their own classloaders.
*
* @param driver the JDBC Driver to drop
* @exception SQLException if a database access error occurs
*/
@CallerSensitive
public static synchronized void deregisterDriver(Driver driver)
throws SQLException {
if (driver == null) {
return;
}
println("DriverManager.deregisterDriver: " + driver);
DriverInfo aDriver = new DriverInfo(driver);
if(registeredDrivers.contains(aDriver)) {
if (isDriverAllowed(driver, Reflection.getCallerClass())) {
registeredDrivers.remove(aDriver);
} else {
// If the caller does not have permission to load the driver then
// throw a SecurityException.
throw new SecurityException();
}
} else {
println(" couldn't find driver to unload");
}
}
Retrieves an Enumeration with all of the currently loaded JDBC drivers
to which the current caller has access.
Note: The classname of a driver can be found using
d.getClass().getName()
Returns: the list of JDBC Drivers loaded by the caller's class loader
/**
* Retrieves an Enumeration with all of the currently loaded JDBC drivers
* to which the current caller has access.
*
* <P><B>Note:</B> The classname of a driver can be found using
* <CODE>d.getClass().getName()</CODE>
*
* @return the list of JDBC Drivers loaded by the caller's class loader
*/
@CallerSensitive
public static java.util.Enumeration<Driver> getDrivers() {
java.util.Vector<Driver> result = new java.util.Vector<Driver>();
Class<?> callerClass = Reflection.getCallerClass();
// Walk through the loaded registeredDrivers.
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerClass)) {
result.addElement(aDriver.driver);
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
return (result.elements());
}
Sets the maximum time in seconds that a driver will wait
while attempting to connect to a database.
Params: - seconds – the login time limit in seconds; zero means there is no limit
See Also:
/**
* Sets the maximum time in seconds that a driver will wait
* while attempting to connect to a database.
*
* @param seconds the login time limit in seconds; zero means there is no limit
* @see #getLoginTimeout
*/
public static void setLoginTimeout(int seconds) {
loginTimeout = seconds;
}
Gets the maximum time in seconds that a driver can wait
when attempting to log in to a database.
See Also: Returns: the driver login time limit in seconds
/**
* Gets the maximum time in seconds that a driver can wait
* when attempting to log in to a database.
*
* @return the driver login time limit in seconds
* @see #setLoginTimeout
*/
public static int getLoginTimeout() {
return (loginTimeout);
}
Sets the logging/tracing PrintStream that is used
by the DriverManager
and all drivers.
In the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
to see that there is an SQLPermission
object before setting
the logging stream. If a SecurityManager
exists and its
checkPermission
method denies setting the log writer, this
method throws a java.lang.SecurityException
.
Params: - out – the new logging/tracing PrintStream; to disable, set to
null
Throws: - SecurityException – if a security manager exists and its
checkPermission
method denies setting the log stream
See Also: Deprecated:
/**
* Sets the logging/tracing PrintStream that is used
* by the <code>DriverManager</code>
* and all drivers.
*<P>
* In the Java 2 SDK, Standard Edition, version 1.3 release, this method checks
* to see that there is an <code>SQLPermission</code> object before setting
* the logging stream. If a <code>SecurityManager</code> exists and its
* <code>checkPermission</code> method denies setting the log writer, this
* method throws a <code>java.lang.SecurityException</code>.
*
* @param out the new logging/tracing PrintStream; to disable, set to <code>null</code>
* @deprecated
* @throws SecurityException if a security manager exists and its
* <code>checkPermission</code> method denies setting the log stream
*
* @see SecurityManager#checkPermission
* @see #getLogStream
*/
public static void setLogStream(java.io.PrintStream out) {
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
sec.checkPermission(SET_LOG_PERMISSION);
}
logStream = out;
if ( out != null )
logWriter = new java.io.PrintWriter(out);
else
logWriter = null;
}
Retrieves the logging/tracing PrintStream that is used by the DriverManager
and all drivers.
See Also: Returns: the logging/tracing PrintStream; if disabled, is null
Deprecated:
/**
* Retrieves the logging/tracing PrintStream that is used by the <code>DriverManager</code>
* and all drivers.
*
* @return the logging/tracing PrintStream; if disabled, is <code>null</code>
* @deprecated
* @see #setLogStream
*/
public static java.io.PrintStream getLogStream() {
return logStream;
}
Prints a message to the current JDBC log stream.
Params: - message – a log or tracing message
/**
* Prints a message to the current JDBC log stream.
*
* @param message a log or tracing message
*/
public static void println(String message) {
synchronized (logSync) {
if (logWriter != null) {
logWriter.println(message);
// automatic flushing is never enabled, so we must do it ourselves
logWriter.flush();
}
}
}
//------------------------------------------------------------------------
// Indicates whether the class object that would be created if the code calling
// DriverManager is accessible.
private static boolean isDriverAllowed(Driver driver, Class<?> caller) {
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
return isDriverAllowed(driver, callerCL);
}
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
boolean result = false;
if(driver != null) {
Class<?> aClass = null;
try {
aClass = Class.forName(driver.getClass().getName(), true, classLoader);
} catch (Exception ex) {
result = false;
}
result = ( aClass == driver.getClass() ) ? true : false;
}
return result;
}
private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
// Worker method called by the public getConnection() methods.
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized (DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
}
/*
* Wrapper class for registered Drivers in order to not expose Driver.equals()
* to avoid the capture of the Driver it being compared to as it might not
* normally have access.
*/
class DriverInfo {
final Driver driver;
DriverInfo(Driver driver) {
this.driver = driver;
}
public boolean equals(Object other) {
return (other instanceof DriverInfo)
&& this.driver == ((DriverInfo) other).driver;
}
public int hashCode() {
return driver.hashCode();
}
public String toString() {
return ("driver[className=" + driver + "]");
}
}