Modified: 
tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java?rev=1701793&r1=1701792&r2=1701793&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 
(original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 
Tue Sep  8 12:49:24 2015
@@ -14,3773 +14,50 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
 package org.apache.catalina.loader;
 
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FilePermission;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.instrument.IllegalClassFormatException;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.nio.charset.Charset;
-import java.security.AccessControlException;
-import java.security.AccessController;
-import java.security.CodeSource;
-import java.security.Permission;
-import java.security.PermissionCollection;
-import java.security.Policy;
-import java.security.PrivilegedAction;
-import java.security.ProtectionDomain;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.ConcurrentModificationException;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.ResourceBundle;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.jar.Attributes;
-import java.util.jar.Attributes.Name;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
-
-import javax.naming.Binding;
-import javax.naming.NameClassPair;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.DirContext;
-
-import org.apache.catalina.Globals;
-import org.apache.catalina.Lifecycle;
 import org.apache.catalina.LifecycleException;
-import org.apache.catalina.LifecycleListener;
-import org.apache.catalina.LifecycleState;
-import org.apache.naming.JndiPermission;
-import org.apache.naming.resources.ProxyDirContext;
-import org.apache.naming.resources.Resource;
-import org.apache.naming.resources.ResourceAttributes;
-import org.apache.tomcat.InstrumentableClassLoader;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.IntrospectionUtils;
-import org.apache.tomcat.util.compat.JreVendor;
-import org.apache.tomcat.util.res.StringManager;
-
-/**
- * Specialized web application class loader.
- * <p>
- * This class loader is a full reimplementation of the
- * <code>URLClassLoader</code> from the JDK. It is designed to be fully
- * compatible with a normal <code>URLClassLoader</code>, although its internal
- * behavior may be completely different.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong> - By default, this class loader follows
- * the delegation model required by the specification. The system class
- * loader will be queried first, then the local repositories, and only then
- * delegation to the parent class loader will occur. This allows the web
- * application to override any shared class except the classes from J2SE.
- * Special handling is provided from the JAXP XML parser interfaces, the JNDI
- * interfaces, and the classes from the servlet API, which are never loaded
- * from the webapp repositories. The <code>delegate</code> property
- * allows an application to modify this behavior to move the parent class 
loader
- * ahead of the local repositories.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong> - Due to limitations in Jasper
- * compilation technology, any repository which contains classes from
- * the servlet API will be ignored by the class loader.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong> - The class loader generates source
- * URLs which include the full JAR URL when a class is loaded from a JAR file,
- * which allows setting security permission at the class level, even when a
- * class is contained inside a JAR.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
- * the order they are added via the initial constructor and/or any subsequent
- * calls to <code>addRepository()</code> or <code>addJar()</code>.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or
- * security is made unless a security manager is present.
- * <p>
- * TODO: Is there any requirement to provide a proper Lifecycle implementation
- *       rather than the current stubbed implementation?
- * <strong>IMPLEMENTATION NOTE</strong> - As of 7.0.64/8.0, this class
- * loader implements {@link InstrumentableClassLoader}, permitting web
- * application classes to instrument other classes in the same web
- * application. It does not permit instrumentation of system or container
- * classes or classes in other web apps.
- *
- * @author Remy Maucherat
- * @author Craig R. McClanahan
- */
-public class WebappClassLoader extends URLClassLoader
-        implements Lifecycle, InstrumentableClassLoader {
-
-    private static final org.apache.juli.logging.Log log=
-        org.apache.juli.logging.LogFactory.getLog( WebappClassLoader.class );
-
-    private static final Charset CHARSET_UTF8 = Charset.forName("UTF-8");
-    /**
-     * List of ThreadGroup names to ignore when scanning for web application
-     * started threads that need to be shut down.
-     */
-    private static final List<String> JVM_THREAD_GROUP_NAMES =
-        new ArrayList<String>();
-
-    private static final String JVM_THREAD_GROUP_SYSTEM = "system";
-
-    private static final String SERVICES_PREFIX = "META-INF/services/";
 
-    private static final String CLASS_FILE_SUFFIX = ".class";
-
-    private static final Manifest MANIFEST_UNKNOWN = new Manifest();
+public class WebappClassLoader extends WebappClassLoaderBase {
     
-    static {
-        JVM_THREAD_GROUP_NAMES.add(JVM_THREAD_GROUP_SYSTEM);
-        JVM_THREAD_GROUP_NAMES.add("RMI Runtime");
-    }
-
-    protected class PrivilegedFindResourceByName
-        implements PrivilegedAction<ResourceEntry> {
-
-        protected String name;
-        protected String path;
-        protected boolean manifestRequired;
-
-        PrivilegedFindResourceByName(String name, String path, boolean 
manifestRequired) {
-            this.name = name;
-            this.path = path;
-            this.manifestRequired = manifestRequired;
-        }
-
-        @Override
-        public ResourceEntry run() {
-            return findResourceInternal(name, path, manifestRequired);
-        }
-
-    }
-
-
-    protected static final class PrivilegedGetClassLoader
-        implements PrivilegedAction<ClassLoader> {
-
-        public Class<?> clazz;
-
-        public PrivilegedGetClassLoader(Class<?> clazz){
-            this.clazz = clazz;
-        }
-
-        @Override
-        public ClassLoader run() {
-            return clazz.getClassLoader();
-        }
-    }
-
-
-    // ------------------------------------------------------- Static Variables
-
-
-    /**
-     * The set of trigger classes that will cause a proposed repository not
-     * to be added if this class is visible to the class loader that loaded
-     * this factory class.  Typically, trigger classes will be listed for
-     * components that have been integrated into the JDK for later versions,
-     * but where the corresponding JAR files are required to run on
-     * earlier versions.
-     */
-    protected static final String[] triggers = {
-        "javax.servlet.Servlet", "javax.el.Expression"       // Servlet API
-    };
-
-
-    /**
-     * Set of package names which are not allowed to be loaded from a webapp
-     * class loader without delegating first.
-     */
-    protected static final String[] packageTriggers = {
-    };
-
-
-    /**
-     * The string manager for this package.
-     */
-    protected static final StringManager sm =
-        StringManager.getManager(Constants.Package);
-
-
-    /**
-     * Use anti JAR locking code, which does URL rerouting when accessing
-     * resources.
-     */
-    boolean antiJARLocking = false;
-
-    // ----------------------------------------------------------- Constructors
-
-
-    /**
-     * Construct a new ClassLoader with no defined repositories and no
-     * parent ClassLoader.
-     */
     public WebappClassLoader() {
-
-        super(new URL[0]);
-
-        ClassLoader p = getParent();
-        if (p == null) {
-            p = getSystemClassLoader();
-        }
-        this.parent = p;
-
-        ClassLoader j = String.class.getClassLoader();
-        if (j == null) {
-            j = getSystemClassLoader();
-            while (j.getParent() != null) {
-                j = j.getParent();
-            }
-        }
-        this.j2seClassLoader = j;
-
-        securityManager = System.getSecurityManager();
-        if (securityManager != null) {
-            refreshPolicy();
-        }
+        super();
     }
 
 
-    /**
-     * Construct a new ClassLoader with no defined repositories and the given
-     * parent ClassLoader.
-     * <p>
-     * Method is used via reflection -
-     * see {@link WebappLoader#createClassLoader()}
-     *
-     * @param parent Our parent class loader
-     */
     public WebappClassLoader(ClassLoader parent) {
-
-        super(new URL[0], parent);
-
-        ClassLoader p = getParent();
-        if (p == null) {
-            p = getSystemClassLoader();
-        }
-        this.parent = p;
-
-        ClassLoader j = String.class.getClassLoader();
-        if (j == null) {
-            j = getSystemClassLoader();
-            while (j.getParent() != null) {
-                j = j.getParent();
-            }
-        }
-        this.j2seClassLoader = j;
-
-        securityManager = System.getSecurityManager();
-        if (securityManager != null) {
-            refreshPolicy();
-        }
-    }
-
-
-    // ----------------------------------------------------- Instance Variables
-
-
-    /**
-     * Associated directory context giving access to the resources in this
-     * webapp.
-     */
-    protected DirContext resources = null;
-
-
-    /**
-     * The cache of ResourceEntry for classes and resources we have loaded,
-     * keyed by resource name.
-     */
-    protected HashMap<String, ResourceEntry> resourceEntries = new 
HashMap<String, ResourceEntry>();
-
-
-    /**
-     * The list of not found resources.
-     */
-    protected HashMap<String, String> notFoundResources =
-        new LinkedHashMap<String, String>() {
-        private static final long serialVersionUID = 1L;
-        @Override
-        protected boolean removeEldestEntry(
-                Map.Entry<String, String> eldest) {
-            return size() > 1000;
-        }
-    };
-
-
-    /**
-     * Should this class loader delegate to the parent class loader
-     * <strong>before</strong> searching its own repositories (i.e. the
-     * usual Java2 delegation model)?  If set to <code>false</code>,
-     * this class loader will search its own repositories first, and
-     * delegate to the parent only if the class or resource is not
-     * found locally. Note that the default, <code>false</code>, is
-     * the behavior called for by the servlet specification.
-     */
-    protected boolean delegate = false;
-
-
-    /**
-     * Last time a JAR was accessed.
-     */
-    protected long lastJarAccessed = 0L;
-
-
-    /**
-     * The list of local repositories, in the order they should be searched
-     * for locally loaded classes or resources.
-     */
-    protected String[] repositories = new String[0];
-
-
-     /**
-      * Repositories URLs, used to cache the result of getURLs.
-      */
-     protected URL[] repositoryURLs = null;
-
-
-    /**
-     * Repositories translated as path in the work directory (for Jasper
-     * originally), but which is used to generate fake URLs should getURLs be
-     * called.
-     */
-    protected File[] files = new File[0];
-
-
-    /**
-     * The list of JARs, in the order they should be searched
-     * for locally loaded classes or resources.
-     */
-    protected JarFile[] jarFiles = new JarFile[0];
-
-
-    /**
-     * The list of JARs, in the order they should be searched
-     * for locally loaded classes or resources.
-     */
-    protected File[] jarRealFiles = new File[0];
-
-
-    /**
-     * The path which will be monitored for added Jar files.
-     */
-    protected String jarPath = null;
-
-
-    /**
-     * The list of JARs, in the order they should be searched
-     * for locally loaded classes or resources.
-     */
-    protected String[] jarNames = new String[0];
-
-
-    /**
-     * The list of JARs last modified dates, in the order they should be
-     * searched for locally loaded classes or resources.
-     */
-    protected long[] lastModifiedDates = new long[0];
-
-
-    /**
-     * The list of resources which should be checked when checking for
-     * modifications.
-     */
-    protected String[] paths = new String[0];
-
-
-    /**
-     * A list of read File and Jndi Permission's required if this loader
-     * is for a web application context.
-     */
-    protected ArrayList<Permission> permissionList =
-        new ArrayList<Permission>();
-
-
-    /**
-     * Path where resources loaded from JARs will be extracted.
-     */
-    protected File loaderDir = null;
-    protected String canonicalLoaderDir = null;
-
-    /**
-     * The PermissionCollection for each CodeSource for a web
-     * application context.
-     */
-    protected HashMap<String, PermissionCollection> loaderPC = new 
HashMap<String, PermissionCollection>();
-
-
-    /**
-     * Instance of the SecurityManager installed.
-     */
-    protected SecurityManager securityManager = null;
-
-
-    /**
-     * The parent class loader.
-     */
-    protected ClassLoader parent = null;
-
-
-    /**
-     * The system class loader.
-     */
-    protected ClassLoader system = null;
-
-
-    /**
-     * The bootstrap class loader used to load the JavaSE classes. In some
-     * implementations this class loader is always <code>null</null> and in
-     * those cases {@link ClassLoader#getParent()} will be called recursively 
on
-     * the system class loader and the last non-null result used.
-     */
-    protected ClassLoader j2seClassLoader;
-
-
-    /**
-     * Has this component been started?
-     */
-    protected boolean started = false;
-
-
-    /**
-     * Has external repositories.
-     */
-    protected boolean hasExternalRepositories = false;
-
-    /**
-     * Search external repositories first
-     */
-    protected boolean searchExternalFirst = false;
-
-    /**
-     * need conversion for properties files
-     */
-    protected boolean needConvert = false;
-
-
-    /**
-     * All permission.
-     */
-    protected Permission allPermission = new java.security.AllPermission();
-
-
-    /**
-     * Should Tomcat attempt to null out any static or final fields from loaded
-     * classes when a web application is stopped as a work around for apparent
-     * garbage collection bugs and application coding errors? There have been
-     * some issues reported with log4j when this option is true. Applications
-     * without memory leaks using recent JVMs should operate correctly with 
this
-     * option set to <code>false</code>. If not specified, the default value of
-     * <code>false</code> will be used.
-     */
-    private boolean clearReferencesStatic = false;
-
-    /**
-     * Should Tomcat attempt to terminate threads that have been started by the
-     * web application? Stopping threads is performed via the deprecated (for
-     * good reason) <code>Thread.stop()</code> method and is likely to result 
in
-     * instability. As such, enabling this should be viewed as an option of 
last
-     * resort in a development environment and is not recommended in a
-     * production environment. If not specified, the default value of
-     * <code>false</code> will be used.
-     */
-    private boolean clearReferencesStopThreads = false;
-
-    /**
-     * Should Tomcat attempt to terminate any {@link java.util.TimerThread}s
-     * that have been started by the web application? If not specified, the
-     * default value of <code>false</code> will be used.
-     */
-    private boolean clearReferencesStopTimerThreads = false;
-
-    /**
-     * Should Tomcat call {@link org.apache.juli.logging.LogFactory#release()}
-     * when the class loader is stopped? If not specified, the default value
-     * of <code>true</code> is used. Changing the default setting is likely to
-     * lead to memory leaks and other issues.
-     */
-    private boolean clearReferencesLogFactoryRelease = true;
-
-    /**
-     * If an HttpClient keep-alive timer thread has been started by this web
-     * application and is still running, should Tomcat change the context class
-     * loader from the current {@link WebappClassLoader} to
-     * {@link WebappClassLoader#parent} to prevent a memory leak? Note that the
-     * keep-alive timer thread will stop on its own once the keep-alives all
-     * expire however, on a busy system that might not happen for some time.
-     */
-    private boolean clearReferencesHttpClientKeepAliveThread = true;
-
-    /**
-     * Name of associated context used with logging and JMX to associate with
-     * the right web application. Particularly useful for the clear references
-     * messages. Defaults to unknown but if standard Tomcat components are used
-     * it will be updated during initialisation from the resources.
-     */
-    private String contextName = "unknown";
-
-    /**
-     * Holds the class file transformers decorating this class loader. The
-     * CopyOnWriteArrayList is thread safe. It is expensive on writes, but
-     * those should be rare. It is very fast on reads, since synchronization
-     * is not actually used. Importantly, the ClassLoader will never block
-     * iterating over the transformers while loading a class.
-     */
-    private final List<ClassFileTransformer> transformers = new 
CopyOnWriteArrayList<ClassFileTransformer>();
-
-    /**
-     * Code base to use for classes loaded from WEB-INF/classes.
-     */
-    private URL webInfClassesCodeBase = null;
-
-    // ------------------------------------------------------------- Properties
-
-
-    /**
-     * Get associated resources.
-     */
-    public DirContext getResources() {
-
-        return this.resources;
-
-    }
-
-
-    /**
-     * Set associated resources.
-     */
-    public void setResources(DirContext resources) {
-
-        this.resources = resources;
-
-        if (resources instanceof ProxyDirContext) {
-            contextName = ((ProxyDirContext) resources).getContextName();
-        }
-    }
-
-
-    /**
-     * Return the context name for this class loader.
-     */
-    public String getContextName() {
-
-        return (this.contextName);
-
-    }
-
-
-    /**
-     * Return the "delegate first" flag for this class loader.
-     */
-    public boolean getDelegate() {
-
-        return (this.delegate);
-
+        super(parent);
     }
 
 
     /**
-     * Set the "delegate first" flag for this class loader.
-     * If this flag is true, this class loader delegates
-     * to the parent class loader
-     * <strong>before</strong> searching its own repositories, as
-     * in an ordinary (non-servlet) chain of Java class loaders.
-     * If set to <code>false</code> (the default),
-     * this class loader will search its own repositories first, and
-     * delegate to the parent only if the class or resource is not
-     * found locally, as per the servlet specification.
+     * Returns a copy of this class loader without any class file
+     * transformers. This is a tool often used by Java Persistence API
+     * providers to inspect entity classes in the absence of any
+     * instrumentation, something that can't be guaranteed within the
+     * context of a {@link java.lang.instrument.ClassFileTransformer}'s
+     * {@link java.lang.instrument.ClassFileTransformer#transform(ClassLoader,
+     * String, Class, java.security.ProtectionDomain, byte[]) transform} 
method.
+     * <p>
+     * The returned class loader's resource cache will have been cleared
+     * so that classes already instrumented will not be retained or
+     * returned.
      *
-     * @param delegate The new "delegate first" flag
+     * @return the transformer-free copy of this class loader.
      */
-    public void setDelegate(boolean delegate) {
-
-        this.delegate = delegate;
-
-    }
+    @Override
+    public WebappClassLoader copyWithoutTransformers() {
 
+        WebappClassLoader result = new WebappClassLoader(getParent());
 
-    /**
-     * @return Returns the antiJARLocking.
-     */
-    public boolean getAntiJARLocking() {
-        return antiJARLocking;
-    }
+        super.copyStateWithoutTransformers(result);
 
+        try {
+            result.start();
+        } catch (LifecycleException e) {
+            throw new IllegalStateException(e);
+        }
 
-    /**
-     * @param antiJARLocking The antiJARLocking to set.
-     */
-    public void setAntiJARLocking(boolean antiJARLocking) {
-        this.antiJARLocking = antiJARLocking;
+        return result;
     }
-
-    /**
-     * @return Returns the searchExternalFirst.
-     */
-    public boolean getSearchExternalFirst() {
-        return searchExternalFirst;
-    }
-
-    /**
-     * @param searchExternalFirst Whether external repositories should be 
searched first
-     */
-    public void setSearchExternalFirst(boolean searchExternalFirst) {
-        this.searchExternalFirst = searchExternalFirst;
-    }
-
-
-    /**
-     * If there is a Java SecurityManager create a read FilePermission
-     * or JndiPermission for the file directory path.
-     *
-     * @param filepath file directory path
-     */
-    public void addPermission(String filepath) {
-        if (filepath == null) {
-            return;
-        }
-
-        String path = filepath;
-
-        if (securityManager != null) {
-            Permission permission = null;
-            if (path.startsWith("jndi:") || path.startsWith("jar:jndi:")) {
-                if (!path.endsWith("/")) {
-                    path = path + "/";
-                }
-                permission = new JndiPermission(path + "*");
-                addPermission(permission);
-            } else {
-                if (!path.endsWith(File.separator)) {
-                    permission = new FilePermission(path, "read");
-                    addPermission(permission);
-                    path = path + File.separator;
-                }
-                permission = new FilePermission(path + "-", "read");
-                addPermission(permission);
-            }
-        }
-    }
-
-
-    /**
-     * If there is a Java SecurityManager create a read FilePermission
-     * or JndiPermission for URL.
-     *
-     * @param url URL for a file or directory on local system
-     */
-    public void addPermission(URL url) {
-        if (url != null) {
-            addPermission(url.toString());
-        }
-    }
-
-
-    /**
-     * If there is a Java SecurityManager create a Permission.
-     *
-     * @param permission The permission
-     */
-    public void addPermission(Permission permission) {
-        if ((securityManager != null) && (permission != null)) {
-            permissionList.add(permission);
-        }
-    }
-
-
-    /**
-     * Return the JAR path.
-     */
-    public String getJarPath() {
-
-        return this.jarPath;
-
-    }
-
-
-    /**
-     * Change the Jar path.
-     */
-    public void setJarPath(String jarPath) {
-
-        this.jarPath = jarPath;
-
-    }
-
-
-    /**
-     * Change the work directory.
-     */
-    public void setWorkDir(File workDir) {
-        this.loaderDir = new File(workDir, "loader");
-        if (loaderDir == null) {
-            canonicalLoaderDir = null;
-        } else {
-            try {
-                canonicalLoaderDir = loaderDir.getCanonicalPath();
-                if (!canonicalLoaderDir.endsWith(File.separator)) {
-                    canonicalLoaderDir += File.separator;
-                }
-            } catch (IOException ioe) {
-                canonicalLoaderDir = null;
-            }
-        }
-    }
-
-     /**
-      * Utility method for use in subclasses.
-      * Must be called before Lifecycle methods to have any effect.
-      *
-      * @deprecated Will be removed in 8.0.x onwards.
-      */
-    @Deprecated
-     protected void setParentClassLoader(ClassLoader pcl) {
-         parent = pcl;
-     }
-
-     /**
-      * Return the clearReferencesStatic flag for this Context.
-      */
-     public boolean getClearReferencesStatic() {
-         return (this.clearReferencesStatic);
-     }
-
-
-     /**
-      * Set the clearReferencesStatic feature for this Context.
-      *
-      * @param clearReferencesStatic The new flag value
-      */
-     public void setClearReferencesStatic(boolean clearReferencesStatic) {
-         this.clearReferencesStatic = clearReferencesStatic;
-     }
-
-
-     /**
-      * Return the clearReferencesStopThreads flag for this Context.
-      */
-     public boolean getClearReferencesStopThreads() {
-         return (this.clearReferencesStopThreads);
-     }
-
-
-     /**
-      * Set the clearReferencesStopThreads feature for this Context.
-      *
-      * @param clearReferencesStopThreads The new flag value
-      */
-     public void setClearReferencesStopThreads(
-             boolean clearReferencesStopThreads) {
-         this.clearReferencesStopThreads = clearReferencesStopThreads;
-     }
-
-
-     /**
-      * Return the clearReferencesStopTimerThreads flag for this Context.
-      */
-     public boolean getClearReferencesStopTimerThreads() {
-         return (this.clearReferencesStopTimerThreads);
-     }
-
-
-     /**
-      * Set the clearReferencesStopTimerThreads feature for this Context.
-      *
-      * @param clearReferencesStopTimerThreads The new flag value
-      */
-     public void setClearReferencesStopTimerThreads(
-             boolean clearReferencesStopTimerThreads) {
-         this.clearReferencesStopTimerThreads = 
clearReferencesStopTimerThreads;
-     }
-
-
-     /**
-      * Return the clearReferencesLogFactoryRelease flag for this Context.
-      */
-     public boolean getClearReferencesLogFactoryRelease() {
-         return (this.clearReferencesLogFactoryRelease);
-     }
-
-
-     /**
-      * Set the clearReferencesLogFactoryRelease feature for this Context.
-      *
-      * @param clearReferencesLogFactoryRelease The new flag value
-      */
-     public void setClearReferencesLogFactoryRelease(
-             boolean clearReferencesLogFactoryRelease) {
-         this.clearReferencesLogFactoryRelease =
-             clearReferencesLogFactoryRelease;
-     }
-
-
-     /**
-      * Return the clearReferencesHttpClientKeepAliveThread flag for this
-      * Context.
-      */
-     public boolean getClearReferencesHttpClientKeepAliveThread() {
-         return (this.clearReferencesHttpClientKeepAliveThread);
-     }
-
-
-     /**
-      * Set the clearReferencesHttpClientKeepAliveThread feature for this
-      * Context.
-      *
-      * @param clearReferencesHttpClientKeepAliveThread The new flag value
-      */
-     public void setClearReferencesHttpClientKeepAliveThread(
-             boolean clearReferencesHttpClientKeepAliveThread) {
-         this.clearReferencesHttpClientKeepAliveThread =
-             clearReferencesHttpClientKeepAliveThread;
-     }
-
-
-    // ------------------------------------------------------- Reloader Methods
-
-    /**
-     * Adds the specified class file transformer to this class loader. The
-     * transformer will then be able to modify the bytecode of any classes
-     * loaded by this class loader after the invocation of this method.
-     *
-     * @param transformer The transformer to add to the class loader
-     */
-    @Override
-    public void addTransformer(ClassFileTransformer transformer) {
-
-        if (transformer == null) {
-            throw new IllegalArgumentException(sm.getString(
-                    "webappClassLoader.addTransformer.illegalArgument", 
getContextName()));
-        }
-
-        if (this.transformers.contains(transformer)) {
-            // if the same instance of this transformer was already added, 
bail out
-            log.warn(sm.getString("webappClassLoader.addTransformer.duplicate",
-                    transformer, getContextName()));
-            return;
-        }
-        this.transformers.add(transformer);
-
-        log.info(sm.getString("webappClassLoader.addTransformer", transformer, 
getContextName()));
-
-    }
-
-    /**
-     * Removes the specified class file transformer from this class loader.
-     * It will no longer be able to modify the byte code of any classes
-     * loaded by the class loader after the invocation of this method.
-     * However, any classes already modified by this transformer will
-     * remain transformed.
-     *
-     * @param transformer The transformer to remove
-     */
-    @Override
-    public void removeTransformer(ClassFileTransformer transformer) {
-
-        if (transformer == null) {
-            return;
-        }
-
-        if (this.transformers.remove(transformer)) {
-            log.info(sm.getString("webappClassLoader.removeTransformer",
-                    transformer, getContextName()));
-            return;
-        }
-
-    }
-
-    /**
-     * Returns a copy of this class loader without any class file
-     * transformers. This is a tool often used by Java Persistence API
-     * providers to inspect entity classes in the absence of any
-     * instrumentation, something that can't be guaranteed within the
-     * context of a {@link ClassFileTransformer}'s
-     * {@link ClassFileTransformer#transform(ClassLoader, String, Class,
-     * ProtectionDomain, byte[]) transform} method.
-     * <p>
-     * The returned class loader's resource cache will have been cleared
-     * so that classes already instrumented will not be retained or
-     * returned.
-     *
-     * @return the transformer-free copy of this class loader.
-     */
-    @Override
-    public WebappClassLoader copyWithoutTransformers() {
-
-        WebappClassLoader result = new WebappClassLoader(this.parent);
-
-        result.antiJARLocking = this.antiJARLocking;
-        result.resources = this.resources;
-        result.files = this.files;
-        result.delegate = this.delegate;
-        result.lastJarAccessed = this.lastJarAccessed;
-        result.repositories = this.repositories;
-        result.jarPath = this.jarPath;
-        result.loaderDir = this.loaderDir;
-        result.canonicalLoaderDir = this.canonicalLoaderDir;
-        result.clearReferencesStatic = this.clearReferencesStatic;
-        result.clearReferencesStopThreads = this.clearReferencesStopThreads;
-        result.clearReferencesStopTimerThreads = 
this.clearReferencesStopTimerThreads;
-        result.clearReferencesLogFactoryRelease = 
this.clearReferencesLogFactoryRelease;
-        result.clearReferencesHttpClientKeepAliveThread = 
this.clearReferencesHttpClientKeepAliveThread;
-        result.repositoryURLs = this.repositoryURLs.clone();
-        result.jarFiles = this.jarFiles.clone();
-        result.jarRealFiles = this.jarRealFiles.clone();
-        result.jarNames = this.jarNames.clone();
-        result.lastModifiedDates = this.lastModifiedDates.clone();
-        result.paths = this.paths.clone();
-        result.notFoundResources.putAll(this.notFoundResources);
-        result.permissionList.addAll(this.permissionList);
-        result.loaderPC.putAll(this.loaderPC);
-        result.contextName = this.contextName;
-        result.hasExternalRepositories = this.hasExternalRepositories;
-        result.searchExternalFirst = this.searchExternalFirst;
-
-        try {
-            result.start();
-        } catch (LifecycleException e) {
-            throw new IllegalStateException(e);
-        }
-
-        return result;
-    }
-
-   /**
-     * Add a new repository to the set of places this ClassLoader can look for
-     * classes to be loaded.
-     *
-     * @param repository Name of a source of classes to be loaded, such as a
-     *  directory pathname, a JAR file pathname, or a ZIP file pathname
-     *
-     * @exception IllegalArgumentException if the specified repository is
-     *  invalid or does not exist
-     */
-    public void addRepository(String repository) {
-
-        // Ignore any of the standard repositories, as they are set up using
-        // either addJar or addRepository
-        if (repository.startsWith("/WEB-INF/lib")
-            || repository.startsWith("/WEB-INF/classes"))
-            return;
-
-        // Add this repository to our underlying class loader
-        try {
-            URL url = new URL(repository);
-            super.addURL(url);
-            hasExternalRepositories = true;
-            repositoryURLs = null;
-        } catch (MalformedURLException e) {
-            IllegalArgumentException iae = new IllegalArgumentException
-                ("Invalid repository: " + repository);
-            iae.initCause(e);
-            throw iae;
-        }
-
-    }
-
-
-    /**
-     * Add a new repository to the set of places this ClassLoader can look for
-     * classes to be loaded.
-     *
-     * @param repository Name of a source of classes to be loaded, such as a
-     *  directory pathname, a JAR file pathname, or a ZIP file pathname
-     *
-     * @exception IllegalArgumentException if the specified repository is
-     *  invalid or does not exist
-     */
-    synchronized void addRepository(String repository, File file) {
-
-        // Note : There should be only one (of course), but I think we should
-        // keep this a bit generic
-
-        if (repository == null)
-            return;
-
-        if (log.isDebugEnabled())
-            log.debug("addRepository(" + repository + ")");
-
-        int i;
-
-        // Add this repository to our internal list
-        String[] result = new String[repositories.length + 1];
-        for (i = 0; i < repositories.length; i++) {
-            result[i] = repositories[i];
-        }
-        result[repositories.length] = repository;
-        repositories = result;
-
-        // Add the file to the list
-        File[] result2 = new File[files.length + 1];
-        for (i = 0; i < files.length; i++) {
-            result2[i] = files[i];
-        }
-        result2[files.length] = file;
-        files = result2;
-
-    }
-
-
-    synchronized void addJar(String jar, JarFile jarFile, File file)
-        throws IOException {
-
-        if (jar == null)
-            return;
-        if (jarFile == null)
-            return;
-        if (file == null)
-            return;
-
-        if (log.isDebugEnabled())
-            log.debug("addJar(" + jar + ")");
-
-        int i;
-
-        if ((jarPath != null) && (jar.startsWith(jarPath))) {
-
-            String jarName = jar.substring(jarPath.length());
-            while (jarName.startsWith("/"))
-                jarName = jarName.substring(1);
-
-            String[] result = new String[jarNames.length + 1];
-            for (i = 0; i < jarNames.length; i++) {
-                result[i] = jarNames[i];
-            }
-            result[jarNames.length] = jarName;
-            jarNames = result;
-
-        }
-
-        try {
-
-            // Register the JAR for tracking
-
-            long lastModified =
-                ((ResourceAttributes) resources.getAttributes(jar))
-                .getLastModified();
-
-            String[] result = new String[paths.length + 1];
-            for (i = 0; i < paths.length; i++) {
-                result[i] = paths[i];
-            }
-            result[paths.length] = jar;
-            paths = result;
-
-            long[] result3 = new long[lastModifiedDates.length + 1];
-            for (i = 0; i < lastModifiedDates.length; i++) {
-                result3[i] = lastModifiedDates[i];
-            }
-            result3[lastModifiedDates.length] = lastModified;
-            lastModifiedDates = result3;
-
-        } catch (NamingException e) {
-            // Ignore
-        }
-
-        // If the JAR currently contains invalid classes, don't actually use it
-        // for classloading
-        if (!validateJarFile(file))
-            return;
-
-        JarFile[] result2 = new JarFile[jarFiles.length + 1];
-        for (i = 0; i < jarFiles.length; i++) {
-            result2[i] = jarFiles[i];
-        }
-        result2[jarFiles.length] = jarFile;
-        jarFiles = result2;
-
-        // Add the file to the list
-        File[] result4 = new File[jarRealFiles.length + 1];
-        for (i = 0; i < jarRealFiles.length; i++) {
-            result4[i] = jarRealFiles[i];
-        }
-        result4[jarRealFiles.length] = file;
-        jarRealFiles = result4;
-    }
-
-
-    /**
-     * Return a String array of the current repositories for this class
-     * loader.  If there are no repositories, a zero-length array is
-     * returned.For security reason, returns a clone of the Array (since
-     * String are immutable).
-     */
-    public String[] findRepositories() {
-
-        return (repositories.clone());
-
-    }
-
-
-    /**
-     * Have one or more classes or resources been modified so that a reload
-     * is appropriate?
-     */
-    public boolean modified() {
-
-        if (log.isDebugEnabled())
-            log.debug("modified()");
-
-        // Checking for modified loaded resources
-        int length = paths.length;
-
-        // A rare race condition can occur in the updates of the two arrays
-        // It's totally ok if the latest class added is not checked (it will
-        // be checked the next time
-        int length2 = lastModifiedDates.length;
-        if (length > length2)
-            length = length2;
-
-        for (int i = 0; i < length; i++) {
-            try {
-                long lastModified =
-                    ((ResourceAttributes) resources.getAttributes(paths[i]))
-                    .getLastModified();
-                if (lastModified != lastModifiedDates[i]) {
-                    if( log.isDebugEnabled() )
-                        log.debug("  Resource '" + paths[i]
-                                  + "' was modified; Date is now: "
-                                  + new java.util.Date(lastModified) + " Was: "
-                                  + new java.util.Date(lastModifiedDates[i]));
-                    return (true);
-                }
-            } catch (NamingException e) {
-                log.error("    Resource '" + paths[i] + "' is missing");
-                return (true);
-            }
-        }
-
-        length = jarNames.length;
-
-        // Check if JARs have been added or removed
-        if (getJarPath() != null) {
-
-            try {
-                NamingEnumeration<Binding> enumeration =
-                    resources.listBindings(getJarPath());
-                int i = 0;
-                while (enumeration.hasMoreElements() && (i < length)) {
-                    NameClassPair ncPair = enumeration.nextElement();
-                    String name = ncPair.getName();
-                    // Ignore non JARs present in the lib folder
-                    if (!name.endsWith(".jar"))
-                        continue;
-                    if (!name.equals(jarNames[i])) {
-                        // Missing JAR
-                        log.info("    Additional JARs have been added : '"
-                                 + name + "'");
-                        return (true);
-                    }
-                    i++;
-                }
-                if (enumeration.hasMoreElements()) {
-                    while (enumeration.hasMoreElements()) {
-                        NameClassPair ncPair = enumeration.nextElement();
-                        String name = ncPair.getName();
-                        // Additional non-JAR files are allowed
-                        if (name.endsWith(".jar")) {
-                            // There was more JARs
-                            log.info("    Additional JARs have been added");
-                            return (true);
-                        }
-                    }
-                } else if (i < jarNames.length) {
-                    // There was less JARs
-                    log.info("    Additional JARs have been added");
-                    return (true);
-                }
-            } catch (NamingException e) {
-                if (log.isDebugEnabled())
-                    log.debug("    Failed tracking modifications of '"
-                        + getJarPath() + "'");
-            } catch (ClassCastException e) {
-                log.error("    Failed tracking modifications of '"
-                          + getJarPath() + "' : " + e.getMessage());
-            }
-
-        }
-
-        // No classes have been modified
-        return (false);
-
-    }
-
-
-    /**
-     * Render a String representation of this object.
-     */
-    @Override
-    public String toString() {
-
-        StringBuilder sb = new StringBuilder("WebappClassLoader\r\n");
-        sb.append("  context: ");
-        sb.append(contextName);
-        sb.append("\r\n");
-        sb.append("  delegate: ");
-        sb.append(delegate);
-        sb.append("\r\n");
-        sb.append("  repositories:\r\n");
-        if (repositories != null) {
-            for (int i = 0; i < repositories.length; i++) {
-                sb.append("    ");
-                sb.append(repositories[i]);
-                sb.append("\r\n");
-            }
-        }
-        if (this.parent != null) {
-            sb.append("----------> Parent Classloader:\r\n");
-            sb.append(this.parent.toString());
-            sb.append("\r\n");
-        }
-        if (this.transformers.size() > 0) {
-            sb.append("----------> Class file transformers:\r\n");
-            for (ClassFileTransformer transformer : this.transformers) {
-                sb.append(transformer).append("\r\n");
-            }
-        }
-        return (sb.toString());
-
-    }
-
-
-    // ---------------------------------------------------- ClassLoader Methods
-
-
-    /**
-     * Add the specified URL to the classloader.
-     */
-    @Override
-    protected void addURL(URL url) {
-        super.addURL(url);
-        hasExternalRepositories = true;
-        repositoryURLs = null;
-    }
-
-
-    /**
-     * Expose this method for use by the unit tests.
-     */
-    protected final Class<?> doDefineClass(String name, byte[] b, int off, int 
len,
-            ProtectionDomain protectionDomain) {
-        return super.defineClass(name, b, off, len, protectionDomain);
-    }
-
-    /**
-     * Find the specified class in our local repositories, if possible.  If
-     * not found, throw <code>ClassNotFoundException</code>.
-     *
-     * @param name Name of the class to be loaded
-     *
-     * @exception ClassNotFoundException if the class was not found
-     */
-    @Override
-    public Class<?> findClass(String name) throws ClassNotFoundException {
-
-        if (log.isDebugEnabled())
-            log.debug("    findClass(" + name + ")");
-
-        // Cannot load anything from local repositories if class loader is 
stopped
-        if (!started) {
-            throw new ClassNotFoundException(name);
-        }
-
-        // (1) Permission to define this class when using a SecurityManager
-        if (securityManager != null) {
-            int i = name.lastIndexOf('.');
-            if (i >= 0) {
-                try {
-                    if (log.isTraceEnabled())
-                        log.trace("      
securityManager.checkPackageDefinition");
-                    
securityManager.checkPackageDefinition(name.substring(0,i));
-                } catch (Exception se) {
-                    if (log.isTraceEnabled())
-                        log.trace("      
-->Exception-->ClassNotFoundException", se);
-                    throw new ClassNotFoundException(name, se);
-                }
-            }
-        }
-
-        // Ask our superclass to locate this class, if possible
-        // (throws ClassNotFoundException if it is not found)
-        Class<?> clazz = null;
-        try {
-            if (log.isTraceEnabled())
-                log.trace("      findClassInternal(" + name + ")");
-            if (hasExternalRepositories && searchExternalFirst) {
-                try {
-                    clazz = super.findClass(name);
-                } catch(ClassNotFoundException cnfe) {
-                    // Ignore - will search internal repositories next
-                } catch(AccessControlException ace) {
-                    log.warn("WebappClassLoader.findClassInternal(" + name
-                            + ") security exception: " + ace.getMessage(), 
ace);
-                    throw new ClassNotFoundException(name, ace);
-                } catch (RuntimeException e) {
-                    if (log.isTraceEnabled())
-                        log.trace("      -->RuntimeException Rethrown", e);
-                    throw e;
-                }
-            }
-            if ((clazz == null)) {
-                try {
-                    clazz = findClassInternal(name);
-                } catch(ClassNotFoundException cnfe) {
-                    if (!hasExternalRepositories || searchExternalFirst) {
-                        throw cnfe;
-                    }
-                } catch(AccessControlException ace) {
-                    log.warn("WebappClassLoader.findClassInternal(" + name
-                            + ") security exception: " + ace.getMessage(), 
ace);
-                    throw new ClassNotFoundException(name, ace);
-                } catch (RuntimeException e) {
-                    if (log.isTraceEnabled())
-                        log.trace("      -->RuntimeException Rethrown", e);
-                    throw e;
-                }
-            }
-            if ((clazz == null) && hasExternalRepositories && 
!searchExternalFirst) {
-                try {
-                    clazz = super.findClass(name);
-                } catch(AccessControlException ace) {
-                    log.warn("WebappClassLoader.findClassInternal(" + name
-                            + ") security exception: " + ace.getMessage(), 
ace);
-                    throw new ClassNotFoundException(name, ace);
-                } catch (RuntimeException e) {
-                    if (log.isTraceEnabled())
-                        log.trace("      -->RuntimeException Rethrown", e);
-                    throw e;
-                }
-            }
-            if (clazz == null) {
-                if (log.isDebugEnabled())
-                    log.debug("    --> Returning ClassNotFoundException");
-                throw new ClassNotFoundException(name);
-            }
-        } catch (ClassNotFoundException e) {
-            if (log.isTraceEnabled())
-                log.trace("    --> Passing on ClassNotFoundException");
-            throw e;
-        }
-
-        // Return the class we have located
-        if (log.isTraceEnabled())
-            log.debug("      Returning class " + clazz);
-
-        if (log.isTraceEnabled()) {
-            ClassLoader cl;
-            if (Globals.IS_SECURITY_ENABLED){
-                cl = AccessController.doPrivileged(
-                    new PrivilegedGetClassLoader(clazz));
-            } else {
-                cl = clazz.getClassLoader();
-            }
-            log.debug("      Loaded by " + cl.toString());
-        }
-        return (clazz);
-
-    }
-
-
-    /**
-     * Find the specified resource in our local repository, and return a
-     * <code>URL</code> referring to it, or <code>null</code> if this resource
-     * cannot be found.
-     *
-     * @param name Name of the resource to be found
-     */
-    @Override
-    public URL findResource(final String name) {
-
-        if (log.isDebugEnabled())
-            log.debug("    findResource(" + name + ")");
-
-        URL url = null;
-
-        if (hasExternalRepositories && searchExternalFirst)
-            url = super.findResource(name);
-
-        if (url == null) {
-            ResourceEntry entry = resourceEntries.get(name);
-            if (entry == null) {
-                if (securityManager != null) {
-                    PrivilegedAction<ResourceEntry> dp =
-                        new PrivilegedFindResourceByName(name, name, false);
-                    entry = AccessController.doPrivileged(dp);
-                } else {
-                    entry = findResourceInternal(name, name, false);
-                }
-            }
-            if (entry != null) {
-                url = entry.source;
-            }
-        }
-
-        if ((url == null) && hasExternalRepositories && !searchExternalFirst)
-            url = super.findResource(name);
-
-        if (log.isDebugEnabled()) {
-            if (url != null)
-                log.debug("    --> Returning '" + url.toString() + "'");
-            else
-                log.debug("    --> Resource not found, returning null");
-        }
-        return (url);
-
-    }
-
-
-    /**
-     * Return an enumeration of <code>URLs</code> representing all of the
-     * resources with the given name.  If no resources with this name are
-     * found, return an empty enumeration.
-     *
-     * @param name Name of the resources to be found
-     *
-     * @exception IOException if an input/output error occurs
-     */
-    @Override
-    public Enumeration<URL> findResources(String name) throws IOException {
-
-        if (log.isDebugEnabled())
-            log.debug("    findResources(" + name + ")");
-
-        //we use a LinkedHashSet instead of a Vector to avoid duplicates with 
virtualmappings
-        LinkedHashSet<URL> result = new LinkedHashSet<URL>();
-
-        int jarFilesLength = jarFiles.length;
-        int repositoriesLength = repositories.length;
-
-        int i;
-
-        // Adding the results of a call to the superclass
-        if (hasExternalRepositories && searchExternalFirst) {
-
-            Enumeration<URL> otherResourcePaths = super.findResources(name);
-
-            while (otherResourcePaths.hasMoreElements()) {
-                result.add(otherResourcePaths.nextElement());
-            }
-
-        }
-        // Looking at the repositories
-        for (i = 0; i < repositoriesLength; i++) {
-            try {
-                String fullPath = repositories[i] + name;
-                resources.lookup(fullPath);
-                // Note : Not getting an exception here means the resource was
-                // found
-                try {
-                    result.add(getURI(new File(files[i], name)));
-                } catch (MalformedURLException e) {
-                    // Ignore
-                }
-            } catch (NamingException e) {
-                // Ignore
-            }
-        }
-
-        // Looking at the JAR files
-        synchronized (jarFiles) {
-            if (openJARs()) {
-                for (i = 0; i < jarFilesLength; i++) {
-                    JarEntry jarEntry = jarFiles[i].getJarEntry(name);
-                    if (jarEntry != null) {
-                        try {
-                            String jarFakeUrl = 
getURI(jarRealFiles[i]).toString();
-                            jarFakeUrl = "jar:" + jarFakeUrl + "!/" + name;
-                            result.add(new URL(jarFakeUrl));
-                        } catch (MalformedURLException e) {
-                            // Ignore
-                        }
-                    }
-                }
-            }
-        }
-
-        // Adding the results of a call to the superclass
-        if (hasExternalRepositories && !searchExternalFirst) {
-
-            Enumeration<URL> otherResourcePaths = super.findResources(name);
-
-            while (otherResourcePaths.hasMoreElements()) {
-                result.add(otherResourcePaths.nextElement());
-            }
-
-        }
-
-        return Collections.enumeration(result);
-    }
-
-
-    /**
-     * Find the resource with the given name.  A resource is some data
-     * (images, audio, text, etc.) that can be accessed by class code in a
-     * way that is independent of the location of the code.  The name of a
-     * resource is a "/"-separated path name that identifies the resource.
-     * If the resource cannot be found, return <code>null</code>.
-     * <p>
-     * This method searches according to the following algorithm, returning
-     * as soon as it finds the appropriate URL.  If the resource cannot be
-     * found, returns <code>null</code>.
-     * <ul>
-     * <li>If the <code>delegate</code> property is set to <code>true</code>,
-     *     call the <code>getResource()</code> method of the parent class
-     *     loader, if any.</li>
-     * <li>Call <code>findResource()</code> to find this resource in our
-     *     locally defined repositories.</li>
-     * <li>Call the <code>getResource()</code> method of the parent class
-     *     loader, if any.</li>
-     * </ul>
-     *
-     * @param name Name of the resource to return a URL for
-     */
-    @Override
-    public URL getResource(String name) {
-
-        if (log.isDebugEnabled())
-            log.debug("getResource(" + name + ")");
-        URL url = null;
-
-        // (1) Delegate to parent if requested
-        if (delegate) {
-            if (log.isDebugEnabled())
-                log.debug("  Delegating to parent classloader " + parent);
-            url = parent.getResource(name);
-            if (url != null) {
-                if (log.isDebugEnabled())
-                    log.debug("  --> Returning '" + url.toString() + "'");
-                return (url);
-            }
-        }
-
-        // (2) Search local repositories
-        url = findResource(name);
-        if (url != null) {
-            // Locating the repository for special handling in the case
-            // of a JAR
-            if (antiJARLocking) {
-                ResourceEntry entry = resourceEntries.get(name);
-                try {
-                    String repository = entry.codeBase.toString();
-                    if ((repository.endsWith(".jar"))
-                            && (!(name.endsWith(CLASS_FILE_SUFFIX)))) {
-                        // Copy binary content to the work directory if not 
present
-                        File resourceFile = new File(loaderDir, name);
-                        url = getURI(resourceFile);
-                    }
-                } catch (Exception e) {
-                    // Ignore
-                }
-            }
-            if (log.isDebugEnabled())
-                log.debug("  --> Returning '" + url.toString() + "'");
-            return (url);
-        }
-
-        // (3) Delegate to parent unconditionally if not already attempted
-        if( !delegate ) {
-            url = parent.getResource(name);
-            if (url != null) {
-                if (log.isDebugEnabled())
-                    log.debug("  --> Returning '" + url.toString() + "'");
-                return (url);
-            }
-        }
-
-        // (4) Resource was not found
-        if (log.isDebugEnabled())
-            log.debug("  --> Resource not found, returning null");
-        return (null);
-
-    }
-
-
-    /**
-     * Find the resource with the given name, and return an input stream
-     * that can be used for reading it.  The search order is as described
-     * for <code>getResource()</code>, after checking to see if the resource
-     * data has been previously cached.  If the resource cannot be found,
-     * return <code>null</code>.
-     *
-     * @param name Name of the resource to return an input stream for
-     */
-    @Override
-    public InputStream getResourceAsStream(String name) {
-
-        if (log.isDebugEnabled())
-            log.debug("getResourceAsStream(" + name + ")");
-        InputStream stream = null;
-
-        // (0) Check for a cached copy of this resource
-        stream = findLoadedResource(name);
-        if (stream != null) {
-            if (log.isDebugEnabled())
-                log.debug("  --> Returning stream from cache");
-            return (stream);
-        }
-
-        // (1) Delegate to parent if requested
-        if (delegate) {
-            if (log.isDebugEnabled())
-                log.debug("  Delegating to parent classloader " + parent);
-            stream = parent.getResourceAsStream(name);
-            if (stream != null) {
-                // FIXME - cache???
-                if (log.isDebugEnabled())
-                    log.debug("  --> Returning stream from parent");
-                return (stream);
-            }
-        }
-
-        // (2) Search local repositories
-        if (log.isDebugEnabled())
-            log.debug("  Searching local repositories");
-        URL url = findResource(name);
-        if (url != null) {
-            // FIXME - cache???
-            if (log.isDebugEnabled())
-                log.debug("  --> Returning stream from local");
-            stream = findLoadedResource(name);
-            try {
-                if (hasExternalRepositories && (stream == null))
-                    stream = url.openStream();
-            } catch (IOException e) {
-                // Ignore
-            }
-            if (stream != null)
-                return (stream);
-        }
-
-        // (3) Delegate to parent unconditionally
-        if (!delegate) {
-            if (log.isDebugEnabled())
-                log.debug("  Delegating to parent classloader unconditionally 
" + parent);
-            stream = parent.getResourceAsStream(name);
-            if (stream != null) {
-                // FIXME - cache???
-                if (log.isDebugEnabled())
-                    log.debug("  --> Returning stream from parent");
-                return (stream);
-            }
-        }
-
-        // (4) Resource was not found
-        if (log.isDebugEnabled())
-            log.debug("  --> Resource not found, returning null");
-        return (null);
-
-    }
-
-
-    /**
-     * Load the class with the specified name.  This method searches for
-     * classes in the same manner as <code>loadClass(String, boolean)</code>
-     * with <code>false</code> as the second argument.
-     *
-     * @param name Name of the class to be loaded
-     *
-     * @exception ClassNotFoundException if the class was not found
-     */
-    @Override
-    public Class<?> loadClass(String name) throws ClassNotFoundException {
-
-        return (loadClass(name, false));
-
-    }
-
-
-    /**
-     * Load the class with the specified name, searching using the following
-     * algorithm until it finds and returns the class.  If the class cannot
-     * be found, returns <code>ClassNotFoundException</code>.
-     * <ul>
-     * <li>Call <code>findLoadedClass(String)</code> to check if the
-     *     class has already been loaded.  If it has, the same
-     *     <code>Class</code> object is returned.</li>
-     * <li>If the <code>delegate</code> property is set to <code>true</code>,
-     *     call the <code>loadClass()</code> method of the parent class
-     *     loader, if any.</li>
-     * <li>Call <code>findClass()</code> to find this class in our locally
-     *     defined repositories.</li>
-     * <li>Call the <code>loadClass()</code> method of our parent
-     *     class loader, if any.</li>
-     * </ul>
-     * If the class was found using the above steps, and the
-     * <code>resolve</code> flag is <code>true</code>, this method will then
-     * call <code>resolveClass(Class)</code> on the resulting Class object.
-     *
-     * @param name Name of the class to be loaded
-     * @param resolve If <code>true</code> then resolve the class
-     *
-     * @exception ClassNotFoundException if the class was not found
-     */
-    @Override
-    public synchronized Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException {
-
-        if (log.isDebugEnabled())
-            log.debug("loadClass(" + name + ", " + resolve + ")");
-        Class<?> clazz = null;
-
-        // Log access to stopped classloader
-        if (!started) {
-            try {
-                throw new IllegalStateException();
-            } catch (IllegalStateException e) {
-                log.info(sm.getString("webappClassLoader.stopped", name), e);
-            }
-        }
-
-        // (0) Check our previously loaded local class cache
-        clazz = findLoadedClass0(name);
-        if (clazz != null) {
-            if (log.isDebugEnabled())
-                log.debug("  Returning class from cache");
-            if (resolve)
-                resolveClass(clazz);
-            return (clazz);
-        }
-
-        // (0.1) Check our previously loaded class cache
-        clazz = findLoadedClass(name);
-        if (clazz != null) {
-            if (log.isDebugEnabled())
-                log.debug("  Returning class from cache");
-            if (resolve)
-                resolveClass(clazz);
-            return (clazz);
-        }
-
-        // (0.2) Try loading the class with the system class loader, to prevent
-        //       the webapp from overriding J2SE classes
-        try {
-            clazz = j2seClassLoader.loadClass(name);
-            if (clazz != null) {
-                if (resolve)
-                    resolveClass(clazz);
-                return (clazz);
-            }
-        } catch (ClassNotFoundException e) {
-            // Ignore
-        }
-
-        // (0.5) Permission to access this class when using a SecurityManager
-        if (securityManager != null) {
-            int i = name.lastIndexOf('.');
-            if (i >= 0) {
-                try {
-                    securityManager.checkPackageAccess(name.substring(0,i));
-                } catch (SecurityException se) {
-                    String error = "Security Violation, attempt to use " +
-                        "Restricted Class: " + name;
-                    log.info(error, se);
-                    throw new ClassNotFoundException(error, se);
-                }
-            }
-        }
-
-        boolean delegateLoad = delegate || filter(name);
-
-        // (1) Delegate to our parent if requested
-        if (delegateLoad) {
-            if (log.isDebugEnabled())
-                log.debug("  Delegating to parent classloader1 " + parent);
-            try {
-                clazz = Class.forName(name, false, parent);
-                if (clazz != null) {
-                    if (log.isDebugEnabled())
-                        log.debug("  Loading class from parent");
-                    if (resolve)
-                        resolveClass(clazz);
-                    return (clazz);
-                }
-            } catch (ClassNotFoundException e) {
-                // Ignore
-            }
-        }
-
-        // (2) Search local repositories
-        if (log.isDebugEnabled())
-            log.debug("  Searching local repositories");
-        try {
-            clazz = findClass(name);
-            if (clazz != null) {
-                if (log.isDebugEnabled())
-                    log.debug("  Loading class from local repository");
-                if (resolve)
-                    resolveClass(clazz);
-                return (clazz);
-            }
-        } catch (ClassNotFoundException e) {
-            // Ignore
-        }
-
-        // (3) Delegate to parent unconditionally
-        if (!delegateLoad) {
-            if (log.isDebugEnabled())
-                log.debug("  Delegating to parent classloader at end: " + 
parent);
-            try {
-                clazz = Class.forName(name, false, parent);
-                if (clazz != null) {
-                    if (log.isDebugEnabled())
-                        log.debug("  Loading class from parent");
-                    if (resolve)
-                        resolveClass(clazz);
-                    return (clazz);
-                }
-            } catch (ClassNotFoundException e) {
-                // Ignore
-            }
-        }
-
-        throw new ClassNotFoundException(name);
-
-    }
-
-
-    /**
-     * Get the Permissions for a CodeSource.  If this instance
-     * of WebappClassLoader is for a web application context,
-     * add read FilePermission or JndiPermissions for the base
-     * directory (if unpacked),
-     * the context URL, and jar file resources.
-     *
-     * @param codeSource where the code was loaded from
-     * @return PermissionCollection for CodeSource
-     */
-    @Override
-    protected PermissionCollection getPermissions(CodeSource codeSource) {
-
-        String codeUrl = codeSource.getLocation().toString();
-        PermissionCollection pc;
-        if ((pc = loaderPC.get(codeUrl)) == null) {
-            pc = super.getPermissions(codeSource);
-            if (pc != null) {
-                Iterator<Permission> perms = permissionList.iterator();
-                while (perms.hasNext()) {
-                    Permission p = perms.next();
-                    pc.add(p);
-                }
-                loaderPC.put(codeUrl,pc);
-            }
-        }
-        return (pc);
-
-    }
-
-
-    /**
-     * Returns the search path of URLs for loading classes and resources.
-     * This includes the original list of URLs specified to the constructor,
-     * along with any URLs subsequently appended by the addURL() method.
-     * @return the search path of URLs for loading classes and resources.
-     */
-    @Override
-    public URL[] getURLs() {
-
-        if (repositoryURLs != null) {
-            return repositoryURLs.clone();
-        }
-
-        URL[] external = super.getURLs();
-
-        int filesLength = files.length;
-        int jarFilesLength = jarRealFiles.length;
-        int externalsLength = external.length;
-        int off = 0;
-        int i;
-
-        try {
-
-            URL[] urls = new URL[filesLength + jarFilesLength + 
externalsLength];
-            if (searchExternalFirst) {
-                for (i = 0; i < externalsLength; i++) {
-                    urls[i] = external[i];
-                }
-                off = externalsLength;
-            }
-            for (i = 0; i < filesLength; i++) {
-                urls[off + i] = getURI(files[i]);
-            }
-            off += filesLength;
-            for (i = 0; i < jarFilesLength; i++) {
-                urls[off + i] = getURI(jarRealFiles[i]);
-            }
-            off += jarFilesLength;
-            if (!searchExternalFirst) {
-                for (i = 0; i < externalsLength; i++) {
-                    urls[off + i] = external[i];
-                }
-            }
-
-            repositoryURLs = urls;
-
-        } catch (MalformedURLException e) {
-            repositoryURLs = new URL[0];
-        }
-
-        return repositoryURLs.clone();
-
-    }
-
-
-    // ------------------------------------------------------ Lifecycle Methods
-
-
-    /**
-     * Add a lifecycle event listener to this component.
-     *
-     * @param listener The listener to add
-     */
-    @Override
-    public void addLifecycleListener(LifecycleListener listener) {
-        // NOOP
-    }
-
-
-    /**
-     * Get the lifecycle listeners associated with this lifecycle. If this
-     * Lifecycle has no listeners registered, a zero-length array is returned.
-     */
-    @Override
-    public LifecycleListener[] findLifecycleListeners() {
-        return new LifecycleListener[0];
-    }
-
-
-    /**
-     * Remove a lifecycle event listener from this component.
-     *
-     * @param listener The listener to remove
-     */
-    @Override
-    public void removeLifecycleListener(LifecycleListener listener) {
-        // NOOP
-    }
-
-
-    /**
-     * Obtain the current state of the source component.
-     *
-     * @return The current state of the source component.
-     */
-    @Override
-    public LifecycleState getState() {
-        return LifecycleState.NEW;
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String getStateName() {
-        return getState().toString();
-    }
-
-
-    @Override
-    public void init() {
-        // NOOP
-    }
-
-
-    /**
-     * Start the class loader.
-     *
-     * @exception LifecycleException if a lifecycle error occurs
-     */
-    @Override
-    public void start() throws LifecycleException {
-
-        started = true;
-        String encoding = null;
-        try {
-            encoding = System.getProperty("file.encoding");
-        } catch (SecurityException e) {
-            return;
-        }
-        if (encoding.indexOf("EBCDIC")!=-1) {
-            needConvert = true;
-        }
-
-        for (int i = 0; i < repositories.length; i++) {
-            if (repositories[i].equals("/WEB-INF/classes/")) {
-                try {
-                    webInfClassesCodeBase = files[i].toURI().toURL();
-                } catch (MalformedURLException e) {
-                    // Ignore - leave it as null
-                }
-                break;
-            }
-        }
-        
-    }
-
-
-    public boolean isStarted() {
-        return started;
-    }
-
-    /**
-     * Stop the class loader.
-     *
-     * @exception LifecycleException if a lifecycle error occurs
-     */
-    @Override
-    public void stop() throws LifecycleException {
-
-        // Clearing references should be done before setting started to
-        // false, due to possible side effects
-        clearReferences();
-
-        started = false;
-
-        int length = files.length;
-        for (int i = 0; i < length; i++) {
-            files[i] = null;
-        }
-
-        length = jarFiles.length;
-        for (int i = 0; i < length; i++) {
-            try {
-                if (jarFiles[i] != null) {
-                    jarFiles[i].close();
-                }
-            } catch (IOException e) {
-                // Ignore
-            }
-            jarFiles[i] = null;
-        }
-
-        notFoundResources.clear();
-        resourceEntries.clear();
-        resources = null;
-        repositories = null;
-        repositoryURLs = null;
-        files = null;
-        jarFiles = null;
-        jarRealFiles = null;
-        jarPath = null;
-        jarNames = null;
-        lastModifiedDates = null;
-        paths = null;
-        hasExternalRepositories = false;
-        parent = null;
-        webInfClassesCodeBase = null;
-
-        permissionList.clear();
-        loaderPC.clear();
-
-        if (loaderDir != null) {
-            deleteDir(loaderDir);
-        }
-
-    }
-
-
-    @Override
-    public void destroy() {
-        // NOOP
-    }
-
-
-    /**
-     * Used to periodically signal to the classloader to release
-     * JAR resources.
-     */
-    public void closeJARs(boolean force) {
-        if (jarFiles.length > 0) {
-                synchronized (jarFiles) {
-                    if (force || (System.currentTimeMillis()
-                                  > (lastJarAccessed + 90000))) {
-                        for (int i = 0; i < jarFiles.length; i++) {
-                            try {
-                                if (jarFiles[i] != null) {
-                                    jarFiles[i].close();
-                                    jarFiles[i] = null;
-                                }
-                            } catch (IOException e) {
-                                if (log.isDebugEnabled()) {
-                                    log.debug("Failed to close JAR", e);
-                                }
-                            }
-                        }
-                    }
-                }
-        }
-    }
-
-
-    // ------------------------------------------------------ Protected Methods
-
-    protected ClassLoader getJavaseClassLoader() {
-        return j2seClassLoader;
-    }
-
-    protected void setJavaseClassLoader(ClassLoader classLoader) {
-        if (classLoader == null) {
-            throw new IllegalArgumentException(
-                    sm.getString("webappClassLoader.javaseClassLoaderNull"));
-        }
-        j2seClassLoader = classLoader;
-    }
-
-    /**
-     * Clear references.
-     */
-    protected void clearReferences() {
-
-        // De-register any remaining JDBC drivers
-        clearReferencesJdbc();
-
-        // Stop any threads the web application started
-        clearReferencesThreads();
-
-        // Check for leaks triggered by ThreadLocals loaded by this class 
loader
-        checkThreadLocalsForLeaks();
-
-        // Clear RMI Targets loaded by this class loader
-        clearReferencesRmiTargets();
-
-        // Null out any static or final fields from loaded classes,
-        // as a workaround for apparent garbage collection bugs
-        if (clearReferencesStatic) {
-            clearReferencesStaticFinal();
-        }
-
-         // Clear the IntrospectionUtils cache.
-        IntrospectionUtils.clear();
-
-        // Clear the classloader reference in common-logging
-        if (clearReferencesLogFactoryRelease) {
-            org.apache.juli.logging.LogFactory.release(this);
-        }
-
-        // Clear the resource bundle cache
-        // This shouldn't be necessary, the cache uses weak references but
-        // it has caused leaks. Oddly, using the leak detection code in
-        // standard host allows the class loader to be GC'd. This has been seen
-        // on Sun but not IBM JREs. Maybe a bug in Sun's GC impl?
-        clearReferencesResourceBundles();
-
-        // Clear the classloader reference in the VM's bean introspector
-        java.beans.Introspector.flushCaches();
-
-    }
-
-
-    /**
-     * 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...
-     */
-    private final void clearReferencesJdbc() {
-        InputStream is = getResourceAsStream(
-                "org/apache/catalina/loader/JdbcLeakPrevention.class");
-        // We know roughly how big the class will be (~ 1K) so allow 2k as a
-        // starting point
-        byte[] classBytes = new byte[2048];
-        int offset = 0;
-        try {
-            int read = is.read(classBytes, offset, classBytes.length-offset);
-            while (read > -1) {
-                offset += read;
-                if (offset == classBytes.length) {
-                    // Buffer full - double size
-                    byte[] tmp = new byte[classBytes.length * 2];
-                    System.arraycopy(classBytes, 0, tmp, 0, classBytes.length);
-                    classBytes = tmp;
-                }
-                read = is.read(classBytes, offset, classBytes.length-offset);
-            }
-            Class<?> lpClass =
-                defineClass("org.apache.catalina.loader.JdbcLeakPrevention",
-                    classBytes, 0, offset, 
this.getClass().getProtectionDomain());
-            Object obj = lpClass.newInstance();
-            @SuppressWarnings("unchecked") // clearJdbcDriverRegistrations() 
returns List<String>
-            List<String> driverNames = (List<String>) obj.getClass().getMethod(
-                    "clearJdbcDriverRegistrations").invoke(obj);
-            for (String name : driverNames) {
-                log.error(sm.getString("webappClassLoader.clearJdbc",
-                        contextName, name));
-            }
-        } catch (Exception e) {
-            // So many things to go wrong above...
-            Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
-            ExceptionUtils.handleThrowable(t);
-            log.warn(sm.getString(
-                    "webappClassLoader.jdbcRemoveFailed", contextName), t);
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException ioe) {
-                    log.warn(sm.getString(
-                            "webappClassLoader.jdbcRemoveStreamError",
-                            contextName), ioe);
-                }
-            }
-        }
-    }
-
-
-    private final void clearReferencesStaticFinal() {
-
-        @SuppressWarnings("unchecked") // resourceEntries is HashMap<String, 
ResourceEntry>
-        Collection<ResourceEntry> values =
-            ((HashMap<String,ResourceEntry>) resourceEntries.clone()).values();
-        Iterator<ResourceEntry> loadedClasses = values.iterator();
-        //
-        // walk through all loaded class to trigger initialization for
-        //    any uninitialized classes, otherwise initialization of
-        //    one class may call a previously cleared class.
-        while(loadedClasses.hasNext()) {
-            ResourceEntry entry = loadedClasses.next();
-            if (entry.loadedClass != null) {
-                Class<?> clazz = entry.loadedClass;
-                try {
-                    Field[] fields = clazz.getDeclaredFields();
-                    for (int i = 0; i < fields.length; i++) {
-                        if(Modifier.isStatic(fields[i].getModifiers())) {
-                            fields[i].get(null);
-                            break;
-                        }
-                    }
-                } catch(Throwable t) {
-                    // Ignore
-                }
-            }
-        }
-        loadedClasses = values.iterator();
-        while (loadedClasses.hasNext()) {
-            ResourceEntry entry = loadedClasses.next();
-            if (entry.loadedClass != null) {
-                Class<?> clazz = entry.loadedClass;
-                try {
-                    Field[] fields = clazz.getDeclaredFields();
-                    for (int i = 0; i < fields.length; i++) {
-                        Field field = fields[i];
-                        int mods = field.getModifiers();
-                        if (field.getType().isPrimitive()
-                                || (field.getName().indexOf("$") != -1)) {
-                            continue;
-                        }
-                        if (Modifier.isStatic(mods)) {
-                            try {
-                                field.setAccessible(true);
-                                if (Modifier.isFinal(mods)) {
-                                    if 
(!((field.getType().getName().startsWith("java."))
-                                            || 
(field.getType().getName().startsWith("javax.")))) {
-                                        nullInstance(field.get(null));
-                                    }
-                                } else {
-                                    field.set(null, null);
-                                    if (log.isDebugEnabled()) {
-                                        log.debug("Set field " + 
field.getName()
-                                                + " to null in class " + 
clazz.getName());
-                                    }
-                                }
-                            } catch (Throwable t) {
-                                ExceptionUtils.handleThrowable(t);
-                                if (log.isDebugEnabled()) {
-                                    log.debug("Could not set field " + 
field.getName()
-                                            + " to null in class " + 
clazz.getName(), t);
-                                }
-                            }
-                        }
-                    }
-                } catch (Throwable t) {
-                    ExceptionUtils.handleThrowable(t);
-                    if (log.isDebugEnabled()) {
-                        log.debug("Could not clean fields for class " + 
clazz.getName(), t);
-                    }
-                }
-            }
-        }
-
-    }
-
-
-    private void nullInstance(Object instance) {
-        if (instance == null) {
-            return;
-        }
-        Field[] fields = instance.getClass().getDeclaredFields();
-        for (int i = 0; i < fields.length; i++) {
-            Field field = fields[i];
-            int mods = field.getModifiers();
-            if (field.getType().isPrimitive()
-                    || (field.getName().indexOf("$") != -1)) {
-                continue;
-            }
-            try {
-                field.setAccessible(true);
-                if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) {
-                    // Doing something recursively is too risky
-                    continue;
-                }
-                Object value = field.get(instance);
-                if (null != value) {
-                    Class<? extends Object> valueClass = value.getClass();
-                    if (!loadedByThisOrChild(valueClass)) {
-                        if (log.isDebugEnabled()) {
-                            log.debug("Not setting field " + field.getName() +
-                                    " to null in object of class " +
-                                    instance.getClass().getName() +
-                                    " because the referenced object was of 
type " +
-                                    valueClass.getName() +
-                                    " which was not loaded by this 
WebappClassLoader.");
-                        }
-                    } else {
-                        field.set(instance, null);
-                        if (log.isDebugEnabled()) {
-                            log.debug("Set field " + field.getName()
-                                    + " to null in class " + 
instance.getClass().getName());
-                        }
-                    }
-                }
-            } catch (Throwable t) {
-                ExceptionUtils.handleThrowable(t);
-                if (log.isDebugEnabled()) {
-                    log.debug("Could not set field " + field.getName()
-                            + " to null in object instance of class "
-                            + instance.getClass().getName(), t);
-                }
-            }
-        }
-    }
-
-
-    @SuppressWarnings("deprecation") // thread.stop()
-    private void clearReferencesThreads() {
-        Thread[] threads = getThreads();
-        List<Thread> executorThreadsToStop = new ArrayList<Thread>();
-
-        // Iterate over the set of threads
-        for (Thread thread : threads) {
-            if (thread != null) {
-                ClassLoader ccl = thread.getContextClassLoader();
-                if (ccl == this) {
-                    // Don't warn about this thread
-                    if (thread == Thread.currentThread()) {
-                        continue;
-                    }
-
-                    // JVM controlled threads
-                    ThreadGroup tg = thread.getThreadGroup();
-                    if (tg != null &&
-                            JVM_THREAD_GROUP_NAMES.contains(tg.getName())) {
-
-                        // HttpClient keep-alive threads
-                        if (clearReferencesHttpClientKeepAliveThread &&
-                                thread.getName().equals("Keep-Alive-Timer")) {
-                            thread.setContextClassLoader(parent);
-                            log.debug(sm.getString(
-                                    
"webappClassLoader.checkThreadsHttpClient"));
-                        }
-
-                        // Don't warn about remaining JVM controlled threads
-                        continue;
-                    }
-
-                    // Skip threads that have already died
-                    if (!thread.isAlive()) {
-                        continue;
-                    }
-
-                    // TimerThread can be stopped safely so treat separately
-                    // "java.util.TimerThread" in Sun/Oracle JDK
-                    // "java.util.Timer$TimerImpl" in Apache Harmony and in 
IBM JDK
-                    if 
(thread.getClass().getName().startsWith("java.util.Timer") &&
-                            clearReferencesStopTimerThreads) {
-                        clearReferencesStopTimerThread(thread);
-                        continue;
-                    }
-
-                    if (isRequestThread(thread)) {
-                        
log.error(sm.getString("webappClassLoader.warnRequestThread",
-                                contextName, thread.getName()));
-                    } else {
-                        log.error(sm.getString("webappClassLoader.warnThread",
-                                contextName, thread.getName()));
-                    }
-
-                    // Don't try an stop the threads unless explicitly
-                    // configured to do so
-                    if (!clearReferencesStopThreads) {
-                        continue;
-                    }
-
-                    // If the thread has been started via an executor, try
-                    // shutting down the executor
-                    boolean usingExecutor = false;
-                    try {
-
-                        // Runnable wrapped by Thread
-                        // "target" in Sun/Oracle JDK
-                        // "runnable" in IBM JDK
-                        // "action" in Apache Harmony
-                        Object target = null;
-                        for (String fieldName : new String[] { "target",
-                                "runnable", "action" }) {
-                            try {
-                                Field targetField = thread.getClass()
-                                        .getDeclaredField(fieldName);
-                                targetField.setAccessible(true);
-                                target = targetField.get(thread);
-                                break;
-                            } catch (NoSuchFieldException nfe) {
-                                continue;
-                            }
-                        }
-
-                        // "java.util.concurrent" code is in public domain,
-                        // so all implementations are similar
-                        if (target != null &&
-                                target.getClass().getCanonicalName() != null
-                                && target.getClass().getCanonicalName().equals(
-                                
"java.util.concurrent.ThreadPoolExecutor.Worker")) {
-                            Field executorField =
-                                target.getClass().getDeclaredField("this$0");
-                            executorField.setAccessible(true);
-                            Object executor = executorField.get(target);
-                            if (executor instanceof ThreadPoolExecutor) {
-                                ((ThreadPoolExecutor) executor).shutdownNow();
-                                usingExecutor = true;
-                            }
-                        }
-                    } catch (SecurityException e) {
-                        log.warn(sm.getString(
-                                "webappClassLoader.stopThreadFail",
-                                thread.getName(), contextName), e);
-                    } catch (NoSuchFieldException e) {
-                        log.warn(sm.getString(
-                                "webappClassLoader.stopThreadFail",
-                                thread.getName(), contextName), e);
-                    } catch (IllegalArgumentException e) {
-                        log.warn(sm.getString(
-                                "webappClassLoader.stopThreadFail",
-                                thread.getName(), contextName), e);
-                    } catch (IllegalAccessException e) {
-                        log.warn(sm.getString(
-                                "webappClassLoader.stopThreadFail",
-                                thread.getName(), contextName), e);
-                    }
-
-                    if (usingExecutor) {
-                        // Executor may take a short time to stop all the
-                        // threads. Make a note of threads that should be

[... 1278 lines stripped ...]


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

Reply via email to