--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+ }
+}
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;
*/
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);
}
}
}