This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch redoparent
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git

commit d04637efeabb07e1dd99714a955ce9796ab1facd
Author: David Blevins <[email protected]>
AuthorDate: Mon Mar 22 22:52:05 2021 -0700

    Delete Tomcat modifications now that we use Tomcat 10
---
 .../catalina/loader/WebappClassLoaderBase.java     | 2725 --------------------
 .../MbeansDescriptorsIntrospectionSource.java      |  392 ---
 2 files changed, 3117 deletions(-)

diff --git 
a/src/patch/java/org/apache/catalina/loader/WebappClassLoaderBase.java 
b/src/patch/java/org/apache/catalina/loader/WebappClassLoaderBase.java
deleted file mode 100644
index 2e75d4c..0000000
--- a/src/patch/java/org/apache/catalina/loader/WebappClassLoaderBase.java
+++ /dev/null
@@ -1,2725 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.catalina.loader;
-
-import java.io.File;
-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.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLClassLoader;
-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.security.cert.Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.ConcurrentModificationException;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.concurrent.ConcurrentHashMap;
-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.Manifest;
-
-import org.apache.catalina.Container;
-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.catalina.WebResource;
-import org.apache.catalina.WebResourceRoot;
-import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
-import org.apache.juli.WebappProperties;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.InstrumentableClassLoader;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.IntrospectionUtils;
-import org.apache.tomcat.util.compat.JreCompat;
-import org.apache.tomcat.util.res.StringManager;
-import org.apache.tomcat.util.security.PermissionCheck;
-
-/**
- * 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.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or
- * security is made unless a security manager is present.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong> - As of 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 abstract class WebappClassLoaderBase extends URLClassLoader
-        implements Lifecycle, InstrumentableClassLoader, WebappProperties, 
PermissionCheck {
-
-    private static final Log log = 
LogFactory.getLog(WebappClassLoaderBase.class);
-
-    /**
-     * 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<>();
-
-    private static final String JVM_THREAD_GROUP_SYSTEM = "system";
-
-    private static final String CLASS_FILE_SUFFIX = ".class";
-
-    static {
-        if (!JreCompat.isGraalAvailable()) {
-            ClassLoader.registerAsParallelCapable();
-        }
-        JVM_THREAD_GROUP_NAMES.add(JVM_THREAD_GROUP_SYSTEM);
-        JVM_THREAD_GROUP_NAMES.add("RMI Runtime");
-    }
-
-    protected class PrivilegedFindClassByName implements 
PrivilegedAction<Class<?>> {
-
-        private final String name;
-
-        PrivilegedFindClassByName(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public Class<?> run() {
-            return findClassInternal(name);
-        }
-    }
-
-
-    protected static final class PrivilegedGetClassLoader implements 
PrivilegedAction<ClassLoader> {
-
-        private final Class<?> clazz;
-
-        public PrivilegedGetClassLoader(Class<?> clazz){
-            this.clazz = clazz;
-        }
-
-        @Override
-        public ClassLoader run() {
-            return clazz.getClassLoader();
-        }
-    }
-
-
-    protected final class PrivilegedJavaseGetResource implements 
PrivilegedAction<URL> {
-
-        private final String name;
-
-        public PrivilegedJavaseGetResource(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public URL run() {
-            return javaseClassLoader.getResource(name);
-        }
-    }
-
-
-    // ------------------------------------------------------- Static Variables
-
-    /**
-     * The string manager for this package.
-     */
-    protected static final StringManager sm =
-        StringManager.getManager(Constants.Package);
-
-
-    // ----------------------------------------------------------- Constructors
-
-    /**
-     * Construct a new ClassLoader with no defined repositories and no
-     * parent ClassLoader.
-     */
-    protected WebappClassLoaderBase() {
-
-        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.javaseClassLoader = j;
-
-        securityManager = System.getSecurityManager();
-        if (securityManager != null) {
-            refreshPolicy();
-        }
-    }
-
-
-    /**
-     * 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
-     */
-    protected WebappClassLoaderBase(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.javaseClassLoader = j;
-
-        securityManager = System.getSecurityManager();
-        if (securityManager != null) {
-            refreshPolicy();
-        }
-    }
-
-
-    // ----------------------------------------------------- Instance Variables
-
-    /**
-     * Associated web resources for this webapp.
-     */
-    protected WebResourceRoot resources = null;
-
-
-    /**
-     * The cache of ResourceEntry for classes and resources we have loaded,
-     * keyed by resource path, not binary name. Path is used as the key since
-     * resources may be requested by binary name (classes) or path (other
-     * resources such as property files) and the mapping from binary name to
-     * path is unambiguous but the reverse mapping is ambiguous.
-     */
-    protected final Map<String, ResourceEntry> resourceEntries =
-            new ConcurrentHashMap<>();
-
-
-    /**
-     * 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;
-
-
-    private final Map<String,Long> jarModificationTimes = new HashMap<>();
-
-
-    /**
-     * A list of read File Permission's required if this loader is for a web
-     * application context.
-     */
-    protected final ArrayList<Permission> permissionList = new ArrayList<>();
-
-
-    /**
-     * The PermissionCollection for each CodeSource for a web
-     * application context.
-     */
-    protected final HashMap<String, PermissionCollection> loaderPC = new 
HashMap<>();
-
-
-    /**
-     * Instance of the SecurityManager installed.
-     */
-    protected final SecurityManager securityManager;
-
-
-    /**
-     * The parent class loader.
-     */
-    protected final ClassLoader parent;
-
-
-    /**
-     * The bootstrap class loader used to load the JavaSE classes. In some
-     * implementations this class loader is always <code>null</code> and in
-     * those cases {@link ClassLoader#getParent()} will be called recursively 
on
-     * the system class loader and the last non-null result used.
-     */
-    private ClassLoader javaseClassLoader;
-
-
-    /**
-     * Enables the RMI Target memory leak detection to be controlled. This is
-     * necessary since the detection can only work on Java 9 if some of the
-     * modularity checks are disabled.
-     */
-    private boolean clearReferencesRmiTargets = true;
-
-    /**
-     * 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(ClassLoader)}
-     * 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 ClassLoader} to
-     * {@link ClassLoader#getParent()} 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;
-
-    /**
-     * Should Tomcat attempt to clear references to classes loaded by this 
class
-     * loader from the ObjectStreamClass caches?
-     */
-    private boolean clearReferencesObjectStreamClassCaches = true;
-
-    /**
-     * Should Tomcat attempt to clear references to classes loaded by this 
class
-     * loader from ThreadLocals?
-     */
-    private boolean clearReferencesThreadLocals = true;
-
-    /**
-     * Should Tomcat skip the memory leak checks when the web application is
-     * stopped as part of the process of shutting down the JVM?
-     */
-    private boolean skipMemoryLeakChecksOnJvmShutdown = false;
-
-    /**
-     * 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<>();
-
-
-    /**
-     * Flag that indicates that {@link #addURL(URL)} has been called which
-     * creates a requirement to check the super class when searching for
-     * resources.
-     */
-    private boolean hasExternalRepositories = false;
-
-
-    /**
-     * Repositories managed by this class rather than the super class.
-     */
-    private List<URL> localRepositories = new ArrayList<>();
-
-
-    private volatile LifecycleState state = LifecycleState.NEW;
-
-
-    // ------------------------------------------------------------- Properties
-
-    /**
-     * @return associated resources.
-     */
-    public WebResourceRoot getResources() {
-        return this.resources;
-    }
-
-
-    /**
-     * Set associated resources.
-     * @param resources the resources from which the classloader will
-     *     load the classes
-     */
-    public void setResources(WebResourceRoot resources) {
-        this.resources = resources;
-    }
-
-
-    /**
-     * @return the context name for this class loader.
-     */
-    public String getContextName() {
-        if (resources == null) {
-            return "Unknown";
-        } else {
-            return resources.getContext().getBaseName();
-        }
-    }
-
-
-    /**
-     * Return the "delegate first" flag for this class loader.
-     * @return <code>true</code> if the class lookup will delegate to
-     *   the parent first. The default in Tomcat is <code>false</code>.
-     */
-    public boolean getDelegate() {
-        return this.delegate;
-    }
-
-
-    /**
-     * 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.
-     *
-     * @param delegate The new "delegate first" flag
-     */
-    public void setDelegate(boolean delegate) {
-        this.delegate = delegate;
-    }
-
-
-    /**
-     * If there is a Java SecurityManager create a read permission for the
-     * target of the given URL as appropriate.
-     *
-     * @param url URL for a file or directory on local system
-     */
-    void addPermission(URL url) {
-        if (url == null) {
-            return;
-        }
-        if (securityManager != null) {
-            String protocol = url.getProtocol();
-            if ("file".equalsIgnoreCase(protocol)) {
-                URI uri;
-                File f;
-                String path;
-                try {
-                    uri = url.toURI();
-                    f = new File(uri);
-                    path = f.getCanonicalPath();
-                } catch (IOException | URISyntaxException e) {
-                    log.warn(sm.getString(
-                            "webappClassLoader.addPermisionNoCanonicalFile",
-                            url.toExternalForm()));
-                    return;
-                }
-                if (f.isFile()) {
-                    // Allow the file to be read
-                    addPermission(new FilePermission(path, "read"));
-                } else if (f.isDirectory()) {
-                    addPermission(new FilePermission(path, "read"));
-                    addPermission(new FilePermission(
-                            path + File.separator + "-", "read"));
-                } else {
-                    // File does not exist - ignore (shouldn't happen)
-                }
-            } else {
-                // Unsupported URL protocol
-                log.warn(sm.getString(
-                        "webappClassLoader.addPermisionNoProtocol",
-                        protocol, url.toExternalForm()));
-            }
-        }
-    }
-
-
-    /**
-     * If there is a Java SecurityManager create a Permission.
-     *
-     * @param permission The permission
-     */
-    void addPermission(Permission permission) {
-        if ((securityManager != null) && (permission != null)) {
-            permissionList.add(permission);
-        }
-    }
-
-
-    public boolean getClearReferencesRmiTargets() {
-        return this.clearReferencesRmiTargets;
-    }
-
-
-    public void setClearReferencesRmiTargets(boolean 
clearReferencesRmiTargets) {
-        this.clearReferencesRmiTargets = clearReferencesRmiTargets;
-    }
-
-
-    /**
-     * @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;
-    }
-
-
-    public boolean getClearReferencesObjectStreamClassCaches() {
-        return clearReferencesObjectStreamClassCaches;
-    }
-
-
-    public void setClearReferencesObjectStreamClassCaches(
-            boolean clearReferencesObjectStreamClassCaches) {
-        this.clearReferencesObjectStreamClassCaches = 
clearReferencesObjectStreamClassCaches;
-    }
-
-
-    public boolean getClearReferencesThreadLocals() {
-        return clearReferencesThreadLocals;
-    }
-
-
-    public void setClearReferencesThreadLocals(boolean 
clearReferencesThreadLocals) {
-        this.clearReferencesThreadLocals = clearReferencesThreadLocals;
-    }
-
-
-    public boolean getSkipMemoryLeakChecksOnJvmShutdown() {
-        return skipMemoryLeakChecksOnJvmShutdown;
-    }
-
-
-    public void setSkipMemoryLeakChecksOnJvmShutdown(boolean 
skipMemoryLeakChecksOnJvmShutdown) {
-        this.skipMemoryLeakChecksOnJvmShutdown = 
skipMemoryLeakChecksOnJvmShutdown;
-    }
-
-
-    // ------------------------------------------------------- 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()));
-        }
-    }
-
-    protected void copyStateWithoutTransformers(WebappClassLoaderBase base) {
-        base.resources = this.resources;
-        base.delegate = this.delegate;
-        base.state = LifecycleState.NEW;
-        base.clearReferencesStopThreads = this.clearReferencesStopThreads;
-        base.clearReferencesStopTimerThreads = 
this.clearReferencesStopTimerThreads;
-        base.clearReferencesLogFactoryRelease = 
this.clearReferencesLogFactoryRelease;
-        base.clearReferencesHttpClientKeepAliveThread = 
this.clearReferencesHttpClientKeepAliveThread;
-        base.jarModificationTimes.putAll(this.jarModificationTimes);
-        base.permissionList.addAll(this.permissionList);
-        base.loaderPC.putAll(this.loaderPC);
-    }
-
-    /**
-     * Have one or more classes or resources been modified so that a reload
-     * is appropriate?
-     * @return <code>true</code> if there's been a modification
-     */
-    public boolean modified() {
-
-        if (log.isDebugEnabled())
-            log.debug("modified()");
-
-        for (Entry<String,ResourceEntry> entry : resourceEntries.entrySet()) {
-            long cachedLastModified = entry.getValue().lastModified;
-            long lastModified = resources.getClassLoaderResource(
-                    entry.getKey()).getLastModified();
-            if (lastModified != cachedLastModified) {
-                if( log.isDebugEnabled() )
-                    
log.debug(sm.getString("webappClassLoader.resourceModified",
-                            entry.getKey(),
-                            new Date(cachedLastModified),
-                            new Date(lastModified)));
-                return true;
-            }
-        }
-
-        // Check if JARs have been added or removed
-        WebResource[] jars = resources.listResources("/WEB-INF/lib");
-        // Filter out non-JAR resources
-
-        int jarCount = 0;
-        for (WebResource jar : jars) {
-            if (jar.getName().endsWith(".jar") && jar.isFile() && 
jar.canRead()) {
-                jarCount++;
-                Long recordedLastModified = 
jarModificationTimes.get(jar.getName());
-                if (recordedLastModified == null) {
-                    // Jar has been added
-                    log.info(sm.getString("webappClassLoader.jarsAdded",
-                            resources.getContext().getName()));
-                    return true;
-                }
-                if (recordedLastModified.longValue() != jar.getLastModified()) 
{
-                    // Jar has been changed
-                    log.info(sm.getString("webappClassLoader.jarsModified",
-                            resources.getContext().getName()));
-                    return true;
-                }
-            }
-        }
-
-        if (jarCount < jarModificationTimes.size()){
-            log.info(sm.getString("webappClassLoader.jarsRemoved",
-                    resources.getContext().getName()));
-            return true;
-        }
-
-
-        // No classes have been modified
-        return false;
-    }
-
-
-    @Override
-    public String toString() {
-
-        StringBuilder sb = new StringBuilder(this.getClass().getSimpleName());
-        sb.append("\r\n  context: ");
-        sb.append(getContextName());
-        sb.append("\r\n  delegate: ");
-        sb.append(delegate);
-        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
-
-
-    // Note: exposed for use by 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 The binary 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 + ")");
-
-        checkStateForClassLoading(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 + ")");
-            try {
-                if (securityManager != null) {
-                    PrivilegedAction<Class<?>> dp =
-                        new PrivilegedFindClassByName(name);
-                    clazz = AccessController.doPrivileged(dp);
-                } else {
-                    clazz = findClassInternal(name);
-                }
-            } catch(AccessControlException ace) {
-                log.warn(sm.getString("webappClassLoader.securityException", 
name,
-                        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) {
-                try {
-                    clazz = super.findClass(name);
-                } catch(AccessControlException ace) {
-                    
log.warn(sm.getString("webappClassLoader.securityException", name,
-                            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 + ")");
-
-        checkStateForResourceLoading(name);
-
-        URL url = null;
-
-        String path = nameToPath(name);
-
-        WebResource resource = resources.getClassLoaderResource(path);
-        if (resource.exists()) {
-            url = resource.getURL();
-            trackLastModified(path, resource);
-        }
-
-        if ((url == null) && hasExternalRepositories) {
-            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;
-    }
-
-
-    private void trackLastModified(String path, WebResource resource) {
-        if (resourceEntries.containsKey(path)) {
-            return;
-        }
-        ResourceEntry entry = new ResourceEntry();
-        entry.lastModified = resource.getLastModified();
-        synchronized(resourceEntries) {
-            resourceEntries.putIfAbsent(path, entry);
-        }
-    }
-
-
-    /**
-     * 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 + ")");
-
-        checkStateForResourceLoading(name);
-
-        LinkedHashSet<URL> result = new LinkedHashSet<>();
-
-        String path = nameToPath(name);
-
-        WebResource[] webResources = resources.getClassLoaderResources(path);
-        for (WebResource webResource : webResources) {
-            if (webResource.exists()) {
-                result.add(webResource.getURL());
-            }
-        }
-
-        // Adding the results of a call to the superclass
-        if (hasExternalRepositories) {
-            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 + ")");
-
-        checkStateForResourceLoading(name);
-
-        URL url = null;
-
-        boolean delegateFirst = delegate || filter(name, false);
-
-        // (1) Delegate to parent if requested
-        if (delegateFirst) {
-            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) {
-            if (log.isDebugEnabled())
-                log.debug("  --> Returning '" + url.toString() + "'");
-            return url;
-        }
-
-        // (3) Delegate to parent unconditionally if not already attempted
-        if (!delegateFirst) {
-            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;
-
-    }
-
-
-    @Override
-    public Enumeration<URL> getResources(String name) throws IOException {
-
-        Enumeration<URL> parentResources = getParent().getResources(name);
-        Enumeration<URL> localResources = findResources(name);
-
-        // Need to combine these enumerations. The order in which the
-        // Enumerations are combined depends on how delegation is configured
-        boolean delegateFirst = delegate || filter(name, false);
-
-        if (delegateFirst) {
-            return new CombinedEnumeration(parentResources, localResources);
-        } else {
-            return new CombinedEnumeration(localResources, parentResources);
-        }
-    }
-
-
-    /**
-     * 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 + ")");
-
-        checkStateForResourceLoading(name);
-
-        InputStream stream = null;
-
-        boolean delegateFirst = delegate || filter(name, false);
-
-        // (1) Delegate to parent if requested
-        if (delegateFirst) {
-            if (log.isDebugEnabled())
-                log.debug("  Delegating to parent classloader " + parent);
-            stream = parent.getResourceAsStream(name);
-            if (stream != null) {
-                if (log.isDebugEnabled())
-                    log.debug("  --> Returning stream from parent");
-                return stream;
-            }
-        }
-
-        // (2) Search local repositories
-        if (log.isDebugEnabled())
-            log.debug("  Searching local repositories");
-        String path = nameToPath(name);
-        WebResource resource = resources.getClassLoaderResource(path);
-        if (resource.exists()) {
-            stream = resource.getInputStream();
-            trackLastModified(path, resource);
-        }
-        try {
-            if (hasExternalRepositories && stream == null) {
-                URL url = super.findResource(name);
-                if (url != null) {
-                    stream = url.openStream();
-                }
-            }
-        } catch (IOException e) {
-            // Ignore
-        }
-        if (stream != null) {
-            if (log.isDebugEnabled())
-                log.debug("  --> Returning stream from local");
-            return stream;
-        }
-
-        // (3) Delegate to parent unconditionally
-        if (!delegateFirst) {
-            if (log.isDebugEnabled())
-                log.debug("  Delegating to parent classloader unconditionally 
" + parent);
-            stream = parent.getResourceAsStream(name);
-            if (stream != null) {
-                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 The binary 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 The binary 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 Class<?> loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
-
-        synchronized (JreCompat.isGraalAvailable() ? this : 
getClassLoadingLock(name)) {
-            if (log.isDebugEnabled())
-                log.debug("loadClass(" + name + ", " + resolve + ")");
-            Class<?> clazz = null;
-
-            // Log access to stopped class loader
-            checkStateForClassLoading(name);
-
-            // (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 = JreCompat.isGraalAvailable() ? null : 
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 Java SE classes. This 
implements
-            //       SRV.10.7.2
-            String resourceName = binaryNameToPath(name, false);
-
-            ClassLoader javaseLoader = getJavaseClassLoader();
-            boolean tryLoadingFromJavaseLoader;
-            try {
-                // Use getResource as it won't trigger an expensive
-                // ClassNotFoundException if the resource is not available from
-                // the Java SE class loader. However (see
-                // https://bz.apache.org/bugzilla/show_bug.cgi?id=58125 for
-                // details) when running under a security manager in rare cases
-                // this call may trigger a ClassCircularityError.
-                // See https://bz.apache.org/bugzilla/show_bug.cgi?id=61424 for
-                // details of how this may trigger a StackOverflowError
-                // Given these reported errors, catch Throwable to ensure any
-                // other edge cases are also caught
-                URL url;
-                if (securityManager != null) {
-                    PrivilegedAction<URL> dp = new 
PrivilegedJavaseGetResource(resourceName);
-                    url = AccessController.doPrivileged(dp);
-                } else {
-                    url = javaseLoader.getResource(resourceName);
-                }
-                tryLoadingFromJavaseLoader = (url != null);
-            } catch (Throwable t) {
-                // Swallow all exceptions apart from those that must be 
re-thrown
-                ExceptionUtils.handleThrowable(t);
-                // The getResource() trick won't work for this class. We have 
to
-                // try loading it directly and accept that we might get a
-                // ClassNotFoundException.
-                tryLoadingFromJavaseLoader = true;
-            }
-
-            if (tryLoadingFromJavaseLoader) {
-                try {
-                    clazz = javaseLoader.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 = 
sm.getString("webappClassLoader.restrictedPackage", name);
-                        log.info(error, se);
-                        throw new ClassNotFoundException(error, se);
-                    }
-                }
-            }
-
-            boolean delegateLoad = delegate || filter(name, true);
-
-            // (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);
-    }
-
-
-    protected void checkStateForClassLoading(String className) throws 
ClassNotFoundException {
-        // It is not permitted to load new classes once the web application has
-        // been stopped.
-        try {
-            checkStateForResourceLoading(className);
-        } catch (IllegalStateException ise) {
-            throw new ClassNotFoundException(ise.getMessage(), ise);
-        }
-    }
-
-
-    protected void checkStateForResourceLoading(String resource) throws 
IllegalStateException {
-        // It is not permitted to load resources once the web application has
-        // been stopped.
-        if (!state.isAvailable()) {
-            String msg = sm.getString("webappClassLoader.stopped", resource);
-            IllegalStateException ise = new IllegalStateException(msg);
-            log.info(msg, ise);
-            throw ise;
-        }
-    }
-
-    /**
-     * Get the Permissions for a CodeSource.  If this instance
-     * of WebappClassLoaderBase is for a web application context,
-     * add read FilePermission for the appropriate 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) {
-                for (Permission p : permissionList) {
-                    pc.add(p);
-                }
-                loaderPC.put(codeUrl,pc);
-            }
-        }
-        return pc;
-    }
-
-
-    @Override
-    public boolean check(Permission permission) {
-        if (!Globals.IS_SECURITY_ENABLED) {
-            return true;
-        }
-        Policy currentPolicy = Policy.getPolicy();
-        if (currentPolicy != null) {
-            URL contextRootUrl = resources.getResource("/").getCodeBase();
-            CodeSource cs = new CodeSource(contextRootUrl, (Certificate[]) 
null);
-            PermissionCollection pc = currentPolicy.getPermissions(cs);
-            if (pc.implies(permission)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Note that list of URLs returned by this method may not be complete. The
-     * web application class loader accesses class loader resources via the
-     * {@link WebResourceRoot} which supports the arbitrary mapping of
-     * additional files, directories and contents of JAR files under
-     * WEB-INF/classes. Any such resources will not be included in the URLs
-     * returned here.
-     */
-    @Override
-    public URL[] getURLs() {
-        ArrayList<URL> result = new ArrayList<>();
-        result.addAll(localRepositories);
-        result.addAll(Arrays.asList(super.getURLs()));
-        return result.toArray(new URL[0]);
-    }
-
-
-    // ------------------------------------------------------ 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 state;
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String getStateName() {
-        return getState().toString();
-    }
-
-
-    @Override
-    public void init() {
-        state = LifecycleState.INITIALIZED;
-    }
-
-
-    /**
-     * Start the class loader.
-     *
-     * @exception LifecycleException if a lifecycle error occurs
-     */
-    @Override
-    public void start() throws LifecycleException {
-
-        state = LifecycleState.STARTING_PREP;
-
-        WebResource[] classesResources = 
resources.getResources("/WEB-INF/classes");
-        for (WebResource classes : classesResources) {
-            if (classes.isDirectory() && classes.canRead()) {
-                localRepositories.add(classes.getURL());
-            }
-        }
-        WebResource[] jars = resources.listResources("/WEB-INF/lib");
-        for (WebResource jar : jars) {
-            if (jar.getName().endsWith(".jar") && jar.isFile() && 
jar.canRead()) {
-                localRepositories.add(jar.getURL());
-                jarModificationTimes.put(
-                        jar.getName(), Long.valueOf(jar.getLastModified()));
-            }
-        }
-
-        state = LifecycleState.STARTED;
-    }
-
-
-    /**
-     * Stop the class loader.
-     *
-     * @exception LifecycleException if a lifecycle error occurs
-     */
-    @Override
-    public void stop() throws LifecycleException {
-
-        state = LifecycleState.STOPPING_PREP;
-
-        // Clearing references should be done before setting started to
-        // false, due to possible side effects
-        clearReferences();
-
-        state = LifecycleState.STOPPING;
-
-        resourceEntries.clear();
-        jarModificationTimes.clear();
-        resources = null;
-
-        permissionList.clear();
-        loaderPC.clear();
-
-        state = LifecycleState.STOPPED;
-    }
-
-
-    @Override
-    public void destroy() {
-        state = LifecycleState.DESTROYING;
-
-        try {
-            super.close();
-        } catch (IOException ioe) {
-            log.warn(sm.getString("webappClassLoader.superCloseFail"), ioe);
-        }
-        state = LifecycleState.DESTROYED;
-    }
-
-
-    // ------------------------------------------------------ Protected Methods
-
-    protected ClassLoader getJavaseClassLoader() {
-        return javaseClassLoader;
-    }
-
-    protected void setJavaseClassLoader(ClassLoader classLoader) {
-        if (classLoader == null) {
-            throw new IllegalArgumentException(
-                    sm.getString("webappClassLoader.javaseClassLoaderNull"));
-        }
-        javaseClassLoader = classLoader;
-    }
-
-    /**
-     * Clear references.
-     */
-    protected void clearReferences() {
-
-        // If the JVM is shutting down, skip the memory leak checks
-        if (skipMemoryLeakChecksOnJvmShutdown
-            && !resources.getContext().getParent().getState().isAvailable()) {
-            // During reloading / redeployment the parent is expected to be
-            // available. Parent is not available so this might be a JVM
-            // shutdown.
-            try {
-                Thread dummyHook = new Thread();
-                Runtime.getRuntime().addShutdownHook(dummyHook);
-                Runtime.getRuntime().removeShutdownHook(dummyHook);
-            } catch (IllegalStateException ise) {
-                return;
-            }
-        }
-
-        if (!JreCompat.isGraalAvailable()) {
-            // De-register any remaining JDBC drivers
-            clearReferencesJdbc();
-        }
-
-        // Stop any threads the web application started
-        clearReferencesThreads();
-
-        // Clear any references retained in the serialization caches
-        if (clearReferencesObjectStreamClassCaches && 
!JreCompat.isGraalAvailable()) {
-            clearReferencesObjectStreamClassCaches();
-        }
-
-        // Check for leaks triggered by ThreadLocals loaded by this class 
loader
-        if (clearReferencesThreadLocals && !JreCompat.isGraalAvailable()) {
-            checkThreadLocalsForLeaks();
-        }
-
-        // Clear RMI Targets loaded by this class loader
-        if (clearReferencesRmiTargets) {
-            clearReferencesRmiTargets();
-        }
-
-         // Clear the IntrospectionUtils cache.
-        IntrospectionUtils.clear();
-
-        // Clear the classloader reference in common-logging
-        if (clearReferencesLogFactoryRelease) {
-            org.apache.juli.logging.LogFactory.release(this);
-        }
-
-        // Clear the classloader reference in the VM's bean introspector
-        java.beans.Introspector.flushCaches();
-
-        // Clear any custom URLStreamHandlers
-        TomcatURLStreamHandlerFactory.release(this);
-    }
-
-
-    /**
-     * Deregister any JDBC drivers registered by the webapp that the webapp
-     * forgot. This is made unnecessary complex because a) DriverManager
-     * checks the class loader of the calling class (it would be much easier
-     * if it checked the context class loader) b) using reflection would
-     * create a dependency on the DriverManager implementation which can,
-     * and has, changed.
-     *
-     * We can't just create an instance of JdbcLeakPrevention as it will be
-     * loaded by the common class loader (since it's .class file is in the
-     * $CATALINA_HOME/lib directory). This would fail DriverManager's check
-     * on the class loader of the calling class. So, we load the bytes via
-     * our parent class loader but define the class with this class loader
-     * so the JdbcLeakPrevention looks like a webapp class to the
-     * DriverManager.
-     *
-     * If only apps cleaned up after themselves...
-     */
-    private final void clearReferencesJdbc() {
-        // 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 (InputStream is = getResourceAsStream(
-                "org/apache/catalina/loader/JdbcLeakPrevention.class")) {
-            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.getConstructor().newInstance();
-            @SuppressWarnings("unchecked")
-            List<String> driverNames = (List<String>) obj.getClass().getMethod(
-                    "clearJdbcDriverRegistrations").invoke(obj);
-            for (String name : driverNames) {
-                log.warn(sm.getString("webappClassLoader.clearJdbc",
-                        getContextName(), 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", getContextName()), 
t);
-        }
-    }
-
-
-    @SuppressWarnings("deprecation") // thread.stop()
-    private void clearReferencesThreads() {
-        Thread[] threads = getThreads();
-        List<Thread> threadsToStop = new ArrayList<>();
-
-        // 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;
-                    }
-
-                    final String threadName = thread.getName();
-
-                    // JVM controlled threads
-                    ThreadGroup tg = thread.getThreadGroup();
-                    if (tg != null && 
JVM_THREAD_GROUP_NAMES.contains(tg.getName())) {
-                        // HttpClient keep-alive threads
-                        if (clearReferencesHttpClientKeepAliveThread &&
-                                threadName.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.warn(sm.getString("webappClassLoader.stackTraceRequestThread",
-                                getContextName(), threadName, 
getStackTrace(thread)));
-                    } else {
-                        log.warn(sm.getString("webappClassLoader.stackTrace",
-                                getContextName(), threadName, 
getStackTrace(thread)));
-                    }
-
-                    // Don't try and 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 | NoSuchFieldException | 
IllegalArgumentException |
-                            IllegalAccessException e) {
-                        
log.warn(sm.getString("webappClassLoader.stopThreadFail",
-                                thread.getName(), getContextName()), e);
-                    }
-
-                    // Stopping an executor automatically interrupts the
-                    // associated threads. For non-executor threads, interrupt
-                    // them here.
-                    if (!usingExecutor && !thread.isInterrupted()) {
-                        thread.interrupt();
-                    }
-
-                    // Threads are expected to take a short time to stop after
-                    // being interrupted. Make a note of all threads that are
-                    // expected to stop to enable them to be checked at the end
-                    // of this method.
-                    threadsToStop.add(thread);
-                }
-            }
-        }
-
-        // If thread stopping is enabled, threads should have been stopped 
above
-        // when the executor was shut down or the thread was interrupted but
-        // that depends on the thread correctly handling the interrupt. Check
-        // each thread and if any are still running give all threads up to a
-        // total of 2 seconds to shutdown.
-        int count = 0;
-        for (Thread t : threadsToStop) {
-            while (t.isAlive() && count < 100) {
-                try {
-                    Thread.sleep(20);
-                } catch (InterruptedException e) {
-                    // Quit the while loop
-                    break;
-                }
-                count++;
-            }
-            if (t.isAlive()) {
-                // This method is deprecated and for good reason. This is
-                // very risky code but is the only option at this point.
-                // A *very* good reason for apps to do this clean-up
-                // themselves.
-                t.stop();
-            }
-        }
-    }
-
-
-    /*
-     * Look at a threads stack trace to see if it is a request thread or not. 
It
-     * isn't perfect, but it should be good-enough for most cases.
-     */
-    private boolean isRequestThread(Thread thread) {
-
-        StackTraceElement[] elements = thread.getStackTrace();
-
-        if (elements == null || elements.length == 0) {
-            // Must have stopped already. Too late to ignore it. Assume not a
-            // request processing thread.
-            return false;
-        }
-
-        // Step through the methods in reverse order looking for calls to any
-        // CoyoteAdapter method. All request threads will have this unless
-        // Tomcat has been heavily modified - in which case there isn't much we
-        // can do.
-        for (int i = 0; i < elements.length; i++) {
-            StackTraceElement element = elements[elements.length - (i+1)];
-            if ("org.apache.catalina.connector.CoyoteAdapter".equals(
-                    element.getClassName())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-
-    private void clearReferencesStopTimerThread(Thread thread) {
-
-        // Need to get references to:
-        // in Sun/Oracle JDK:
-        // - newTasksMayBeScheduled field (in java.util.TimerThread)
-        // - queue field
-        // - queue.clear()
-        // in IBM JDK, Apache Harmony:
-        // - cancel() method (in java.util.Timer$TimerImpl)
-
-        try {
-
-            try {
-                Field newTasksMayBeScheduledField =
-                    
thread.getClass().getDeclaredField("newTasksMayBeScheduled");
-                newTasksMayBeScheduledField.setAccessible(true);
-                Field queueField = thread.getClass().getDeclaredField("queue");
-                queueField.setAccessible(true);
-
-                Object queue = queueField.get(thread);
-
-                Method clearMethod = 
queue.getClass().getDeclaredMethod("clear");
-                clearMethod.setAccessible(true);
-
-                synchronized(queue) {
-                    newTasksMayBeScheduledField.setBoolean(thread, false);
-                    clearMethod.invoke(queue);
-                    // In case queue was already empty. Should only be one
-                    // thread waiting but use notifyAll() to be safe.
-                    queue.notifyAll();
-                }
-
-            }catch (NoSuchFieldException nfe){
-                Method cancelMethod = 
thread.getClass().getDeclaredMethod("cancel");
-                synchronized(thread) {
-                    cancelMethod.setAccessible(true);
-                    cancelMethod.invoke(thread);
-                }
-            }
-
-            log.warn(sm.getString("webappClassLoader.warnTimerThread",
-                    getContextName(), thread.getName()));
-
-        } catch (Exception e) {
-            // So many things to go wrong above...
-            Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
-            ExceptionUtils.handleThrowable(t);
-            log.warn(sm.getString(
-                    "webappClassLoader.stopTimerThreadFail",
-                    thread.getName(), getContextName()), t);
-        }
-    }
-
-    private void checkThreadLocalsForLeaks() {
-        Thread[] threads = getThreads();
-
-        try {
-            // Make the fields in the Thread class that store ThreadLocals
-            // accessible
-            Field threadLocalsField =
-                Thread.class.getDeclaredField("threadLocals");
-            threadLocalsField.setAccessible(true);
-            Field inheritableThreadLocalsField =
-                Thread.class.getDeclaredField("inheritableThreadLocals");
-            inheritableThreadLocalsField.setAccessible(true);
-            // Make the underlying array of ThreadLoad.ThreadLocalMap.Entry 
objects
-            // accessible
-            Class<?> tlmClass = 
Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
-            Field tableField = tlmClass.getDeclaredField("table");
-            tableField.setAccessible(true);
-            Method expungeStaleEntriesMethod = 
tlmClass.getDeclaredMethod("expungeStaleEntries");
-            expungeStaleEntriesMethod.setAccessible(true);
-
-            for (Thread thread : threads) {
-                Object threadLocalMap;
-                if (thread != null) {
-
-                    // Clear the first map
-                    threadLocalMap = threadLocalsField.get(thread);
-                    if (null != threadLocalMap) {
-                        expungeStaleEntriesMethod.invoke(threadLocalMap);
-                        checkThreadLocalMapForLeaks(threadLocalMap, 
tableField);
-                    }
-
-                    // Clear the second map
-                    threadLocalMap = inheritableThreadLocalsField.get(thread);
-                    if (null != threadLocalMap) {
-                        expungeStaleEntriesMethod.invoke(threadLocalMap);
-                        checkThreadLocalMapForLeaks(threadLocalMap, 
tableField);
-                    }
-                }
-            }
-        } catch (Throwable t) {
-            JreCompat jreCompat = JreCompat.getInstance();
-            if (jreCompat.isInstanceOfInaccessibleObjectException(t)) {
-                // Must be running on Java 9 without the necessary command line
-                // options.
-                
log.warn(sm.getString("webappClassLoader.addExportsThreadLocal"));
-            } else {
-                ExceptionUtils.handleThrowable(t);
-                log.warn(sm.getString(
-                        "webappClassLoader.checkThreadLocalsForLeaksFail",
-                        getContextName()), t);
-            }
-        }
-    }
-
-
-    /**
-     * Analyzes the given thread local map object. Also pass in the field that
-     * points to the internal table to save re-calculating it on every
-     * call to this method.
-     */
-    private void checkThreadLocalMapForLeaks(Object map,
-            Field internalTableField) throws IllegalAccessException,
-            NoSuchFieldException {
-        if (map != null) {
-            Object[] table = (Object[]) internalTableField.get(map);
-            if (table != null) {
-                for (Object obj : table) {
-                    if (obj != null) {
-                        boolean keyLoadedByWebapp = false;
-                        boolean valueLoadedByWebapp = false;
-                        // Check the key
-                        Object key = ((Reference<?>) obj).get();
-                        if (this.equals(key) || loadedByThisOrChild(key)) {
-                            keyLoadedByWebapp = true;
-                        }
-                        // Check the value
-                        Field valueField =
-                                obj.getClass().getDeclaredField("value");
-                        valueField.setAccessible(true);
-                        Object value = valueField.get(obj);
-                        if (this.equals(value) || loadedByThisOrChild(value)) {
-                            valueLoadedByWebapp = true;
-                        }
-                        if (keyLoadedByWebapp || valueLoadedByWebapp) {
-                            Object[] args = new Object[5];
-                            args[0] = getContextName();
-                            if (key != null) {
-                                args[1] = getPrettyClassName(key.getClass());
-                                try {
-                                    args[2] = key.toString();
-                                } catch (Exception e) {
-                                    log.warn(sm.getString(
-                                            
"webappClassLoader.checkThreadLocalsForLeaks.badKey",
-                                            args[1]), e);
-                                    args[2] = sm.getString(
-                                            
"webappClassLoader.checkThreadLocalsForLeaks.unknown");
-                                }
-                            }
-                            if (value != null) {
-                                args[3] = getPrettyClassName(value.getClass());
-                                try {
-                                    args[4] = value.toString();
-                                } catch (Exception e) {
-                                    log.warn(sm.getString(
-                                            
"webappClassLoader.checkThreadLocalsForLeaks.badValue",
-                                            args[3]), e);
-                                    args[4] = sm.getString(
-                                            
"webappClassLoader.checkThreadLocalsForLeaks.unknown");
-                                }
-                            }
-                            if (valueLoadedByWebapp) {
-                                log.error(sm.getString(
-                                        
"webappClassLoader.checkThreadLocalsForLeaks",
-                                        args));
-                            } else if (value == null) {
-                                if (log.isDebugEnabled()) {
-                                    log.debug(sm.getString(
-                                            
"webappClassLoader.checkThreadLocalsForLeaksNull",
-                                            args));
-                                }
-                            } else {
-                                if (log.isDebugEnabled()) {
-                                    log.debug(sm.getString(
-                                            
"webappClassLoader.checkThreadLocalsForLeaksNone",
-                                            args));
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private String getPrettyClassName(Class<?> clazz) {
-        String name = clazz.getCanonicalName();
-        if (name==null){
-            name = clazz.getName();
-        }
-        return name;
-    }
-
-    private String getStackTrace(Thread thread) {
-        StringBuilder builder = new StringBuilder();
-        for (StackTraceElement ste : thread.getStackTrace()) {
-            builder.append("\n ").append(ste);
-        }
-        return builder.toString();
-    }
-
-    /**
-     * @param o object to test, may be null
-     * @return <code>true</code> if o has been loaded by the current 
classloader
-     * or one of its descendants.
-     */
-    private boolean loadedByThisOrChild(Object o) {
-        if (o == null) {
-            return false;
-        }
-
-        Class<?> clazz;
-        if (o instanceof Class) {
-            clazz = (Class<?>) o;
-        } else {
-            clazz = o.getClass();
-        }
-
-        ClassLoader cl = clazz.getClassLoader();
-        while (cl != null) {
-            if (cl == this) {
-                return true;
-            }
-            cl = cl.getParent();
-        }
-
-        if (o instanceof Collection<?>) {
-            Iterator<?> iter = ((Collection<?>) o).iterator();
-            try {
-                while (iter.hasNext()) {
-                    Object entry = iter.next();
-                    if (loadedByThisOrChild(entry)) {
-                        return true;
-                    }
-                }
-            } catch (ConcurrentModificationException e) {
-                log.warn(sm.getString(
-                        "webappClassLoader.loadedByThisOrChildFail", 
clazz.getName(), getContextName()),
-                        e);
-            }
-        }
-        return false;
-    }
-
-    /*
-     * Get the set of current threads as an array.
-     */
-    private Thread[] getThreads() {
-        // Get the current thread group
-        ThreadGroup tg = Thread.currentThread().getThreadGroup();
-        // Find the root thread group
-        try {
-            while (tg.getParent() != null) {
-                tg = tg.getParent();
-            }
-        } catch (SecurityException se) {
-            String msg = sm.getString(
-                    "webappClassLoader.getThreadGroupError", tg.getName());
-            if (log.isDebugEnabled()) {
-                log.debug(msg, se);
-            } else {
-                log.warn(msg);
-            }
-        }
-
-        int threadCountGuess = tg.activeCount() + 50;
-        Thread[] threads = new Thread[threadCountGuess];
-        int threadCountActual = tg.enumerate(threads);
-        // Make sure we don't miss any threads
-        while (threadCountActual == threadCountGuess) {
-            threadCountGuess *=2;
-            threads = new Thread[threadCountGuess];
-            // Note tg.enumerate(Thread[]) silently ignores any threads that
-            // can't fit into the array
-            threadCountActual = tg.enumerate(threads);
-        }
-
-        return threads;
-    }
-
-
-    /**
-     * This depends on the internals of the Sun JVM so it does everything by
-     * reflection.
-     */
-    private void clearReferencesRmiTargets() {
-        try {
-            // Need access to the ccl field of sun.rmi.transport.Target to find
-            // the leaks
-            Class<?> objectTargetClass =
-                Class.forName("sun.rmi.transport.Target");
-            Field cclField = objectTargetClass.getDeclaredField("ccl");
-            cclField.setAccessible(true);
-            // Need access to the stub field to report the leaks
-            Field stubField = objectTargetClass.getDeclaredField("stub");
-            stubField.setAccessible(true);
-
-            // Clear the objTable map
-            Class<?> objectTableClass = 
Class.forName("sun.rmi.transport.ObjectTable");
-            Field objTableField = 
objectTableClass.getDeclaredField("objTable");
-            objTableField.setAccessible(true);
-            Object objTable = objTableField.get(null);
-            if (objTable == null) {
-                return;
-            }
-            Field tableLockField = 
objectTableClass.getDeclaredField("tableLock");
-            tableLockField.setAccessible(true);
-            Object tableLock = tableLockField.get(null);
-
-            synchronized (tableLock) {
-                // Iterate over the values in the table
-                if (objTable instanceof Map<?,?>) {
-                    Iterator<?> iter = ((Map<?,?>) 
objTable).values().iterator();
-                    while (iter.hasNext()) {
-                        Object obj = iter.next();
-                        Object cclObject = cclField.get(obj);
-                        if (this == cclObject) {
-                            iter.remove();
-                            Object stubObject = stubField.get(obj);
-                            
log.error(sm.getString("webappClassLoader.clearRmi",
-                                    stubObject.getClass().getName(), 
stubObject));
-                        }
-                    }
-                }
-
-                // Clear the implTable map
-                Field implTableField = 
objectTableClass.getDeclaredField("implTable");
-                implTableField.setAccessible(true);
-                Object implTable = implTableField.get(null);
-                if (implTable == null) {
-                    return;
-                }
-
-                // Iterate over the values in the table
-                if (implTable instanceof Map<?,?>) {
-                    Iterator<?> iter = ((Map<?,?>) 
implTable).values().iterator();
-                    while (iter.hasNext()) {
-                        Object obj = iter.next();
-                        Object cclObject = cclField.get(obj);
-                        if (this == cclObject) {
-                            iter.remove();
-                        }
-                    }
-                }
-            }
-        } catch (ClassNotFoundException e) {
-            log.info(sm.getString("webappClassLoader.clearRmiInfo",
-                    getContextName()), e);
-        } catch (SecurityException | NoSuchFieldException | 
IllegalArgumentException |
-                IllegalAccessException e) {
-            log.warn(sm.getString("webappClassLoader.clearRmiFail",
-                    getContextName()), e);
-        } catch (Exception e) {
-            JreCompat jreCompat = JreCompat.getInstance();
-            if (jreCompat.isInstanceOfInaccessibleObjectException(e)) {
-                // Must be running on Java 9 without the necessary command line
-                // options.
-                log.warn(sm.getString("webappClassLoader.addExportsRmi"));
-            } else {
-                // Re-throw all other exceptions
-                throw e;
-            }
-        }
-    }
-
-
-    private void clearReferencesObjectStreamClassCaches() {
-        try {
-            Class<?> clazz = Class.forName("java.io.ObjectStreamClass$Caches");
-            clearCache(clazz, "localDescs");
-            clearCache(clazz, "reflectors");
-        } catch (ReflectiveOperationException | SecurityException | 
ClassCastException e) {
-            log.warn(sm.getString(
-                    "webappClassLoader.clearObjectStreamClassCachesFail", 
getContextName()), e);
-        }
-    }
-
-
-    private void clearCache(Class<?> target, String mapName)
-            throws ReflectiveOperationException, SecurityException, 
ClassCastException {
-        Field f = target.getDeclaredField(mapName);
-        f.setAccessible(true);
-        Map<?,?> map = (Map<?,?>) f.get(null);
-        Iterator<?> keys = map.keySet().iterator();
-        while (keys.hasNext()) {
-            Object key = keys.next();
-            if (key instanceof Reference) {
-                Object clazz = ((Reference<?>) key).get();
-                if (loadedByThisOrChild(clazz)) {
-                    keys.remove();
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Find specified class in local repositories.
-     *
-     * @param name The binary name of the class to be loaded
-     *
-     * @return the loaded class, or null if the class isn't found
-     */
-    protected Class<?> findClassInternal(String name) {
-
-        checkStateForResourceLoading(name);
-
-        if (name == null) {
-            return null;
-        }
-        String path = binaryNameToPath(name, true);
-
-        ResourceEntry entry = resourceEntries.get(path);
-        WebResource resource = null;
-
-        if (entry == null) {
-            resource = resources.getClassLoaderResource(path);
-
-            if (!resource.exists()) {
-                return null;
-            }
-
-            entry = new ResourceEntry();
-            entry.lastModified = resource.getLastModified();
-
-            // Add the entry in the local resource repository
-            synchronized (resourceEntries) {
-                // Ensures that all the threads which may be in a race to load
-                // a particular class all end up with the same ResourceEntry
-                // instance
-                ResourceEntry entry2 = resourceEntries.get(path);
-                if (entry2 == null) {
-                    resourceEntries.put(path, entry);
-                } else {
-                    entry = entry2;
-                }
-            }
-        }
-
-        Class<?> clazz = entry.loadedClass;
-        if (clazz != null)
-            return clazz;
-
-        synchronized (JreCompat.isGraalAvailable() ? this : 
getClassLoadingLock(name)) {
-            clazz = entry.loadedClass;
-            if (clazz != null)
-                return clazz;
-
-            if (resource == null) {
-                resource = resources.getClassLoaderResource(path);
-            }
-
-            if (!resource.exists()) {
-                return null;
-            }
-
-            byte[] binaryContent = resource.getContent();
-            if (binaryContent == null) {
-                // Something went wrong reading the class bytes (and will have
-                // been logged at debug level).
-                return null;
-            }
-            Manifest manifest = resource.getManifest();
-            URL codeBase = resource.getCodeBase();
-            Certificate[] certificates = resource.getCertificates();
-
-            if (transformers.size() > 0) {
-                // If the resource is a class just being loaded, decorate it
-                // with any attached transformers
-
-                // Ignore leading '/' and trailing CLASS_FILE_SUFFIX
-                // Should be cheaper than replacing '.' by '/' in class name.
-                String internalName = path.substring(1, path.length() - 
CLASS_FILE_SUFFIX.length());
-
-                for (ClassFileTransformer transformer : this.transformers) {
-                    try {
-                        byte[] transformed = transformer.transform(
-                                this, internalName, null, null, binaryContent);
-                        if (transformed != null) {
-                            binaryContent = transformed;
-                        }
-                    } catch (IllegalClassFormatException e) {
-                        
log.error(sm.getString("webappClassLoader.transformError", name), e);
-                        return null;
-                    }
-                }
-            }
-
-            // Looking up the package
-            String packageName = null;
-            int pos = name.lastIndexOf('.');
-            if (pos != -1)
-                packageName = name.substring(0, pos);
-
-            Package pkg = null;
-
-            if (packageName != null) {
-                pkg = getPackage(packageName);
-                // Define the package (if null)
-                if (pkg == null) {
-                    try {
-                        if (manifest == null) {
-                            definePackage(packageName, null, null, null, null, 
null, null, null);
-                        } else {
-                            definePackage(packageName, manifest, codeBase);
-                        }
-                    } catch (IllegalArgumentException e) {
-                        // Ignore: normal error due to dual definition of 
package
-                    }
-                    pkg = getPackage(packageName);
-                }
-            }
-
-            if (securityManager != null) {
-
-                // Checking sealing
-                if (pkg != null) {
-                    boolean sealCheck = true;
-                    if (pkg.isSealed()) {
-                        sealCheck = pkg.isSealed(codeBase);
-                    } else {
-                        sealCheck = (manifest == null) || 
!isPackageSealed(packageName, manifest);
-                    }
-                    if (!sealCheck)
-                        throw new SecurityException
-                            ("Sealing violation loading " + name + " : Package 
"
-                             + packageName + " is sealed.");
-                }
-
-            }
-
-            try {
-                clazz = defineClass(name, binaryContent, 0,
-                        binaryContent.length, new CodeSource(codeBase, 
certificates));
-            } catch (UnsupportedClassVersionError ucve) {
-                throw new UnsupportedClassVersionError(
-                        ucve.getLocalizedMessage() + " " +
-                        sm.getString("webappClassLoader.wrongVersion",
-                                name));
-            }
-            entry.loadedClass = clazz;
-        }
-
-        return clazz;
-    }
-
-
-    private String binaryNameToPath(String binaryName, boolean 
withLeadingSlash) {
-        // 1 for leading '/', 6 for ".class"
-        StringBuilder path = new StringBuilder(7 + binaryName.length());
-        if (withLeadingSlash) {
-            path.append('/');
-        }
-        path.append(binaryName.replace('.', '/'));
-        path.append(CLASS_FILE_SUFFIX);
-        return path.toString();
-    }
-
-
-    private String nameToPath(String name) {
-        if (name.startsWith("/")) {
-            return name;
-        }
-        StringBuilder path = new StringBuilder(
-                1 + name.length());
-        path.append('/');
-        path.append(name);
-        return path.toString();
-    }
-
-
-    /**
-     * Returns true if the specified package name is sealed according to the
-     * given manifest.
-     *
-     * @param name Path name to check
-     * @param man Associated manifest
-     * @return <code>true</code> if the manifest associated says it is sealed
-     */
-    protected boolean isPackageSealed(String name, Manifest man) {
-
-        String path = name.replace('.', '/') + '/';
-        Attributes attr = man.getAttributes(path);
-        String sealed = null;
-        if (attr != null) {
-            sealed = attr.getValue(Name.SEALED);
-        }
-        if (sealed == null) {
-            if ((attr = man.getMainAttributes()) != null) {
-                sealed = attr.getValue(Name.SEALED);
-            }
-        }
-        return "true".equalsIgnoreCase(sealed);
-
-    }
-
-
-    /**
-     * Finds the class with the given name if it has previously been
-     * loaded and cached by this class loader, and return the Class object.
-     * If this class has not been cached, return <code>null</code>.
-     *
-     * @param name The binary name of the resource to return
-     * @return a loaded class
-     */
-    protected Class<?> findLoadedClass0(String name) {
-
-        String path = binaryNameToPath(name, true);
-
-        ResourceEntry entry = resourceEntries.get(path);
-        if (entry != null) {
-            return entry.loadedClass;
-        }
-        return null;
-    }
-
-
-    /**
-     * Refresh the system policy file, to pick up eventual changes.
-     */
-    protected void refreshPolicy() {
-
-        try {
-            // The policy file may have been modified to adjust
-            // permissions, so we're reloading it when loading or
-            // reloading a Context
-            Policy policy = Policy.getPolicy();
-            policy.refresh();
-        } catch (AccessControlException e) {
-            // Some policy files may restrict this, even for the core,
-            // so this exception is ignored
-        }
-
-    }
-
-
-    /**
-     * Filter classes.
-     *
-     * @param name class name
-     * @param isClassName <code>true</code> if name is a class name,
-     *                <code>false</code> if name is a resource name
-     * @return <code>true</code> if the class should be filtered
-     */
-    protected boolean filter(String name, boolean isClassName) {
-
-        if (name == null)
-            return false;
-
-        char ch;
-        if (name.startsWith("jakarta")) {
-            /* 7 == length("jakarta") */
-            if (name.length() == 7) {
-                return false;
-            }
-            ch = name.charAt(7);
-            if (isClassName && ch == '.') {
-                /* 8 == length("jakarta.") */
-                if (name.startsWith("servlet.jsp.jstl.", 8)) {
-                    return false;
-                }
-                if (name.startsWith("el.", 8) ||
-                    name.startsWith("servlet.", 8) ||
-                    name.startsWith("websocket.", 8) ||
-                    name.startsWith("security.auth.message.", 8)) {
-                    return true;
-                }
-            } else if (!isClassName && ch == '/') {
-                /* 8 == length("jakarta/") */
-                if (name.startsWith("servlet/jsp/jstl/", 8)) {
-                    return false;
-                }
-                if (name.startsWith("el/", 8) ||
-                    name.startsWith("servlet/", 8) ||
-                    name.startsWith("websocket/", 8) ||
-                    name.startsWith("security/auth/message/", 8)) {
-                    return true;
-                }
-            }
-        } else if (name.startsWith("javax")) {
-            /* 5 == length("javax") */
-            if (name.length() == 5) {
-                return false;
-            }
-            ch = name.charAt(5);
-            if (isClassName && ch == '.') {
-                /* 6 == length("javax.") */
-                if (name.startsWith("websocket.", 6)) {
-                    return true;
-                }
-            } else if (!isClassName && ch == '/') {
-                /* 6 == length("javax/") */
-                if (name.startsWith("websocket/", 6)) {
-                    return true;
-                }
-            }
-        } else if (name.startsWith("org")) {
-            /* 3 == length("org") */
-            if (name.length() == 3) {
-                return false;
-            }
-            ch = name.charAt(3);
-            if (isClassName && ch == '.') {
-                /* 4 == length("org.") */
-                if (name.startsWith("apache.", 4)) {
-                    /* 11 == length("org.apache.") */
-                    if (name.startsWith("tomcat.jdbc.", 11)) {
-                        return false;
-                    }
-                    if (name.startsWith("el.", 11) ||
-                        name.startsWith("catalina.", 11) ||
-                        name.startsWith("jasper.", 11) ||
-                        name.startsWith("juli.", 11) ||
-                        name.startsWith("tomcat.", 11) ||
-                        name.startsWith("naming.", 11) ||
-                        name.startsWith("coyote.", 11)) {
-                        return true;
-                    }
-                }
-            } else if (!isClassName && ch == '/') {
-                /* 4 == length("org/") */
-                if (name.startsWith("apache/", 4)) {
-                    /* 11 == length("org/apache/") */
-                    if (name.startsWith("tomcat/jdbc/", 11)) {
-                        return false;
-                    }
-                    if (name.startsWith("el/", 11) ||
-                        name.startsWith("catalina/", 11) ||
-                        name.startsWith("jasper/", 11) ||
-                        name.startsWith("juli/", 11) ||
-                        name.startsWith("tomcat/", 11) ||
-                        name.startsWith("naming/", 11) ||
-                        name.startsWith("coyote/", 11)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-
-    @Override
-    protected void addURL(URL url) {
-        super.addURL(url);
-        hasExternalRepositories = true;
-    }
-
-
-    @Override
-    public String getWebappName() {
-        return getContextName();
-    }
-
-
-    @Override
-    public String getHostName() {
-        if (resources != null) {
-            Container host = resources.getContext().getParent();
-            if (host != null) {
-                return host.getName();
-            }
-        }
-        return null;
-    }
-
-
-    @Override
-    public String getServiceName() {
-        if (resources != null) {
-            Container host = resources.getContext().getParent();
-            if (host != null) {
-                Container engine = host.getParent();
-                if (engine != null) {
-                    return engine.getName();
-                }
-            }
-        }
-        return null;
-    }
-
-
-    @Override
-    public boolean hasLoggingConfig() {
-        if (Globals.IS_SECURITY_ENABLED) {
-            Boolean result = AccessController.doPrivileged(new 
PrivilegedHasLoggingConfig());
-            return result.booleanValue();
-        } else {
-            return findResource("logging.properties") != null;
-        }
-    }
-
-
-    private class PrivilegedHasLoggingConfig implements 
PrivilegedAction<Boolean> {
-
-        @Override
-        public Boolean run() {
-            return Boolean.valueOf(findResource("logging.properties") != null);
-        }
-    }
-
-
-    private static class CombinedEnumeration implements Enumeration<URL> {
-
-        private final Enumeration<URL>[] sources;
-        private int index = 0;
-
-        public CombinedEnumeration(Enumeration<URL> enum1, Enumeration<URL> 
enum2) {
-            @SuppressWarnings("unchecked")
-            Enumeration<URL>[] sources = new Enumeration[] { enum1, enum2 };
-            this.sources = sources;
-        }
-
-
-        @Override
-        public boolean hasMoreElements() {
-            return inc();
-        }
-
-
-        @Override
-        public URL nextElement() {
-            if (inc()) {
-                return sources[index].nextElement();
-            }
-            throw new NoSuchElementException();
-        }
-
-
-        private boolean inc() {
-            while (index < sources.length) {
-                if (sources[index].hasMoreElements()) {
-                    return true;
-                }
-                index++;
-            }
-            return false;
-        }
-    }
-}
\ No newline at end of file
diff --git 
a/src/patch/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java
 
b/src/patch/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java
deleted file mode 100644
index 0c59207..0000000
--- 
a/src/patch/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tomcat.util.modeler.modules;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map.Entry;
-
-import javax.management.ObjectName;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.modeler.AttributeInfo;
-import org.apache.tomcat.util.modeler.ManagedBean;
-import org.apache.tomcat.util.modeler.OperationInfo;
-import org.apache.tomcat.util.modeler.ParameterInfo;
-import org.apache.tomcat.util.modeler.Registry;
-
-public class MbeansDescriptorsIntrospectionSource extends ModelerSource
-{
-    private static final Log log = 
LogFactory.getLog(MbeansDescriptorsIntrospectionSource.class);
-
-    private Registry registry;
-    private String type;
-    private final List<ObjectName> mbeans = new ArrayList<>();
-
-    public void setRegistry(Registry reg) {
-        this.registry=reg;
-    }
-
-    /**
-     * Used if a single component is loaded
-     *
-     * @param type The type
-     */
-    public void setType( String type ) {
-       this.type=type;
-    }
-
-    public void setSource( Object source ) {
-        this.source=source;
-    }
-
-    @Override
-    public List<ObjectName> loadDescriptors(Registry registry, String type,
-            Object source) throws Exception {
-        setRegistry(registry);
-        setType(type);
-        setSource(source);
-        execute();
-        return mbeans;
-    }
-
-    public void execute() throws Exception {
-        if( registry==null ) registry=Registry.getRegistry(null, null);
-        try {
-            ManagedBean managed = createManagedBean(registry, null,
-                    (Class<?>)source, type);
-            if( managed==null ) return;
-            managed.setName( type );
-
-            registry.addManagedBean(managed);
-
-        } catch( Exception ex ) {
-            log.error(sm.getString("modules.readDescriptorsError"), ex);
-        }
-    }
-
-
-
-    // ------------ Implementation for non-declared introspection classes
-
-    private static final Hashtable<String,String> specialMethods = new 
Hashtable<>();
-    static {
-        specialMethods.put( "preDeregister", "");
-        specialMethods.put( "postDeregister", "");
-    }
-
-    private static final Class<?>[] supportedTypes  = new Class[] {
-        Boolean.class,
-        Boolean.TYPE,
-        Byte.class,
-        Byte.TYPE,
-        Character.class,
-        Character.TYPE,
-        Short.class,
-        Short.TYPE,
-        Integer.class,
-        Integer.TYPE,
-        Long.class,
-        Long.TYPE,
-        Float.class,
-        Float.TYPE,
-        Double.class,
-        Double.TYPE,
-        String.class,
-        String[].class,
-        BigDecimal.class,
-        BigInteger.class,
-        ObjectName.class,
-        Object[].class,
-        java.io.File.class,
-    };
-
-    /**
-     * Check if this class is one of the supported types.
-     * If the class is supported, returns true.  Otherwise,
-     * returns false.
-     * @param ret The class to check
-     * @return boolean True if class is supported
-     */
-    private boolean supportedType(Class<?> ret) {
-        for (Class<?> supportedType : supportedTypes) {
-            if (ret == supportedType) {
-                return true;
-            }
-        }
-        if (isBeanCompatible(ret)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Check if this class conforms to JavaBeans specifications.
-     * If the class is conformant, returns true.
-     *
-     * @param javaType The class to check
-     * @return boolean True if the class is compatible.
-     */
-    private boolean isBeanCompatible(Class<?> javaType) {
-        // Must be a non-primitive and non array
-        if (javaType.isArray() || javaType.isPrimitive()) {
-            return false;
-        }
-
-        // Anything in the java or javax package that
-        // does not have a defined mapping is excluded.
-        if (javaType.getName().startsWith("java.") ||
-            javaType.getName().startsWith("javax.") ||
-            javaType.getName().startsWith("jakarta.")) {
-            return false;
-        }
-
-        try {
-            javaType.getConstructor(new Class[]{});
-        } catch (java.lang.NoSuchMethodException e) {
-            return false;
-        }
-
-        // Make sure superclass is compatible
-        Class<?> superClass = javaType.getSuperclass();
-        if (superClass != null &&
-            superClass != java.lang.Object.class &&
-            superClass != java.lang.Exception.class &&
-            superClass != java.lang.Throwable.class) {
-            if (!isBeanCompatible(superClass)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Process the methods and extract 'attributes', methods, etc.
-     *
-     * @param realClass The class to process
-     * @param methods The methods to process
-     * @param attMap The attribute map (complete)
-     * @param getAttMap The readable attributes map
-     * @param setAttMap The settable attributes map
-     * @param invokeAttMap The invokable attributes map
-     */
-    private void initMethods(Class<?> realClass, Method methods[], 
Hashtable<String,Method> attMap,
-            Hashtable<String,Method> getAttMap, Hashtable<String,Method> 
setAttMap,
-            Hashtable<String,Method> invokeAttMap) {
-
-        for (Method method : methods) {
-            String name = method.getName();
-
-            if (Modifier.isStatic(method.getModifiers())) {
-                continue;
-            }
-            if (!Modifier.isPublic(method.getModifiers())) {
-                if (log.isDebugEnabled()) {
-                    log.debug("Not public " + method);
-                }
-                continue;
-            }
-            if (method.getDeclaringClass() == Object.class) {
-                continue;
-            }
-            Class<?> params[] = method.getParameterTypes();
-
-            if (name.startsWith("get") && params.length == 0) {
-                Class<?> ret = method.getReturnType();
-                if (!supportedType(ret)) {
-                    if (log.isDebugEnabled()) {
-                        log.debug("Unsupported type " + method);
-                    }
-                    continue;
-                }
-                name = unCapitalize(name.substring(3));
-
-                getAttMap.put(name, method);
-                // just a marker, we don't use the value
-                attMap.put(name, method);
-            } else if (name.startsWith("is") && params.length == 0) {
-                Class<?> ret = method.getReturnType();
-                if (Boolean.TYPE != ret) {
-                    if (log.isDebugEnabled()) {
-                        log.debug("Unsupported type " + method + " " + ret);
-                    }
-                    continue;
-                }
-                name = unCapitalize(name.substring(2));
-
-                getAttMap.put(name, method);
-                // just a marker, we don't use the value
-                attMap.put(name, method);
-
-            } else if (name.startsWith("set") && params.length == 1) {
-                if (!supportedType(params[0])) {
-                    if (log.isDebugEnabled()) {
-                        log.debug("Unsupported type " + method + " " + 
params[0]);
-                    }
-                    continue;
-                }
-                name = unCapitalize(name.substring(3));
-                setAttMap.put(name, method);
-                attMap.put(name, method);
-            } else {
-                if (params.length == 0) {
-                    if (specialMethods.get(method.getName()) != null) {
-                        continue;
-                    }
-                    invokeAttMap.put(name, method);
-                } else {
-                    boolean supported = true;
-                    for (Class<?> param : params) {
-                        if (!supportedType(param)) {
-                            supported = false;
-                            break;
-                        }
-                    }
-                    if (supported) {
-                        invokeAttMap.put(name, method);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * XXX Find if the 'className' is the name of the MBean or
-     *       the real class ( I suppose first )
-     * XXX Read (optional) descriptions from a .properties, generated
-     *       from source
-     * XXX Deal with constructors
-     *
-     * @param registry The Bean registry (not used)
-     * @param domain The bean domain (not used)
-     * @param realClass The class to analyze
-     * @param type The bean type
-     * @return ManagedBean The create MBean
-     */
-    public ManagedBean createManagedBean(Registry registry, String domain,
-                                         Class<?> realClass, String type)
-    {
-        ManagedBean mbean= new ManagedBean();
-
-        Method methods[]=null;
-
-        Hashtable<String,Method> attMap = new Hashtable<>();
-        // key: attribute val: getter method
-        Hashtable<String,Method> getAttMap = new Hashtable<>();
-        // key: attribute val: setter method
-        Hashtable<String,Method> setAttMap = new Hashtable<>();
-        // key: operation val: invoke method
-        Hashtable<String,Method> invokeAttMap = new Hashtable<>();
-
-        methods = realClass.getMethods();
-
-        initMethods(realClass, methods, attMap, getAttMap, setAttMap, 
invokeAttMap );
-
-        try {
-
-            Enumeration<String> en = attMap.keys();
-            while( en.hasMoreElements() ) {
-                String name = en.nextElement();
-                AttributeInfo ai=new AttributeInfo();
-                ai.setName( name );
-                Method gm = getAttMap.get(name);
-                if( gm!=null ) {
-                    //ai.setGetMethodObj( gm );
-                    ai.setGetMethod( gm.getName());
-                    Class<?> t=gm.getReturnType();
-                    if( t!=null )
-                        ai.setType( t.getName() );
-                }
-                Method sm = setAttMap.get(name);
-                if( sm!=null ) {
-                    //ai.setSetMethodObj(sm);
-                    Class<?> t = sm.getParameterTypes()[0];
-                    if( t!=null )
-                        ai.setType( t.getName());
-                    ai.setSetMethod( sm.getName());
-                }
-                ai.setDescription("Introspected attribute " + name);
-                if( log.isDebugEnabled()) log.debug("Introspected attribute " +
-                        name + " " + gm + " " + sm);
-                if( gm==null )
-                    ai.setReadable(false);
-                if( sm==null )
-                    ai.setWriteable(false);
-                if( sm!=null || gm!=null )
-                    mbean.addAttribute(ai);
-            }
-
-            // This map is populated by iterating the methods (which end up as
-            // values in the Map) and obtaining the key from the value. It is
-            // impossible for a key to be associated with a null value.
-            for (Entry<String,Method> entry : invokeAttMap.entrySet()) {
-                String name = entry.getKey();
-                Method m = entry.getValue();
-
-                OperationInfo op=new OperationInfo();
-                op.setName(name);
-                op.setReturnType(m.getReturnType().getName());
-                op.setDescription("Introspected operation " + name);
-                Class<?> parms[] = m.getParameterTypes();
-                for(int i=0; i<parms.length; i++ ) {
-                    ParameterInfo pi=new ParameterInfo();
-                    pi.setType(parms[i].getName());
-                    pi.setName(("param" + i).intern());
-                    pi.setDescription(("Introspected parameter param" + 
i).intern());
-                    op.addParameter(pi);
-                }
-                mbean.addOperation(op);
-            }
-
-            if( log.isDebugEnabled())
-                log.debug("Setting name: " + type );
-            mbean.setName( type );
-
-            return mbean;
-        } catch( Exception ex ) {
-            ex.printStackTrace();
-            return null;
-        }
-    }
-
-
-    // -------------------- Utils --------------------
-    /**
-     * Converts the first character of the given
-     * String into lower-case.
-     *
-     * @param name The string to convert
-     * @return String
-     */
-    private static String unCapitalize(String name) {
-        if (name == null || name.length() == 0) {
-            return name;
-        }
-        char chars[] = name.toCharArray();
-        chars[0] = Character.toLowerCase(chars[0]);
-        return new String(chars);
-    }
-
-}

Reply via email to