8010416: Add a way for java.sql.Driver to be notified when it is deregistered
authorlancea
Tue, 30 Apr 2013 14:44:25 -0400
changeset 17199 28853592ce2b
parent 17198 32db080a2a70
child 17201 a25f69a6e69f
8010416: Add a way for java.sql.Driver to be notified when it is deregistered Reviewed-by: alanb, ulfzibis
jdk/src/share/classes/java/sql/Driver.java
jdk/src/share/classes/java/sql/DriverAction.java
jdk/src/share/classes/java/sql/DriverManager.java
jdk/src/share/classes/java/sql/SQLPermission.java
--- 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