diff --git a/classloader-leak-prevention/classloader-leak-prevention-core/README.md b/classloader-leak-prevention/classloader-leak-prevention-core/README.md index 3869e77c..c462a32f 100644 --- a/classloader-leak-prevention/classloader-leak-prevention-core/README.md +++ b/classloader-leak-prevention/classloader-leak-prevention-core/README.md @@ -1,63 +1,63 @@ -# Classloader Leak Prevention library integration - -_This document is about using the Classloader Leak Prevention library in -a non-servlet environment. For general information and use in servlet -environments, please see the [root README.md](../../README.md)_ - -Version 2.x of the Classloader Leak Prevention library has been refactored -to allow for use outside a servlet environment, or by all means in a -servlet container (Java EE application server). - -# Setting up -What you will want to do is first create a [ClassLoaderLeakPreventorFactory](src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorFactory.java) -instance, either by using the default constructor that will configure the system -`ClassLoader` (`ClassLoader.getSystemClassLoader()`) to be used for pre-inits, -or provide your own leak safe `ClassLoader` to the constructor. - -Make any configurations on the factory instance, i.e. add or remove any -[cleanup](src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderPreMortemCleanUp.java) -or [pre-init](https://github.com/mjiderhamn/classloader-leak-prevention/blob/master/classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/PreClassLoaderInitiator.java) -plugins, or change parameters of any of the default plugins. - -# Protect ClassLoader -Then for every `ClassLoader` that needs leak protection, create a new -[ClassLoaderLeakPreventor](src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventor.java) -using -```java -classLoaderLeakPreventor = classLoaderLeakPreventorFactory.newLeakPreventor(classLoader); -``` - -Before letting any code execute inside the `ClassLoader` (or at least as -soon as possible), invoke -```java -classLoaderLeakPreventor.runPreClassLoaderInitiators(); -``` - -You can reuse the same `ClassLoaderLeakPreventorFactory` for multiple -`ClassLoaders`, but please be aware that any configuration changes made -to plugins of the factory will affect all `ClassLoaderLeakPreventor`s -created by the factory - both future and existing. If however you add -or remove plugins, that will only affect new `ClassLoaderLeakPreventor`s. - -# Shutting down -When you believe the `ClassLoader` should no longer be used, but be ready -for Garbage Collection, invoke -```java -classLoaderLeakPreventor.runCleanUps(); -``` -on the `ClassLoaderLeakPreventor` that corresponds to the `ClassLoader`. - -# Example -For an example how to use the framework, feel free to study the -[ClassLoaderLeakPreventorListener](../classloader-leak-prevention-servlet/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorListener.java) -in the `classloader-leak-prevention-servlet` module. - -# Maven -The module is available in Maven as -```xml - - se.jiderhamn.classloader-leak-prevention - classloader-leak-prevention-core - 2.6.1 - +# Classloader Leak Prevention library integration + +_This document is about using the Classloader Leak Prevention library in +a non-servlet environment. For general information and use in servlet +environments, please see the [root README.md](../../README.md)_ + +Version 2.x of the Classloader Leak Prevention library has been refactored +to allow for use outside a servlet environment, or by all means in a +servlet container (Java EE application server). + +# Setting up +What you will want to do is first create a [ClassLoaderLeakPreventorFactory](src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorFactory.java) +instance, either by using the default constructor that will configure the system +`ClassLoader` (`ClassLoader.getSystemClassLoader()`) to be used for pre-inits, +or provide your own leak safe `ClassLoader` to the constructor. + +Make any configurations on the factory instance, i.e. add or remove any +[cleanup](src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderPreMortemCleanUp.java) +or [pre-init](https://github.com/mjiderhamn/classloader-leak-prevention/blob/master/classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/PreClassLoaderInitiator.java) +plugins, or change parameters of any of the default plugins. + +# Protect ClassLoader +Then for every `ClassLoader` that needs leak protection, create a new +[ClassLoaderLeakPreventor](src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventor.java) +using +```java +classLoaderLeakPreventor = classLoaderLeakPreventorFactory.newLeakPreventor(classLoader); +``` + +Before letting any code execute inside the `ClassLoader` (or at least as +soon as possible), invoke +```java +classLoaderLeakPreventor.runPreClassLoaderInitiators(); +``` + +You can reuse the same `ClassLoaderLeakPreventorFactory` for multiple +`ClassLoaders`, but please be aware that any configuration changes made +to plugins of the factory will affect all `ClassLoaderLeakPreventor`s +created by the factory - both future and existing. If however you add +or remove plugins, that will only affect new `ClassLoaderLeakPreventor`s. + +# Shutting down +When you believe the `ClassLoader` should no longer be used, but be ready +for Garbage Collection, invoke +```java +classLoaderLeakPreventor.runCleanUps(); +``` +on the `ClassLoaderLeakPreventor` that corresponds to the `ClassLoader`. + +# Example +For an example how to use the framework, feel free to study the +[ClassLoaderLeakPreventorListener](../classloader-leak-prevention-servlet/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorListener.java) +in the `classloader-leak-prevention-servlet` module. + +# Maven +The module is available in Maven as +```xml + + se.jiderhamn.classloader-leak-prevention + classloader-leak-prevention-core + 2.6.1 + ``` \ No newline at end of file diff --git a/classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/DriverManagerCleanUp.java b/classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/DriverManagerCleanUp.java index 0b4787f0..37670eb2 100644 --- a/classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/DriverManagerCleanUp.java +++ b/classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/DriverManagerCleanUp.java @@ -1,9 +1,11 @@ package se.jiderhamn.classloader.leak.prevention.cleanup; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.sql.Driver; import java.sql.DriverManager; -import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.List; @@ -12,27 +14,96 @@ /** * Deregister JDBC drivers loaded by classloader + * * @author Mattias Jiderhamn + * @author SONGQQ 2018-4-19 */ public class DriverManagerCleanUp implements ClassLoaderPreMortemCleanUp { @Override public void cleanUp(ClassLoaderLeakPreventor preventor) { - final List driversToDeregister = new ArrayList(); - final Enumeration allDrivers = DriverManager.getDrivers(); - while(allDrivers.hasMoreElements()) { - final Driver driver = allDrivers.nextElement(); - if(preventor.isLoadedInClassLoader(driver)) // Should be true for all returned by DriverManager.getDrivers() - driversToDeregister.add(driver); - } - - for(Driver driver : driversToDeregister) { - try { - preventor.warn("JDBC driver loaded by protected ClassLoader deregistered: " + driver.getClass()); - DriverManager.deregisterDriver(driver); - } - catch (SQLException e) { - preventor.error(e); - } - } + final Enumeration allDrivers = getAllDrivers(preventor); + while (allDrivers.hasMoreElements()) { + final Driver driver = allDrivers.nextElement(); + if (preventor.isLoadedInClassLoader(driver)) { + try { + preventor.warn("JDBC driver loaded by protected ClassLoader deregistered: " + driver.getClass()); + deregisterDriver(preventor, driver); + } catch (Exception e) { + preventor.error(e); + } + } + } } -} \ No newline at end of file + + /** + * DriverManager.getDrivers() only returns the drivers that are loaded by + * the classloader of the caller. For many scenarios the callers classloader is not the + * same classloader which load the jdbc drivers. + * @return All drivers in DriverManager's registeredDrivers field,or + * DriverManager.getDrivers() if exception occurred + */ + public Enumeration getAllDrivers(ClassLoaderLeakPreventor preventor) { + List result = new ArrayList(); + try { + List driverinfos = preventor.getStaticFieldValue(DriverManager.class, "registeredDrivers"); + for (Object driverinfo : driverinfos) { + Driver driver = (Driver) preventor.getFieldValue(driverinfo, "driver"); + if (driver == null) + throw new NullPointerException("Driver missing in DriverInfo!"); + else + result.add(driver); + } + } catch (Exception e) { + preventor.error("Error getting registered drivers from DriverManager"); + return DriverManager.getDrivers(); + } + return Collections.enumeration(result); + } + + /** + * Do the work as DriverManager.deregisterDriver,but this method don't check the + * Security of the caller's Classloader.If Exception occur, invoke DriverManager.deregisterDriver(driver) + */ + private void deregisterDriver(ClassLoaderLeakPreventor preventor, final Driver driver) throws Exception { + synchronized (DriverManager.class) { + if (driver == null) { + return; + } + try { + List registeredDrivers = preventor.getStaticFieldValue(DriverManager.class, "registeredDrivers"); + Class innerClass = Class.forName("java.sql.DriverInfo"); + Method actionMethod = null; + Object innerInstance = null; + // DriverInfo is Changed in JDK8. So the Code must be backward compatible + try { + actionMethod = innerClass.getDeclaredMethod("action", new Class[0]); + } catch (NoSuchMethodException e) { + // No DriverInfo.action() means we're running JDK prior to 8 + } + Constructor ctor = null; + if (actionMethod != null) { + ctor = innerClass.getDeclaredConstructor(Driver.class, actionMethod.getReturnType()); + ctor.setAccessible(true); + innerInstance = ctor.newInstance(driver, null); + // Find DriverInfo with DriverAction in list by leveraging DriverInfo.equals() + Object driverInfo = registeredDrivers.get(registeredDrivers.indexOf(innerInstance)); + actionMethod.setAccessible(true); + Object driverAction = actionMethod.invoke(driverInfo); + if (driverAction != null) { + Method deregisterMethod = driverAction.getClass().getDeclaredMethod("deregister", new Class[0]); + if (deregisterMethod != null) + deregisterMethod.invoke(driverAction); + } + } else { + ctor = innerClass.getDeclaredConstructor(Driver.class); + ctor.setAccessible(true); + innerInstance = ctor.newInstance(driver); + } + registeredDrivers.remove(innerInstance); + } catch (Exception e) { + preventor.error("reflection to deregisterDriver error,invoke the orgin DriverManager.deregisterDriver()"); + DriverManager.deregisterDriver(driver); + } + } + } +}