Author: markt
Date: Mon Jun 29 17:20:24 2009
New Revision: 789389

URL: http://svn.apache.org/viewvc?rev=789389&view=rev
Log:
Fix the JDBC driver clean-up. This was broken with the latest 1.4.x, 1.5.x and 
1.6.x JVMs as they checked if the classloader of the caller, which for the 
WebappClassLoader was the common loader.
It's not pretty, so there are plenty of comments explaining what is going on.

Added:
    tomcat/trunk/java/org/apache/catalina/loader/JdbcLeakPrevention.java   
(with props)
Modified:
    tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java

Added: tomcat/trunk/java/org/apache/catalina/loader/JdbcLeakPrevention.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/JdbcLeakPrevention.java?rev=789389&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/loader/JdbcLeakPrevention.java (added)
+++ tomcat/trunk/java/org/apache/catalina/loader/JdbcLeakPrevention.java Mon 
Jun 29 17:20:24 2009
@@ -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);
+            }
+        }
+        
+    }
+}

Propchange: tomcat/trunk/java/org/apache/catalina/loader/JdbcLeakPrevention.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: tomcat/trunk/java/org/apache/catalina/loader/JdbcLeakPrevention.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision

Modified: tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java?rev=789389&r1=789388&r2=789389&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java Mon Jun 
29 17:20:24 2009
@@ -36,9 +36,6 @@
 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 @@
      */
     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);
                 }
             }
         }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to