Fix the JDBC driver clean-up. This was broken with the latest 1.4.x, 1.5.x and 1...
authormarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Mon, 29 Jun 2009 17:20:24 +0000 (17:20 +0000)
committermarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Mon, 29 Jun 2009 17:20:24 +0000 (17:20 +0000)
It's not pretty, so there are plenty of comments explaining what is going on.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@789389 13f79535-47bb-0310-9956-ffa450edef68

java/org/apache/catalina/loader/JdbcLeakPrevention.java [new file with mode: 0644]
java/org/apache/catalina/loader/WebappClassLoader.java

diff --git a/java/org/apache/catalina/loader/JdbcLeakPrevention.java b/java/org/apache/catalina/loader/JdbcLeakPrevention.java
new file mode 100644 (file)
index 0000000..1ec4dc2
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.catalina.loader;
+
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Enumeration;
+
+import org.apache.catalina.util.StringManager;
+
+/**
+ * This class is loaded by the  {@link WebappClassLoader} to enable it to
+ * deregister JDBC drivers forgotten by the web application. There are some
+ * classloading hacks involved - see {@link WebappClassLoader#clearReferences()}
+ * for details - but the short version is do not just create a new instance of
+ * this class with the new keyword.
+ */
+public class JdbcLeakPrevention {
+
+    /**
+     * The logger for this class.
+     */
+    protected static org.apache.juli.logging.Log log=
+        org.apache.juli.logging.LogFactory.getLog( JdbcLeakPrevention.class );
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    public void clearJdbcDriverRegistrations() {
+        // Unregister any JDBC drivers loaded by the class loader that loaded
+        // this class - ie the webapp class loader
+        Enumeration<Driver> drivers = DriverManager.getDrivers();
+        while (drivers.hasMoreElements()) {
+            Driver driver = drivers.nextElement();
+            try {
+                DriverManager.deregisterDriver(driver);
+            } catch (SQLException sqle) {
+                log.warn(sm.getString("jdbcLeakPrevention.jdbcRemoveFailed",
+                        driver.toString()), sqle);
+            }
+        }
+        
+    }
+}
index 1a48c75..e201cf3 100644 (file)
@@ -36,9 +36,6 @@ import java.security.Permission;
 import java.security.PermissionCollection;
 import java.security.Policy;
 import java.security.PrivilegedAction;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -1603,15 +1600,52 @@ public class WebappClassLoader
      */
     protected void clearReferences() {
 
-        // Unregister any JDBC drivers loaded by this classloader
-        Enumeration<Driver> drivers = DriverManager.getDrivers();
-        while (drivers.hasMoreElements()) {
-            Driver driver = drivers.nextElement();
-            if (driver.getClass().getClassLoader() == this) {
+        /*
+         * Deregister any JDBC drivers registered by the webapp that the webapp
+         * forgot. This is made unnecessary complex because a) DriverManager
+         * checks the class loader of the calling class (it would be much easier
+         * if it checked the context class loader) b) using reflection would
+         * create a dependency on the DriverManager implementation which can,
+         * and has, changed.
+         * 
+         * We can't just create an instance of JdbcLeakPrevention as it will be
+         * loaded by the common class loader (since it's .class file is in the
+         * $CATALINA_HOME/lib directory). This would fail DriverManager's check
+         * on the class loader of the calling class. So, we load the bytes via
+         * our parent class loader but define the class with this class loader
+         * so the JdbcLeakPrevention looks like a webapp class to the
+         * DriverManager.
+         * 
+         * If only apps cleaned up after themselves...
+         */
+        InputStream is = getResourceAsStream(
+                "org/apache/catalina/loader/JdbcLeakPrevention.class");
+        // Cheat - we know roughly how big the class will be (~1K) but allow
+        // plenty room to grow
+        byte[] classBytes = new byte[4096];
+        int offset = 0;
+        try {
+            int read = is.read(classBytes, offset, 4096-offset);
+            while (read > -1) {
+                offset += read;
+                read = is.read(classBytes, offset, 4096-offset);
+            }
+            Class<?> lpClass =
+                defineClass("org.apache.catalina.loader.JdbcLeakPrevention",
+                    classBytes, 0, offset);
+            Object obj = lpClass.newInstance();
+            obj.getClass().getMethod(
+                    "clearJdbcDriverRegistrations").invoke(obj);
+        } catch (Exception e) {
+            // So many things to go wrong above...
+            log.warn(sm.getString("webappClassLoader.jdbcRemoveFailed"), e);
+        } finally {
+            if (is != null) {
                 try {
-                    DriverManager.deregisterDriver(driver);
-                } catch (SQLException e) {
-                    log.warn("SQL driver deregistration failed", e);
+                    is.close();
+                } catch (IOException ioe) {
+                    log.warn(sm.getString(
+                            "webappClassLoader.jdbcRemoveStreamError"), ioe);
                 }
             }
         }