8010416: Add a way for java.sql.Driver to be notified when it is deregistered
Reviewed-by: alanb, ulfzibis
--- a/jdk/src/share/classes/java/sql/Driver.java Mon Apr 29 23:07:22 2013 +0100
+++ b/jdk/src/share/classes/java/sql/Driver.java Tue Apr 30 14:44:25 2013 -0400
@@ -44,13 +44,16 @@
*
* <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 {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/sql/DriverAction.java Tue Apr 30 14:44:25 2013 -0400
@@ -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();
+
+}
--- a/jdk/src/share/classes/java/sql/DriverManager.java Mon Apr 29 23:07:22 2013 +0100
+++ b/jdk/src/share/classes/java/sql/DriverManager.java Tue Apr 30 14:44:25 2013 -0400
@@ -110,6 +110,14 @@
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 @@
/**
- * 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 @@
}
/**
- * 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 @@
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 @@
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 @@
public String toString() {
return ("driver[className=" + driver + "]");
}
+
+ DriverAction action() {
+ return da;
+ }
}
--- a/jdk/src/share/classes/java/sql/SQLPermission.java Mon Apr 29 23:07:22 2013 +0100
+++ b/jdk/src/share/classes/java/sql/SQLPermission.java Tue Apr 30 14:44:25 2013 -0400
@@ -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 @@
/**
* 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 @@
* <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