8010416: Add a way for java.sql.Driver to be notified when it is deregistered

Reviewed-by: alanb, ulfzibis
This commit is contained in:
Lance Andersen 2013-04-30 14:44:25 -04:00
parent 8394ed673f
commit 785f0d2a94
4 changed files with 158 additions and 22 deletions

View File

@ -44,13 +44,16 @@ import java.util.logging.Logger;
*
* <P>When a Driver class is loaded, it should create an instance of
* itself and register it with the DriverManager. This means that a
* user can load and register a driver by calling
* <pre>
* <code>Class.forName("foo.bah.Driver")</code>
* </pre>
*
* user can load and register a driver by calling:
* <p>
* {@code Class.forName("foo.bah.Driver")}
* <p>
* A JDBC driver may create a {@linkplain DriverAction} implementation in order
* to receive notifications when {@linkplain DriverManager#deregisterDriver} has
* been called.
* @see DriverManager
* @see Connection
* @see DriverAction
*/
public interface Driver {

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 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;
/**
* An interface that must be implemented when a {@linkplain Driver} wants to be
* notified by {@code DriverManager}.
*<P>
* A {@code DriverAction} implementation is not intended to be used
* directly by applications. A JDBC Driver may choose
* to create its {@code DriverAction} implementation in a private class
* to avoid it being called directly.
* <o>
* The JDBC driver's static initialization block must call
* {@linkplain DriverManager#registerDriver(java.sql.Driver, java.sql.DriverAction) } in order
* to inform {@code DriverManager} which {@code DriverAction} implementation to
* call when the JDBC driver is de-registered.
* @since 1.8
*/
public interface DriverAction {
/**
* Method called by
* {@linkplain DriverManager#deregisterDriver(Driver) }
* to notify the JDBC driver that it was de-registered.
* <p>
* The {@code deregister} method is intended only to be used by JDBC Drivers
* and not by applications. JDBC drivers are recommended to not implement
* {@code DriverAction} in a public class. If there are active
* connections to the database at the time that the {@code deregister}
* method is called, it is implementation specific as to whether the
* connections are closed or allowed to continue. Once this method is
* called, it is implementation specific as to whether the driver may
* limit the ability to create new connections to the database, invoke
* other {@code Driver} methods or throw a {@code SQLException}.
* Consult your JDBC driver's documentation for additional information
* on its behavior.
* @see DriverManager#registerDriver(java.sql.Driver, java.sql.DriverAction)
* @see DriverManager#deregisterDriver(Driver)
* @since 1.8
*/
void deregister();
}

View File

@ -110,6 +110,14 @@ public class DriverManager {
final static SQLPermission SET_LOG_PERMISSION =
new SQLPermission("setLog");
/**
* The {@code SQLPermission} constant that allows the
* un-register a registered JDBC driver.
* @since 1.8
*/
final static SQLPermission DEREGISTER_DRIVER_PERMISSION =
new SQLPermission("deregisterDriver");
//--------------------------JDBC 2.0-----------------------------
/**
@ -309,21 +317,42 @@ public class DriverManager {
/**
* Registers the given driver with the <code>DriverManager</code>.
* Registers the given driver with the {@code DriverManager}.
* A newly-loaded driver class should call
* the method <code>registerDriver</code> to make itself
* known to the <code>DriverManager</code>.
* the method {@code registerDriver} to make itself
* known to the {@code DriverManager}. If the driver had previously been
* registered, no action is taken.
*
* @param driver the new JDBC Driver that is to be registered with the
* <code>DriverManager</code>
* {@code DriverManager}
* @exception SQLException if a database access error occurs
*/
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
registerDriver(driver, null);
}
/**
* Registers the given driver with the {@code DriverManager}.
* A newly-loaded driver class should call
* the method {@code registerDriver} to make itself
* known to the {@code DriverManager}. If the driver had previously been
* registered, no action is taken.
*
* @param driver the new JDBC Driver that is to be registered with the
* {@code DriverManager}
* @param da the {@code DriverAction} implementation to be used when
* {@code DriverManager#deregisterDriver} is called
* @exception SQLException if a database access error occurs
*/
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
/* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
@ -334,11 +363,29 @@ public class DriverManager {
}
/**
* Drops a driver from the <code>DriverManager</code>'s list.
* Applets can only deregister drivers from their own classloaders.
* Removes the specified driver from the {@code DriverManager}'s list of
* registered drivers.
* <p>
* If a {@code null} value is specified for the driver to be removed, then no
* action is taken.
* <p>
* If a security manager exists and its {@code checkPermission} denies
* permission, then a {@code SecurityException} will be thrown.
* <p>
* If the specified driver is not found in the list of registered drivers,
* then no action is taken. If the driver was found, it will be removed
* from the list of registered drivers.
* <p>
* If a {@code DriverAction} instance was specified when the JDBC driver was
* registered, its deregister method will be called
* prior to the driver being removed from the list of registered drivers.
*
* @param driver the JDBC Driver to drop
* @param driver the JDBC Driver to remove
* @exception SQLException if a database access error occurs
* @throws SecurityException if a security manager exists and its
* {@code checkPermission} method denies permission to deregister a driver.
*
* @see SecurityManager#checkPermission
*/
@CallerSensitive
public static synchronized void deregisterDriver(Driver driver)
@ -347,11 +394,22 @@ public class DriverManager {
return;
}
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
sec.checkPermission(DEREGISTER_DRIVER_PERMISSION);
}
println("DriverManager.deregisterDriver: " + driver);
DriverInfo aDriver = new DriverInfo(driver);
DriverInfo aDriver = new DriverInfo(driver, null);
if(registeredDrivers.contains(aDriver)) {
if (isDriverAllowed(driver, Reflection.getCallerClass())) {
DriverInfo di = registeredDrivers.get(registeredDrivers.indexOf(aDriver));
// If a DriverAction was specified, Call it to notify the
// driver that it has been deregistered
if(di.action() != null) {
di.action().deregister();
}
registeredDrivers.remove(aDriver);
} else {
// If the caller does not have permission to load the driver then
@ -639,8 +697,10 @@ public class DriverManager {
class DriverInfo {
final Driver driver;
DriverInfo(Driver driver) {
DriverAction da;
DriverInfo(Driver driver, DriverAction action) {
this.driver = driver;
da = action;
}
@Override
@ -658,4 +718,8 @@ class DriverInfo {
public String toString() {
return ("driver[className=" + driver + "]");
}
DriverAction action() {
return da;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 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
@ -30,8 +30,9 @@ import java.security.*;
/**
* The permission for which the <code>SecurityManager</code> will check
* when code that is running in an applet, or an application with a
* when code that is running an application with a
* <code>SecurityManager</code> enabled, calls the
* {@code DriverManager.deregisterDriver} method,
* <code>DriverManager.setLogWriter</code> method,
* <code>DriverManager.setLogStream</code> (deprecated) method,
* {@code SyncFactory.setJNDIContext} method,
@ -95,14 +96,16 @@ import java.security.*;
* <code>Connection</code> or
* objects created from the <code>Connection</code>
* will wait for the database to reply to any one request.</td>
* <tr>
* <td>deregisterDriver</td>
* <td>Allows the invocation of the {@code DriverManager}
* method {@code deregisterDriver}</td>
* <td>Permits an application to remove a JDBC driver from the list of
* registered Drivers and release its resources.</td>
* </tr>
* </tr>
* </table>
*<p>
* The person running an applet decides what permissions to allow
* and will run the <code>Policy Tool</code> to create an
* <code>SQLPermission</code> in a policy file. A programmer does
* not use a constructor directly to create an instance of <code>SQLPermission</code>
* but rather uses a tool.
* @since 1.3
* @see java.security.BasicPermission
* @see java.security.Permission