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);
+ }
+ }
+ }
+}