SLING-4783 - The JavaScript scripting engine can generate classloader leaks

* implemented Rhino class cache cleaning if the current thread's class loader is a DynamicClassLoader
that's been marked as dirty

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1684189 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 59c6095..fff69e4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -177,6 +177,13 @@
             <version>3.2.1</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.classloader</artifactId>
+            <version>1.3.0</version>
+            <scope>provided</scope>
+        </dependency>
+
         <!-- Testing -->
         <dependency>
             <groupId>org.apache.sling</groupId>
diff --git a/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngine.java b/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngine.java
index 53d070d..b762ece 100644
--- a/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngine.java
+++ b/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngine.java
@@ -28,8 +28,10 @@
 
 import org.apache.sling.api.scripting.SlingBindings;
 import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.commons.classloader.DynamicClassLoader;
 import org.apache.sling.scripting.api.AbstractSlingScriptEngine;
 import org.apache.sling.scripting.javascript.io.EspReader;
+import org.mozilla.javascript.ClassCache;
 import org.mozilla.javascript.Context;
 import org.mozilla.javascript.ImporterTopLevel;
 import org.mozilla.javascript.JavaScriptException;
@@ -40,6 +42,7 @@
 import org.mozilla.javascript.WrapFactory;
 import org.mozilla.javascript.Wrapper;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * A ScriptEngine that uses the Rhino interpreter to process Sling requests with
@@ -47,6 +50,8 @@
  */
 public class RhinoJavaScriptEngine extends AbstractSlingScriptEngine {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(RhinoJavaScriptEngine.class);
+
     private Scriptable rootScope;
 
     public RhinoJavaScriptEngine(ScriptEngineFactory factory,
@@ -171,7 +176,22 @@
 
             // if properties have been replaced, reset them
             resetBoundProperties(scope, replacedProperties);
-
+            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+            if (classLoader instanceof DynamicClassLoader) {
+                DynamicClassLoader dynamicClassLoader = (DynamicClassLoader) classLoader;
+                if (!dynamicClassLoader.isLive()) {
+                    /**
+                     * if the classloader on this thread is a dynamic class loader and it's dirty we should clear Rhino's class cache
+                     * to avoid classloader leaks
+                     */
+                    if (scope != null) {
+                        ClassCache classCache = ClassCache.get(scope);
+                        classCache.clearCaches();
+                        LOGGER.info("Detected dirty classloader on thread {}. Emptying Rhino's class cache.", Thread.currentThread()
+                                .getName());
+                    }
+                }
+            }
             Context.exit();
         }
     }