You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/04/12 17:00:18 UTC

[tomcat] branch master updated: Ensure dcoBase is configured using a canonical path

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

markt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/master by this push:
     new 9d9bc8c  Ensure dcoBase is configured using a canonical path
9d9bc8c is described below

commit 9d9bc8ceed67a1a63ebeb86b09c1310f6fb405e3
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Apr 12 17:58:43 2019 +0100

    Ensure dcoBase is configured using a canonical path
    
    This should fix test failures on Windows when starting the tests from a
    command prompt using a lower case drive letter.
---
 .../org/apache/catalina/startup/ContextConfig.java |  5419 +++----
 webapps/docs/changelog.xml                         | 14857 ++++++++++---------
 2 files changed, 10145 insertions(+), 10131 deletions(-)

diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index 0c67af3..544b3b4 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -1,2706 +1,2713 @@
-/*
- * 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.startup;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.servlet.MultipartConfigElement;
-import javax.servlet.ServletContainerInitializer;
-import javax.servlet.ServletContext;
-import javax.servlet.SessionCookieConfig;
-import javax.servlet.annotation.HandlesTypes;
-
-import org.apache.catalina.Authenticator;
-import org.apache.catalina.Container;
-import org.apache.catalina.Context;
-import org.apache.catalina.Engine;
-import org.apache.catalina.Globals;
-import org.apache.catalina.Host;
-import org.apache.catalina.Lifecycle;
-import org.apache.catalina.LifecycleEvent;
-import org.apache.catalina.LifecycleListener;
-import org.apache.catalina.Pipeline;
-import org.apache.catalina.Server;
-import org.apache.catalina.Service;
-import org.apache.catalina.Valve;
-import org.apache.catalina.WebResource;
-import org.apache.catalina.WebResourceRoot;
-import org.apache.catalina.Wrapper;
-import org.apache.catalina.core.StandardContext;
-import org.apache.catalina.core.StandardHost;
-import org.apache.catalina.util.ContextName;
-import org.apache.catalina.util.Introspection;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.Jar;
-import org.apache.tomcat.JarScanType;
-import org.apache.tomcat.JarScanner;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.bcel.classfile.AnnotationElementValue;
-import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;
-import org.apache.tomcat.util.bcel.classfile.ArrayElementValue;
-import org.apache.tomcat.util.bcel.classfile.ClassFormatException;
-import org.apache.tomcat.util.bcel.classfile.ClassParser;
-import org.apache.tomcat.util.bcel.classfile.ElementValue;
-import org.apache.tomcat.util.bcel.classfile.ElementValuePair;
-import org.apache.tomcat.util.bcel.classfile.JavaClass;
-import org.apache.tomcat.util.buf.UriUtil;
-import org.apache.tomcat.util.descriptor.InputSourceUtil;
-import org.apache.tomcat.util.descriptor.XmlErrorHandler;
-import org.apache.tomcat.util.descriptor.web.ContextEjb;
-import org.apache.tomcat.util.descriptor.web.ContextEnvironment;
-import org.apache.tomcat.util.descriptor.web.ContextLocalEjb;
-import org.apache.tomcat.util.descriptor.web.ContextResource;
-import org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef;
-import org.apache.tomcat.util.descriptor.web.ContextService;
-import org.apache.tomcat.util.descriptor.web.ErrorPage;
-import org.apache.tomcat.util.descriptor.web.FilterDef;
-import org.apache.tomcat.util.descriptor.web.FilterMap;
-import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
-import org.apache.tomcat.util.descriptor.web.JspPropertyGroup;
-import org.apache.tomcat.util.descriptor.web.LoginConfig;
-import org.apache.tomcat.util.descriptor.web.MessageDestinationRef;
-import org.apache.tomcat.util.descriptor.web.MultipartDef;
-import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
-import org.apache.tomcat.util.descriptor.web.SecurityRoleRef;
-import org.apache.tomcat.util.descriptor.web.ServletDef;
-import org.apache.tomcat.util.descriptor.web.SessionConfig;
-import org.apache.tomcat.util.descriptor.web.WebXml;
-import org.apache.tomcat.util.descriptor.web.WebXmlParser;
-import org.apache.tomcat.util.digester.Digester;
-import org.apache.tomcat.util.digester.RuleSet;
-import org.apache.tomcat.util.file.ConfigFileLoader;
-import org.apache.tomcat.util.file.ConfigurationSource;
-import org.apache.tomcat.util.res.StringManager;
-import org.apache.tomcat.util.scan.JarFactory;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXParseException;
-
-/**
- * Startup event listener for a <b>Context</b> that configures the properties
- * of that Context, and the associated defined servlets.
- *
- * @author Craig R. McClanahan
- */
-public class ContextConfig implements LifecycleListener {
-
-    private static final Log log = LogFactory.getLog(ContextConfig.class);
-
-
-    /**
-     * The string resources for this package.
-     */
-    protected static final StringManager sm =
-        StringManager.getManager(Constants.Package);
-
-
-    protected static final LoginConfig DUMMY_LOGIN_CONFIG =
-        new LoginConfig("NONE", null, null, null);
-
-
-    /**
-     * The set of Authenticators that we know how to configure.  The key is
-     * the name of the implemented authentication method, and the value is
-     * the fully qualified Java class name of the corresponding Valve.
-     */
-    protected static final Properties authenticators;
-
-    static {
-        // Load our mapping properties for the standard authenticators
-        Properties props = new Properties();
-        try (InputStream is = ContextConfig.class.getClassLoader().getResourceAsStream(
-                "org/apache/catalina/startup/Authenticators.properties")) {
-            if (is != null) {
-                props.load(is);
-            }
-        } catch (IOException ioe) {
-            props = null;
-        }
-        authenticators = props;
-    }
-
-    /**
-     * Deployment count.
-     */
-    protected static long deploymentCount = 0L;
-
-
-    /**
-     * Cache of default web.xml fragments per Host
-     */
-    protected static final Map<Host,DefaultWebXmlCacheEntry> hostWebXmlCache =
-            new ConcurrentHashMap<>();
-
-
-    /**
-     * Set used as the value for {@code JavaClassCacheEntry.sciSet} when there
-     * are no SCIs associated with a class.
-     */
-    private static final Set<ServletContainerInitializer> EMPTY_SCI_SET = Collections.emptySet();
-
-
-    // ----------------------------------------------------- Instance Variables
-    /**
-     * Custom mappings of login methods to authenticators
-     */
-    protected Map<String,Authenticator> customAuthenticators;
-
-
-    /**
-     * The Context we are associated with.
-     */
-    protected volatile Context context = null;
-
-
-    /**
-     * The default web application's deployment descriptor location.
-     */
-    protected String defaultWebXml = null;
-
-
-    /**
-     * Track any fatal errors during startup configuration processing.
-     */
-    protected boolean ok = false;
-
-
-    /**
-     * Original docBase.
-     */
-    protected String originalDocBase = null;
-
-
-    /**
-     * Anti-locking docBase. It is a path to a copy of the web application
-     * in the java.io.tmpdir directory. This path is always an absolute one.
-     */
-    private File antiLockingDocBase = null;
-
-
-    /**
-     * Map of ServletContainerInitializer to classes they expressed interest in.
-     */
-    protected final Map<ServletContainerInitializer, Set<Class<?>>> initializerClassMap =
-            new LinkedHashMap<>();
-
-    /**
-     * Map of Types to ServletContainerInitializer that are interested in those
-     * types.
-     */
-    protected final Map<Class<?>, Set<ServletContainerInitializer>> typeInitializerMap =
-            new HashMap<>();
-
-    /**
-     * Flag that indicates if at least one {@link HandlesTypes} entry is present
-     * that represents an annotation.
-     */
-    protected boolean handlesTypesAnnotations = false;
-
-    /**
-     * Flag that indicates if at least one {@link HandlesTypes} entry is present
-     * that represents a non-annotation.
-     */
-    protected boolean handlesTypesNonAnnotations = false;
-
-
-    // ------------------------------------------------------------- Properties
-
-    /**
-     * Obtain the location of the default deployment descriptor.
-     *
-     * @return The path to the default web.xml. If not absolute, it is relative
-     *         to CATALINA_BASE.
-     */
-    public String getDefaultWebXml() {
-        if (defaultWebXml == null) {
-            defaultWebXml = Constants.DefaultWebXml;
-        }
-        return defaultWebXml;
-    }
-
-
-    /**
-     * Set the location of the default deployment descriptor.
-     *
-     * @param path The path to the default web.xml. If not absolute, it is
-     *             relative to CATALINA_BASE.
-     */
-    public void setDefaultWebXml(String path) {
-        this.defaultWebXml = path;
-    }
-
-
-    /**
-     * Sets custom mappings of login methods to authenticators.
-     *
-     * @param customAuthenticators Custom mappings of login methods to
-     * authenticators
-     */
-    public void setCustomAuthenticators(
-            Map<String,Authenticator> customAuthenticators) {
-        this.customAuthenticators = customAuthenticators;
-    }
-
-
-    // --------------------------------------------------------- Public Methods
-
-
-    /**
-     * Process events for an associated Context.
-     *
-     * @param event The lifecycle event that has occurred
-     */
-    @Override
-    public void lifecycleEvent(LifecycleEvent event) {
-
-        // Identify the context we are associated with
-        try {
-            context = (Context) event.getLifecycle();
-        } catch (ClassCastException e) {
-            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
-            return;
-        }
-
-        // Process the event that has occurred
-        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
-            configureStart();
-        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
-            beforeStart();
-        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
-            // Restore docBase for management tools
-            if (originalDocBase != null) {
-                context.setDocBase(originalDocBase);
-            }
-        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
-            configureStop();
-        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
-            init();
-        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
-            destroy();
-        }
-
-    }
-
-
-    // -------------------------------------------------------- protected Methods
-
-
-    /**
-     * Process the application classes annotations, if it exists.
-     */
-    protected void applicationAnnotationsConfig() {
-
-        long t1=System.currentTimeMillis();
-
-        WebAnnotationSet.loadApplicationAnnotations(context);
-
-        long t2=System.currentTimeMillis();
-        if (context instanceof StandardContext) {
-            ((StandardContext) context).setStartupTime(t2-t1+
-                    ((StandardContext) context).getStartupTime());
-        }
-    }
-
-
-    /**
-     * Set up an Authenticator automatically if required, and one has not
-     * already been configured.
-     */
-    protected void authenticatorConfig() {
-
-        LoginConfig loginConfig = context.getLoginConfig();
-        if (loginConfig == null) {
-            // Need an authenticator to support HttpServletRequest.login()
-            loginConfig = DUMMY_LOGIN_CONFIG;
-            context.setLoginConfig(loginConfig);
-        }
-
-        // Has an authenticator been configured already?
-        if (context.getAuthenticator() != null) {
-            return;
-        }
-
-        // Has a Realm been configured for us to authenticate against?
-        if (context.getRealm() == null) {
-            log.error(sm.getString("contextConfig.missingRealm"));
-            ok = false;
-            return;
-        }
-
-        /*
-         * First check to see if there is a custom mapping for the login
-         * method. If so, use it. Otherwise, check if there is a mapping in
-         * org/apache/catalina/startup/Authenticators.properties.
-         */
-        Valve authenticator = null;
-        if (customAuthenticators != null) {
-            authenticator = (Valve) customAuthenticators.get(loginConfig.getAuthMethod());
-        }
-
-        if (authenticator == null) {
-            if (authenticators == null) {
-                log.error(sm.getString("contextConfig.authenticatorResources"));
-                ok = false;
-                return;
-            }
-
-            // Identify the class name of the Valve we should configure
-            String authenticatorName = authenticators.getProperty(loginConfig.getAuthMethod());
-            if (authenticatorName == null) {
-                log.error(sm.getString("contextConfig.authenticatorMissing",
-                                 loginConfig.getAuthMethod()));
-                ok = false;
-                return;
-            }
-
-            // Instantiate and install an Authenticator of the requested class
-            try {
-                Class<?> authenticatorClass = Class.forName(authenticatorName);
-                authenticator = (Valve) authenticatorClass.getConstructor().newInstance();
-            } catch (Throwable t) {
-                ExceptionUtils.handleThrowable(t);
-                log.error(sm.getString(
-                                    "contextConfig.authenticatorInstantiate",
-                                    authenticatorName),
-                          t);
-                ok = false;
-            }
-        }
-
-        if (authenticator != null) {
-            Pipeline pipeline = context.getPipeline();
-            if (pipeline != null) {
-                pipeline.addValve(authenticator);
-                if (log.isDebugEnabled()) {
-                    log.debug(sm.getString(
-                                    "contextConfig.authenticatorConfigured",
-                                    loginConfig.getAuthMethod()));
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Create (if necessary) and return a Digester configured to process the
-     * context configuration descriptor for an application.
-     * @return the digester for context.xml files
-     */
-    protected Digester createContextDigester() {
-        Digester digester = new Digester();
-        digester.setValidating(false);
-        digester.setRulesValidation(true);
-        Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
-        List<String> objectAttrs = new ArrayList<>();
-        objectAttrs.add("className");
-        fakeAttributes.put(Object.class, objectAttrs);
-        // Ignore attribute added by Eclipse for its internal tracking
-        List<String> contextAttrs = new ArrayList<>();
-        contextAttrs.add("source");
-        fakeAttributes.put(StandardContext.class, contextAttrs);
-        digester.setFakeAttributes(fakeAttributes);
-        RuleSet contextRuleSet = new ContextRuleSet("", false);
-        digester.addRuleSet(contextRuleSet);
-        RuleSet namingRuleSet = new NamingRuleSet("Context/");
-        digester.addRuleSet(namingRuleSet);
-        return digester;
-    }
-
-
-    /**
-     * Process the default configuration file, if it exists.
-     * @param digester The digester that will be used for XML parsing
-     */
-    protected void contextConfig(Digester digester) {
-
-        String defaultContextXml = null;
-
-        // Open the default context.xml file, if it exists
-        if (context instanceof StandardContext) {
-            defaultContextXml = ((StandardContext)context).getDefaultContextXml();
-        }
-        // set the default if we don't have any overrides
-        if (defaultContextXml == null) {
-            defaultContextXml = Constants.DefaultContextXml;
-        }
-
-        if (!context.getOverride()) {
-            try (ConfigurationSource.Resource contextXmlResource =
-                    ConfigFileLoader.getSource().getResource(defaultContextXml)) {
-                URL defaultContextUrl = contextXmlResource.getURI().toURL();
-                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
-            } catch (MalformedURLException e) {
-                log.error(sm.getString("contextConfig.badUrl", defaultContextXml), e);
-            } catch (IOException e) {
-                // Not found
-            }
-
-            String hostContextFile = Container.getConfigPath(context, Constants.HostContextXml);
-            try (ConfigurationSource.Resource contextXmlResource =
-                    ConfigFileLoader.getSource().getConfResource(hostContextFile)) {
-                URL defaultContextUrl = contextXmlResource.getURI().toURL();
-                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
-            } catch (MalformedURLException e) {
-                log.error(sm.getString("contextConfig.badUrl", hostContextFile), e);
-            } catch (IOException e) {
-                // Not found
-            }
-        }
-        if (context.getConfigFile() != null) {
-            processContextConfig(digester, context.getConfigFile(), null);
-        }
-
-    }
-
-
-    /**
-     * Process a context.xml.
-     * @param digester The digester that will be used for XML parsing
-     * @param contextXml The URL to the context.xml configuration
-     * @param stream The XML resource stream
-     */
-    protected void processContextConfig(Digester digester, URL contextXml, InputStream stream) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("Processing context [" + context.getName()
-                    + "] configuration file [" + contextXml + "]");
-        }
-
-        InputSource source = null;
-
-        try {
-            source = new InputSource(contextXml.toString());
-            if (stream == null) {
-                URLConnection xmlConn = contextXml.openConnection();
-                xmlConn.setUseCaches(false);
-                stream = xmlConn.getInputStream();
-            }
-        } catch (Exception e) {
-            log.error(sm.getString("contextConfig.contextMissing",
-                      contextXml) , e);
-        }
-
-        if (source == null) {
-            return;
-        }
-
-        try {
-            source.setByteStream(stream);
-            digester.setClassLoader(this.getClass().getClassLoader());
-            digester.setUseContextClassLoader(false);
-            digester.push(context.getParent());
-            digester.push(context);
-            XmlErrorHandler errorHandler = new XmlErrorHandler();
-            digester.setErrorHandler(errorHandler);
-            digester.parse(source);
-            if (errorHandler.getWarnings().size() > 0 ||
-                    errorHandler.getErrors().size() > 0) {
-                errorHandler.logFindings(log, contextXml.toString());
-                ok = false;
-            }
-            if (log.isDebugEnabled()) {
-                log.debug("Successfully processed context [" + context.getName()
-                        + "] configuration file [" + contextXml + "]");
-            }
-        } catch (SAXParseException e) {
-            log.error(sm.getString("contextConfig.contextParse",
-                    context.getName()), e);
-            log.error(sm.getString("contextConfig.defaultPosition",
-                             "" + e.getLineNumber(),
-                             "" + e.getColumnNumber()));
-            ok = false;
-        } catch (Exception e) {
-            log.error(sm.getString("contextConfig.contextParse",
-                    context.getName()), e);
-            ok = false;
-        } finally {
-            try {
-                if (stream != null) {
-                    stream.close();
-                }
-            } catch (IOException e) {
-                log.error(sm.getString("contextConfig.contextClose"), e);
-            }
-        }
-    }
-
-
-    /**
-     * Adjust docBase.
-     * @throws IOException cannot access the context base path
-     */
-    protected void fixDocBase() throws IOException {
-
-        Host host = (Host) context.getParent();
-        File appBase = host.getAppBaseFile();
-
-        String docBase = context.getDocBase();
-        if (docBase == null) {
-            // Trying to guess the docBase according to the path
-            String path = context.getPath();
-            if (path == null) {
-                return;
-            }
-            ContextName cn = new ContextName(path, context.getWebappVersion());
-            docBase = cn.getBaseName();
-        }
-
-        File file = new File(docBase);
-        if (!file.isAbsolute()) {
-            docBase = (new File(appBase, docBase)).getAbsolutePath();
-        } else {
-            docBase = file.getAbsolutePath();
-        }
-        file = new File(docBase);
-        String origDocBase = docBase;
-
-        ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
-        String pathName = cn.getBaseName();
-
-        boolean unpackWARs = true;
-        if (host instanceof StandardHost) {
-            unpackWARs = ((StandardHost) host).isUnpackWARs();
-            if (unpackWARs && context instanceof StandardContext) {
-                unpackWARs =  ((StandardContext) context).getUnpackWAR();
-            }
-        }
-
-        boolean docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
-
-        if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) {
-            URL war = UriUtil.buildJarUrl(new File(docBase));
-            if (unpackWARs) {
-                docBase = ExpandWar.expand(host, war, pathName);
-                file = new File(docBase);
-                docBase = file.getCanonicalPath();
-                if (context instanceof StandardContext) {
-                    ((StandardContext) context).setOriginalDocBase(origDocBase);
-                }
-            } else {
-                ExpandWar.validate(host, war, pathName);
-            }
-        } else {
-            File docDir = new File(docBase);
-            File warFile = new File(docBase + ".war");
-            URL war = null;
-            if (warFile.exists() && docBaseInAppBase) {
-                war = UriUtil.buildJarUrl(warFile);
-            }
-            if (docDir.exists()) {
-                if (war != null && unpackWARs) {
-                    // Check if WAR needs to be re-expanded (e.g. if it has
-                    // changed). Note: HostConfig.deployWar() takes care of
-                    // ensuring that the correct XML file is used.
-                    // This will be a NO-OP if the WAR is unchanged.
-                    ExpandWar.expand(host, war, pathName);
-                }
-            } else {
-                if (war != null) {
-                    if (unpackWARs) {
-                        docBase = ExpandWar.expand(host, war, pathName);
-                        file = new File(docBase);
-                        docBase = file.getCanonicalPath();
-                    } else {
-                        docBase = warFile.getCanonicalPath();
-                        ExpandWar.validate(host, war, pathName);
-                    }
-                }
-                if (context instanceof StandardContext) {
-                    ((StandardContext) context).setOriginalDocBase(origDocBase);
-                }
-            }
-        }
-
-        // Re-calculate now docBase is a canonical path
-        docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
-
-        if (docBaseInAppBase) {
-            docBase = docBase.substring(appBase.getPath().length());
-            docBase = docBase.replace(File.separatorChar, '/');
-            if (docBase.startsWith("/")) {
-                docBase = docBase.substring(1);
-            }
-        } else {
-            docBase = docBase.replace(File.separatorChar, '/');
-        }
-
-        context.setDocBase(docBase);
-    }
-
-
-    protected void antiLocking() {
-
-        if ((context instanceof StandardContext)
-            && ((StandardContext) context).getAntiResourceLocking()) {
-
-            Host host = (Host) context.getParent();
-            String docBase = context.getDocBase();
-            if (docBase == null) {
-                return;
-            }
-            originalDocBase = docBase;
-
-            File docBaseFile = new File(docBase);
-            if (!docBaseFile.isAbsolute()) {
-                docBaseFile = new File(host.getAppBaseFile(), docBase);
-            }
-
-            String path = context.getPath();
-            if (path == null) {
-                return;
-            }
-            ContextName cn = new ContextName(path, context.getWebappVersion());
-            docBase = cn.getBaseName();
-
-            if (originalDocBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
-                antiLockingDocBase = new File(
-                        System.getProperty("java.io.tmpdir"),
-                        deploymentCount++ + "-" + docBase + ".war");
-            } else {
-                antiLockingDocBase = new File(
-                        System.getProperty("java.io.tmpdir"),
-                        deploymentCount++ + "-" + docBase);
-            }
-            antiLockingDocBase = antiLockingDocBase.getAbsoluteFile();
-
-            if (log.isDebugEnabled()) {
-                log.debug("Anti locking context[" + context.getName()
-                        + "] setting docBase to " +
-                        antiLockingDocBase.getPath());
-            }
-
-            // Cleanup just in case an old deployment is lying around
-            ExpandWar.delete(antiLockingDocBase);
-            if (ExpandWar.copy(docBaseFile, antiLockingDocBase)) {
-                context.setDocBase(antiLockingDocBase.getPath());
-            }
-        }
-    }
-
-
-    /**
-     * Process a "init" event for this Context.
-     */
-    protected synchronized void init() {
-        // Called from StandardContext.init()
-
-        Digester contextDigester = createContextDigester();
-        contextDigester.getParser();
-
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("contextConfig.init"));
-        }
-        context.setConfigured(false);
-        ok = true;
-
-        contextConfig(contextDigester);
-    }
-
-
-    /**
-     * Process a "before start" event for this Context.
-     */
-    protected synchronized void beforeStart() {
-
-        try {
-            fixDocBase();
-        } catch (IOException e) {
-            log.error(sm.getString(
-                    "contextConfig.fixDocBase", context.getName()), e);
-        }
-
-        antiLocking();
-    }
-
-
-    /**
-     * Process a "contextConfig" event for this Context.
-     */
-    protected synchronized void configureStart() {
-        // Called from StandardContext.start()
-
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("contextConfig.start"));
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("contextConfig.xmlSettings",
-                    context.getName(),
-                    Boolean.valueOf(context.getXmlValidation()),
-                    Boolean.valueOf(context.getXmlNamespaceAware())));
-        }
-
-        webConfig();
-
-        if (!context.getIgnoreAnnotations()) {
-            applicationAnnotationsConfig();
-        }
-        if (ok) {
-            validateSecurityRoles();
-        }
-
-        // Configure an authenticator if we need one
-        if (ok) {
-            authenticatorConfig();
-        }
-
-        // Dump the contents of this pipeline if requested
-        if (log.isDebugEnabled()) {
-            log.debug("Pipeline Configuration:");
-            Pipeline pipeline = context.getPipeline();
-            Valve valves[] = null;
-            if (pipeline != null) {
-                valves = pipeline.getValves();
-            }
-            if (valves != null) {
-                for (int i = 0; i < valves.length; i++) {
-                    log.debug("  " + valves[i].getClass().getName());
-                }
-            }
-            log.debug("======================");
-        }
-
-        // Make our application available if no problems were encountered
-        if (ok) {
-            context.setConfigured(true);
-        } else {
-            log.error(sm.getString("contextConfig.unavailable"));
-            context.setConfigured(false);
-        }
-
-    }
-
-
-    /**
-     * Process a "stop" event for this Context.
-     */
-    protected synchronized void configureStop() {
-
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("contextConfig.stop"));
-        }
-
-        int i;
-
-        // Removing children
-        Container[] children = context.findChildren();
-        for (i = 0; i < children.length; i++) {
-            context.removeChild(children[i]);
-        }
-
-        // Removing application parameters
-        /*
-        ApplicationParameter[] applicationParameters =
-            context.findApplicationParameters();
-        for (i = 0; i < applicationParameters.length; i++) {
-            context.removeApplicationParameter
-                (applicationParameters[i].getName());
-        }
-        */
-
-        // Removing security constraints
-        SecurityConstraint[] securityConstraints = context.findConstraints();
-        for (i = 0; i < securityConstraints.length; i++) {
-            context.removeConstraint(securityConstraints[i]);
-        }
-
-        // Removing Ejbs
-        /*
-        ContextEjb[] contextEjbs = context.findEjbs();
-        for (i = 0; i < contextEjbs.length; i++) {
-            context.removeEjb(contextEjbs[i].getName());
-        }
-        */
-
-        // Removing environments
-        /*
-        ContextEnvironment[] contextEnvironments = context.findEnvironments();
-        for (i = 0; i < contextEnvironments.length; i++) {
-            context.removeEnvironment(contextEnvironments[i].getName());
-        }
-        */
-
-        // Removing errors pages
-        ErrorPage[] errorPages = context.findErrorPages();
-        for (i = 0; i < errorPages.length; i++) {
-            context.removeErrorPage(errorPages[i]);
-        }
-
-        // Removing filter defs
-        FilterDef[] filterDefs = context.findFilterDefs();
-        for (i = 0; i < filterDefs.length; i++) {
-            context.removeFilterDef(filterDefs[i]);
-        }
-
-        // Removing filter maps
-        FilterMap[] filterMaps = context.findFilterMaps();
-        for (i = 0; i < filterMaps.length; i++) {
-            context.removeFilterMap(filterMaps[i]);
-        }
-
-        // Removing local ejbs
-        /*
-        ContextLocalEjb[] contextLocalEjbs = context.findLocalEjbs();
-        for (i = 0; i < contextLocalEjbs.length; i++) {
-            context.removeLocalEjb(contextLocalEjbs[i].getName());
-        }
-        */
-
-        // Removing Mime mappings
-        String[] mimeMappings = context.findMimeMappings();
-        for (i = 0; i < mimeMappings.length; i++) {
-            context.removeMimeMapping(mimeMappings[i]);
-        }
-
-        // Removing parameters
-        String[] parameters = context.findParameters();
-        for (i = 0; i < parameters.length; i++) {
-            context.removeParameter(parameters[i]);
-        }
-
-        // Removing resource env refs
-        /*
-        String[] resourceEnvRefs = context.findResourceEnvRefs();
-        for (i = 0; i < resourceEnvRefs.length; i++) {
-            context.removeResourceEnvRef(resourceEnvRefs[i]);
-        }
-        */
-
-        // Removing resource links
-        /*
-        ContextResourceLink[] contextResourceLinks =
-            context.findResourceLinks();
-        for (i = 0; i < contextResourceLinks.length; i++) {
-            context.removeResourceLink(contextResourceLinks[i].getName());
-        }
-        */
-
-        // Removing resources
-        /*
-        ContextResource[] contextResources = context.findResources();
-        for (i = 0; i < contextResources.length; i++) {
-            context.removeResource(contextResources[i].getName());
-        }
-        */
-
-        // Removing security role
-        String[] securityRoles = context.findSecurityRoles();
-        for (i = 0; i < securityRoles.length; i++) {
-            context.removeSecurityRole(securityRoles[i]);
-        }
-
-        // Removing servlet mappings
-        String[] servletMappings = context.findServletMappings();
-        for (i = 0; i < servletMappings.length; i++) {
-            context.removeServletMapping(servletMappings[i]);
-        }
-
-        // FIXME : Removing status pages
-
-        // Removing welcome files
-        String[] welcomeFiles = context.findWelcomeFiles();
-        for (i = 0; i < welcomeFiles.length; i++) {
-            context.removeWelcomeFile(welcomeFiles[i]);
-        }
-
-        // Removing wrapper lifecycles
-        String[] wrapperLifecycles = context.findWrapperLifecycles();
-        for (i = 0; i < wrapperLifecycles.length; i++) {
-            context.removeWrapperLifecycle(wrapperLifecycles[i]);
-        }
-
-        // Removing wrapper listeners
-        String[] wrapperListeners = context.findWrapperListeners();
-        for (i = 0; i < wrapperListeners.length; i++) {
-            context.removeWrapperListener(wrapperListeners[i]);
-        }
-
-        // Remove (partially) folders and files created by antiLocking
-        if (antiLockingDocBase != null) {
-            // No need to log failure - it is expected in this case
-            ExpandWar.delete(antiLockingDocBase, false);
-        }
-
-        // Reset ServletContextInitializer scanning
-        initializerClassMap.clear();
-        typeInitializerMap.clear();
-
-        ok = true;
-
-    }
-
-
-    /**
-     * Process a "destroy" event for this Context.
-     */
-    protected synchronized void destroy() {
-        // Called from StandardContext.destroy()
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("contextConfig.destroy"));
-        }
-
-        // Skip clearing the work directory if Tomcat is being shutdown
-        Server s = getServer();
-        if (s != null && !s.getState().isAvailable()) {
-            return;
-        }
-
-        // Changed to getWorkPath per Bugzilla 35819.
-        if (context instanceof StandardContext) {
-            String workDir = ((StandardContext) context).getWorkPath();
-            if (workDir != null) {
-                ExpandWar.delete(new File(workDir));
-            }
-        }
-    }
-
-
-    private Server getServer() {
-        Container c = context;
-        while (c != null && !(c instanceof Engine)) {
-            c = c.getParent();
-        }
-
-        if (c == null) {
-            return null;
-        }
-
-        Service s = ((Engine)c).getService();
-
-        if (s == null) {
-            return null;
-        }
-
-        return s.getServer();
-    }
-
-    /**
-     * Validate the usage of security role names in the web application
-     * deployment descriptor.  If any problems are found, issue warning
-     * messages (for backwards compatibility) and add the missing roles.
-     * (To make these problems fatal instead, simply set the <code>ok</code>
-     * instance variable to <code>false</code> as well).
-     */
-    protected void validateSecurityRoles() {
-
-        // Check role names used in <security-constraint> elements
-        SecurityConstraint constraints[] = context.findConstraints();
-        for (int i = 0; i < constraints.length; i++) {
-            String roles[] = constraints[i].findAuthRoles();
-            for (int j = 0; j < roles.length; j++) {
-                if (!"*".equals(roles[j]) &&
-                    !context.findSecurityRole(roles[j])) {
-                    log.warn(sm.getString("contextConfig.role.auth", roles[j]));
-                    context.addSecurityRole(roles[j]);
-                }
-            }
-        }
-
-        // Check role names used in <servlet> elements
-        Container wrappers[] = context.findChildren();
-        for (int i = 0; i < wrappers.length; i++) {
-            Wrapper wrapper = (Wrapper) wrappers[i];
-            String runAs = wrapper.getRunAs();
-            if ((runAs != null) && !context.findSecurityRole(runAs)) {
-                log.warn(sm.getString("contextConfig.role.runas", runAs));
-                context.addSecurityRole(runAs);
-            }
-            String names[] = wrapper.findSecurityReferences();
-            for (int j = 0; j < names.length; j++) {
-                String link = wrapper.findSecurityReference(names[j]);
-                if ((link != null) && !context.findSecurityRole(link)) {
-                    log.warn(sm.getString("contextConfig.role.link", link));
-                    context.addSecurityRole(link);
-                }
-            }
-        }
-
-    }
-
-
-    protected File getHostConfigBase() {
-        File file = null;
-        if (context.getParent() instanceof Host) {
-            file = ((Host)context.getParent()).getConfigBaseFile();
-        }
-        return file;
-    }
-
-    /**
-     * Scan the web.xml files that apply to the web application and merge them
-     * using the rules defined in the spec. For the global web.xml files,
-     * where there is duplicate configuration, the most specific level wins. ie
-     * an application's web.xml takes precedence over the host level or global
-     * web.xml file.
-     */
-    protected void webConfig() {
-        /*
-         * Anything and everything can override the global and host defaults.
-         * This is implemented in two parts
-         * - Handle as a web fragment that gets added after everything else so
-         *   everything else takes priority
-         * - Mark Servlets as overridable so SCI configuration can replace
-         *   configuration from the defaults
-         */
-
-        /*
-         * The rules for annotation scanning are not as clear-cut as one might
-         * think. Tomcat implements the following process:
-         * - As per SRV.1.6.2, Tomcat will scan for annotations regardless of
-         *   which Servlet spec version is declared in web.xml. The EG has
-         *   confirmed this is the expected behaviour.
-         * - As per http://java.net/jira/browse/SERVLET_SPEC-36, if the main
-         *   web.xml is marked as metadata-complete, JARs are still processed
-         *   for SCIs.
-         * - If metadata-complete=true and an absolute ordering is specified,
-         *   JARs excluded from the ordering are also excluded from the SCI
-         *   processing.
-         * - If an SCI has a @HandlesType annotation then all classes (except
-         *   those in JARs excluded from an absolute ordering) need to be
-         *   scanned to check if they match.
-         */
-        WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
-                context.getXmlValidation(), context.getXmlBlockExternal());
-
-        Set<WebXml> defaults = new HashSet<>();
-        defaults.add(getDefaultWebXmlFragment(webXmlParser));
-
-        Set<WebXml> tomcatWebXml = new HashSet<>();
-        tomcatWebXml.add(getTomcatWebXmlFragment(webXmlParser));
-
-        WebXml webXml = createWebXml();
-
-        // Parse context level web.xml
-        InputSource contextWebXml = getContextWebXmlSource();
-        if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
-            ok = false;
-        }
-
-        ServletContext sContext = context.getServletContext();
-
-        // Ordering is important here
-
-        // Step 1. Identify all the JARs packaged with the application and those
-        // provided by the container. If any of the application JARs have a
-        // web-fragment.xml it will be parsed at this point. web-fragment.xml
-        // files are ignored for container provided JARs.
-        Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
-
-        // Step 2. Order the fragments.
-        Set<WebXml> orderedFragments = null;
-        orderedFragments =
-                WebXml.orderWebFragments(webXml, fragments, sContext);
-
-        // Step 3. Look for ServletContainerInitializer implementations
-        if (ok) {
-            processServletContainerInitializers();
-        }
-
-        if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
-            // Steps 4 & 5.
-            processClasses(webXml, orderedFragments);
-        }
-
-        if (!webXml.isMetadataComplete()) {
-            // Step 6. Merge web-fragment.xml files into the main web.xml
-            // file.
-            if (ok) {
-                ok = webXml.merge(orderedFragments);
-            }
-
-            // Step 7a
-            // merge tomcat-web.xml
-            webXml.merge(tomcatWebXml);
-
-            // Step 7b. Apply global defaults
-            // Have to merge defaults before JSP conversion since defaults
-            // provide JSP servlet definition.
-            webXml.merge(defaults);
-
-            // Step 8. Convert explicitly mentioned jsps to servlets
-            if (ok) {
-                convertJsps(webXml);
-            }
-
-            // Step 9. Apply merged web.xml to Context
-            if (ok) {
-                configureContext(webXml);
-            }
-        } else {
-            webXml.merge(tomcatWebXml);
-            webXml.merge(defaults);
-            convertJsps(webXml);
-            configureContext(webXml);
-        }
-
-        if (context.getLogEffectiveWebXml()) {
-            log.info(sm.getString("contextConfig.effectiveWebXml", webXml.toXml()));
-        }
-
-        // Always need to look for static resources
-        // Step 10. Look for static resources packaged in JARs
-        if (ok) {
-            // Spec does not define an order.
-            // Use ordered JARs followed by remaining JARs
-            Set<WebXml> resourceJars = new LinkedHashSet<>();
-            for (WebXml fragment : orderedFragments) {
-                resourceJars.add(fragment);
-            }
-            for (WebXml fragment : fragments.values()) {
-                if (!resourceJars.contains(fragment)) {
-                    resourceJars.add(fragment);
-                }
-            }
-            processResourceJARs(resourceJars);
-            // See also StandardContext.resourcesStart() for
-            // WEB-INF/classes/META-INF/resources configuration
-        }
-
-        // Step 11. Apply the ServletContainerInitializer config to the
-        // context
-        if (ok) {
-            for (Map.Entry<ServletContainerInitializer,
-                    Set<Class<?>>> entry :
-                        initializerClassMap.entrySet()) {
-                if (entry.getValue().isEmpty()) {
-                    context.addServletContainerInitializer(
-                            entry.getKey(), null);
-                } else {
-                    context.addServletContainerInitializer(
-                            entry.getKey(), entry.getValue());
-                }
-            }
-        }
-    }
-
-
-    protected void processClasses(WebXml webXml, Set<WebXml> orderedFragments) {
-        // Step 4. Process /WEB-INF/classes for annotations and
-        // @HandlesTypes matches
-        Map<String, JavaClassCacheEntry> javaClassCache = new HashMap<>();
-
-        if (ok) {
-            WebResource[] webResources =
-                    context.getResources().listResources("/WEB-INF/classes");
-
-            for (WebResource webResource : webResources) {
-                // Skip the META-INF directory from any JARs that have been
-                // expanded in to WEB-INF/classes (sometimes IDEs do this).
-                if ("META-INF".equals(webResource.getName())) {
-                    continue;
-                }
-                processAnnotationsWebResource(webResource, webXml,
-                        webXml.isMetadataComplete(), javaClassCache);
-            }
-        }
-
-        // Step 5. Process JARs for annotations and
-        // @HandlesTypes matches - only need to process those fragments we
-        // are going to use (remember orderedFragments includes any
-        // container fragments)
-        if (ok) {
-            processAnnotations(
-                    orderedFragments, webXml.isMetadataComplete(), javaClassCache);
-        }
-
-        // Cache, if used, is no longer required so clear it
-        javaClassCache.clear();
-    }
-
-
-    private void configureContext(WebXml webxml) {
-        // As far as possible, process in alphabetical order so it is easy to
-        // check everything is present
-        // Some validation depends on correct public ID
-        context.setPublicId(webxml.getPublicId());
-
-        // Everything else in order
-        context.setEffectiveMajorVersion(webxml.getMajorVersion());
-        context.setEffectiveMinorVersion(webxml.getMinorVersion());
-
-        for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
-            context.addParameter(entry.getKey(), entry.getValue());
-        }
-        context.setDenyUncoveredHttpMethods(
-                webxml.getDenyUncoveredHttpMethods());
-        context.setDisplayName(webxml.getDisplayName());
-        context.setDistributable(webxml.isDistributable());
-        for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
-            context.getNamingResources().addLocalEjb(ejbLocalRef);
-        }
-        for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
-            context.getNamingResources().addEjb(ejbRef);
-        }
-        for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
-            context.getNamingResources().addEnvironment(environment);
-        }
-        for (ErrorPage errorPage : webxml.getErrorPages().values()) {
-            context.addErrorPage(errorPage);
-        }
-        for (FilterDef filter : webxml.getFilters().values()) {
-            if (filter.getAsyncSupported() == null) {
-                filter.setAsyncSupported("false");
-            }
-            context.addFilterDef(filter);
-        }
-        for (FilterMap filterMap : webxml.getFilterMappings()) {
-            context.addFilterMap(filterMap);
-        }
-        context.setJspConfigDescriptor(webxml.getJspConfigDescriptor());
-        for (String listener : webxml.getListeners()) {
-            context.addApplicationListener(listener);
-        }
-        for (Entry<String, String> entry :
-                webxml.getLocaleEncodingMappings().entrySet()) {
-            context.addLocaleEncodingMappingParameter(entry.getKey(),
-                    entry.getValue());
-        }
-        // Prevents IAE
-        if (webxml.getLoginConfig() != null) {
-            context.setLoginConfig(webxml.getLoginConfig());
-        }
-        for (MessageDestinationRef mdr :
-                webxml.getMessageDestinationRefs().values()) {
-            context.getNamingResources().addMessageDestinationRef(mdr);
-        }
-
-        // messageDestinations were ignored in Tomcat 6, so ignore here
-
-        context.setIgnoreAnnotations(webxml.isMetadataComplete());
-        for (Entry<String, String> entry :
-                webxml.getMimeMappings().entrySet()) {
-            context.addMimeMapping(entry.getKey(), entry.getValue());
-        }
-        context.setRequestCharacterEncoding(webxml.getRequestCharacterEncoding());
-        // Name is just used for ordering
-        for (ContextResourceEnvRef resource :
-                webxml.getResourceEnvRefs().values()) {
-            context.getNamingResources().addResourceEnvRef(resource);
-        }
-        for (ContextResource resource : webxml.getResourceRefs().values()) {
-            context.getNamingResources().addResource(resource);
-        }
-        context.setResponseCharacterEncoding(webxml.getResponseCharacterEncoding());
-        boolean allAuthenticatedUsersIsAppRole =
-                webxml.getSecurityRoles().contains(
-                        SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
-        for (SecurityConstraint constraint : webxml.getSecurityConstraints()) {
-            if (allAuthenticatedUsersIsAppRole) {
-                constraint.treatAllAuthenticatedUsersAsApplicationRole();
-            }
-            context.addConstraint(constraint);
-        }
-        for (String role : webxml.getSecurityRoles()) {
-            context.addSecurityRole(role);
-        }
-        for (ContextService service : webxml.getServiceRefs().values()) {
-            context.getNamingResources().addService(service);
-        }
-        for (ServletDef servlet : webxml.getServlets().values()) {
-            Wrapper wrapper = context.createWrapper();
-            // Description is ignored
-            // Display name is ignored
-            // Icons are ignored
-
-            // jsp-file gets passed to the JSP Servlet as an init-param
-
-            if (servlet.getLoadOnStartup() != null) {
-                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
-            }
-            if (servlet.getEnabled() != null) {
-                wrapper.setEnabled(servlet.getEnabled().booleanValue());
-            }
-            wrapper.setName(servlet.getServletName());
-            Map<String,String> params = servlet.getParameterMap();
-            for (Entry<String, String> entry : params.entrySet()) {
-                wrapper.addInitParameter(entry.getKey(), entry.getValue());
-            }
-            wrapper.setRunAs(servlet.getRunAs());
-            Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
-            for (SecurityRoleRef roleRef : roleRefs) {
-                wrapper.addSecurityReference(
-                        roleRef.getName(), roleRef.getLink());
-            }
-            wrapper.setServletClass(servlet.getServletClass());
-            MultipartDef multipartdef = servlet.getMultipartDef();
-            if (multipartdef != null) {
-                if (multipartdef.getMaxFileSize() != null &&
-                        multipartdef.getMaxRequestSize()!= null &&
-                        multipartdef.getFileSizeThreshold() != null) {
-                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
-                            multipartdef.getLocation(),
-                            Long.parseLong(multipartdef.getMaxFileSize()),
-                            Long.parseLong(multipartdef.getMaxRequestSize()),
-                            Integer.parseInt(
-                                    multipartdef.getFileSizeThreshold())));
-                } else {
-                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
-                            multipartdef.getLocation()));
-                }
-            }
-            if (servlet.getAsyncSupported() != null) {
-                wrapper.setAsyncSupported(
-                        servlet.getAsyncSupported().booleanValue());
-            }
-            wrapper.setOverridable(servlet.isOverridable());
-            context.addChild(wrapper);
-        }
-        for (Entry<String, String> entry :
-                webxml.getServletMappings().entrySet()) {
-            context.addServletMappingDecoded(entry.getKey(), entry.getValue());
-        }
-        SessionConfig sessionConfig = webxml.getSessionConfig();
-        if (sessionConfig != null) {
-            if (sessionConfig.getSessionTimeout() != null) {
-                context.setSessionTimeout(
-                        sessionConfig.getSessionTimeout().intValue());
-            }
-            SessionCookieConfig scc =
-                context.getServletContext().getSessionCookieConfig();
-            scc.setName(sessionConfig.getCookieName());
-            scc.setDomain(sessionConfig.getCookieDomain());
-            scc.setPath(sessionConfig.getCookiePath());
-            scc.setComment(sessionConfig.getCookieComment());
-            if (sessionConfig.getCookieHttpOnly() != null) {
-                scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());
-            }
-            if (sessionConfig.getCookieSecure() != null) {
-                scc.setSecure(sessionConfig.getCookieSecure().booleanValue());
-            }
-            if (sessionConfig.getCookieMaxAge() != null) {
-                scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());
-            }
-            if (sessionConfig.getSessionTrackingModes().size() > 0) {
-                context.getServletContext().setSessionTrackingModes(
-                        sessionConfig.getSessionTrackingModes());
-            }
-        }
-
-        // Context doesn't use version directly
-
-        for (String welcomeFile : webxml.getWelcomeFiles()) {
-            /*
-             * The following will result in a welcome file of "" so don't add
-             * that to the context
-             * <welcome-file-list>
-             *   <welcome-file/>
-             * </welcome-file-list>
-             */
-            if (welcomeFile != null && welcomeFile.length() > 0) {
-                context.addWelcomeFile(welcomeFile);
-            }
-        }
-
-        // Do this last as it depends on servlets
-        for (JspPropertyGroup jspPropertyGroup :
-                webxml.getJspPropertyGroups()) {
-            String jspServletName = context.findServletMapping("*.jsp");
-            if (jspServletName == null) {
-                jspServletName = "jsp";
-            }
-            if (context.findChild(jspServletName) != null) {
-                for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
-                    context.addServletMappingDecoded(urlPattern, jspServletName, true);
-                }
-            } else {
-                if(log.isDebugEnabled()) {
-                    for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
-                        log.debug("Skipping " + urlPattern + " , no servlet " +
-                                jspServletName);
-                    }
-                }
-            }
-        }
-
-        for (Entry<String, String> entry :
-                webxml.getPostConstructMethods().entrySet()) {
-            context.addPostConstructMethod(entry.getKey(), entry.getValue());
-        }
-
-        for (Entry<String, String> entry :
-            webxml.getPreDestroyMethods().entrySet()) {
-            context.addPreDestroyMethod(entry.getKey(), entry.getValue());
-        }
-    }
-
-
-    private WebXml getTomcatWebXmlFragment(WebXmlParser webXmlParser) {
-
-        WebXml webXmlTomcatFragment = createWebXml();
-        webXmlTomcatFragment.setOverridable(true);
-
-        // Set to distributable else every app will be prevented from being
-        // distributable when the Tomcat fragment is merged with the main
-        // web.xml
-        webXmlTomcatFragment.setDistributable(true);
-        // When merging, the default welcome files are only used if the app has
-        // not defined any welcomes files.
-        webXmlTomcatFragment.setAlwaysAddWelcomeFiles(false);
-
-        WebResource resource = context.getResources().getResource(Constants.TomcatWebXml);
-        if (resource.isFile()) {
-            try {
-                InputSource source = new InputSource(resource.getURL().toURI().toString());
-                source.setByteStream(resource.getInputStream());
-                if (!webXmlParser.parseWebXml(source, webXmlTomcatFragment, false)) {
-                    ok = false;
-                }
-            } catch (URISyntaxException e) {
-                log.error(sm.getString("contextConfig.tomcatWebXmlError"), e);
-            }
-        }
-        return webXmlTomcatFragment;
-    }
-
-
-    private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {
-
-        // Host should never be null
-        Host host = (Host) context.getParent();
-
-        DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
-
-        InputSource globalWebXml = getGlobalWebXmlSource();
-        InputSource hostWebXml = getHostWebXmlSource();
-
-        long globalTimeStamp = 0;
-        long hostTimeStamp = 0;
-
-        if (globalWebXml != null) {
-            URLConnection uc = null;
-            try {
-                URL url = new URL(globalWebXml.getSystemId());
-                uc = url.openConnection();
-                globalTimeStamp = uc.getLastModified();
-            } catch (IOException e) {
-                globalTimeStamp = -1;
-            } finally {
-                if (uc != null) {
-                    try {
-                        uc.getInputStream().close();
-                    } catch (IOException e) {
-                        ExceptionUtils.handleThrowable(e);
-                        globalTimeStamp = -1;
-                    }
-                }
-            }
-        }
-
-        if (hostWebXml != null) {
-            URLConnection uc = null;
-            try {
-                URL url = new URL(hostWebXml.getSystemId());
-                uc = url.openConnection();
-                hostTimeStamp = uc.getLastModified();
-            } catch (IOException e) {
-                hostTimeStamp = -1;
-            } finally {
-                if (uc != null) {
-                    try {
-                        uc.getInputStream().close();
-                    } catch (IOException e) {
-                        ExceptionUtils.handleThrowable(e);
-                        hostTimeStamp = -1;
-                    }
-                }
-            }
-        }
-
-        if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
-                entry.getHostTimeStamp() == hostTimeStamp) {
-            InputSourceUtil.close(globalWebXml);
-            InputSourceUtil.close(hostWebXml);
-            return entry.getWebXml();
-        }
-
-        // Parsing global web.xml is relatively expensive. Use a sync block to
-        // make sure it only happens once. Use the pipeline since a lock will
-        // already be held on the host by another thread
-        synchronized (host.getPipeline()) {
-            entry = hostWebXmlCache.get(host);
-            if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
-                    entry.getHostTimeStamp() == hostTimeStamp) {
-                return entry.getWebXml();
-            }
-
-            WebXml webXmlDefaultFragment = createWebXml();
-            webXmlDefaultFragment.setOverridable(true);
-            // Set to distributable else every app will be prevented from being
-            // distributable when the default fragment is merged with the main
-            // web.xml
-            webXmlDefaultFragment.setDistributable(true);
-            // When merging, the default welcome files are only used if the app has
-            // not defined any welcomes files.
-            webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
-
-            // Parse global web.xml if present
-            if (globalWebXml == null) {
-                // This is unusual enough to log
-                log.info(sm.getString("contextConfig.defaultMissing"));
-            } else {
-                if (!webXmlParser.parseWebXml(
-                        globalWebXml, webXmlDefaultFragment, false)) {
-                    ok = false;
-                }
-            }
-
-            // Parse host level web.xml if present
-            // Additive apart from welcome pages
-            webXmlDefaultFragment.setReplaceWelcomeFiles(true);
-
-            if (!webXmlParser.parseWebXml(
-                    hostWebXml, webXmlDefaultFragment, false)) {
-                ok = false;
-            }
-
-            // Don't update the cache if an error occurs
-            if (globalTimeStamp != -1 && hostTimeStamp != -1) {
-                entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
-                        globalTimeStamp, hostTimeStamp);
-                hostWebXmlCache.put(host, entry);
-            }
-
-            return webXmlDefaultFragment;
-        }
-    }
-
-
-    private void convertJsps(WebXml webXml) {
-        Map<String,String> jspInitParams;
-        ServletDef jspServlet = webXml.getServlets().get("jsp");
-        if (jspServlet == null) {
-            jspInitParams = new HashMap<>();
-            Wrapper w = (Wrapper) context.findChild("jsp");
-            if (w != null) {
-                String[] params = w.findInitParameters();
-                for (String param : params) {
-                    jspInitParams.put(param, w.findInitParameter(param));
-                }
-            }
-        } else {
-            jspInitParams = jspServlet.getParameterMap();
-        }
-        for (ServletDef servletDef: webXml.getServlets().values()) {
-            if (servletDef.getJspFile() != null) {
-                convertJsp(servletDef, jspInitParams);
-            }
-        }
-    }
-
-    private void convertJsp(ServletDef servletDef,
-            Map<String,String> jspInitParams) {
-        servletDef.setServletClass(org.apache.catalina.core.Constants.JSP_SERVLET_CLASS);
-        String jspFile = servletDef.getJspFile();
-        if ((jspFile != null) && !jspFile.startsWith("/")) {
-            if (context.isServlet22()) {
-                if(log.isDebugEnabled()) {
-                    log.debug(sm.getString("contextConfig.jspFile.warning",
-                                       jspFile));
-                }
-                jspFile = "/" + jspFile;
-            } else {
-                throw new IllegalArgumentException
-                    (sm.getString("contextConfig.jspFile.error", jspFile));
-            }
-        }
-        servletDef.getParameterMap().put("jspFile", jspFile);
-        servletDef.setJspFile(null);
-        for (Map.Entry<String, String> initParam: jspInitParams.entrySet()) {
-            servletDef.addInitParameter(initParam.getKey(), initParam.getValue());
-        }
-    }
-
-    protected WebXml createWebXml() {
-        return new WebXml();
-    }
-
-    /**
-     * Scan JARs for ServletContainerInitializer implementations.
-     */
-    protected void processServletContainerInitializers() {
-
-        List<ServletContainerInitializer> detectedScis;
-        try {
-            WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
-            detectedScis = loader.load(ServletContainerInitializer.class);
-        } catch (IOException e) {
-            log.error(sm.getString(
-                    "contextConfig.servletContainerInitializerFail",
-                    context.getName()),
-                e);
-            ok = false;
-            return;
-        }
-
-        for (ServletContainerInitializer sci : detectedScis) {
-            initializerClassMap.put(sci, new HashSet<Class<?>>());
-
-            HandlesTypes ht;
-            try {
-                ht = sci.getClass().getAnnotation(HandlesTypes.class);
-            } catch (Exception e) {
-                if (log.isDebugEnabled()) {
-                    log.info(sm.getString("contextConfig.sci.debug",
-                            sci.getClass().getName()),
-                            e);
-                } else {
-                    log.info(sm.getString("contextConfig.sci.info",
-                            sci.getClass().getName()));
-                }
-                continue;
-            }
-            if (ht == null) {
-                continue;
-            }
-            Class<?>[] types = ht.value();
-            if (types == null) {
-                continue;
-            }
-
-            for (Class<?> type : types) {
-                if (type.isAnnotation()) {
-                    handlesTypesAnnotations = true;
-                } else {
-                    handlesTypesNonAnnotations = true;
-                }
-                Set<ServletContainerInitializer> scis =
-                        typeInitializerMap.get(type);
-                if (scis == null) {
-                    scis = new HashSet<>();
-                    typeInitializerMap.put(type, scis);
-                }
-                scis.add(sci);
-            }
-        }
-    }
-
-    /**
-     * Scan JARs that contain web-fragment.xml files that will be used to
-     * configure this application to see if they also contain static resources.
-     * If static resources are found, add them to the context. Resources are
-     * added in web-fragment.xml priority order.
-     * @param fragments The set of fragments that will be scanned for
-     *  static resources
-     */
-    protected void processResourceJARs(Set<WebXml> fragments) {
-        for (WebXml fragment : fragments) {
-            URL url = fragment.getURL();
-            try {
-                if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
-                    try (Jar jar = JarFactory.newInstance(url)) {
-                        jar.nextEntry();
-                        String entryName = jar.getEntryName();
-                        while (entryName != null) {
-                            if (entryName.startsWith("META-INF/resources/")) {
-                                context.getResources().createWebResourceSet(
-                                        WebResourceRoot.ResourceSetType.RESOURCE_JAR,
-                                        "/", url, "/META-INF/resources");
-                                break;
-                            }
-                            jar.nextEntry();
-                            entryName = jar.getEntryName();
-                        }
-                    }
-                } else if ("file".equals(url.getProtocol())) {
-                    File file = new File(url.toURI());
-                    File resources = new File(file, "META-INF/resources/");
-                    if (resources.isDirectory()) {
-                        context.getResources().createWebResourceSet(
-                                WebResourceRoot.ResourceSetType.RESOURCE_JAR,
-                                "/", resources.getAbsolutePath(), null, "/");
-                    }
-                }
-            } catch (IOException ioe) {
-                log.error(sm.getString("contextConfig.resourceJarFail", url,
-                        context.getName()));
-            } catch (URISyntaxException e) {
-                log.error(sm.getString("contextConfig.resourceJarFail", url,
-                    context.getName()));
-            }
-        }
-    }
-
-
-    /**
-     * Identify the default web.xml to be used and obtain an input source for
-     * it.
-     * @return an input source to the default web.xml
-     */
-    protected InputSource getGlobalWebXmlSource() {
-        // Is a default web.xml specified for the Context?
-        if (defaultWebXml == null && context instanceof StandardContext) {
-            defaultWebXml = ((StandardContext) context).getDefaultWebXml();
-        }
-        // Set the default if we don't have any overrides
-        if (defaultWebXml == null) {
-            getDefaultWebXml();
-        }
-
-        // Is it explicitly suppressed, e.g. in embedded environment?
-        if (Constants.NoDefaultWebXml.equals(defaultWebXml)) {
-            return null;
-        }
-        return getWebXmlSource(defaultWebXml, true);
-    }
-
-    /**
-     * Identify the host web.xml to be used and obtain an input source for
-     * it.
-     * @return an input source to the default per host web.xml
-     */
-    protected InputSource getHostWebXmlSource() {
-        File hostConfigBase = getHostConfigBase();
-        if (hostConfigBase == null)
-            return null;
-
-        return getWebXmlSource(hostConfigBase.getPath(), false);
-    }
-
-    /**
-     * Identify the application web.xml to be used and obtain an input source
-     * for it.
-     * @return an input source to the context web.xml
-     */
-    protected InputSource getContextWebXmlSource() {
-        InputStream stream = null;
-        InputSource source = null;
-        URL url = null;
-
-        String altDDName = null;
-
-        // Open the application web.xml file, if it exists
-        ServletContext servletContext = context.getServletContext();
-        try {
-            if (servletContext != null) {
-                altDDName = (String)servletContext.getAttribute(Globals.ALT_DD_ATTR);
-                if (altDDName != null) {
-                    try {
-                        stream = new FileInputStream(altDDName);
-                        url = new File(altDDName).toURI().toURL();
-                    } catch (FileNotFoundException e) {
-                        log.error(sm.getString("contextConfig.altDDNotFound",
-                                               altDDName));
-                    } catch (MalformedURLException e) {
-                        log.error(sm.getString("contextConfig.applicationUrl"));
-                    }
-                }
-                else {
-                    stream = servletContext.getResourceAsStream
-                        (Constants.ApplicationWebXml);
-                    try {
-                        url = servletContext.getResource(
-                                Constants.ApplicationWebXml);
-                    } catch (MalformedURLException e) {
-                        log.error(sm.getString("contextConfig.applicationUrl"));
-                    }
-                }
-            }
-            if (stream == null || url == null) {
-                if (log.isDebugEnabled()) {
-                    log.debug(sm.getString("contextConfig.applicationMissing") + " " + context);
-                }
-            } else {
-                source = new InputSource(url.toExternalForm());
-                source.setByteStream(stream);
-            }
-        } finally {
-            if (source == null && stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException e) {
-                    // Ignore
-                }
-            }
-        }
-
-        return source;
-    }
-
-    public String getConfigBasePath() {
-        String path = null;
-        if (context.getParent() instanceof Host) {
-            Host host = (Host) context.getParent();
-            if (host.getXmlBase() != null) {
-                path = host.getXmlBase();
-            } else {
-                StringBuilder xmlDir = new StringBuilder("conf");
-                Container parent = host.getParent();
-                if (parent instanceof Engine) {
-                    xmlDir.append('/');
-                    xmlDir.append(parent.getName());
-                }
-                xmlDir.append('/');
-                xmlDir.append(host.getName());
-                path = xmlDir.toString();
-            }
-        }
-        return path;
-    }
-
-    /**
-     * Utility method to create an input source from the specified XML file.
-     * @param filename  Name of the file (possibly with one or more leading path
-     *                  segments) to read
-     * @param global true if processing a shared resource, false if processing
-     *        a host based resource
-     * @return the input source
-     */
-    protected InputSource getWebXmlSource(String filename, boolean global) {
-        ConfigurationSource.Resource webXmlResource = null;
-        try {
-            if (global) {
-                if (Constants.DefaultWebXml.equals(filename)) {
-                    webXmlResource = ConfigFileLoader.getSource().getSharedWebXml();
-                } else {
-                    webXmlResource = ConfigFileLoader.getSource().getResource(filename);
-                }
-            } else {
-                String hostWebXml = Container.getConfigPath(context, Constants.HostWebXml);
-                webXmlResource = ConfigFileLoader.getSource().getConfResource(hostWebXml);
-            }
-        } catch (IOException e) {
-            // Ignore if not found
-            return null;
-        }
-
-        InputStream stream = null;
-        InputSource source = null;
-
-        try {
-            stream = webXmlResource.getInputStream();
-            source = new InputSource(webXmlResource.getURI().toString());
-            if (stream != null) {
-                source.setByteStream(stream);
-            }
-        } catch (Exception e) {
-            log.error(sm.getString("contextConfig.defaultError", filename, webXmlResource.getURI()), e);
-        } finally {
-            if (source == null && stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException e) {
-                    // Ignore
-                }
-            }
-        }
-
-        return source;
-    }
-
-
-    /**
-     * Scan /WEB-INF/lib for JARs and for each one found add it and any
-     * /META-INF/web-fragment.xml to the resulting Map. web-fragment.xml files
-     * will be parsed before being added to the map. Every JAR will be added and
-     * <code>null</code> will be used if no web-fragment.xml was found. Any JARs
-     * known not contain fragments will be skipped.
-     *
-     * @param application The main web.xml metadata
-     * @param webXmlParser The parser to use to process the web.xml file
-     * @return A map of JAR name to processed web fragment (if any)
-     */
-    protected Map<String,WebXml> processJarsForWebFragments(WebXml application,
-            WebXmlParser webXmlParser) {
-
-        JarScanner jarScanner = context.getJarScanner();
-        boolean delegate = false;
-        if (context instanceof StandardContext) {
-            delegate = ((StandardContext) context).getDelegate();
-        }
-        boolean parseRequired = true;
-        Set<String> absoluteOrder = application.getAbsoluteOrdering();
-        if (absoluteOrder != null && absoluteOrder.isEmpty() &&
-                !context.getXmlValidation()) {
-            // Skip parsing when there is an empty absolute ordering and
-            // validation is not enabled
-            parseRequired = false;
-        }
-        FragmentJarScannerCallback callback =
-                new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);
-
-        jarScanner.scan(JarScanType.PLUGGABILITY,
-                context.getServletContext(), callback);
-
-        if (!callback.isOk()) {
-            ok = false;
-        }
-        return callback.getFragments();
-    }
-
-    protected void processAnnotations(Set<WebXml> fragments,
-            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
-        for(WebXml fragment : fragments) {
-            // Only need to scan for @HandlesTypes matches if any of the
-            // following are true:
-            // - it has already been determined only @HandlesTypes is required
-            //   (e.g. main web.xml has metadata-complete="true"
-            // - this fragment is for a container JAR (Servlet 3.1 section 8.1)
-            // - this fragment has metadata-complete="true"
-            boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() ||
-                    fragment.isMetadataComplete();
-
-            WebXml annotations = new WebXml();
-            // no impact on distributable
-            annotations.setDistributable(true);
-            URL url = fragment.getURL();
-            processAnnotationsUrl(url, annotations, htOnly, javaClassCache);
-            Set<WebXml> set = new HashSet<>();
-            set.add(annotations);
-            // Merge annotations into fragment - fragment takes priority
-            fragment.merge(set);
-        }
-    }
-
-    protected void processAnnotationsWebResource(WebResource webResource,
-            WebXml fragment, boolean handlesTypesOnly,
-            Map<String,JavaClassCacheEntry> javaClassCache) {
-
-        if (webResource.isDirectory()) {
-            WebResource[] webResources =
-                    webResource.getWebResourceRoot().listResources(
-                            webResource.getWebappPath());
-            if (webResources.length > 0) {
-                if (log.isDebugEnabled()) {
-                    log.debug(sm.getString(
-                            "contextConfig.processAnnotationsWebDir.debug",
-                            webResource.getURL()));
-                }
-                for (WebResource r : webResources) {
-                    processAnnotationsWebResource(r, fragment, handlesTypesOnly, javaClassCache);
-                }
-            }
-        } else if (webResource.isFile() &&
-                webResource.getName().endsWith(".class")) {
-            try (InputStream is = webResource.getInputStream()) {
-                processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
-            } catch (IOException e) {
-                log.error(sm.getString("contextConfig.inputStreamWebResource",
-                        webResource.getWebappPath()),e);
-            } catch (ClassFormatException e) {
-                log.error(sm.getString("contextConfig.inputStreamWebResource",
-                        webResource.getWebappPath()),e);
-            }
-        }
-    }
-
-
-    protected void processAnnotationsUrl(URL url, WebXml fragment,
-            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
-        if (url == null) {
-            // Nothing to do.
-            return;
-        } else if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
-            processAnnotationsJar(url, fragment, handlesTypesOnly, javaClassCache);
-        } else if ("file".equals(url.getProtocol())) {
-            try {
-                processAnnotationsFile(
-                        new File(url.toURI()), fragment, handlesTypesOnly, javaClassCache);
-            } catch (URISyntaxException e) {
-                log.error(sm.getString("contextConfig.fileUrl", url), e);
-            }
-        } else {
-            log.error(sm.getString("contextConfig.unknownUrlProtocol",
-                    url.getProtocol(), url));
-        }
-
-    }
-
-
-    protected void processAnnotationsJar(URL url, WebXml fragment,
-            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
-
-        try (Jar jar = JarFactory.newInstance(url)) {
-            if (log.isDebugEnabled()) {
-                log.debug(sm.getString(
-                        "contextConfig.processAnnotationsJar.debug", url));
-            }
-
-            jar.nextEntry();
-            String entryName = jar.getEntryName();
-            while (entryName != null) {
-                if (entryName.endsWith(".class")) {
-                    try (InputStream is = jar.getEntryInputStream()) {
-                        processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
-                    } catch (IOException e) {
-                        log.error(sm.getString("contextConfig.inputStreamJar",
-                                entryName, url),e);
-                    } catch (ClassFormatException e) {
-                        log.error(sm.getString("contextConfig.inputStreamJar",
-                                entryName, url),e);
-                    }
-                }
-                jar.nextEntry();
-                entryName = jar.getEntryName();
-            }
-        } catch (IOException e) {
-            log.error(sm.getString("contextConfig.jarFile", url), e);
-        }
-    }
-
-
-    protected void processAnnotationsFile(File file, WebXml fragment,
-            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
-
-        if (file.isDirectory()) {
-            // Returns null if directory is not readable
-            String[] dirs = file.list();
-            if (dirs != null) {
-                if (log.isDebugEnabled()) {
-                    log.debug(sm.getString(
-                            "contextConfig.processAnnotationsDir.debug", file));
-                }
-                for (String dir : dirs) {
-                    processAnnotationsFile(
-                            new File(file,dir), fragment, handlesTypesOnly, javaClassCache);
-                }
-            }
-        } else if (file.getName().endsWith(".class") && file.canRead()) {
-            try (FileInputStream fis = new FileInputStream(file)) {
-                processAnnotationsStream(fis, fragment, handlesTypesOnly, javaClassCache);
-            } catch (IOException e) {
-                log.error(sm.getString("contextConfig.inputStreamFile",
-                        file.getAbsolutePath()),e);
-            } catch (ClassFormatException e) {
-                log.error(sm.getString("contextConfig.inputStreamFile",
-                        file.getAbsolutePath()),e);
-            }
-        }
-    }
-
-
-    protected void processAnnotationsStream(InputStream is, WebXml fragment,
-            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache)
-            throws ClassFormatException, IOException {
-
-        ClassParser parser = new ClassParser(is);
-        JavaClass clazz = parser.parse();
-        checkHandlesTypes(clazz, javaClassCache);
-
-        if (handlesTypesOnly) {
-            return;
-        }
-
-        processClass(fragment, clazz);
-    }
-
-
-    protected void processClass(WebXml fragment, JavaClass clazz) {
-        AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
-        if (annotationsEntries != null) {
-            String className = clazz.getClassName();
-            for (AnnotationEntry ae : annotationsEntries) {
-                String type = ae.getAnnotationType();
-                if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
-                    processAnnotationWebServlet(className, ae, fragment);
-                }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
-                    processAnnotationWebFilter(className, ae, fragment);
-                }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
-                    fragment.addListener(className);
-                } else {
-                    // Unknown annotation - ignore
-                }
-            }
-        }
-    }
-
-
-    /**
-     * For classes packaged with the web application, the class and each
-     * super class needs to be checked for a match with {@link HandlesTypes} or
-     * for an annotation that matches {@link HandlesTypes}.
-     * @param javaClass the class to check
-     * @param javaClassCache a class cache
-     */
-    protected void checkHandlesTypes(JavaClass javaClass,
-            Map<String,JavaClassCacheEntry> javaClassCache) {
-
-        // Skip this if we can
-        if (typeInitializerMap.size() == 0) {
-            return;
-        }
-
-        if ((javaClass.getAccessFlags() &
-                org.apache.tomcat.util.bcel.Const.ACC_ANNOTATION) != 0) {
-            // Skip annotations.
-            return;
-        }
-
-        String className = javaClass.getClassName();
-
-        Class<?> clazz = null;
-        if (handlesTypesNonAnnotations) {
-            // This *might* be match for a HandlesType.
-            populateJavaClassCache(className, javaClass, javaClassCache);
-            JavaClassCacheEntry entry = javaClassCache.get(className);
-            if (entry.getSciSet() == null) {
-                try {
-                    populateSCIsForCacheEntry(entry, javaClassCache);
-                } catch (StackOverflowError soe) {
-                    throw new IllegalStateException(sm.getString(
-                            "contextConfig.annotationsStackOverflow",
-                            context.getName(),
-                            classHierarchyToString(className, entry, javaClassCache)));
-                }
-            }
-            if (!entry.getSciSet().isEmpty()) {
-                // Need to try and load the class
-                clazz = Introspection.loadClass(context, className);
-                if (clazz == null) {
-                    // Can't load the class so no point continuing
-                    return;
-                }
-
-                for (ServletContainerInitializer sci : entry.getSciSet()) {
-                    Set<Class<?>> classes = initializerClassMap.get(sci);
-                    if (classes == null) {
-                        classes = new HashSet<>();
-                        initializerClassMap.put(sci, classes);
-                    }
-                    classes.add(clazz);
-                }
-            }
-        }
-
-        if (handlesTypesAnnotations) {
-            AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries();
-            if (annotationEntries != null) {
-                for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
-                        typeInitializerMap.entrySet()) {
-                    if (entry.getKey().isAnnotation()) {
-                        String entryClassName = entry.getKey().getName();
-                        for (AnnotationEntry annotationEntry : annotationEntries) {
-                            if (entryClassName.equals(
-                                    getClassName(annotationEntry.getAnnotationType()))) {
-                                if (clazz == null) {
-                                    clazz = Introspection.loadClass(
-                                            context, className);
-                                    if (clazz == null) {
-                                        // Can't load the class so no point
-                                        // continuing
-                                        return;
-                                    }
-                                }
-                                for (ServletContainerInitializer sci : entry.getValue()) {
-                                    initializerClassMap.get(sci).add(clazz);
-                                }
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-
-    private String classHierarchyToString(String className,
-            JavaClassCacheEntry entry, Map<String,JavaClassCacheEntry> javaClassCache) {
-        JavaClassCacheEntry start = entry;
-        StringBuilder msg = new StringBuilder(className);
-        msg.append("->");
-
-        String parentName = entry.getSuperclassName();
-        JavaClassCacheEntry parent = javaClassCache.get(parentName);
-        int count = 0;
-
-        while (count < 100 && parent != null && parent != start) {
-            msg.append(parentName);
-            msg.append("->");
-
-            count ++;
-            parentName = parent.getSuperclassName();
-            parent = javaClassCache.get(parentName);
-        }
-
-        msg.append(parentName);
-
-        return msg.toString();
-    }
-
-    private void populateJavaClassCache(String className, JavaClass javaClass,
-            Map<String,JavaClassCacheEntry> javaClassCache) {
-        if (javaClassCache.containsKey(className)) {
-            return;
-        }
-
-        // Add this class to the cache
-        javaClassCache.put(className, new JavaClassCacheEntry(javaClass));
-
-        populateJavaClassCache(javaClass.getSuperclassName(), javaClassCache);
-
-        for (String interfaceName : javaClass.getInterfaceNames()) {
-            populateJavaClassCache(interfaceName, javaClassCache);
-        }
-    }
-
-    private void populateJavaClassCache(String className,
-            Map<String,JavaClassCacheEntry> javaClassCache) {
-        if (!javaClassCache.containsKey(className)) {
-            String name = className.replace('.', '/') + ".class";
-            try (InputStream is = context.getLoader().getClassLoader().getResourceAsStream(name)) {
-                if (is == null) {
-                    return;
-                }
-                ClassParser parser = new ClassParser(is);
-                JavaClass clazz = parser.parse();
-                populateJavaClassCache(clazz.getClassName(), clazz, javaClassCache);
-            } catch (ClassFormatException e) {
-                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
-                        className), e);
-            } catch (IOException e) {
-                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
-                        className), e);
-            }
-        }
-    }
-
-    private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry,
-            Map<String,JavaClassCacheEntry> javaClassCache) {
-        Set<ServletContainerInitializer> result = new HashSet<>();
-
-        // Super class
-        String superClassName = cacheEntry.getSuperclassName();
-        JavaClassCacheEntry superClassCacheEntry =
-                javaClassCache.get(superClassName);
-
-        // Avoid an infinite loop with java.lang.Object
-        if (cacheEntry.equals(superClassCacheEntry)) {
-            cacheEntry.setSciSet(EMPTY_SCI_SET);
-            return;
-        }
-
-        // May be null of the class is not present or could not be loaded.
-        if (superClassCacheEntry != null) {
-            if (superClassCacheEntry.getSciSet() == null) {
-                populateSCIsForCacheEntry(superClassCacheEntry, javaClassCache);
-            }
-            result.addAll(superClassCacheEntry.getSciSet());
-        }
-        result.addAll(getSCIsForClass(superClassName));
-
-        // Interfaces
-        for (String interfaceName : cacheEntry.getInterfaceNames()) {
-            JavaClassCacheEntry interfaceEntry =
-                    javaClassCache.get(interfaceName);
-            // A null could mean that the class not present in application or
-            // that there is nothing of interest. Either way, nothing to do here
-            // so move along
-            if (interfaceEntry != null) {
-                if (interfaceEntry.getSciSet() == null) {
-                    populateSCIsForCacheEntry(interfaceEntry, javaClassCache);
-                }
-                result.addAll(interfaceEntry.getSciSet());
-            }
-            result.addAll(getSCIsForClass(interfaceName));
-        }
-
-        cacheEntry.setSciSet(result.isEmpty() ? EMPTY_SCI_SET : result);
-    }
-
-    private Set<ServletContainerInitializer> getSCIsForClass(String className) {
-        for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
-                typeInitializerMap.entrySet()) {
-            Class<?> clazz = entry.getKey();
-            if (!clazz.isAnnotation()) {
-                if (clazz.getName().equals(className)) {
-                    return entry.getValue();
-                }
-            }
-        }
-        return EMPTY_SCI_SET;
-    }
-
-    private static final String getClassName(String internalForm) {
-        if (!internalForm.startsWith("L")) {
-            return internalForm;
-        }
-
-        // Assume starts with L, ends with ; and uses / rather than .
-        return internalForm.substring(1,
-                internalForm.length() - 1).replace('/', '.');
-    }
-
-    protected void processAnnotationWebServlet(String className,
-            AnnotationEntry ae, WebXml fragment) {
-        String servletName = null;
-        // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
-        List<ElementValuePair> evps = ae.getElementValuePairs();
-        for (ElementValuePair evp : evps) {
-            String name = evp.getNameString();
-            if ("name".equals(name)) {
-                servletName = evp.getValue().stringifyValue();
-                break;
-            }
-        }
-        if (servletName == null) {
-            // classname is default servletName as annotation has no name!
-            servletName = className;
-        }
-        ServletDef servletDef = fragment.getServlets().get(servletName);
-
-        boolean isWebXMLservletDef;
-        if (servletDef == null) {
-            servletDef = new ServletDef();
-            servletDef.setServletName(servletName);
-            servletDef.setServletClass(className);
-            isWebXMLservletDef = false;
-        } else {
-            isWebXMLservletDef = true;
-        }
-
-        boolean urlPatternsSet = false;
-        String[] urlPatterns = null;
-
-        // List<ElementValuePair> evps = ae.getElementValuePairs();
-        for (ElementValuePair evp : evps) {
-            String name = evp.getNameString();
-            if ("value".equals(name) || "urlPatterns".equals(name)) {
-                if (urlPatternsSet) {
-                    throw new IllegalArgumentException(sm.getString(
-                            "contextConfig.urlPatternValue", "WebServlet", className));
-                }
-                urlPatternsSet = true;
-                urlPatterns = processAnnotationsStringArray(evp.getValue());
-            } else if ("description".equals(name)) {
-                if (servletDef.getDescription() == null) {
-                    servletDef.setDescription(evp.getValue().stringifyValue());
-                }
-            } else if ("displayName".equals(name)) {
-                if (servletDef.getDisplayName() == null) {
-                    servletDef.setDisplayName(evp.getValue().stringifyValue());
-                }
-            } else if ("largeIcon".equals(name)) {
-                if (servletDef.getLargeIcon() == null) {
-                    servletDef.setLargeIcon(evp.getValue().stringifyValue());
-                }
-            } else if ("smallIcon".equals(name)) {
-                if (servletDef.getSmallIcon() == null) {
-                    servletDef.setSmallIcon(evp.getValue().stringifyValue());
-                }
-            } else if ("asyncSupported".equals(name)) {
-                if (servletDef.getAsyncSupported() == null) {
-                    servletDef.setAsyncSupported(evp.getValue()
-                            .stringifyValue());
-                }
-            } else if ("loadOnStartup".equals(name)) {
-                if (servletDef.getLoadOnStartup() == null) {
-                    servletDef
-                            .setLoadOnStartup(evp.getValue().stringifyValue());
-                }
-            } else if ("initParams".equals(name)) {
-                Map<String, String> initParams = processAnnotationWebInitParams(evp
-                        .getValue());
-                if (isWebXMLservletDef) {
-                    Map<String, String> webXMLInitParams = servletDef
-                            .getParameterMap();
-                    for (Map.Entry<String, String> entry : initParams
-                            .entrySet()) {
-                        if (webXMLInitParams.get(entry.getKey()) == null) {
-                            servletDef.addInitParameter(entry.getKey(), entry
-                                    .getValue());
-                        }
-                    }
-                } else {
-                    for (Map.Entry<String, String> entry : initParams
-                            .entrySet()) {
-                        servletDef.addInitParameter(entry.getKey(), entry
-                                .getValue());
-                    }
-                }
-            }
-        }
-        if (!isWebXMLservletDef && urlPatterns != null) {
-            fragment.addServlet(servletDef);
-        }
-        if (urlPatterns != null) {
-            if (!fragment.getServletMappings().containsValue(servletName)) {
-                for (String urlPattern : urlPatterns) {
-                    fragment.addServletMapping(urlPattern, servletName);
-                }
-            }
-        }
-
-    }
-
-    /**
-     * process filter annotation and merge with existing one!
-     * FIXME: refactoring method too long and has redundant subroutines with
-     *        processAnnotationWebServlet!
-     * @param className The filter class name
-     * @param ae The filter annotation
-     * @param fragment The corresponding fragment
-     */
-    protected void processAnnotationWebFilter(String className,
-            AnnotationEntry ae, WebXml fragment) {
-        String filterName = null;
-        // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
-        List<ElementValuePair> evps = ae.getElementValuePairs();
-        for (ElementValuePair evp : evps) {
-            String name = evp.getNameString();
-            if ("filterName".equals(name)) {
-                filterName = evp.getValue().stringifyValue();
-                break;
-            }
-        }
-        if (filterName == null) {
-            // classname is default filterName as annotation has no name!
-            filterName = className;
-        }
-        FilterDef filterDef = fragment.getFilters().get(filterName);
-        FilterMap filterMap = new FilterMap();
-
-        boolean isWebXMLfilterDef;
-        if (filterDef == null) {
-            filterDef = new FilterDef();
-            filterDef.setFilterName(filterName);
-            filterDef.setFilterClass(className);
-            isWebXMLfilterDef = false;
-        } else {
-            isWebXMLfilterDef = true;
-        }
-
-        boolean urlPatternsSet = false;
-        boolean servletNamesSet = false;
-        boolean dispatchTypesSet = false;
-        String[] urlPatterns = null;
-
-        for (ElementValuePair evp : evps) {
-            String name = evp.getNameString();
-            if ("value".equals(name) || "urlPatterns".equals(name)) {
-                if (urlPatternsSet) {
-                    throw new IllegalArgumentException(sm.getString(
-                            "contextConfig.urlPatternValue", "WebFilter", className));
-                }
-                urlPatterns = processAnnotationsStringArray(evp.getValue());
-                urlPatternsSet = urlPatterns.length > 0;
-                for (String urlPattern : urlPatterns) {
-                    // % decoded (if required) using UTF-8
-                    filterMap.addURLPattern(urlPattern);
-                }
-            } else if ("servletNames".equals(name)) {
-                String[] servletNames = processAnnotationsStringArray(evp
-                        .getValue());
-                servletNamesSet = servletNames.length > 0;
-                for (String servletName : servletNames) {
-                    filterMap.addServletName(servletName);
-                }
-            } else if ("dispatcherTypes".equals(name)) {
-                String[] dispatcherTypes = processAnnotationsStringArray(evp
-                        .getValue());
-                dispatchTypesSet = dispatcherTypes.length > 0;
-                for (String dispatcherType : dispatcherTypes) {
-                    filterMap.setDispatcher(dispatcherType);
-                }
-            } else if ("description".equals(name)) {
-                if (filterDef.getDescription() == null) {
-                    filterDef.setDescription(evp.getValue().stringifyValue());
-                }
-            } else if ("displayName".equals(name)) {
-                if (filterDef.getDisplayName() == null) {
-                    filterDef.setDisplayName(evp.getValue().stringifyValue());
-                }
-            } else if ("largeIcon".equals(name)) {
-                if (filterDef.getLargeIcon() == null) {
-                    filterDef.setLargeIcon(evp.getValue().stringifyValue());
-                }
-            } else if ("smallIcon".equals(name)) {
-                if (filterDef.getSmallIcon() == null) {
-                    filterDef.setSmallIcon(evp.getValue().stringifyValue());
-                }
-            } else if ("asyncSupported".equals(name)) {
-                if (filterDef.getAsyncSupported() == null) {
-                    filterDef
-                            .setAsyncSupported(evp.getValue().stringifyValue());
-                }
-            } else if ("initParams".equals(name)) {
-                Map<String, String> initParams = processAnnotationWebInitParams(evp
-                        .getValue());
-                if (isWebXMLfilterDef) {
-                    Map<String, String> webXMLInitParams = filterDef
-                            .getParameterMap();
-                    for (Map.Entry<String, String> entry : initParams
-                            .entrySet()) {
-                        if (webXMLInitParams.get(entry.getKey()) == null) {
-                            filterDef.addInitParameter(entry.getKey(), entry
-                                    .getValue());
-                        }
-                    }
-                } else {
-                    for (Map.Entry<String, String> entry : initParams
-                            .entrySet()) {
-                        filterDef.addInitParameter(entry.getKey(), entry
-                                .getValue());
-                    }
-                }
-
-            }
-        }
-        if (!isWebXMLfilterDef) {
-            fragment.addFilter(filterDef);
-            if (urlPatternsSet || servletNamesSet) {
-                filterMap.setFilterName(filterName);
-                fragment.addFilterMapping(filterMap);
-            }
-        }
-        if (urlPatternsSet || dispatchTypesSet) {
-            Set<FilterMap> fmap = fragment.getFilterMappings();
-            FilterMap descMap = null;
-            for (FilterMap map : fmap) {
-                if (filterName.equals(map.getFilterName())) {
-                    descMap = map;
-                    break;
-                }
-            }
-            if (descMap != null) {
-                String[] urlsPatterns = descMap.getURLPatterns();
-                if (urlPatternsSet
-                        && (urlsPatterns == null || urlsPatterns.length == 0)) {
-                    for (String urlPattern : filterMap.getURLPatterns()) {
-                        // % decoded (if required) using UTF-8
-                        descMap.addURLPattern(urlPattern);
-                    }
-                }
-                String[] dispatcherNames = descMap.getDispatcherNames();
-                if (dispatchTypesSet
-                        && (dispatcherNames == null || dispatcherNames.length == 0)) {
-                    for (String dis : filterMap.getDispatcherNames()) {
-                        descMap.setDispatcher(dis);
-                    }
-                }
-            }
-        }
-
-    }
-
-    protected String[] processAnnotationsStringArray(ElementValue ev) {
-        List<String> values = new ArrayList<>();
-        if (ev instanceof ArrayElementValue) {
-            ElementValue[] arrayValues =
-                ((ArrayElementValue) ev).getElementValuesArray();
-            for (ElementValue value : arrayValues) {
-                values.add(value.stringifyValue());
-            }
-        } else {
-            values.add(ev.stringifyValue());
-        }
-        String[] result = new String[values.size()];
-        return values.toArray(result);
-    }
-
-    protected Map<String,String> processAnnotationWebInitParams(
-            ElementValue ev) {
-        Map<String, String> result = new HashMap<>();
-        if (ev instanceof ArrayElementValue) {
-            ElementValue[] arrayValues =
-                ((ArrayElementValue) ev).getElementValuesArray();
-            for (ElementValue value : arrayValues) {
-                if (value instanceof AnnotationElementValue) {
-                    List<ElementValuePair> evps = ((AnnotationElementValue) value)
-                            .getAnnotationEntry().getElementValuePairs();
-                    String initParamName = null;
-                    String initParamValue = null;
-                    for (ElementValuePair evp : evps) {
-                        if ("name".equals(evp.getNameString())) {
-                            initParamName = evp.getValue().stringifyValue();
-                        } else if ("value".equals(evp.getNameString())) {
-                            initParamValue = evp.getValue().stringifyValue();
-                        } else {
-                            // Ignore
-                        }
-                    }
-                    result.put(initParamName, initParamValue);
-                }
-            }
-        }
-        return result;
-    }
-
-    private static class DefaultWebXmlCacheEntry {
-        private final WebXml webXml;
-        private final long globalTimeStamp;
-        private final long hostTimeStamp;
-
-        public DefaultWebXmlCacheEntry(WebXml webXml, long globalTimeStamp,
-                long hostTimeStamp) {
-            this.webXml = webXml;
-            this.globalTimeStamp = globalTimeStamp;
-            this.hostTimeStamp = hostTimeStamp;
-        }
-
-        public WebXml getWebXml() {
-            return webXml;
-        }
-
-        public long getGlobalTimeStamp() {
-            return globalTimeStamp;
-        }
-
-        public long getHostTimeStamp() {
-            return hostTimeStamp;
-        }
-    }
-
-    static class JavaClassCacheEntry {
-        public final String superclassName;
-
-        public final String[] interfaceNames;
-
-        private Set<ServletContainerInitializer> sciSet = null;
-
-        public JavaClassCacheEntry(JavaClass javaClass) {
-            superclassName = javaClass.getSuperclassName();
-            interfaceNames = javaClass.getInterfaceNames();
-        }
-
-        public String getSuperclassName() {
-            return superclassName;
-        }
-
-        public String[] getInterfaceNames() {
-            return interfaceNames;
-        }
-
-        public Set<ServletContainerInitializer> getSciSet() {
-            return sciSet;
-        }
-
-        public void setSciSet(Set<ServletContainerInitializer> sciSet) {
-            this.sciSet = sciSet;
-        }
-    }
-}
+/*
+ * 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.startup;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.ServletContainerInitializer;
+import javax.servlet.ServletContext;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.annotation.HandlesTypes;
+
+import org.apache.catalina.Authenticator;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.Valve;
+import org.apache.catalina.WebResource;
+import org.apache.catalina.WebResourceRoot;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.ContextName;
+import org.apache.catalina.util.Introspection;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.Jar;
+import org.apache.tomcat.JarScanType;
+import org.apache.tomcat.JarScanner;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.bcel.classfile.AnnotationElementValue;
+import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;
+import org.apache.tomcat.util.bcel.classfile.ArrayElementValue;
+import org.apache.tomcat.util.bcel.classfile.ClassFormatException;
+import org.apache.tomcat.util.bcel.classfile.ClassParser;
+import org.apache.tomcat.util.bcel.classfile.ElementValue;
+import org.apache.tomcat.util.bcel.classfile.ElementValuePair;
+import org.apache.tomcat.util.bcel.classfile.JavaClass;
+import org.apache.tomcat.util.buf.UriUtil;
+import org.apache.tomcat.util.descriptor.InputSourceUtil;
+import org.apache.tomcat.util.descriptor.XmlErrorHandler;
+import org.apache.tomcat.util.descriptor.web.ContextEjb;
+import org.apache.tomcat.util.descriptor.web.ContextEnvironment;
+import org.apache.tomcat.util.descriptor.web.ContextLocalEjb;
+import org.apache.tomcat.util.descriptor.web.ContextResource;
+import org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef;
+import org.apache.tomcat.util.descriptor.web.ContextService;
+import org.apache.tomcat.util.descriptor.web.ErrorPage;
+import org.apache.tomcat.util.descriptor.web.FilterDef;
+import org.apache.tomcat.util.descriptor.web.FilterMap;
+import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
+import org.apache.tomcat.util.descriptor.web.JspPropertyGroup;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.apache.tomcat.util.descriptor.web.MessageDestinationRef;
+import org.apache.tomcat.util.descriptor.web.MultipartDef;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
+import org.apache.tomcat.util.descriptor.web.SecurityRoleRef;
+import org.apache.tomcat.util.descriptor.web.ServletDef;
+import org.apache.tomcat.util.descriptor.web.SessionConfig;
+import org.apache.tomcat.util.descriptor.web.WebXml;
+import org.apache.tomcat.util.descriptor.web.WebXmlParser;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSet;
+import org.apache.tomcat.util.file.ConfigFileLoader;
+import org.apache.tomcat.util.file.ConfigurationSource;
+import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.scan.JarFactory;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Startup event listener for a <b>Context</b> that configures the properties
+ * of that Context, and the associated defined servlets.
+ *
+ * @author Craig R. McClanahan
+ */
+public class ContextConfig implements LifecycleListener {
+
+    private static final Log log = LogFactory.getLog(ContextConfig.class);
+
+
+    /**
+     * The string resources for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    protected static final LoginConfig DUMMY_LOGIN_CONFIG =
+        new LoginConfig("NONE", null, null, null);
+
+
+    /**
+     * The set of Authenticators that we know how to configure.  The key is
+     * the name of the implemented authentication method, and the value is
+     * the fully qualified Java class name of the corresponding Valve.
+     */
+    protected static final Properties authenticators;
+
+    static {
+        // Load our mapping properties for the standard authenticators
+        Properties props = new Properties();
+        try (InputStream is = ContextConfig.class.getClassLoader().getResourceAsStream(
+                "org/apache/catalina/startup/Authenticators.properties")) {
+            if (is != null) {
+                props.load(is);
+            }
+        } catch (IOException ioe) {
+            props = null;
+        }
+        authenticators = props;
+    }
+
+    /**
+     * Deployment count.
+     */
+    protected static long deploymentCount = 0L;
+
+
+    /**
+     * Cache of default web.xml fragments per Host
+     */
+    protected static final Map<Host,DefaultWebXmlCacheEntry> hostWebXmlCache =
+            new ConcurrentHashMap<>();
+
+
+    /**
+     * Set used as the value for {@code JavaClassCacheEntry.sciSet} when there
+     * are no SCIs associated with a class.
+     */
+    private static final Set<ServletContainerInitializer> EMPTY_SCI_SET = Collections.emptySet();
+
+
+    // ----------------------------------------------------- Instance Variables
+    /**
+     * Custom mappings of login methods to authenticators
+     */
+    protected Map<String,Authenticator> customAuthenticators;
+
+
+    /**
+     * The Context we are associated with.
+     */
+    protected volatile Context context = null;
+
+
+    /**
+     * The default web application's deployment descriptor location.
+     */
+    protected String defaultWebXml = null;
+
+
+    /**
+     * Track any fatal errors during startup configuration processing.
+     */
+    protected boolean ok = false;
+
+
+    /**
+     * Original docBase.
+     */
+    protected String originalDocBase = null;
+
+
+    /**
+     * Anti-locking docBase. It is a path to a copy of the web application
+     * in the java.io.tmpdir directory. This path is always an absolute one.
+     */
+    private File antiLockingDocBase = null;
+
+
+    /**
+     * Map of ServletContainerInitializer to classes they expressed interest in.
+     */
+    protected final Map<ServletContainerInitializer, Set<Class<?>>> initializerClassMap =
+            new LinkedHashMap<>();
+
+    /**
+     * Map of Types to ServletContainerInitializer that are interested in those
+     * types.
+     */
+    protected final Map<Class<?>, Set<ServletContainerInitializer>> typeInitializerMap =
+            new HashMap<>();
+
+    /**
+     * Flag that indicates if at least one {@link HandlesTypes} entry is present
+     * that represents an annotation.
+     */
+    protected boolean handlesTypesAnnotations = false;
+
+    /**
+     * Flag that indicates if at least one {@link HandlesTypes} entry is present
+     * that represents a non-annotation.
+     */
+    protected boolean handlesTypesNonAnnotations = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Obtain the location of the default deployment descriptor.
+     *
+     * @return The path to the default web.xml. If not absolute, it is relative
+     *         to CATALINA_BASE.
+     */
+    public String getDefaultWebXml() {
+        if (defaultWebXml == null) {
+            defaultWebXml = Constants.DefaultWebXml;
+        }
+        return defaultWebXml;
+    }
+
+
+    /**
+     * Set the location of the default deployment descriptor.
+     *
+     * @param path The path to the default web.xml. If not absolute, it is
+     *             relative to CATALINA_BASE.
+     */
+    public void setDefaultWebXml(String path) {
+        this.defaultWebXml = path;
+    }
+
+
+    /**
+     * Sets custom mappings of login methods to authenticators.
+     *
+     * @param customAuthenticators Custom mappings of login methods to
+     * authenticators
+     */
+    public void setCustomAuthenticators(
+            Map<String,Authenticator> customAuthenticators) {
+        this.customAuthenticators = customAuthenticators;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process events for an associated Context.
+     *
+     * @param event The lifecycle event that has occurred
+     */
+    @Override
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        // Identify the context we are associated with
+        try {
+            context = (Context) event.getLifecycle();
+        } catch (ClassCastException e) {
+            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
+            return;
+        }
+
+        // Process the event that has occurred
+        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
+            configureStart();
+        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
+            beforeStart();
+        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
+            // Restore docBase for management tools
+            if (originalDocBase != null) {
+                context.setDocBase(originalDocBase);
+            }
+        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
+            configureStop();
+        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
+            init();
+        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
+            destroy();
+        }
+
+    }
+
+
+    // -------------------------------------------------------- protected Methods
+
+
+    /**
+     * Process the application classes annotations, if it exists.
+     */
+    protected void applicationAnnotationsConfig() {
+
+        long t1=System.currentTimeMillis();
+
+        WebAnnotationSet.loadApplicationAnnotations(context);
+
+        long t2=System.currentTimeMillis();
+        if (context instanceof StandardContext) {
+            ((StandardContext) context).setStartupTime(t2-t1+
+                    ((StandardContext) context).getStartupTime());
+        }
+    }
+
+
+    /**
+     * Set up an Authenticator automatically if required, and one has not
+     * already been configured.
+     */
+    protected void authenticatorConfig() {
+
+        LoginConfig loginConfig = context.getLoginConfig();
+        if (loginConfig == null) {
+            // Need an authenticator to support HttpServletRequest.login()
+            loginConfig = DUMMY_LOGIN_CONFIG;
+            context.setLoginConfig(loginConfig);
+        }
+
+        // Has an authenticator been configured already?
+        if (context.getAuthenticator() != null) {
+            return;
+        }
+
+        // Has a Realm been configured for us to authenticate against?
+        if (context.getRealm() == null) {
+            log.error(sm.getString("contextConfig.missingRealm"));
+            ok = false;
+            return;
+        }
+
+        /*
+         * First check to see if there is a custom mapping for the login
+         * method. If so, use it. Otherwise, check if there is a mapping in
+         * org/apache/catalina/startup/Authenticators.properties.
+         */
+        Valve authenticator = null;
+        if (customAuthenticators != null) {
+            authenticator = (Valve) customAuthenticators.get(loginConfig.getAuthMethod());
+        }
+
+        if (authenticator == null) {
+            if (authenticators == null) {
+                log.error(sm.getString("contextConfig.authenticatorResources"));
+                ok = false;
+                return;
+            }
+
+            // Identify the class name of the Valve we should configure
+            String authenticatorName = authenticators.getProperty(loginConfig.getAuthMethod());
+            if (authenticatorName == null) {
+                log.error(sm.getString("contextConfig.authenticatorMissing",
+                                 loginConfig.getAuthMethod()));
+                ok = false;
+                return;
+            }
+
+            // Instantiate and install an Authenticator of the requested class
+            try {
+                Class<?> authenticatorClass = Class.forName(authenticatorName);
+                authenticator = (Valve) authenticatorClass.getConstructor().newInstance();
+            } catch (Throwable t) {
+                ExceptionUtils.handleThrowable(t);
+                log.error(sm.getString(
+                                    "contextConfig.authenticatorInstantiate",
+                                    authenticatorName),
+                          t);
+                ok = false;
+            }
+        }
+
+        if (authenticator != null) {
+            Pipeline pipeline = context.getPipeline();
+            if (pipeline != null) {
+                pipeline.addValve(authenticator);
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString(
+                                    "contextConfig.authenticatorConfigured",
+                                    loginConfig.getAuthMethod()));
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Create (if necessary) and return a Digester configured to process the
+     * context configuration descriptor for an application.
+     * @return the digester for context.xml files
+     */
+    protected Digester createContextDigester() {
+        Digester digester = new Digester();
+        digester.setValidating(false);
+        digester.setRulesValidation(true);
+        Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
+        List<String> objectAttrs = new ArrayList<>();
+        objectAttrs.add("className");
+        fakeAttributes.put(Object.class, objectAttrs);
+        // Ignore attribute added by Eclipse for its internal tracking
+        List<String> contextAttrs = new ArrayList<>();
+        contextAttrs.add("source");
+        fakeAttributes.put(StandardContext.class, contextAttrs);
+        digester.setFakeAttributes(fakeAttributes);
+        RuleSet contextRuleSet = new ContextRuleSet("", false);
+        digester.addRuleSet(contextRuleSet);
+        RuleSet namingRuleSet = new NamingRuleSet("Context/");
+        digester.addRuleSet(namingRuleSet);
+        return digester;
+    }
+
+
+    /**
+     * Process the default configuration file, if it exists.
+     * @param digester The digester that will be used for XML parsing
+     */
+    protected void contextConfig(Digester digester) {
+
+        String defaultContextXml = null;
+
+        // Open the default context.xml file, if it exists
+        if (context instanceof StandardContext) {
+            defaultContextXml = ((StandardContext)context).getDefaultContextXml();
+        }
+        // set the default if we don't have any overrides
+        if (defaultContextXml == null) {
+            defaultContextXml = Constants.DefaultContextXml;
+        }
+
+        if (!context.getOverride()) {
+            try (ConfigurationSource.Resource contextXmlResource =
+                    ConfigFileLoader.getSource().getResource(defaultContextXml)) {
+                URL defaultContextUrl = contextXmlResource.getURI().toURL();
+                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
+            } catch (MalformedURLException e) {
+                log.error(sm.getString("contextConfig.badUrl", defaultContextXml), e);
+            } catch (IOException e) {
+                // Not found
+            }
+
+            String hostContextFile = Container.getConfigPath(context, Constants.HostContextXml);
+            try (ConfigurationSource.Resource contextXmlResource =
+                    ConfigFileLoader.getSource().getConfResource(hostContextFile)) {
+                URL defaultContextUrl = contextXmlResource.getURI().toURL();
+                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
+            } catch (MalformedURLException e) {
+                log.error(sm.getString("contextConfig.badUrl", hostContextFile), e);
+            } catch (IOException e) {
+                // Not found
+            }
+        }
+        if (context.getConfigFile() != null) {
+            processContextConfig(digester, context.getConfigFile(), null);
+        }
+
+    }
+
+
+    /**
+     * Process a context.xml.
+     * @param digester The digester that will be used for XML parsing
+     * @param contextXml The URL to the context.xml configuration
+     * @param stream The XML resource stream
+     */
+    protected void processContextConfig(Digester digester, URL contextXml, InputStream stream) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Processing context [" + context.getName()
+                    + "] configuration file [" + contextXml + "]");
+        }
+
+        InputSource source = null;
+
+        try {
+            source = new InputSource(contextXml.toString());
+            if (stream == null) {
+                URLConnection xmlConn = contextXml.openConnection();
+                xmlConn.setUseCaches(false);
+                stream = xmlConn.getInputStream();
+            }
+        } catch (Exception e) {
+            log.error(sm.getString("contextConfig.contextMissing",
+                      contextXml) , e);
+        }
+
+        if (source == null) {
+            return;
+        }
+
+        try {
+            source.setByteStream(stream);
+            digester.setClassLoader(this.getClass().getClassLoader());
+            digester.setUseContextClassLoader(false);
+            digester.push(context.getParent());
+            digester.push(context);
+            XmlErrorHandler errorHandler = new XmlErrorHandler();
+            digester.setErrorHandler(errorHandler);
+            digester.parse(source);
+            if (errorHandler.getWarnings().size() > 0 ||
+                    errorHandler.getErrors().size() > 0) {
+                errorHandler.logFindings(log, contextXml.toString());
+                ok = false;
+            }
+            if (log.isDebugEnabled()) {
+                log.debug("Successfully processed context [" + context.getName()
+                        + "] configuration file [" + contextXml + "]");
+            }
+        } catch (SAXParseException e) {
+            log.error(sm.getString("contextConfig.contextParse",
+                    context.getName()), e);
+            log.error(sm.getString("contextConfig.defaultPosition",
+                             "" + e.getLineNumber(),
+                             "" + e.getColumnNumber()));
+            ok = false;
+        } catch (Exception e) {
+            log.error(sm.getString("contextConfig.contextParse",
+                    context.getName()), e);
+            ok = false;
+        } finally {
+            try {
+                if (stream != null) {
+                    stream.close();
+                }
+            } catch (IOException e) {
+                log.error(sm.getString("contextConfig.contextClose"), e);
+            }
+        }
+    }
+
+
+    /**
+     * Adjust docBase.
+     * @throws IOException cannot access the context base path
+     */
+    protected void fixDocBase() throws IOException {
+
+        Host host = (Host) context.getParent();
+        File appBase = host.getAppBaseFile();
+
+        // This could be blank, relative, absolute or canonical
+        String docBaseConfigured = context.getDocBase();
+        // If there is no explicit docBase, derive it from the path and version
+        if (docBaseConfigured == null) {
+            // Trying to guess the docBase according to the path
+            String path = context.getPath();
+            if (path == null) {
+                return;
+            }
+            ContextName cn = new ContextName(path, context.getWebappVersion());
+            docBaseConfigured = cn.getBaseName();
+        }
+
+        // Obtain the absolute docBase in String and File form
+        String docBaseAbsolute;
+        File docBaseConfiguredFile = new File(docBaseConfigured);
+        if (!docBaseConfiguredFile.isAbsolute()) {
+            docBaseAbsolute = (new File(appBase, docBaseConfigured)).getAbsolutePath();
+        } else {
+            docBaseAbsolute = docBaseConfiguredFile.getAbsolutePath();
+        }
+        File docBaseAbsoluteFile = new File(docBaseAbsolute);
+        String originalDocBase = docBaseAbsolute;
+
+        ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
+        String pathName = cn.getBaseName();
+
+        boolean unpackWARs = true;
+        if (host instanceof StandardHost) {
+            unpackWARs = ((StandardHost) host).isUnpackWARs();
+            if (unpackWARs && context instanceof StandardContext) {
+                unpackWARs =  ((StandardContext) context).getUnpackWAR();
+            }
+        }
+
+        // At this point we need to determine if we have a WAR file in the
+        // appBase that needs to be expanded. Therefore we consider the absolute
+        // docBase NOT the canonical docBase. This is because some users symlink
+        // WAR files into the appBase and we want this to work correctly.
+        boolean docBaseAbsoluteInAppBase = docBaseAbsolute.startsWith(appBase.getPath() + File.separatorChar);
+        if (docBaseAbsolute.toLowerCase(Locale.ENGLISH).endsWith(".war") && !docBaseAbsoluteFile.isDirectory()) {
+            URL war = UriUtil.buildJarUrl(docBaseAbsoluteFile);
+            if (unpackWARs) {
+                docBaseAbsolute = ExpandWar.expand(host, war, pathName);
+                docBaseAbsoluteFile = new File(docBaseAbsolute);
+                if (context instanceof StandardContext) {
+                    ((StandardContext) context).setOriginalDocBase(originalDocBase);
+                }
+            } else {
+                ExpandWar.validate(host, war, pathName);
+            }
+        } else {
+            File docBaseAbsoluteFileWar = new File(docBaseAbsolute + ".war");
+            URL war = null;
+            if (docBaseAbsoluteFileWar.exists() && docBaseAbsoluteInAppBase) {
+                war = UriUtil.buildJarUrl(docBaseAbsoluteFileWar);
+            }
+            if (docBaseAbsoluteFile.exists()) {
+                if (war != null && unpackWARs) {
+                    // Check if WAR needs to be re-expanded (e.g. if it has
+                    // changed). Note: HostConfig.deployWar() takes care of
+                    // ensuring that the correct XML file is used.
+                    // This will be a NO-OP if the WAR is unchanged.
+                    ExpandWar.expand(host, war, pathName);
+                }
+            } else {
+                if (war != null) {
+                    if (unpackWARs) {
+                        docBaseAbsolute = ExpandWar.expand(host, war, pathName);
+                        docBaseAbsoluteFile = new File(docBaseAbsolute);
+                    } else {
+                        docBaseAbsolute = docBaseAbsoluteFileWar.getAbsolutePath();
+                        docBaseAbsoluteFile = docBaseAbsoluteFileWar;
+                        ExpandWar.validate(host, war, pathName);
+                    }
+                }
+                if (context instanceof StandardContext) {
+                    ((StandardContext) context).setOriginalDocBase(originalDocBase);
+                }
+            }
+        }
+
+        String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath();
+
+        // Re-calculate now docBase is a canonical path
+        boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar);
+        String docBase;
+        if (docBaseCanonicalInAppBase) {
+            docBase = docBaseCanonical.substring(appBase.getPath().length());
+            docBase = docBase.replace(File.separatorChar, '/');
+            if (docBase.startsWith("/")) {
+                docBase = docBase.substring(1);
+            }
+        } else {
+            docBase = docBaseCanonical.replace(File.separatorChar, '/');
+        }
+
+        context.setDocBase(docBase);
+    }
+
+
+    protected void antiLocking() {
+
+        if ((context instanceof StandardContext)
+            && ((StandardContext) context).getAntiResourceLocking()) {
+
+            Host host = (Host) context.getParent();
+            String docBase = context.getDocBase();
+            if (docBase == null) {
+                return;
+            }
+            originalDocBase = docBase;
+
+            File docBaseFile = new File(docBase);
+            if (!docBaseFile.isAbsolute()) {
+                docBaseFile = new File(host.getAppBaseFile(), docBase);
+            }
+
+            String path = context.getPath();
+            if (path == null) {
+                return;
+            }
+            ContextName cn = new ContextName(path, context.getWebappVersion());
+            docBase = cn.getBaseName();
+
+            if (originalDocBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
+                antiLockingDocBase = new File(
+                        System.getProperty("java.io.tmpdir"),
+                        deploymentCount++ + "-" + docBase + ".war");
+            } else {
+                antiLockingDocBase = new File(
+                        System.getProperty("java.io.tmpdir"),
+                        deploymentCount++ + "-" + docBase);
+            }
+            antiLockingDocBase = antiLockingDocBase.getAbsoluteFile();
+
+            if (log.isDebugEnabled()) {
+                log.debug("Anti locking context[" + context.getName()
+                        + "] setting docBase to " +
+                        antiLockingDocBase.getPath());
+            }
+
+            // Cleanup just in case an old deployment is lying around
+            ExpandWar.delete(antiLockingDocBase);
+            if (ExpandWar.copy(docBaseFile, antiLockingDocBase)) {
+                context.setDocBase(antiLockingDocBase.getPath());
+            }
+        }
+    }
+
+
+    /**
+     * Process a "init" event for this Context.
+     */
+    protected synchronized void init() {
+        // Called from StandardContext.init()
+
+        Digester contextDigester = createContextDigester();
+        contextDigester.getParser();
+
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("contextConfig.init"));
+        }
+        context.setConfigured(false);
+        ok = true;
+
+        contextConfig(contextDigester);
+    }
+
+
+    /**
+     * Process a "before start" event for this Context.
+     */
+    protected synchronized void beforeStart() {
+
+        try {
+            fixDocBase();
+        } catch (IOException e) {
+            log.error(sm.getString(
+                    "contextConfig.fixDocBase", context.getName()), e);
+        }
+
+        antiLocking();
+    }
+
+
+    /**
+     * Process a "contextConfig" event for this Context.
+     */
+    protected synchronized void configureStart() {
+        // Called from StandardContext.start()
+
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("contextConfig.start"));
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("contextConfig.xmlSettings",
+                    context.getName(),
+                    Boolean.valueOf(context.getXmlValidation()),
+                    Boolean.valueOf(context.getXmlNamespaceAware())));
+        }
+
+        webConfig();
+
+        if (!context.getIgnoreAnnotations()) {
+            applicationAnnotationsConfig();
+        }
+        if (ok) {
+            validateSecurityRoles();
+        }
+
+        // Configure an authenticator if we need one
+        if (ok) {
+            authenticatorConfig();
+        }
+
+        // Dump the contents of this pipeline if requested
+        if (log.isDebugEnabled()) {
+            log.debug("Pipeline Configuration:");
+            Pipeline pipeline = context.getPipeline();
+            Valve valves[] = null;
+            if (pipeline != null) {
+                valves = pipeline.getValves();
+            }
+            if (valves != null) {
+                for (int i = 0; i < valves.length; i++) {
+                    log.debug("  " + valves[i].getClass().getName());
+                }
+            }
+            log.debug("======================");
+        }
+
+        // Make our application available if no problems were encountered
+        if (ok) {
+            context.setConfigured(true);
+        } else {
+            log.error(sm.getString("contextConfig.unavailable"));
+            context.setConfigured(false);
+        }
+
+    }
+
+
+    /**
+     * Process a "stop" event for this Context.
+     */
+    protected synchronized void configureStop() {
+
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("contextConfig.stop"));
+        }
+
+        int i;
+
+        // Removing children
+        Container[] children = context.findChildren();
+        for (i = 0; i < children.length; i++) {
+            context.removeChild(children[i]);
+        }
+
+        // Removing application parameters
+        /*
+        ApplicationParameter[] applicationParameters =
+            context.findApplicationParameters();
+        for (i = 0; i < applicationParameters.length; i++) {
+            context.removeApplicationParameter
+                (applicationParameters[i].getName());
+        }
+        */
+
+        // Removing security constraints
+        SecurityConstraint[] securityConstraints = context.findConstraints();
+        for (i = 0; i < securityConstraints.length; i++) {
+            context.removeConstraint(securityConstraints[i]);
+        }
+
+        // Removing Ejbs
+        /*
+        ContextEjb[] contextEjbs = context.findEjbs();
+        for (i = 0; i < contextEjbs.length; i++) {
+            context.removeEjb(contextEjbs[i].getName());
+        }
+        */
+
+        // Removing environments
+        /*
+        ContextEnvironment[] contextEnvironments = context.findEnvironments();
+        for (i = 0; i < contextEnvironments.length; i++) {
+            context.removeEnvironment(contextEnvironments[i].getName());
+        }
+        */
+
+        // Removing errors pages
+        ErrorPage[] errorPages = context.findErrorPages();
+        for (i = 0; i < errorPages.length; i++) {
+            context.removeErrorPage(errorPages[i]);
+        }
+
+        // Removing filter defs
+        FilterDef[] filterDefs = context.findFilterDefs();
+        for (i = 0; i < filterDefs.length; i++) {
+            context.removeFilterDef(filterDefs[i]);
+        }
+
+        // Removing filter maps
+        FilterMap[] filterMaps = context.findFilterMaps();
+        for (i = 0; i < filterMaps.length; i++) {
+            context.removeFilterMap(filterMaps[i]);
+        }
+
+        // Removing local ejbs
+        /*
+        ContextLocalEjb[] contextLocalEjbs = context.findLocalEjbs();
+        for (i = 0; i < contextLocalEjbs.length; i++) {
+            context.removeLocalEjb(contextLocalEjbs[i].getName());
+        }
+        */
+
+        // Removing Mime mappings
+        String[] mimeMappings = context.findMimeMappings();
+        for (i = 0; i < mimeMappings.length; i++) {
+            context.removeMimeMapping(mimeMappings[i]);
+        }
+
+        // Removing parameters
+        String[] parameters = context.findParameters();
+        for (i = 0; i < parameters.length; i++) {
+            context.removeParameter(parameters[i]);
+        }
+
+        // Removing resource env refs
+        /*
+        String[] resourceEnvRefs = context.findResourceEnvRefs();
+        for (i = 0; i < resourceEnvRefs.length; i++) {
+            context.removeResourceEnvRef(resourceEnvRefs[i]);
+        }
+        */
+
+        // Removing resource links
+        /*
+        ContextResourceLink[] contextResourceLinks =
+            context.findResourceLinks();
+        for (i = 0; i < contextResourceLinks.length; i++) {
+            context.removeResourceLink(contextResourceLinks[i].getName());
+        }
+        */
+
+        // Removing resources
+        /*
+        ContextResource[] contextResources = context.findResources();
+        for (i = 0; i < contextResources.length; i++) {
+            context.removeResource(contextResources[i].getName());
+        }
+        */
+
+        // Removing security role
+        String[] securityRoles = context.findSecurityRoles();
+        for (i = 0; i < securityRoles.length; i++) {
+            context.removeSecurityRole(securityRoles[i]);
+        }
+
+        // Removing servlet mappings
+        String[] servletMappings = context.findServletMappings();
+        for (i = 0; i < servletMappings.length; i++) {
+            context.removeServletMapping(servletMappings[i]);
+        }
+
+        // FIXME : Removing status pages
+
+        // Removing welcome files
+        String[] welcomeFiles = context.findWelcomeFiles();
+        for (i = 0; i < welcomeFiles.length; i++) {
+            context.removeWelcomeFile(welcomeFiles[i]);
+        }
+
+        // Removing wrapper lifecycles
+        String[] wrapperLifecycles = context.findWrapperLifecycles();
+        for (i = 0; i < wrapperLifecycles.length; i++) {
+            context.removeWrapperLifecycle(wrapperLifecycles[i]);
+        }
+
+        // Removing wrapper listeners
+        String[] wrapperListeners = context.findWrapperListeners();
+        for (i = 0; i < wrapperListeners.length; i++) {
+            context.removeWrapperListener(wrapperListeners[i]);
+        }
+
+        // Remove (partially) folders and files created by antiLocking
+        if (antiLockingDocBase != null) {
+            // No need to log failure - it is expected in this case
+            ExpandWar.delete(antiLockingDocBase, false);
+        }
+
+        // Reset ServletContextInitializer scanning
+        initializerClassMap.clear();
+        typeInitializerMap.clear();
+
+        ok = true;
+
+    }
+
+
+    /**
+     * Process a "destroy" event for this Context.
+     */
+    protected synchronized void destroy() {
+        // Called from StandardContext.destroy()
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("contextConfig.destroy"));
+        }
+
+        // Skip clearing the work directory if Tomcat is being shutdown
+        Server s = getServer();
+        if (s != null && !s.getState().isAvailable()) {
+            return;
+        }
+
+        // Changed to getWorkPath per Bugzilla 35819.
+        if (context instanceof StandardContext) {
+            String workDir = ((StandardContext) context).getWorkPath();
+            if (workDir != null) {
+                ExpandWar.delete(new File(workDir));
+            }
+        }
+    }
+
+
+    private Server getServer() {
+        Container c = context;
+        while (c != null && !(c instanceof Engine)) {
+            c = c.getParent();
+        }
+
+        if (c == null) {
+            return null;
+        }
+
+        Service s = ((Engine)c).getService();
+
+        if (s == null) {
+            return null;
+        }
+
+        return s.getServer();
+    }
+
+    /**
+     * Validate the usage of security role names in the web application
+     * deployment descriptor.  If any problems are found, issue warning
+     * messages (for backwards compatibility) and add the missing roles.
+     * (To make these problems fatal instead, simply set the <code>ok</code>
+     * instance variable to <code>false</code> as well).
+     */
+    protected void validateSecurityRoles() {
+
+        // Check role names used in <security-constraint> elements
+        SecurityConstraint constraints[] = context.findConstraints();
+        for (int i = 0; i < constraints.length; i++) {
+            String roles[] = constraints[i].findAuthRoles();
+            for (int j = 0; j < roles.length; j++) {
+                if (!"*".equals(roles[j]) &&
+                    !context.findSecurityRole(roles[j])) {
+                    log.warn(sm.getString("contextConfig.role.auth", roles[j]));
+                    context.addSecurityRole(roles[j]);
+                }
+            }
+        }
+
+        // Check role names used in <servlet> elements
+        Container wrappers[] = context.findChildren();
+        for (int i = 0; i < wrappers.length; i++) {
+            Wrapper wrapper = (Wrapper) wrappers[i];
+            String runAs = wrapper.getRunAs();
+            if ((runAs != null) && !context.findSecurityRole(runAs)) {
+                log.warn(sm.getString("contextConfig.role.runas", runAs));
+                context.addSecurityRole(runAs);
+            }
+            String names[] = wrapper.findSecurityReferences();
+            for (int j = 0; j < names.length; j++) {
+                String link = wrapper.findSecurityReference(names[j]);
+                if ((link != null) && !context.findSecurityRole(link)) {
+                    log.warn(sm.getString("contextConfig.role.link", link));
+                    context.addSecurityRole(link);
+                }
+            }
+        }
+
+    }
+
+
+    protected File getHostConfigBase() {
+        File file = null;
+        if (context.getParent() instanceof Host) {
+            file = ((Host)context.getParent()).getConfigBaseFile();
+        }
+        return file;
+    }
+
+    /**
+     * Scan the web.xml files that apply to the web application and merge them
+     * using the rules defined in the spec. For the global web.xml files,
+     * where there is duplicate configuration, the most specific level wins. ie
+     * an application's web.xml takes precedence over the host level or global
+     * web.xml file.
+     */
+    protected void webConfig() {
+        /*
+         * Anything and everything can override the global and host defaults.
+         * This is implemented in two parts
+         * - Handle as a web fragment that gets added after everything else so
+         *   everything else takes priority
+         * - Mark Servlets as overridable so SCI configuration can replace
+         *   configuration from the defaults
+         */
+
+        /*
+         * The rules for annotation scanning are not as clear-cut as one might
+         * think. Tomcat implements the following process:
+         * - As per SRV.1.6.2, Tomcat will scan for annotations regardless of
+         *   which Servlet spec version is declared in web.xml. The EG has
+         *   confirmed this is the expected behaviour.
+         * - As per http://java.net/jira/browse/SERVLET_SPEC-36, if the main
+         *   web.xml is marked as metadata-complete, JARs are still processed
+         *   for SCIs.
+         * - If metadata-complete=true and an absolute ordering is specified,
+         *   JARs excluded from the ordering are also excluded from the SCI
+         *   processing.
+         * - If an SCI has a @HandlesType annotation then all classes (except
+         *   those in JARs excluded from an absolute ordering) need to be
+         *   scanned to check if they match.
+         */
+        WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
+                context.getXmlValidation(), context.getXmlBlockExternal());
+
+        Set<WebXml> defaults = new HashSet<>();
+        defaults.add(getDefaultWebXmlFragment(webXmlParser));
+
+        Set<WebXml> tomcatWebXml = new HashSet<>();
+        tomcatWebXml.add(getTomcatWebXmlFragment(webXmlParser));
+
+        WebXml webXml = createWebXml();
+
+        // Parse context level web.xml
+        InputSource contextWebXml = getContextWebXmlSource();
+        if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
+            ok = false;
+        }
+
+        ServletContext sContext = context.getServletContext();
+
+        // Ordering is important here
+
+        // Step 1. Identify all the JARs packaged with the application and those
+        // provided by the container. If any of the application JARs have a
+        // web-fragment.xml it will be parsed at this point. web-fragment.xml
+        // files are ignored for container provided JARs.
+        Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
+
+        // Step 2. Order the fragments.
+        Set<WebXml> orderedFragments = null;
+        orderedFragments =
+                WebXml.orderWebFragments(webXml, fragments, sContext);
+
+        // Step 3. Look for ServletContainerInitializer implementations
+        if (ok) {
+            processServletContainerInitializers();
+        }
+
+        if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
+            // Steps 4 & 5.
+            processClasses(webXml, orderedFragments);
+        }
+
+        if (!webXml.isMetadataComplete()) {
+            // Step 6. Merge web-fragment.xml files into the main web.xml
+            // file.
+            if (ok) {
+                ok = webXml.merge(orderedFragments);
+            }
+
+            // Step 7a
+            // merge tomcat-web.xml
+            webXml.merge(tomcatWebXml);
+
+            // Step 7b. Apply global defaults
+            // Have to merge defaults before JSP conversion since defaults
+            // provide JSP servlet definition.
+            webXml.merge(defaults);
+
+            // Step 8. Convert explicitly mentioned jsps to servlets
+            if (ok) {
+                convertJsps(webXml);
+            }
+
+            // Step 9. Apply merged web.xml to Context
+            if (ok) {
+                configureContext(webXml);
+            }
+        } else {
+            webXml.merge(tomcatWebXml);
+            webXml.merge(defaults);
+            convertJsps(webXml);
+            configureContext(webXml);
+        }
+
+        if (context.getLogEffectiveWebXml()) {
+            log.info(sm.getString("contextConfig.effectiveWebXml", webXml.toXml()));
+        }
+
+        // Always need to look for static resources
+        // Step 10. Look for static resources packaged in JARs
+        if (ok) {
+            // Spec does not define an order.
+            // Use ordered JARs followed by remaining JARs
+            Set<WebXml> resourceJars = new LinkedHashSet<>();
+            for (WebXml fragment : orderedFragments) {
+                resourceJars.add(fragment);
+            }
+            for (WebXml fragment : fragments.values()) {
+                if (!resourceJars.contains(fragment)) {
+                    resourceJars.add(fragment);
+                }
+            }
+            processResourceJARs(resourceJars);
+            // See also StandardContext.resourcesStart() for
+            // WEB-INF/classes/META-INF/resources configuration
+        }
+
+        // Step 11. Apply the ServletContainerInitializer config to the
+        // context
+        if (ok) {
+            for (Map.Entry<ServletContainerInitializer,
+                    Set<Class<?>>> entry :
+                        initializerClassMap.entrySet()) {
+                if (entry.getValue().isEmpty()) {
+                    context.addServletContainerInitializer(
+                            entry.getKey(), null);
+                } else {
+                    context.addServletContainerInitializer(
+                            entry.getKey(), entry.getValue());
+                }
+            }
+        }
+    }
+
+
+    protected void processClasses(WebXml webXml, Set<WebXml> orderedFragments) {
+        // Step 4. Process /WEB-INF/classes for annotations and
+        // @HandlesTypes matches
+        Map<String, JavaClassCacheEntry> javaClassCache = new HashMap<>();
+
+        if (ok) {
+            WebResource[] webResources =
+                    context.getResources().listResources("/WEB-INF/classes");
+
+            for (WebResource webResource : webResources) {
+                // Skip the META-INF directory from any JARs that have been
+                // expanded in to WEB-INF/classes (sometimes IDEs do this).
+                if ("META-INF".equals(webResource.getName())) {
+                    continue;
+                }
+                processAnnotationsWebResource(webResource, webXml,
+                        webXml.isMetadataComplete(), javaClassCache);
+            }
+        }
+
+        // Step 5. Process JARs for annotations and
+        // @HandlesTypes matches - only need to process those fragments we
+        // are going to use (remember orderedFragments includes any
+        // container fragments)
+        if (ok) {
+            processAnnotations(
+                    orderedFragments, webXml.isMetadataComplete(), javaClassCache);
+        }
+
+        // Cache, if used, is no longer required so clear it
+        javaClassCache.clear();
+    }
+
+
+    private void configureContext(WebXml webxml) {
+        // As far as possible, process in alphabetical order so it is easy to
+        // check everything is present
+        // Some validation depends on correct public ID
+        context.setPublicId(webxml.getPublicId());
+
+        // Everything else in order
+        context.setEffectiveMajorVersion(webxml.getMajorVersion());
+        context.setEffectiveMinorVersion(webxml.getMinorVersion());
+
+        for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
+            context.addParameter(entry.getKey(), entry.getValue());
+        }
+        context.setDenyUncoveredHttpMethods(
+                webxml.getDenyUncoveredHttpMethods());
+        context.setDisplayName(webxml.getDisplayName());
+        context.setDistributable(webxml.isDistributable());
+        for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
+            context.getNamingResources().addLocalEjb(ejbLocalRef);
+        }
+        for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
+            context.getNamingResources().addEjb(ejbRef);
+        }
+        for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
+            context.getNamingResources().addEnvironment(environment);
+        }
+        for (ErrorPage errorPage : webxml.getErrorPages().values()) {
+            context.addErrorPage(errorPage);
+        }
+        for (FilterDef filter : webxml.getFilters().values()) {
+            if (filter.getAsyncSupported() == null) {
+                filter.setAsyncSupported("false");
+            }
+            context.addFilterDef(filter);
+        }
+        for (FilterMap filterMap : webxml.getFilterMappings()) {
+            context.addFilterMap(filterMap);
+        }
+        context.setJspConfigDescriptor(webxml.getJspConfigDescriptor());
+        for (String listener : webxml.getListeners()) {
+            context.addApplicationListener(listener);
+        }
+        for (Entry<String, String> entry :
+                webxml.getLocaleEncodingMappings().entrySet()) {
+            context.addLocaleEncodingMappingParameter(entry.getKey(),
+                    entry.getValue());
+        }
+        // Prevents IAE
+        if (webxml.getLoginConfig() != null) {
+            context.setLoginConfig(webxml.getLoginConfig());
+        }
+        for (MessageDestinationRef mdr :
+                webxml.getMessageDestinationRefs().values()) {
+            context.getNamingResources().addMessageDestinationRef(mdr);
+        }
+
+        // messageDestinations were ignored in Tomcat 6, so ignore here
+
+        context.setIgnoreAnnotations(webxml.isMetadataComplete());
+        for (Entry<String, String> entry :
+                webxml.getMimeMappings().entrySet()) {
+            context.addMimeMapping(entry.getKey(), entry.getValue());
+        }
+        context.setRequestCharacterEncoding(webxml.getRequestCharacterEncoding());
+        // Name is just used for ordering
+        for (ContextResourceEnvRef resource :
+                webxml.getResourceEnvRefs().values()) {
+            context.getNamingResources().addResourceEnvRef(resource);
+        }
+        for (ContextResource resource : webxml.getResourceRefs().values()) {
+            context.getNamingResources().addResource(resource);
+        }
+        context.setResponseCharacterEncoding(webxml.getResponseCharacterEncoding());
+        boolean allAuthenticatedUsersIsAppRole =
+                webxml.getSecurityRoles().contains(
+                        SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
+        for (SecurityConstraint constraint : webxml.getSecurityConstraints()) {
+            if (allAuthenticatedUsersIsAppRole) {
+                constraint.treatAllAuthenticatedUsersAsApplicationRole();
+            }
+            context.addConstraint(constraint);
+        }
+        for (String role : webxml.getSecurityRoles()) {
+            context.addSecurityRole(role);
+        }
+        for (ContextService service : webxml.getServiceRefs().values()) {
+            context.getNamingResources().addService(service);
+        }
+        for (ServletDef servlet : webxml.getServlets().values()) {
+            Wrapper wrapper = context.createWrapper();
+            // Description is ignored
+            // Display name is ignored
+            // Icons are ignored
+
+            // jsp-file gets passed to the JSP Servlet as an init-param
+
+            if (servlet.getLoadOnStartup() != null) {
+                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
+            }
+            if (servlet.getEnabled() != null) {
+                wrapper.setEnabled(servlet.getEnabled().booleanValue());
+            }
+            wrapper.setName(servlet.getServletName());
+            Map<String,String> params = servlet.getParameterMap();
+            for (Entry<String, String> entry : params.entrySet()) {
+                wrapper.addInitParameter(entry.getKey(), entry.getValue());
+            }
+            wrapper.setRunAs(servlet.getRunAs());
+            Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
+            for (SecurityRoleRef roleRef : roleRefs) {
+                wrapper.addSecurityReference(
+                        roleRef.getName(), roleRef.getLink());
+            }
+            wrapper.setServletClass(servlet.getServletClass());
+            MultipartDef multipartdef = servlet.getMultipartDef();
+            if (multipartdef != null) {
+                if (multipartdef.getMaxFileSize() != null &&
+                        multipartdef.getMaxRequestSize()!= null &&
+                        multipartdef.getFileSizeThreshold() != null) {
+                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
+                            multipartdef.getLocation(),
+                            Long.parseLong(multipartdef.getMaxFileSize()),
+                            Long.parseLong(multipartdef.getMaxRequestSize()),
+                            Integer.parseInt(
+                                    multipartdef.getFileSizeThreshold())));
+                } else {
+                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
+                            multipartdef.getLocation()));
+                }
+            }
+            if (servlet.getAsyncSupported() != null) {
+                wrapper.setAsyncSupported(
+                        servlet.getAsyncSupported().booleanValue());
+            }
+            wrapper.setOverridable(servlet.isOverridable());
+            context.addChild(wrapper);
+        }
+        for (Entry<String, String> entry :
+                webxml.getServletMappings().entrySet()) {
+            context.addServletMappingDecoded(entry.getKey(), entry.getValue());
+        }
+        SessionConfig sessionConfig = webxml.getSessionConfig();
+        if (sessionConfig != null) {
+            if (sessionConfig.getSessionTimeout() != null) {
+                context.setSessionTimeout(
+                        sessionConfig.getSessionTimeout().intValue());
+            }
+            SessionCookieConfig scc =
+                context.getServletContext().getSessionCookieConfig();
+            scc.setName(sessionConfig.getCookieName());
+            scc.setDomain(sessionConfig.getCookieDomain());
+            scc.setPath(sessionConfig.getCookiePath());
+            scc.setComment(sessionConfig.getCookieComment());
+            if (sessionConfig.getCookieHttpOnly() != null) {
+                scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());
+            }
+            if (sessionConfig.getCookieSecure() != null) {
+                scc.setSecure(sessionConfig.getCookieSecure().booleanValue());
+            }
+            if (sessionConfig.getCookieMaxAge() != null) {
+                scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());
+            }
+            if (sessionConfig.getSessionTrackingModes().size() > 0) {
+                context.getServletContext().setSessionTrackingModes(
+                        sessionConfig.getSessionTrackingModes());
+            }
+        }
+
+        // Context doesn't use version directly
+
+        for (String welcomeFile : webxml.getWelcomeFiles()) {
+            /*
+             * The following will result in a welcome file of "" so don't add
+             * that to the context
+             * <welcome-file-list>
+             *   <welcome-file/>
+             * </welcome-file-list>
+             */
+            if (welcomeFile != null && welcomeFile.length() > 0) {
+                context.addWelcomeFile(welcomeFile);
+            }
+        }
+
+        // Do this last as it depends on servlets
+        for (JspPropertyGroup jspPropertyGroup :
+                webxml.getJspPropertyGroups()) {
+            String jspServletName = context.findServletMapping("*.jsp");
+            if (jspServletName == null) {
+                jspServletName = "jsp";
+            }
+            if (context.findChild(jspServletName) != null) {
+                for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
+                    context.addServletMappingDecoded(urlPattern, jspServletName, true);
+                }
+            } else {
+                if(log.isDebugEnabled()) {
+                    for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
+                        log.debug("Skipping " + urlPattern + " , no servlet " +
+                                jspServletName);
+                    }
+                }
+            }
+        }
+
+        for (Entry<String, String> entry :
+                webxml.getPostConstructMethods().entrySet()) {
+            context.addPostConstructMethod(entry.getKey(), entry.getValue());
+        }
+
+        for (Entry<String, String> entry :
+            webxml.getPreDestroyMethods().entrySet()) {
+            context.addPreDestroyMethod(entry.getKey(), entry.getValue());
+        }
+    }
+
+
+    private WebXml getTomcatWebXmlFragment(WebXmlParser webXmlParser) {
+
+        WebXml webXmlTomcatFragment = createWebXml();
+        webXmlTomcatFragment.setOverridable(true);
+
+        // Set to distributable else every app will be prevented from being
+        // distributable when the Tomcat fragment is merged with the main
+        // web.xml
+        webXmlTomcatFragment.setDistributable(true);
+        // When merging, the default welcome files are only used if the app has
+        // not defined any welcomes files.
+        webXmlTomcatFragment.setAlwaysAddWelcomeFiles(false);
+
+        WebResource resource = context.getResources().getResource(Constants.TomcatWebXml);
+        if (resource.isFile()) {
+            try {
+                InputSource source = new InputSource(resource.getURL().toURI().toString());
+                source.setByteStream(resource.getInputStream());
+                if (!webXmlParser.parseWebXml(source, webXmlTomcatFragment, false)) {
+                    ok = false;
+                }
+            } catch (URISyntaxException e) {
+                log.error(sm.getString("contextConfig.tomcatWebXmlError"), e);
+            }
+        }
+        return webXmlTomcatFragment;
+    }
+
+
+    private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {
+
+        // Host should never be null
+        Host host = (Host) context.getParent();
+
+        DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
+
+        InputSource globalWebXml = getGlobalWebXmlSource();
+        InputSource hostWebXml = getHostWebXmlSource();
+
+        long globalTimeStamp = 0;
+        long hostTimeStamp = 0;
+
+        if (globalWebXml != null) {
+            URLConnection uc = null;
+            try {
+                URL url = new URL(globalWebXml.getSystemId());
+                uc = url.openConnection();
+                globalTimeStamp = uc.getLastModified();
+            } catch (IOException e) {
+                globalTimeStamp = -1;
+            } finally {
+                if (uc != null) {
+                    try {
+                        uc.getInputStream().close();
+                    } catch (IOException e) {
+                        ExceptionUtils.handleThrowable(e);
+                        globalTimeStamp = -1;
+                    }
+                }
+            }
+        }
+
+        if (hostWebXml != null) {
+            URLConnection uc = null;
+            try {
+                URL url = new URL(hostWebXml.getSystemId());
+                uc = url.openConnection();
+                hostTimeStamp = uc.getLastModified();
+            } catch (IOException e) {
+                hostTimeStamp = -1;
+            } finally {
+                if (uc != null) {
+                    try {
+                        uc.getInputStream().close();
+                    } catch (IOException e) {
+                        ExceptionUtils.handleThrowable(e);
+                        hostTimeStamp = -1;
+                    }
+                }
+            }
+        }
+
+        if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
+                entry.getHostTimeStamp() == hostTimeStamp) {
+            InputSourceUtil.close(globalWebXml);
+            InputSourceUtil.close(hostWebXml);
+            return entry.getWebXml();
+        }
+
+        // Parsing global web.xml is relatively expensive. Use a sync block to
+        // make sure it only happens once. Use the pipeline since a lock will
+        // already be held on the host by another thread
+        synchronized (host.getPipeline()) {
+            entry = hostWebXmlCache.get(host);
+            if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
+                    entry.getHostTimeStamp() == hostTimeStamp) {
+                return entry.getWebXml();
+            }
+
+            WebXml webXmlDefaultFragment = createWebXml();
+            webXmlDefaultFragment.setOverridable(true);
+            // Set to distributable else every app will be prevented from being
+            // distributable when the default fragment is merged with the main
+            // web.xml
+            webXmlDefaultFragment.setDistributable(true);
+            // When merging, the default welcome files are only used if the app has
+            // not defined any welcomes files.
+            webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
+
+            // Parse global web.xml if present
+            if (globalWebXml == null) {
+                // This is unusual enough to log
+                log.info(sm.getString("contextConfig.defaultMissing"));
+            } else {
+                if (!webXmlParser.parseWebXml(
+                        globalWebXml, webXmlDefaultFragment, false)) {
+                    ok = false;
+                }
+            }
+
+            // Parse host level web.xml if present
+            // Additive apart from welcome pages
+            webXmlDefaultFragment.setReplaceWelcomeFiles(true);
+
+            if (!webXmlParser.parseWebXml(
+                    hostWebXml, webXmlDefaultFragment, false)) {
+                ok = false;
+            }
+
+            // Don't update the cache if an error occurs
+            if (globalTimeStamp != -1 && hostTimeStamp != -1) {
+                entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
+                        globalTimeStamp, hostTimeStamp);
+                hostWebXmlCache.put(host, entry);
+            }
+
+            return webXmlDefaultFragment;
+        }
+    }
+
+
+    private void convertJsps(WebXml webXml) {
+        Map<String,String> jspInitParams;
+        ServletDef jspServlet = webXml.getServlets().get("jsp");
+        if (jspServlet == null) {
+            jspInitParams = new HashMap<>();
+            Wrapper w = (Wrapper) context.findChild("jsp");
+            if (w != null) {
+                String[] params = w.findInitParameters();
+                for (String param : params) {
+                    jspInitParams.put(param, w.findInitParameter(param));
+                }
+            }
+        } else {
+            jspInitParams = jspServlet.getParameterMap();
+        }
+        for (ServletDef servletDef: webXml.getServlets().values()) {
+            if (servletDef.getJspFile() != null) {
+                convertJsp(servletDef, jspInitParams);
+            }
+        }
+    }
+
+    private void convertJsp(ServletDef servletDef,
+            Map<String,String> jspInitParams) {
+        servletDef.setServletClass(org.apache.catalina.core.Constants.JSP_SERVLET_CLASS);
+        String jspFile = servletDef.getJspFile();
+        if ((jspFile != null) && !jspFile.startsWith("/")) {
+            if (context.isServlet22()) {
+                if(log.isDebugEnabled()) {
+                    log.debug(sm.getString("contextConfig.jspFile.warning",
+                                       jspFile));
+                }
+                jspFile = "/" + jspFile;
+            } else {
+                throw new IllegalArgumentException
+                    (sm.getString("contextConfig.jspFile.error", jspFile));
+            }
+        }
+        servletDef.getParameterMap().put("jspFile", jspFile);
+        servletDef.setJspFile(null);
+        for (Map.Entry<String, String> initParam: jspInitParams.entrySet()) {
+            servletDef.addInitParameter(initParam.getKey(), initParam.getValue());
+        }
+    }
+
+    protected WebXml createWebXml() {
+        return new WebXml();
+    }
+
+    /**
+     * Scan JARs for ServletContainerInitializer implementations.
+     */
+    protected void processServletContainerInitializers() {
+
+        List<ServletContainerInitializer> detectedScis;
+        try {
+            WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
+            detectedScis = loader.load(ServletContainerInitializer.class);
+        } catch (IOException e) {
+            log.error(sm.getString(
+                    "contextConfig.servletContainerInitializerFail",
+                    context.getName()),
+                e);
+            ok = false;
+            return;
+        }
+
+        for (ServletContainerInitializer sci : detectedScis) {
+            initializerClassMap.put(sci, new HashSet<Class<?>>());
+
+            HandlesTypes ht;
+            try {
+                ht = sci.getClass().getAnnotation(HandlesTypes.class);
+            } catch (Exception e) {
+                if (log.isDebugEnabled()) {
+                    log.info(sm.getString("contextConfig.sci.debug",
+                            sci.getClass().getName()),
+                            e);
+                } else {
+                    log.info(sm.getString("contextConfig.sci.info",
+                            sci.getClass().getName()));
+                }
+                continue;
+            }
+            if (ht == null) {
+                continue;
+            }
+            Class<?>[] types = ht.value();
+            if (types == null) {
+                continue;
+            }
+
+            for (Class<?> type : types) {
+                if (type.isAnnotation()) {
+                    handlesTypesAnnotations = true;
+                } else {
+                    handlesTypesNonAnnotations = true;
+                }
+                Set<ServletContainerInitializer> scis =
+                        typeInitializerMap.get(type);
+                if (scis == null) {
+                    scis = new HashSet<>();
+                    typeInitializerMap.put(type, scis);
+                }
+                scis.add(sci);
+            }
+        }
+    }
+
+    /**
+     * Scan JARs that contain web-fragment.xml files that will be used to
+     * configure this application to see if they also contain static resources.
+     * If static resources are found, add them to the context. Resources are
+     * added in web-fragment.xml priority order.
+     * @param fragments The set of fragments that will be scanned for
+     *  static resources
+     */
+    protected void processResourceJARs(Set<WebXml> fragments) {
+        for (WebXml fragment : fragments) {
+            URL url = fragment.getURL();
+            try {
+                if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
+                    try (Jar jar = JarFactory.newInstance(url)) {
+                        jar.nextEntry();
+                        String entryName = jar.getEntryName();
+                        while (entryName != null) {
+                            if (entryName.startsWith("META-INF/resources/")) {
+                                context.getResources().createWebResourceSet(
+                                        WebResourceRoot.ResourceSetType.RESOURCE_JAR,
+                                        "/", url, "/META-INF/resources");
+                                break;
+                            }
+                            jar.nextEntry();
+                            entryName = jar.getEntryName();
+                        }
+                    }
+                } else if ("file".equals(url.getProtocol())) {
+                    File file = new File(url.toURI());
+                    File resources = new File(file, "META-INF/resources/");
+                    if (resources.isDirectory()) {
+                        context.getResources().createWebResourceSet(
+                                WebResourceRoot.ResourceSetType.RESOURCE_JAR,
+                                "/", resources.getAbsolutePath(), null, "/");
+                    }
+                }
+            } catch (IOException ioe) {
+                log.error(sm.getString("contextConfig.resourceJarFail", url,
+                        context.getName()));
+            } catch (URISyntaxException e) {
+                log.error(sm.getString("contextConfig.resourceJarFail", url,
+                    context.getName()));
+            }
+        }
+    }
+
+
+    /**
+     * Identify the default web.xml to be used and obtain an input source for
+     * it.
+     * @return an input source to the default web.xml
+     */
+    protected InputSource getGlobalWebXmlSource() {
+        // Is a default web.xml specified for the Context?
+        if (defaultWebXml == null && context instanceof StandardContext) {
+            defaultWebXml = ((StandardContext) context).getDefaultWebXml();
+        }
+        // Set the default if we don't have any overrides
+        if (defaultWebXml == null) {
+            getDefaultWebXml();
+        }
+
+        // Is it explicitly suppressed, e.g. in embedded environment?
+        if (Constants.NoDefaultWebXml.equals(defaultWebXml)) {
+            return null;
+        }
+        return getWebXmlSource(defaultWebXml, true);
+    }
+
+    /**
+     * Identify the host web.xml to be used and obtain an input source for
+     * it.
+     * @return an input source to the default per host web.xml
+     */
+    protected InputSource getHostWebXmlSource() {
+        File hostConfigBase = getHostConfigBase();
+        if (hostConfigBase == null)
+            return null;
+
+        return getWebXmlSource(hostConfigBase.getPath(), false);
+    }
+
+    /**
+     * Identify the application web.xml to be used and obtain an input source
+     * for it.
+     * @return an input source to the context web.xml
+     */
+    protected InputSource getContextWebXmlSource() {
+        InputStream stream = null;
+        InputSource source = null;
+        URL url = null;
+
+        String altDDName = null;
+
+        // Open the application web.xml file, if it exists
+        ServletContext servletContext = context.getServletContext();
+        try {
+            if (servletContext != null) {
+                altDDName = (String)servletContext.getAttribute(Globals.ALT_DD_ATTR);
+                if (altDDName != null) {
+                    try {
+                        stream = new FileInputStream(altDDName);
+                        url = new File(altDDName).toURI().toURL();
+                    } catch (FileNotFoundException e) {
+                        log.error(sm.getString("contextConfig.altDDNotFound",
+                                               altDDName));
+                    } catch (MalformedURLException e) {
+                        log.error(sm.getString("contextConfig.applicationUrl"));
+                    }
+                }
+                else {
+                    stream = servletContext.getResourceAsStream
+                        (Constants.ApplicationWebXml);
+                    try {
+                        url = servletContext.getResource(
+                                Constants.ApplicationWebXml);
+                    } catch (MalformedURLException e) {
+                        log.error(sm.getString("contextConfig.applicationUrl"));
+                    }
+                }
+            }
+            if (stream == null || url == null) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("contextConfig.applicationMissing") + " " + context);
+                }
+            } else {
+                source = new InputSource(url.toExternalForm());
+                source.setByteStream(stream);
+            }
+        } finally {
+            if (source == null && stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+
+        return source;
+    }
+
+    public String getConfigBasePath() {
+        String path = null;
+        if (context.getParent() instanceof Host) {
+            Host host = (Host) context.getParent();
+            if (host.getXmlBase() != null) {
+                path = host.getXmlBase();
+            } else {
+                StringBuilder xmlDir = new StringBuilder("conf");
+                Container parent = host.getParent();
+                if (parent instanceof Engine) {
+                    xmlDir.append('/');
+                    xmlDir.append(parent.getName());
+                }
+                xmlDir.append('/');
+                xmlDir.append(host.getName());
+                path = xmlDir.toString();
+            }
+        }
+        return path;
+    }
+
+    /**
+     * Utility method to create an input source from the specified XML file.
+     * @param filename  Name of the file (possibly with one or more leading path
+     *                  segments) to read
+     * @param global true if processing a shared resource, false if processing
+     *        a host based resource
+     * @return the input source
+     */
+    protected InputSource getWebXmlSource(String filename, boolean global) {
+        ConfigurationSource.Resource webXmlResource = null;
+        try {
+            if (global) {
+                if (Constants.DefaultWebXml.equals(filename)) {
+                    webXmlResource = ConfigFileLoader.getSource().getSharedWebXml();
+                } else {
+                    webXmlResource = ConfigFileLoader.getSource().getResource(filename);
+                }
+            } else {
+                String hostWebXml = Container.getConfigPath(context, Constants.HostWebXml);
+                webXmlResource = ConfigFileLoader.getSource().getConfResource(hostWebXml);
+            }
+        } catch (IOException e) {
+            // Ignore if not found
+            return null;
+        }
+
+        InputStream stream = null;
+        InputSource source = null;
+
+        try {
+            stream = webXmlResource.getInputStream();
+            source = new InputSource(webXmlResource.getURI().toString());
+            if (stream != null) {
+                source.setByteStream(stream);
+            }
+        } catch (Exception e) {
+            log.error(sm.getString("contextConfig.defaultError", filename, webXmlResource.getURI()), e);
+        } finally {
+            if (source == null && stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+
+        return source;
+    }
+
+
+    /**
+     * Scan /WEB-INF/lib for JARs and for each one found add it and any
+     * /META-INF/web-fragment.xml to the resulting Map. web-fragment.xml files
+     * will be parsed before being added to the map. Every JAR will be added and
+     * <code>null</code> will be used if no web-fragment.xml was found. Any JARs
+     * known not contain fragments will be skipped.
+     *
+     * @param application The main web.xml metadata
+     * @param webXmlParser The parser to use to process the web.xml file
+     * @return A map of JAR name to processed web fragment (if any)
+     */
+    protected Map<String,WebXml> processJarsForWebFragments(WebXml application,
+            WebXmlParser webXmlParser) {
+
+        JarScanner jarScanner = context.getJarScanner();
+        boolean delegate = false;
+        if (context instanceof StandardContext) {
+            delegate = ((StandardContext) context).getDelegate();
+        }
+        boolean parseRequired = true;
+        Set<String> absoluteOrder = application.getAbsoluteOrdering();
+        if (absoluteOrder != null && absoluteOrder.isEmpty() &&
+                !context.getXmlValidation()) {
+            // Skip parsing when there is an empty absolute ordering and
+            // validation is not enabled
+            parseRequired = false;
+        }
+        FragmentJarScannerCallback callback =
+                new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);
+
+        jarScanner.scan(JarScanType.PLUGGABILITY,
+                context.getServletContext(), callback);
+
+        if (!callback.isOk()) {
+            ok = false;
+        }
+        return callback.getFragments();
+    }
+
+    protected void processAnnotations(Set<WebXml> fragments,
+            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
+        for(WebXml fragment : fragments) {
+            // Only need to scan for @HandlesTypes matches if any of the
+            // following are true:
+            // - it has already been determined only @HandlesTypes is required
+            //   (e.g. main web.xml has metadata-complete="true"
+            // - this fragment is for a container JAR (Servlet 3.1 section 8.1)
+            // - this fragment has metadata-complete="true"
+            boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() ||
+                    fragment.isMetadataComplete();
+
+            WebXml annotations = new WebXml();
+            // no impact on distributable
+            annotations.setDistributable(true);
+            URL url = fragment.getURL();
+            processAnnotationsUrl(url, annotations, htOnly, javaClassCache);
+            Set<WebXml> set = new HashSet<>();
+            set.add(annotations);
+            // Merge annotations into fragment - fragment takes priority
+            fragment.merge(set);
+        }
+    }
+
+    protected void processAnnotationsWebResource(WebResource webResource,
+            WebXml fragment, boolean handlesTypesOnly,
+            Map<String,JavaClassCacheEntry> javaClassCache) {
+
+        if (webResource.isDirectory()) {
+            WebResource[] webResources =
+                    webResource.getWebResourceRoot().listResources(
+                            webResource.getWebappPath());
+            if (webResources.length > 0) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString(
+                            "contextConfig.processAnnotationsWebDir.debug",
+                            webResource.getURL()));
+                }
+                for (WebResource r : webResources) {
+                    processAnnotationsWebResource(r, fragment, handlesTypesOnly, javaClassCache);
+                }
+            }
+        } else if (webResource.isFile() &&
+                webResource.getName().endsWith(".class")) {
+            try (InputStream is = webResource.getInputStream()) {
+                processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
+            } catch (IOException e) {
+                log.error(sm.getString("contextConfig.inputStreamWebResource",
+                        webResource.getWebappPath()),e);
+            } catch (ClassFormatException e) {
+                log.error(sm.getString("contextConfig.inputStreamWebResource",
+                        webResource.getWebappPath()),e);
+            }
+        }
+    }
+
+
+    protected void processAnnotationsUrl(URL url, WebXml fragment,
+            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
+        if (url == null) {
+            // Nothing to do.
+            return;
+        } else if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
+            processAnnotationsJar(url, fragment, handlesTypesOnly, javaClassCache);
+        } else if ("file".equals(url.getProtocol())) {
+            try {
+                processAnnotationsFile(
+                        new File(url.toURI()), fragment, handlesTypesOnly, javaClassCache);
+            } catch (URISyntaxException e) {
+                log.error(sm.getString("contextConfig.fileUrl", url), e);
+            }
+        } else {
+            log.error(sm.getString("contextConfig.unknownUrlProtocol",
+                    url.getProtocol(), url));
+        }
+
+    }
+
+
+    protected void processAnnotationsJar(URL url, WebXml fragment,
+            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
+
+        try (Jar jar = JarFactory.newInstance(url)) {
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString(
+                        "contextConfig.processAnnotationsJar.debug", url));
+            }
+
+            jar.nextEntry();
+            String entryName = jar.getEntryName();
+            while (entryName != null) {
+                if (entryName.endsWith(".class")) {
+                    try (InputStream is = jar.getEntryInputStream()) {
+                        processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
+                    } catch (IOException e) {
+                        log.error(sm.getString("contextConfig.inputStreamJar",
+                                entryName, url),e);
+                    } catch (ClassFormatException e) {
+                        log.error(sm.getString("contextConfig.inputStreamJar",
+                                entryName, url),e);
+                    }
+                }
+                jar.nextEntry();
+                entryName = jar.getEntryName();
+            }
+        } catch (IOException e) {
+            log.error(sm.getString("contextConfig.jarFile", url), e);
+        }
+    }
+
+
+    protected void processAnnotationsFile(File file, WebXml fragment,
+            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
+
+        if (file.isDirectory()) {
+            // Returns null if directory is not readable
+            String[] dirs = file.list();
+            if (dirs != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString(
+                            "contextConfig.processAnnotationsDir.debug", file));
+                }
+                for (String dir : dirs) {
+                    processAnnotationsFile(
+                            new File(file,dir), fragment, handlesTypesOnly, javaClassCache);
+                }
+            }
+        } else if (file.getName().endsWith(".class") && file.canRead()) {
+            try (FileInputStream fis = new FileInputStream(file)) {
+                processAnnotationsStream(fis, fragment, handlesTypesOnly, javaClassCache);
+            } catch (IOException e) {
+                log.error(sm.getString("contextConfig.inputStreamFile",
+                        file.getAbsolutePath()),e);
+            } catch (ClassFormatException e) {
+                log.error(sm.getString("contextConfig.inputStreamFile",
+                        file.getAbsolutePath()),e);
+            }
+        }
+    }
+
+
+    protected void processAnnotationsStream(InputStream is, WebXml fragment,
+            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache)
+            throws ClassFormatException, IOException {
+
+        ClassParser parser = new ClassParser(is);
+        JavaClass clazz = parser.parse();
+        checkHandlesTypes(clazz, javaClassCache);
+
+        if (handlesTypesOnly) {
+            return;
+        }
+
+        processClass(fragment, clazz);
+    }
+
+
+    protected void processClass(WebXml fragment, JavaClass clazz) {
+        AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
+        if (annotationsEntries != null) {
+            String className = clazz.getClassName();
+            for (AnnotationEntry ae : annotationsEntries) {
+                String type = ae.getAnnotationType();
+                if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
+                    processAnnotationWebServlet(className, ae, fragment);
+                }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
+                    processAnnotationWebFilter(className, ae, fragment);
+                }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
+                    fragment.addListener(className);
+                } else {
+                    // Unknown annotation - ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * For classes packaged with the web application, the class and each
+     * super class needs to be checked for a match with {@link HandlesTypes} or
+     * for an annotation that matches {@link HandlesTypes}.
+     * @param javaClass the class to check
+     * @param javaClassCache a class cache
+     */
+    protected void checkHandlesTypes(JavaClass javaClass,
+            Map<String,JavaClassCacheEntry> javaClassCache) {
+
+        // Skip this if we can
+        if (typeInitializerMap.size() == 0) {
+            return;
+        }
+
+        if ((javaClass.getAccessFlags() &
+                org.apache.tomcat.util.bcel.Const.ACC_ANNOTATION) != 0) {
+            // Skip annotations.
+            return;
+        }
+
+        String className = javaClass.getClassName();
+
+        Class<?> clazz = null;
+        if (handlesTypesNonAnnotations) {
+            // This *might* be match for a HandlesType.
+            populateJavaClassCache(className, javaClass, javaClassCache);
+            JavaClassCacheEntry entry = javaClassCache.get(className);
+            if (entry.getSciSet() == null) {
+                try {
+                    populateSCIsForCacheEntry(entry, javaClassCache);
+                } catch (StackOverflowError soe) {
+                    throw new IllegalStateException(sm.getString(
+                            "contextConfig.annotationsStackOverflow",
+                            context.getName(),
+                            classHierarchyToString(className, entry, javaClassCache)));
+                }
+            }
+            if (!entry.getSciSet().isEmpty()) {
+                // Need to try and load the class
+                clazz = Introspection.loadClass(context, className);
+                if (clazz == null) {
+                    // Can't load the class so no point continuing
+                    return;
+                }
+
+                for (ServletContainerInitializer sci : entry.getSciSet()) {
+                    Set<Class<?>> classes = initializerClassMap.get(sci);
+                    if (classes == null) {
+                        classes = new HashSet<>();
+                        initializerClassMap.put(sci, classes);
+                    }
+                    classes.add(clazz);
+                }
+            }
+        }
+
+        if (handlesTypesAnnotations) {
+            AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries();
+            if (annotationEntries != null) {
+                for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
+                        typeInitializerMap.entrySet()) {
+                    if (entry.getKey().isAnnotation()) {
+                        String entryClassName = entry.getKey().getName();
+                        for (AnnotationEntry annotationEntry : annotationEntries) {
+                            if (entryClassName.equals(
+                                    getClassName(annotationEntry.getAnnotationType()))) {
+                                if (clazz == null) {
+                                    clazz = Introspection.loadClass(
+                                            context, className);
+                                    if (clazz == null) {
+                                        // Can't load the class so no point
+                                        // continuing
+                                        return;
+                                    }
+                                }
+                                for (ServletContainerInitializer sci : entry.getValue()) {
+                                    initializerClassMap.get(sci).add(clazz);
+                                }
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    private String classHierarchyToString(String className,
+            JavaClassCacheEntry entry, Map<String,JavaClassCacheEntry> javaClassCache) {
+        JavaClassCacheEntry start = entry;
+        StringBuilder msg = new StringBuilder(className);
+        msg.append("->");
+
+        String parentName = entry.getSuperclassName();
+        JavaClassCacheEntry parent = javaClassCache.get(parentName);
+        int count = 0;
+
+        while (count < 100 && parent != null && parent != start) {
+            msg.append(parentName);
+            msg.append("->");
+
+            count ++;
+            parentName = parent.getSuperclassName();
+            parent = javaClassCache.get(parentName);
+        }
+
+        msg.append(parentName);
+
+        return msg.toString();
+    }
+
+    private void populateJavaClassCache(String className, JavaClass javaClass,
+            Map<String,JavaClassCacheEntry> javaClassCache) {
+        if (javaClassCache.containsKey(className)) {
+            return;
+        }
+
+        // Add this class to the cache
+        javaClassCache.put(className, new JavaClassCacheEntry(javaClass));
+
+        populateJavaClassCache(javaClass.getSuperclassName(), javaClassCache);
+
+        for (String interfaceName : javaClass.getInterfaceNames()) {
+            populateJavaClassCache(interfaceName, javaClassCache);
+        }
+    }
+
+    private void populateJavaClassCache(String className,
+            Map<String,JavaClassCacheEntry> javaClassCache) {
+        if (!javaClassCache.containsKey(className)) {
+            String name = className.replace('.', '/') + ".class";
+            try (InputStream is = context.getLoader().getClassLoader().getResourceAsStream(name)) {
+                if (is == null) {
+                    return;
+                }
+                ClassParser parser = new ClassParser(is);
+                JavaClass clazz = parser.parse();
+                populateJavaClassCache(clazz.getClassName(), clazz, javaClassCache);
+            } catch (ClassFormatException e) {
+                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
+                        className), e);
+            } catch (IOException e) {
+                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
+                        className), e);
+            }
+        }
+    }
+
+    private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry,
+            Map<String,JavaClassCacheEntry> javaClassCache) {
+        Set<ServletContainerInitializer> result = new HashSet<>();
+
+        // Super class
+        String superClassName = cacheEntry.getSuperclassName();
+        JavaClassCacheEntry superClassCacheEntry =
+                javaClassCache.get(superClassName);
+
+        // Avoid an infinite loop with java.lang.Object
+        if (cacheEntry.equals(superClassCacheEntry)) {
+            cacheEntry.setSciSet(EMPTY_SCI_SET);
+            return;
+        }
+
+        // May be null of the class is not present or could not be loaded.
+        if (superClassCacheEntry != null) {
+            if (superClassCacheEntry.getSciSet() == null) {
+                populateSCIsForCacheEntry(superClassCacheEntry, javaClassCache);
+            }
+            result.addAll(superClassCacheEntry.getSciSet());
+        }
+        result.addAll(getSCIsForClass(superClassName));
+
+        // Interfaces
+        for (String interfaceName : cacheEntry.getInterfaceNames()) {
+            JavaClassCacheEntry interfaceEntry =
+                    javaClassCache.get(interfaceName);
+            // A null could mean that the class not present in application or
+            // that there is nothing of interest. Either way, nothing to do here
+            // so move along
+            if (interfaceEntry != null) {
+                if (interfaceEntry.getSciSet() == null) {
+                    populateSCIsForCacheEntry(interfaceEntry, javaClassCache);
+                }
+                result.addAll(interfaceEntry.getSciSet());
+            }
+            result.addAll(getSCIsForClass(interfaceName));
+        }
+
+        cacheEntry.setSciSet(result.isEmpty() ? EMPTY_SCI_SET : result);
+    }
+
+    private Set<ServletContainerInitializer> getSCIsForClass(String className) {
+        for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
+                typeInitializerMap.entrySet()) {
+            Class<?> clazz = entry.getKey();
+            if (!clazz.isAnnotation()) {
+                if (clazz.getName().equals(className)) {
+                    return entry.getValue();
+                }
+            }
+        }
+        return EMPTY_SCI_SET;
+    }
+
+    private static final String getClassName(String internalForm) {
+        if (!internalForm.startsWith("L")) {
+            return internalForm;
+        }
+
+        // Assume starts with L, ends with ; and uses / rather than .
+        return internalForm.substring(1,
+                internalForm.length() - 1).replace('/', '.');
+    }
+
+    protected void processAnnotationWebServlet(String className,
+            AnnotationEntry ae, WebXml fragment) {
+        String servletName = null;
+        // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
+        List<ElementValuePair> evps = ae.getElementValuePairs();
+        for (ElementValuePair evp : evps) {
+            String name = evp.getNameString();
+            if ("name".equals(name)) {
+                servletName = evp.getValue().stringifyValue();
+                break;
+            }
+        }
+        if (servletName == null) {
+            // classname is default servletName as annotation has no name!
+            servletName = className;
+        }
+        ServletDef servletDef = fragment.getServlets().get(servletName);
+
+        boolean isWebXMLservletDef;
+        if (servletDef == null) {
+            servletDef = new ServletDef();
+            servletDef.setServletName(servletName);
+            servletDef.setServletClass(className);
+            isWebXMLservletDef = false;
+        } else {
+            isWebXMLservletDef = true;
+        }
+
+        boolean urlPatternsSet = false;
+        String[] urlPatterns = null;
+
+        // List<ElementValuePair> evps = ae.getElementValuePairs();
+        for (ElementValuePair evp : evps) {
+            String name = evp.getNameString();
+            if ("value".equals(name) || "urlPatterns".equals(name)) {
+                if (urlPatternsSet) {
+                    throw new IllegalArgumentException(sm.getString(
+                            "contextConfig.urlPatternValue", "WebServlet", className));
+                }
+                urlPatternsSet = true;
+                urlPatterns = processAnnotationsStringArray(evp.getValue());
+            } else if ("description".equals(name)) {
+                if (servletDef.getDescription() == null) {
+                    servletDef.setDescription(evp.getValue().stringifyValue());
+                }
+            } else if ("displayName".equals(name)) {
+                if (servletDef.getDisplayName() == null) {
+                    servletDef.setDisplayName(evp.getValue().stringifyValue());
+                }
+            } else if ("largeIcon".equals(name)) {
+                if (servletDef.getLargeIcon() == null) {
+                    servletDef.setLargeIcon(evp.getValue().stringifyValue());
+                }
+            } else if ("smallIcon".equals(name)) {
+                if (servletDef.getSmallIcon() == null) {
+                    servletDef.setSmallIcon(evp.getValue().stringifyValue());
+                }
+            } else if ("asyncSupported".equals(name)) {
+                if (servletDef.getAsyncSupported() == null) {
+                    servletDef.setAsyncSupported(evp.getValue()
+                            .stringifyValue());
+                }
+            } else if ("loadOnStartup".equals(name)) {
+                if (servletDef.getLoadOnStartup() == null) {
+                    servletDef
+                            .setLoadOnStartup(evp.getValue().stringifyValue());
+                }
+            } else if ("initParams".equals(name)) {
+                Map<String, String> initParams = processAnnotationWebInitParams(evp
+                        .getValue());
+                if (isWebXMLservletDef) {
+                    Map<String, String> webXMLInitParams = servletDef
+                            .getParameterMap();
+                    for (Map.Entry<String, String> entry : initParams
+                            .entrySet()) {
+                        if (webXMLInitParams.get(entry.getKey()) == null) {
+                            servletDef.addInitParameter(entry.getKey(), entry
+                                    .getValue());
+                        }
+                    }
+                } else {
+                    for (Map.Entry<String, String> entry : initParams
+                            .entrySet()) {
+                        servletDef.addInitParameter(entry.getKey(), entry
+                                .getValue());
+                    }
+                }
+            }
+        }
+        if (!isWebXMLservletDef && urlPatterns != null) {
+            fragment.addServlet(servletDef);
+        }
+        if (urlPatterns != null) {
+            if (!fragment.getServletMappings().containsValue(servletName)) {
+                for (String urlPattern : urlPatterns) {
+                    fragment.addServletMapping(urlPattern, servletName);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * process filter annotation and merge with existing one!
+     * FIXME: refactoring method too long and has redundant subroutines with
+     *        processAnnotationWebServlet!
+     * @param className The filter class name
+     * @param ae The filter annotation
+     * @param fragment The corresponding fragment
+     */
+    protected void processAnnotationWebFilter(String className,
+            AnnotationEntry ae, WebXml fragment) {
+        String filterName = null;
+        // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
+        List<ElementValuePair> evps = ae.getElementValuePairs();
+        for (ElementValuePair evp : evps) {
+            String name = evp.getNameString();
+            if ("filterName".equals(name)) {
+                filterName = evp.getValue().stringifyValue();
+                break;
+            }
+        }
+        if (filterName == null) {
+            // classname is default filterName as annotation has no name!
+            filterName = className;
+        }
+        FilterDef filterDef = fragment.getFilters().get(filterName);
+        FilterMap filterMap = new FilterMap();
+
+        boolean isWebXMLfilterDef;
+        if (filterDef == null) {
+            filterDef = new FilterDef();
+            filterDef.setFilterName(filterName);
+            filterDef.setFilterClass(className);
+            isWebXMLfilterDef = false;
+        } else {
+            isWebXMLfilterDef = true;
+        }
+
+        boolean urlPatternsSet = false;
+        boolean servletNamesSet = false;
+        boolean dispatchTypesSet = false;
+        String[] urlPatterns = null;
+
+        for (ElementValuePair evp : evps) {
+            String name = evp.getNameString();
+            if ("value".equals(name) || "urlPatterns".equals(name)) {
+                if (urlPatternsSet) {
+                    throw new IllegalArgumentException(sm.getString(
+                            "contextConfig.urlPatternValue", "WebFilter", className));
+                }
+                urlPatterns = processAnnotationsStringArray(evp.getValue());
+                urlPatternsSet = urlPatterns.length > 0;
+                for (String urlPattern : urlPatterns) {
+                    // % decoded (if required) using UTF-8
+                    filterMap.addURLPattern(urlPattern);
+                }
+            } else if ("servletNames".equals(name)) {
+                String[] servletNames = processAnnotationsStringArray(evp
+                        .getValue());
+                servletNamesSet = servletNames.length > 0;
+                for (String servletName : servletNames) {
+                    filterMap.addServletName(servletName);
+                }
+            } else if ("dispatcherTypes".equals(name)) {
+                String[] dispatcherTypes = processAnnotationsStringArray(evp
+                        .getValue());
+                dispatchTypesSet = dispatcherTypes.length > 0;
+                for (String dispatcherType : dispatcherTypes) {
+                    filterMap.setDispatcher(dispatcherType);
+                }
+            } else if ("description".equals(name)) {
+                if (filterDef.getDescription() == null) {
+                    filterDef.setDescription(evp.getValue().stringifyValue());
+                }
+            } else if ("displayName".equals(name)) {
+                if (filterDef.getDisplayName() == null) {
+                    filterDef.setDisplayName(evp.getValue().stringifyValue());
+                }
+            } else if ("largeIcon".equals(name)) {
+                if (filterDef.getLargeIcon() == null) {
+                    filterDef.setLargeIcon(evp.getValue().stringifyValue());
+                }
+            } else if ("smallIcon".equals(name)) {
+                if (filterDef.getSmallIcon() == null) {
+                    filterDef.setSmallIcon(evp.getValue().stringifyValue());
+                }
+            } else if ("asyncSupported".equals(name)) {
+                if (filterDef.getAsyncSupported() == null) {
+                    filterDef
+                            .setAsyncSupported(evp.getValue().stringifyValue());
+                }
+            } else if ("initParams".equals(name)) {
+                Map<String, String> initParams = processAnnotationWebInitParams(evp
+                        .getValue());
+                if (isWebXMLfilterDef) {
+                    Map<String, String> webXMLInitParams = filterDef
+                            .getParameterMap();
+                    for (Map.Entry<String, String> entry : initParams
+                            .entrySet()) {
+                        if (webXMLInitParams.get(entry.getKey()) == null) {
+                            filterDef.addInitParameter(entry.getKey(), entry
+                                    .getValue());
+                        }
+                    }
+                } else {
+                    for (Map.Entry<String, String> entry : initParams
+                            .entrySet()) {
+                        filterDef.addInitParameter(entry.getKey(), entry
+                                .getValue());
+                    }
+                }
+
+            }
+        }
+        if (!isWebXMLfilterDef) {
+            fragment.addFilter(filterDef);
+            if (urlPatternsSet || servletNamesSet) {
+                filterMap.setFilterName(filterName);
+                fragment.addFilterMapping(filterMap);
+            }
+        }
+        if (urlPatternsSet || dispatchTypesSet) {
+            Set<FilterMap> fmap = fragment.getFilterMappings();
+            FilterMap descMap = null;
+            for (FilterMap map : fmap) {
+                if (filterName.equals(map.getFilterName())) {
+                    descMap = map;
+                    break;
+                }
+            }
+            if (descMap != null) {
+                String[] urlsPatterns = descMap.getURLPatterns();
+                if (urlPatternsSet
+                        && (urlsPatterns == null || urlsPatterns.length == 0)) {
+                    for (String urlPattern : filterMap.getURLPatterns()) {
+                        // % decoded (if required) using UTF-8
+                        descMap.addURLPattern(urlPattern);
+                    }
+                }
+                String[] dispatcherNames = descMap.getDispatcherNames();
+                if (dispatchTypesSet
+                        && (dispatcherNames == null || dispatcherNames.length == 0)) {
+                    for (String dis : filterMap.getDispatcherNames()) {
+                        descMap.setDispatcher(dis);
+                    }
+                }
+            }
+        }
+
+    }
+
+    protected String[] processAnnotationsStringArray(ElementValue ev) {
+        List<String> values = new ArrayList<>();
+        if (ev instanceof ArrayElementValue) {
+            ElementValue[] arrayValues =
+                ((ArrayElementValue) ev).getElementValuesArray();
+            for (ElementValue value : arrayValues) {
+                values.add(value.stringifyValue());
+            }
+        } else {
+            values.add(ev.stringifyValue());
+        }
+        String[] result = new String[values.size()];
+        return values.toArray(result);
+    }
+
+    protected Map<String,String> processAnnotationWebInitParams(
+            ElementValue ev) {
+        Map<String, String> result = new HashMap<>();
+        if (ev instanceof ArrayElementValue) {
+            ElementValue[] arrayValues =
+                ((ArrayElementValue) ev).getElementValuesArray();
+            for (ElementValue value : arrayValues) {
+                if (value instanceof AnnotationElementValue) {
+                    List<ElementValuePair> evps = ((AnnotationElementValue) value)
+                            .getAnnotationEntry().getElementValuePairs();
+                    String initParamName = null;
+                    String initParamValue = null;
+                    for (ElementValuePair evp : evps) {
+                        if ("name".equals(evp.getNameString())) {
+                            initParamName = evp.getValue().stringifyValue();
+                        } else if ("value".equals(evp.getNameString())) {
+                            initParamValue = evp.getValue().stringifyValue();
+                        } else {
+                            // Ignore
+                        }
+                    }
+                    result.put(initParamName, initParamValue);
+                }
+            }
+        }
+        return result;
+    }
+
+    private static class DefaultWebXmlCacheEntry {
+        private final WebXml webXml;
+        private final long globalTimeStamp;
+        private final long hostTimeStamp;
+
+        public DefaultWebXmlCacheEntry(WebXml webXml, long globalTimeStamp,
+                long hostTimeStamp) {
+            this.webXml = webXml;
+            this.globalTimeStamp = globalTimeStamp;
+            this.hostTimeStamp = hostTimeStamp;
+        }
+
+        public WebXml getWebXml() {
+            return webXml;
+        }
+
+        public long getGlobalTimeStamp() {
+            return globalTimeStamp;
+        }
+
+        public long getHostTimeStamp() {
+            return hostTimeStamp;
+        }
+    }
+
+    static class JavaClassCacheEntry {
+        public final String superclassName;
+
+        public final String[] interfaceNames;
+
+        private Set<ServletContainerInitializer> sciSet = null;
+
+        public JavaClassCacheEntry(JavaClass javaClass) {
+            superclassName = javaClass.getSuperclassName();
+            interfaceNames = javaClass.getInterfaceNames();
+        }
+
+        public String getSuperclassName() {
+            return superclassName;
+        }
+
+        public String[] getInterfaceNames() {
+            return interfaceNames;
+        }
+
+        public Set<ServletContainerInitializer> getSciSet() {
+            return sciSet;
+        }
+
+        public void setSciSet(Set<ServletContainerInitializer> sciSet) {
+            this.sciSet = sciSet;
+        }
+    }
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 2542985..354aff7 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -1,7425 +1,7432 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  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.
--->
-<!DOCTYPE document [
-  <!ENTITY project SYSTEM "project.xml">
-]>
-<?xml-stylesheet type="text/xsl" href="tomcat-docs.xsl"?>
-<document url="changelog.html">
-
-  &project;
-
-  <properties>
-    <title>Changelog</title>
-    <no-comments />
-  </properties>
-
-<body>
-<!--
-  Subsection ordering:
-  General, Catalina, Coyote, Jasper, Cluster, WebSocket, Web applications,
-  Extras, Tribes, jdbc-pool, Other
-
-  Item Ordering:
-
-  Fixes having an issue number are sorted by their number, ascending.
-
-  There is no ordering by add/update/fix/scode.
-
-  Other fixed issues are added to the end of the list, chronologically.
-  They eventually become mixed with the numbered issues (i.e., numbered
-  issues do not "pop up" wrt. others).
--->
-<section name="Tomcat 9.0.20 (markt)" rtext="in development">
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        The <code>useAsyncIO</code> boolean attribute on the Connector element
-        value now defaults to <code>true</code>. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.19 (markt)" rtext="release in progress">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Fix wrong JMX registration regression in 9.0.18. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <update>
-        Add vectoring for NIO in the base and SSL channels. (remm)
-      </update>
-      <add>
-        Add asynchronous IO from NIO2 to the NIO connector, with support for
-        the async IO implementations for HTTP/2 and Websockets. The
-        <code>useAsyncIO</code> boolean attribute on the Connector element
-        allows enabling use of the asynchronous IO API. (remm)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Ensure that the correct files are included in the source distribution
-        for javacc based parsers depending on whether jjtree is used or not.
-        (markt)
-      </fix>
-      <fix>
-        Ensure that text files in the source distribution have the correct line
-        endings for the target platform. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.18 (markt)" rtext="not released">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>63196</bug>: Provide a default (<code>X-Forwarded-Proto</code>) for
-        the <code>protocolHeader</code> attribute of the
-        <code>RemoteIpFilter</code> and <code>RemoteIpValve</code>. (markt)
-      </fix>
-      <fix>
-        <bug>63235</bug>: Refactor Charset cache to reduce start time. (markt)
-      </fix>
-      <fix>
-        <bug>63249</bug>: Use a consistent log level (<code>WARN</code>) when
-        logging the failure to register or deregister a JMX Bean. (markt)
-      </fix>
-      <fix>
-        <bug>63249</bug>: Use a consistent log level (<code>ERROR</code>) when
-        logging the <code>LifecycleException</code> associated with the failure
-        to start or stop a component. (markt)
-      </fix>
-      <fix>
-        When the SSI directive <code>fsize</code> is used with an invalid
-        target, return a file size of <code>-</code> rather than
-        <code>1k</code>. (markt)
-      </fix>
-      <fix>
-        <bug>63251</bug>: Implement a work-around for a known JRE bug (<a
-        href="https://bugs.openjdk.java.net/browse/JDK-8194653">JDK-8194653</a>)
-        that may cause a dead-lock when Tomcat starts. (markt)
-      </fix>
-      <fix>
-        <bug>63275</bug>: When using a <code>RequestDispatcher</code> ensure
-        that <code>HttpServletRequest.getContextPath()</code> returns an encoded
-        path in the dispatched request. (markt)
-      </fix>
-      <update>
-        Add optional listeners for Server/Listener, as a slight variant of
-        a standard listener. The difference is that loading is not fatal when
-        it fails. This would allow adding example configuration to the standard
-        server.xml if deemed useful. Storeconfig will not attempt to persist
-        the new listener. (remm)
-      </update>
-      <fix>
-        <bug>63286</bug>: Document the differences in behaviour between the
-        <code>LogFormat</code> directive in httpd and the <code>pattern</code>
-        attribute in the <code>AccessLogValve</code> for <code>%D</code> and
-        <code>%T</code>. (markt)
-      </fix>
-      <fix>
-        <bug>63287</bug>: Make logging levels more consistent for similar issues
-        of similar severity. (markt)
-      </fix>
-      <fix>
-        <bug>63311</bug>: Add support for https URLs to the local resolver within
-        Tomcat used to resolve standard XML DTDs and schemas when Tomcat is
-        configured to validate XML configuration files such as web.xml. (markt)
-      </fix>
-      <fix>
-        Encode the output of the SSI <code>printenv</code> command. (markt)
-      </fix>
-      <scode>
-        Use constants for SSI encoding values. (markt)
-      </scode>
-      <add>
-        When the CGI Servlet is configured with
-        <code>enableCmdLineArguments</code> set to true, limit the encoded form
-        of the individual command line arguments to those values allowed by RFC
-        3875. This restriction may be relaxed by the use of the new
-        initialisation parameter <code>cmdLineArgumentsEncoded</code>. (markt)
-      </add>
-      <add>
-        When the CGI Servlet is configured with
-        <code>enableCmdLineArguments</code> set to true, limit the decoded form
-        of the individual command line arguments to known safe values when
-        running on Windows. This restriction may be relaxed by the use of the
-        new initialisation parameter <code>cmdLineArgumentsDecoded</code>. This
-        is the fix for CVE-2019-0232. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Fix bad interaction between NIO2 async read API and the regular read.
-        (remm)
-      </fix>
-      <fix>
-        Refactor NIO2 write pending strategy for the classic IO API. (remm)
-      </fix>
-      <fix>
-        Restore original maxConnections default for NIO2 as the underlying
-        close issues have been fixed. (remm)
-      </fix>
-      <fix>
-        Harmonize NIO2 isReadyForWrite with isReadyForRead code. (remm)
-      </fix>
-      <fix>
-        When using a JSSE TLS connector that supported ALPN (Java 9 onwards) and
-        a protocol was not negotiated, Tomcat failed to fallback to HTTP/1.1 and
-        instead dropped the connection. (markt)
-      </fix>
-      <fix>
-        Correct a regression in the TLS connector refactoring in Tomcat 9.0.17
-        that prevented the use of PKCS#8 private keys with OpenSSL based
-        connectors. (markt)
-      </fix>
-      <fix>
-        Fix NIO2 SSL edge cases. (remm)
-      </fix>
-      <fix>
-        When performing an upgrade from HTTP/1.1 to HTTP/2, ensure that any
-        query string present in the original HTTP/1.1 request is passed to the
-        HTTP/2 request processing. (markt)
-      </fix>
-      <fix>
-        When Tomcat writes a final response without reading all of an HTTP/2
-        request, reset the stream to inform the client that the remaining
-        request body is not required. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <add>
-        Add support for specifying Java 11 (with the value <code>11</code>) as
-        the compiler source and/or compiler target for JSP compilation. (markt)
-      </add>
-      <add>
-        Add support for specifying Java 12 (with the value <code>12</code>) and
-        Java 13 (with the value <code>13</code>) as the compiler source and/or
-        compiler target for JSP compilation. If used with an ECJ version that
-        does not support these values, a warning will be logged and the latest
-        supported version will used. Based on a patch by Thomas Collignon.
-        (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        <bug>63184</bug>: Expand the SSI documentation to provide more
-        information on the supported directives and their attributes. Patch
-        provided by nightwatchcyber. (markt)
-      </fix>
-      <add>
-        Add a note to the documentation about the risk of DoS with poorly
-        written regular expressions and the <code>RewriteValve</code>. Patch
-        provided by salgattas. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="jdbc-pool">
-    <changelog>
-      <fix>
-        Improved maxAge handling. Add support for age check on idle connections.
-        Connection that expired reconnects rather than closes it. Patch provided
-        by toby1984. (kfujino)
-      </fix>
-      <fix>
-        <bug>63320</bug>: Ensure that <code>StatementCache</code> caches
-        statements that include arrays in arguments. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <update>
-        Update to the Eclipse JDT compiler 4.10. (markt)
-      </update>
-      <add>
-        Expand the coverage and quality of the Spanish translations provided
-        with Apache Tomcat. Includes contributions by Ulises Gonzalez Horta.
-        (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Czech translations provided
-        with Apache Tomcat. Includes contributions by Arnošt Havelka. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Chinese translations provided
-        with Apache Tomcat. Includes contributions by winsonzhao and wjt.
-        (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Russian translations provided
-        with Apache Tomcat. (kkolinko)
-      </add>
-      <add>
-        Expand the coverage and quality of the Japanese translations provided
-        with Apache Tomcat. (kfujino)
-      </add>
-      <add>
-        Expand the coverage and quality of the Korean translations provided
-        with Apache Tomcat. (woonsan)
-      </add>
-      <add>
-        Expand the coverage and quality of the German translations provided
-        with Apache Tomcat. (fschumacher)
-      </add>
-      <add>
-        Expand the coverage and quality of the French translations provided
-        with Apache Tomcat. (remm)
-      </add>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.17 (markt)" rtext="2019-03-18">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Refactor how cookies are transferred from the base request to a
-        <code>PushBuilder</code> so that they are accessible, and may be edited,
-        via the standard <code>PushBuilder</code> methods for working with HTTP
-        headers. (markt)
-      </fix>
-      <update>
-        Simplify the value of <code>jarsToSkip</code> property in
-        <code>catalina.properties</code> file for tomcat-i18n jar files.
-        Use prefix pattern instead of listing each language. (kkolinko)
-      </update>
-      <fix>
-        Restore the getter and setter for the access log valve attribute
-        <code>maxLogMessageBufferSize</code> that were accidentally removed.
-        (markt)
-      </fix>
-      <add>
-        <bug>63206</bug>: Add a new attribute to <code>Context</code> -
-        <code>createUploadTargets</code> which, if <code>true</code> enables
-        Tomcat to create the temporary upload location used by a Servlet if the
-        location specified by the Servlet does not already exist. The default
-        value is <code>false</code>. (markt)
-      </add>
-      <fix>
-        <bug>63210</bug>: Ensure that the Apache Commons DBCP 2 based default
-        connection pool is correctly shutdown when it is no longer required.
-        This ensures that a non-daemon thread is not left running that will
-        prevent Tomcat from shutting down cleanly. (markt)
-      </fix>
-      <fix>
-        <bug>63213</bug>: Ensure the correct escaping of group names when
-        searching for nested groups when the JNDIRealm is configured with
-        <code>roleNested</code> set to <code>true</code>. (markt)
-      </fix>
-      <fix>
-        <bug>63236</bug>: Use <code>String.intern()</code> as suggested by
-        Phillip Webb to reduce memory wasted due to String duplication. This
-        changes saves ~245k when starting a clean installation. With additional
-        thanks to YourKit Java profiler for helping to track down the wasted
-        memory and the root causes. (markt)
-      </fix>
-      <fix>
-        <bug>63246</bug>: Fix a potential <code>NullPointerException</code> when
-        calling <code>AsyncContext.dispatch()</code>. (markt)
-      </fix>
-      <fix>
-        Always use the absolute path of the <code>docBase</code> during the
-        deployment process to determine the Context name, deployment type,
-        whether the <code>docBase</code> is located within the
-        <code>appBase</code> etc. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        When performing an HTTP/1.1 upgrade to HTTP/2 (h2c) ensure that the hostname
-        and port from the HTTP/1.1 Host header of the upgraded request are made
-        available via the standard methods
-        <code>ServletRequest.getServerName()</code> and
-        <code>ServletRequest.getServerPort()</code>. (markt)
-      </fix>
-      <fix>
-        Refactor the APR/Native endpoint TLS configuration code to enable JSSE
-        style configuration - including JKS keystores - to be used with the
-        APR/Native connector. (markt)
-      </fix>
-      <add>
-        With the TLS configuration refactoring, the configuration attributes
-        <code>sessionCacheSize</code> and <code>sessionTimeout</code> are no
-        longer limited to JSSE implementations. They may now be used with
-        OpenSSL implementations as well. (markt)
-      </add>
-      <fix>
-        Refactor NIO2 read pending strategy for the classic IO API. (remm)
-      </fix>
-      <fix>
-        <bug>63182</bug>: Avoid extra read notifications for HTTP/1.1 with
-        NIO2 when using asynchronous threads. (remm)
-      </fix>
-      <add>
-        <bug>63205</bug>: Add a work-around for a known
-        <a href="https://bugs.openjdk.java.net/browse/JDK-8157404">JRE KeyStore
-        loading bug</a>. (markt)
-      </add>
-      <fix>
-        NIO2 should try to use SocketTimeoutException everywhere rather than a
-        mix of it and InterruptedByTimeout. (remm)
-      </fix>
-      <fix>
-        Correct an error in the request validation that meant that HTTP/2 push
-        requests always resulted in a 400 response. (markt)
-      </fix>
-      <fix>
-        <bug>63223</bug>: Correctly account for push requests when tracking
-        currently active HTTP/2 streams. (markt)
-      </fix>
-      <fix>
-        Ensure enough buffer space when using TLS with NIO2 by using the main
-        read buffer to store additional decrypted data. (remm)
-      </fix>
-      <fix>
-        Verify HTTP/2 stream is still writable before assuming a timeout
-        occurred. (remm)
-      </fix>
-      <fix>
-        Avoid some overflow cases with OpenSSL to improve efficiency, as the
-        OpenSSL engine has an internal buffer. (remm)
-      </fix>
-      <fix>
-        Harmonize HTTP/1.1 NIO2 keepalive code. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <scode>
-        Remove the <code>STREAMS_DROP_EMPTY_MESSAGES</code> system property that
-        was introduced to work-around four failing TCK tests. An alternative
-        solution has been implemented. Sending messages via
-        <code>getSendStream()</code> and <code>getSendWriter()</code> will now
-        only result in messages on the wire if data is written to the
-        <code>OutputStream</code> or <code>Writer</code>. Writing zero length
-        data will result in an empty message. Note that sending a message via an
-        <code>Encoder</code> may result in the message being send via
-        <code>getSendStream()</code> or <code>getSendWriter()</code>. (markt)
-      </scode>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        Fix messages used by Manager and Host Manager web applications.
-        Disambiguate message keys used when adding or removing a host.
-        Improve display of summary values on the status page: separate
-        terms and values with a whitespace. Improve wording of messages
-        for expire sessions command. (kkolinko)
-      </fix>
-      <fix>
-        Do not add CSRF nonce parameter and suppress Referer header for external
-        links in Manager and Host Manager web applications. (kkolinko)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <add>
-        Add feature that discover local member from the static member list.
-        (kfujino)
-      </add>
-      <fix>
-        Ensure that members registered in the addSuspects list are static
-        members. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <add>
-        Expand the coverage and quality of the French translations provided
-        with Apache Tomcat. (remm)
-      </add>
-      <fix>
-        <bug>63041</bug>: Revert the changes for <bug>53930</bug> that added
-        support for the <code>CATALINA_OUT_CMD</code> environment variable as
-        they prevented correct operation with systemd configurations that did
-        not explicitly specify a PID file. (markt)
-      </fix>
-      <add>
-        Expand the coverage and quality of the Russian translations provided
-        with Apache Tomcat. (kkolinko)
-      </add>
-      <fix>
-        Fix the artifactId of <code>tomcat-i18n-cs</code>. (rjung)
-      </fix>
-      <add>
-        Expand the coverage and quality of the Korean translations provided
-        with Apache Tomcat. (woonsan)
-      </add>
-      <add>
-        Expand the coverage and quality of the Chinese translations provided
-        with Apache Tomcat. Includes contributions by winsonzhao. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Czech translations provided
-        with Apache Tomcat. Includes contributions by Arnošt Havelka. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Spanish translations provided
-        with Apache Tomcat. Includes contributions by Ulises Gonzalez Horta.
-        (markt)
-      </add>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.16 (markt)" rtext="2019-02-08">
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        Use client's preferred language for the Server Status page of the
-        Manager web application. Review and fix several cases when the
-        client's language preference was not respected in Manager and
-        Host Manager web applications. (kkolinko)
-      </fix>
-      <fix>
-        <bug>63141</bug>: Ensure that translated manager response strings still
-        start with <code>OK -</code> where expected by the associated Ant tasks.
-        (markt)
-      </fix>
-      <fix>
-        <bug>63143</bug>: Ensure that the Manager web application respects the
-        language preferences of the user as configured in the browser when the
-        language of the default system locale is not English. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <fix>
-        Remove unnecessary shutdown for executor. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <update>
-        Update the NSIS Installer used to build the Windows installer to version
-        3.04. (markt)
-      </update>
-      <add>
-        Add Czech translations to Apache Tomcat. Includes contributions from
-        Arnošt Havelka and Alice. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Spanish translations provided
-        with Apache Tomcat. Includes contributions from Ulises Gonzalez Horta.
-        (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the French translations provided
-        with Apache Tomcat. (remm)
-      </add>
-      <add>
-        Expand the coverage and quality of the Korean translations provided
-        with Apache Tomcat. (woonsan)
-      </add>
-      <add>
-        Expand the coverage and quality of the Japanese translations provided
-        with Apache Tomcat. Includes contributions from Yujiorama. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Chinese translations provided
-        with Apache Tomcat. Includes contributions from zheng. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Russian translations provided
-        with Apache Tomcat. (kkolinko)
-      </add>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.15 (markt)" rtext="not released">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>54741</bug>: Add a new method,
-        <code>Tomcat.addWebapp(String,URL)</code>, that allows a web application
-        to be deployed from a URL when using Tomcat in embedded mode. (markt)
-      </fix>
-      <fix>
-        <bug>63002</bug>: Fix setting rewrite qsdiscard flag. (remm)
-      </fix>
-      <fix>
-        Implement the requirements of section 8.2.2 2c of the Servlet
-        specification and prevent a web application from deploying if it has
-        fragments with duplicate names and is configured to use relative
-        ordering of fragments. (markt)
-      </fix>
-      <fix>
-        Ensure that the HEAD response is consistent with the GET response when
-        <code>HttpServlet</code> is relied upon to generate the HEAD response
-        and the GET response uses chunking. (markt)
-      </fix>
-      <fix>
-        Ensure that the <code>ServletOutputStream</code> implementation is
-        consistent with the requirements of asynchronous I/O and that all of the
-        write methods use a single write rather than multiple writes. (markt)
-      </fix>
-      <fix>
-        Correct the Javadoc for <code>Context.getDocBase()</code> and
-        <code>Context.setDocBase()</code> and remove text that indicates that a
-        URL may be used for the <code>docBase</code> as this has not been the
-        case for quite some time. (markt)
-      </fix>
-      <update>
-        Add basic health check valve. (remm)
-      </update>
-      <fix>
-        Correct a bug exposed in 9.0.14 and ensure that the Tomcat terminates in
-        a timely manner when running as a service. (markt)
-      </fix>
-      <fix>
-        Log a message when using a Connector that requires Apr without enabling
-        the AprLifecycleListener first. (csutherl)
-      </fix>
-      <fix>
-        Utility thread count for special negative or zero values will again be
-        based on Runtime.getRuntime().availableProcessors(). (remm)
-      </fix>
-      <scode>
-        Treat I/O errors during request body reads the same way as I/O errors
-        during response body writes. The errors are treated as client side
-        errors rather than server side errors and only logged at debug level.
-        (markt)
-      </scode>
-      <fix>
-        <bug>63038</bug>: Ensure that a <code>ClassNotFoundException</code> is
-        thrown when attempting to load a class from a corrupted JAR file.
-        (markt)
-      </fix>
-      <fix>
-        <bug>63078</bug>: Ensure the utility thread pool is at least two, as the
-        deployer uses a blocking pattern. (remm, markt)
-      </fix>
-      <add>
-        Make the removal of leading and trailing whitespace from credentials
-        passed to BASIC authentication configurable via a new attribute,
-        <code>trimCredentials</code> on the <code>BasicAuthenticator</code>.
-        (markt)
-      </add>
-      <fix>
-        <bug>63003</bug>: Extend the <code>unloadDelay</code> attribute on a
-        <code>Context</code> to include in-flight asynchronous requests. (markt)
-      </fix>
-      <add>
-        <bug>63026</bug>: Add a new attribute, <code>forceDnHexEscape</code>, to
-        the <code>JNDIRealm</code> that forces escaping in the String
-        representation of a distinguished name to use the <code>\nn</code> form.
-        This may avoid issues with realms using Active Directory which appears
-        to be more tolerant of optional escaping when the <code>\nn</code> form
-        is used. (markt)
-      </add>
-      <fix>
-        Avoid a swallowed (and therefore ignored) access failure during web
-        application class loading when running under a
-        <code>SecurityManager</code>. (markt)
-      </fix>
-      <update>
-        Add SSL configuration options to the JMX remote listener using the
-        <code>SSLHostConfig</code> framework. (remm)
-      </update>
-      <update>
-        Update the recommended minimum Tomcat Native version to 1.2.21. (markt)
-      </update>
-      <fix>
-        <bug>63137</bug>: If the resources for a web application have been
-        configured with multiple locations mapped to
-        <code>/WEB-INF/classes</code>, ensure that all of those locations are
-        used when building the web application class path. Patch provided by
-        Marcin Gołębski. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <add>
-        <bug>63009</bug>: Include the optional <code>content-length</code>
-        header in HTTP/2 responses where an appropriate value is available.
-        (markt)
-      </add>
-      <fix>
-        <bug>63022</bug>: Do not use the socket open state when using the
-        wrapper isClosed method for NIO and NIO2, as it will disable all
-        further processing. (remm)
-      </fix>
-      <fix>
-        Fix socket close discrepancies for NIO2, now the wrapper close
-        is used everywhere except for socket accept problems. (remm)
-      </fix>
-      <fix>
-        Fix use of write timeout instead of read timeout for HTTP/2 NIO2
-        frame read. (remm)
-      </fix>
-      <fix>
-        Fix incorrect APR sendfile thread stop. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        <bug>63056</bug>: Correct a regression in the fix for <bug>53737</bug>
-        that did not correctly scan the web application directory structure for
-        JSPs. (markt)
-      </fix>
-      <fix>
-        Update the performance optimisation for using expressions in tags that
-        depend on uninitialised tag attributes with implied scope to make the
-        performance optimisation aware of the new public class
-        (<code>java.lang.Enum$EnumDesc</code>) added in Java 12. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <fix>
-        <bug>57974</bug>: Ensure implementation of
-        <code>Session.getOpenSessions()</code> returns correct value for both
-        client-side and server-side calls. (markt)
-      </fix>
-      <fix>
-        <bug>63019</bug>: Use payload remaining bytes rather than limit when
-        writing. Submitted by Benoit Courtilly. (remm)
-      </fix>
-      <fix>
-        When running under a <code>SecurityManager</code>, ensure that the
-        <code>ServiceLoader</code> look-up for the default
-        <code>javax.websocket.server.ServerEndpointConfig.Configurator</code>
-        implementation completes correctly rather than silently using the
-        hard-coded fall-back. (markt)
-      </fix>
-      <fix>
-        Ensure that the network connection is closed if the client receives an
-        I/O error trying to communicate with the server. (markt)
-      </fix>
-      <fix>
-        Ignore synthetic methods when scanning POJO methods. (markt)
-      </fix>
-      <fix>
-        Implement the requirements of section 5.2.1 of the WebSocket 1.1
-        specification and ensure that if the deployment of one Endpoint fails,
-        no Endpoints are deployed for that web application. (markt)
-      </fix>
-      <fix>
-        Implement the requirements of section 4.3 of the WebSocket 1.1
-        specification and ensure that the deployment of an Endpoint fails if
-        <code>@PathParam</code> is used with an invalid parameter type. (markt)
-      </fix>
-      <fix>
-        Ensure a <code>DeploymentException</code> rather than an
-        <code>IllegalArgumentException</code> is thrown if a method annotated
-        with <code>@OnMessage</code> does not conform to the requirements set
-        out in the Javadoc. (markt)
-      </fix>
-      <fix>
-        Improve algorithm that determines if two <code>@OnMessage</code>
-        annotations have been added for the same message type. Prior to this
-        change some matches were missed. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        <bug>63103</bug>: Remove the unused source.jsp file and associated tag
-        from the examples web application as it is no longer used. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <update>
-        Add dns-ping support to enumerate cluster members. This is much simpler
-        than getting the pod list but it does not indicate pod status.
-        Submitted by Maxime Beck. (remm)
-      </update>
-      <fix>
-        Never expire the local member from a Membership. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <update>
-        Update container image with monitoring contraptions. (remm)
-      </update>
-      <add>
-        Expand the coverage and quality of the Korean translations provided with
-        Apache Tomcat. Includes contributions from woonsan and Chris Cho.
-        (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Japanese translations provided
-        with Apache Tomcat. Includes contributions from kfujino, Yujiorama and
-        motohashi.yuki. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the French translations provided with
-        Apache Tomcat. Includes contributions from remm, Ludovic Pénet and
-        evernat. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the German translations provided
-        with Apache Tomcat. Includes contributions from fschumacher, Stefan and
-        burghard. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Chinese (simplified) translations
-        provided with Apache Tomcat. Includes contributions from winsonzhao,
-        Lanranzi, shawn, Winsonzhoa, JinXiqian, RichardHo, qingshi huang,
-        Greenman0007, Jim Ma, huxing, 袁宇杰 and evernat. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Spanish translations provided
-        with Apache Tomcat. Includes contributions from Ulises Gonzalez Horta,
-        Israel, Eduardo Quintanilla and Miguel Ortega. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Russian translations provided
-        with Apache Tomcat. Includes contributions from Andrei Maiseyenka and
-        solomax. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Brazilian Portuguese translations
-        provided with Apache Tomcat. Includes contributions from Victor Caetano
-        and Dabilo. (markt)
-      </add>
-      <fix>
-        <bug>63041</bug>: Correct a regression in the fix for <bug>53930</bug>
-        that prevented Tomcat from working correctly with systemd. Patch
-        provided by Patrik S. (markt)
-      </fix>
-      <update>
-        <fix>63072</fix>: Remove extras (JMX remote listener and webservices
-        object factories) and merge them back into the core build.
-        (remm)
-      </update>
-      <add>
-        Update the internal fork of Apache Commons FileUpload to pick up the
-        changes in the Apache Commons FileUpload 1.4 release. (markt)
-      </add>
-      <update>
-        Update the internal fork of Apache Commons DBCP 2 to de20b77
-        (2019-01-29) to pick up some bug fixes and enhancements. (markt)
-      </update>
-      <update>
-        Update the packaged version of the Tomcat Native Library to 1.2.21 to
-        pick up the memory leak fixes when using NIO/NIO2 with OpenSSL. (markt)
-      </update>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.14 (markt)" rtext="2018-12-12">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>62788</bug>: Add explicit logging configuration to write log files
-        using UTF-8 to align with Tomcat&apos;s use of UTF-8 by default
-        elsewhere. (markt)
-      </fix>
-      <fix>
-        The default Servlet should not override a previously set content-type.
-        (remm)
-      </fix>
-      <fix>
-        Fix storeconfig for the cluster encryption interceptor key attribute.
-        (remm)
-      </fix>
-      <add>
-        Add a scheduled executor to the Server, which can be used to
-        process periodic utility tasks. The utility threads are non daemon
-        by default. (remm)
-      </add>
-      <update>
-        Refactor container background processor using the Server executor, and
-        add monitoring to reschedule it in case of an unexpected error. (remm)
-      </update>
-      <update>
-        Refactor parallel deployment threads using the Server executor. (remm)
-      </update>
-      <add>
-        Introduce a ConfigurationSource API to standardize access to the core
-        configuration resources of Tomcat. (remm)
-      </add>
-      <update>
-        Update the Tomcat embedded API by allowing to set a configuration
-        source, which will allow processing of core configuration. (remm)
-      </update>
-      <update>
-        Refactor processing of server.xml, web.xml, context.xml, other
-        configuration files and resources using the ConfigurationSource API.
-        JASPIC persistent providers load and store remains file based.
-        StoreConfig Tomcat configuration files storing remains file based
-        at their previous default locations. (remm)
-      </update>
-      <add>
-        <bug>62897</bug>: Provide a property
-        (<code>clearReferencesThreadLocals</code>) on the standard
-        <code>Context</code> implementation that enables the check for memory
-        leaks via <code>ThreadLocal</code>s to be disabled because this check
-        depends on the use of an API that has been deprecated in later versions
-        of Java. (markt)
-      </add>
-      <fix>
-        Fix more storeconfig issues with duplicated SSL attributes. (remm)
-      </fix>
-      <fix>
-        <bug>62924</bug>: Fix file descriptor leak introduced in the code that
-        monitors <code>tomcat-users.xml</code> for modifications. (markt)
-      </fix>
-      <update>
-        Add periodic event notification for lifecycle listeners configured on
-        the Server. (remm)
-      </update>
-      <fix>
-        <bug>62968</bug>: Avoid unnecessary (and relatively expensive)
-        <code>getResources()</code> call in the Mapper when processing rule 7.
-        (markt)
-      </fix>
-      <update>
-        Update the recommended minimum Tomcat Native version to 1.2.19. (markt)
-      </update>
-      <fix>
-        <bug>62978</bug>: Update the RemoteIpValve to handle multiple values in
-        the <code>x-forwarded-proto</code> header. Patch provided by Tom Groot.
-        (markt)
-      </fix>
-      <fix>
-        Update the RemoteIpFilter to handle multiple values in the
-        <code>x-forwarded-proto</code> header. Based on a patch provided by Tom
-        Groot. (markt)
-      </fix>
-      <scode>
-        <bug>62986</bug>: Refactor the code that performs class scanning during
-        web application start to make integration simpler for downstream users.
-        Patch provided by rmannibucau. (markt)
-      </scode>
-      <fix>
-        Filter out tomcat-web.xml from the watched resources list in
-        storeconfig. (remm)
-      </fix>
-      <fix>
-        <bug>62988</bug>: Fix the <code>LoadBalancerDrainingValve</code> so it
-        works when the session cookie configuration is not explicitly declared.
-        Based on a patch provided by Andreas Kurth. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <update>
-        Refactor connector async timeout threads using a scheduled executor.
-        (remm)
-      </update>
-      <update>
-        Avoid using a dedicated thread for accept on the NIO2 connector, it is
-        always less efficient. (remm)
-      </update>
-      <update>
-        Load SSL configuration resources for JSSE using the ConfigurationSource
-        API. OpenSSL use requires actual files. (remm)
-      </update>
-      <fix>
-        <bug>62899</bug>: Prevent the incorrect timing out of connections when
-        Servlet non-blocking I/O is used to read a request body over an HTTP/2
-        stream. (markt)
-      </fix>
-      <fix>
-        Avoid bad SSLHostConfig JMX registrations before init. (remm)
-      </fix>
-      <fix>
-        Avoid a potential hang when a client connects using TLS 1.0 to a Tomcat
-        HTTPS connector configured to use NIO or NIO2 with OpenSSL 1.1.1 or
-        later. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <update>
-        Update the Eclipse Compiler for Java to 4.9. Additional patch by Lukasz
-        Jader. (markt)
-      </update>
-      <add>
-        <bug>53737</bug>: Extend JspC, the precompilation tool, to include
-        support for resource JARs. (markt)
-      </add>
-      <fix>
-        <bug>62976</bug>: Avoid an <code>IllegalStateException</code> when using
-        background compilation when tag files are packaged in JAR files. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <add>
-        <bug>53553</bug>: Add the ability to specify a context.xml from the
-        server to use when uploading a web application for deployment with the
-        Manager web application. Patch provided by Anton Lindström. (markt)
-      </add>
-      <fix>
-        <bug>62918</bug>: Filter out subtype mbeans to avoid breaking the
-        connector status page. (remm)
-      </fix>
-      <fix>
-        Unify letter case of the word 'How-To' in the webapps (csutherl)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <update>
-        Refactor various operations performed in tribes using a scheduled
-        executor. When tribes is not running standalone, it will use the
-        executor from the Catalina Server. If running independently, the
-        Channel will provide the executor. (remm)
-      </update>
-      <fix>
-        Make EncryptInterceptor thread-safe. This makes this interceptor
-        actually usable. (schultz/markt)
-      </fix>
-      <add>
-        Add support for GCM mode to EncryptInterceptor. (schultz)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Prevent an error when running in a Cygwin shell and the
-        <code>JAVA_ENDORSED_DIRS</code> system property is empty. Patch provided
-        by Zemian Deng. (markt)
-      </fix>
-      <add>
-        Expand the coverage and quality of the French translations provided with
-        Apache Tomcat. Includes contributions from remm, soliplaya, Ludovic
-        Pénet, David, NicolasG and bdelacretaz. (markt)
-      </add>
-      <add>
-        Add Simplified Chinese translations to the translations to Apache
-        Tomcat. Includes contributions from Darren Luo, syseal, Winsonzhao,
-        袁宇杰, Lanranzi, ZhangJieWen, Jerry, yinzhili001, 安柏诚, shawn, lavender,
-        Zheng Feng, zengwc, RichardHo, mm, gingshi huang, Bob, geekwang, zheng,
-        Deanzhg, Tianfengjingjing, Panblack, oking, Dave Newman, Cnfnss, Jim Ma,
-        852394875, huxing and Greenman0007. (markt)
-      </add>
-      <add>
-        Add Korean translations to Apache Tomcat. Includes contributions from
-        woonsan, JunSang Park, song choe and OhChan. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Spanish translations provided
-        with Apache Tomcat. Includes contributions from Ulises Gonzalez Horta,
-        Israel, Eduardo Quintanilla and Miguel suarez. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Russian translations provided
-        with Apache Tomcat. Includes contributions from solomax, Rafael Sachakov
-        and Andrei Maiseyenka. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the German translations provided
-        with Apache Tomcat. Includes contributions from Matk80, burghard,
-        Daniel Wehringer and Felix Schumacher. (markt)
-      </add>
-      <add>
-        Expand the coverage and quality of the Japanese translations provided
-        with Apache Tomcat. Includes contributions from Yujiorama,
-        motohashi.yuki and kfujino. (markt)
-      </add>
-      <add>
-        Add Brazilian Portuguese translations to Apache Tomcat. Includes
-        contributions from geraldo netto. (markt)
-      </add>
-      <fix>
-        Include Brazilian Portuguese translations in the standard Tomcat
-        distribution. (markt)
-      </fix>
-      <fix>
-        Include Simplified Chinese translations in the standard Tomcat
-        distribution. (markt)
-      </fix>
-      <fix>
-        Include Korean translations in the standard Tomcat distribution. (markt)
-      </fix>
-      <add>
-        Add a packaging method for Tomcat using Maven, as well as a container
-        build file for it. (remm)
-      </add>
-      <fix>
-        Add XML Namespace to the project element of all POM files so that the
-        XML files are Well Formed and Valid. (csutherl)
-      </fix>
-      <add>
-        <bug>53930</bug>: Add support for the <code>CATALINA_OUT_CMD</code>
-        environment variable that defines a command to which captured stdout and
-        stderr will be redirected. Patch provided by Casey Lucas. (markt)
-      </add>
-      <update>
-        Update the packaged version of the Tomcat Native Library to 1.2.19 to
-        pick up the latest Windows binaries built with APR 1.6.5 and OpenSSL
-        1.1.1a. (markt)
-      </update>
-      <update>
-        Add i18n to many strings that lacked it. (remm)
-      </update>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.13 (markt)" rtext="2018-11-07">
-  <subsection name="Catalina">
-    <changelog>
-      <add>
-        <bug>58590</bug>: Add the ability for a UserDatabase to monitor the
-        backing XML file for changes and reload the source file if a change in
-        the last modified time is detected. This is enabled by default meaning
-        that changes to <code>$CATALINA_BASE/conf/tomcat-users.xml</code> will
-        now take effect a short time after the file is saved. (markt)
-      </add>
-      <add>
-        <bug>61171</bug>: Add the <code>portOffset</code> attribute to the
-        <code>Server</code> element which is added to the configured shutdown
-        and <code>Connector</code> ports. Based on a patch by Marek Czernek.
-        (markt)
-      </add>
-      <add>
-        <bug>61692</bug>: Add the ability to control which HTTP methods are
-        handled by the CGI Servlet via a new initialization parameter
-        <code>cgiMethods</code>. (markt)
-      </add>
-      <fix>
-        <bug>62687</bug>: Expose content length information for resources
-        when using a compressed war. (remm)
-      </fix>
-      <fix>
-        <bug>62737</bug>: Fix rewrite substitutions parsing of {} nesting.
-        (remm)
-      </fix>
-      <fix>
-        Add rewrite flags output when getting the rewrite configuration back.
-        (remm)
-      </fix>
-      <fix>
-        Add missing qsdiscard flag to the rewrite flags as a cleaner way to
-        discard the query string. (remm)
-      </fix>
-      <add>
-        <bug>62755</bug>: Add ability to opt out of adding the default web.xml
-        config when embedding Tomcat and adding a context via
-        <code>addWebapp()</code>. Call
-        <code>setAddDefaultWebXmlToWebapp(false)</code> to prevent the automatic
-        config. (isapir)
-      </add>
-      <fix>
-        Add documentation about the files <code>context.xml.default</code> and
-        <code>web.xml.default</code> that can be used to customize
-        <code>conf/context.xml</code> and <code>conf/web.xml</code> on a per
-        host basis. (fschumacher)
-      </fix>
-      <fix>
-        Ensure that a canonical path is always used for the docBase of a Context
-        to ensure consistent behaviour. (markt)
-      </fix>
-      <fix>
-        <bug>62803</bug>: Fix SSL connector configuration processing
-        in storeconfig. (remm)
-      </fix>
-      <fix>
-        <bug>62797</bug>: Pass throwable to keep client aborts with status 200
-        rather than 500. Patch submitted by zikfat. (remm)
-      </fix>
-      <fix>
-        <bug>62802</bug>: Restore the <code>appContextProtection</code>
-        attribute to the <code>JreMemoryLeakPreventionListener</code> as
-        application code may still trigger this memory leak. (markt)
-      </fix>
-      <fix>
-        <bug>62809</bug>: Correct a regression in the implementation of DIGEST
-        authentication support for the Deployer Ant tasks (bug <bug>45832</bug>)
-        that prevented the <code>DeployTask</code> from working when
-        authentication was required. (markt)
-      </fix>
-      <update>
-        Update the recommended minimum Tomcat Native version to 1.2.18. (markt)
-      </update>
-      <add>
-        Ignore an attribute named <code>source</code> on <code>Context</code>
-        elements provided by <code>StandardContext</code>. This is to suppress
-        warnings generated by the Eclipse / Tomcat integration provided by
-        Eclipse. Based on a patch by mdfst13. (markt)
-      </add>
-      <add>
-        <bug>62830</bug>: Added <code>JniLifeCycleListener</code> and static
-        methods <code>Library.loadLibrary(libraryName)</code> and
-        <code>Library.load(filename)</code> to load a native library by a
-        shared class loader so that more than one Webapp can use it. (isapir)
-      </add>
-      <scode>
-        Refactor the <code>Connector</code> so that the port is obtained from
-        the <code>Endpoint</code> rather than a local field that could end up
-        out of sync. (markt)
-      </scode>
-      <fix>
-        Correct a typo in the Spanish resource files. Patch provided by Diego
-        Agulló. (markt)
-      </fix>
-      <fix>
-        <bug>62868</bug>: Order the <code>Enumeration&lt;URL&gt;</code> provided
-        by <code>WebappClassLoaderBase.getResources(String)</code> according to
-        the setting of the delegate flag. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <add>
-        Add TLSv1.3 to the default protocols and to the <code>all</code>
-        alias for JSSE based TLS connectors when running on a JVM that
-        supports TLS version 1.3. One such JVM is OpenJDK version 11. (rjung)
-      </add>
-      <fix>
-        <bug>62685</bug>: Correct an error in host name validation parsing that
-        did not allow a fully qualified domain name to terminate with a period.
-        Patch provided by AG. (markt)
-      </fix>
-      <fix>
-        Make PEM file parser a public utility class. (remm)
-      </fix>
-      <fix>
-        <bug>62739</bug>: Do not reject requests with an empty HTTP Host header.
-        Such requests are unusual but not invalid. Patch provided by Michael
-        Orr. (markt)
-      </fix>
-      <add>
-        <bug>62748</bug>: Add TLS 1.3 support for the APR/Native connector and
-        the NIO/NIO2 connector when using the OpenSSL backed JSSE
-        implementation. (schultz/markt)
-      </add>
-      <fix>
-        <bug>62791</bug>: Remove an unnecessary check in the NIO TLS
-        implementation that prevented from secure WebSocket connections from
-        being established. (markt)
-      </fix>
-      <fix>
-        Fix server initiated TLS renegotiation to obtain a client certificate
-        when using NIO/NIO2 and the OpenSSL backed JSSE TLS implementation.
-        (markt)
-      </fix>
-      <fix>
-        Ensure open sockets etc. are cleaned up if the socket binding process
-        fails. (markt)
-      </fix>
-      <fix>
-        <bug>62871</bug>: Improve MBeans for Endpoint instances (type
-        <code>ThreadPool</code> in JMX) by using explicit declaration of
-        attributes and operations rather than relying on introspection. Add a
-        new MBean to expose the <code>Socketproperties</code> values. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        Correct parsing of XML whitespace in TLD function signatures that
-        incorrectly only looked for the space character. (markt)
-      </fix>
-      <fix>
-        <bug>62674</bug>: Correct a regression in the stand-alone JSP compiler
-        utility, <code>JspC</code>, caused by the fix for <bug>53492</bug>, that
-        caused the JSP compiler to hang. (markt)
-      </fix>
-      <fix>
-        <bug>62721</bug>: Correct generation of web.xml header when using JspC.
-        (markt)
-      </fix>
-      <fix>
-        <bug>62757</bug>: Correct a regression in the fix for <bug>62603</bug>
-        that caused <code>NullPointerException</code>s when compiling tag files
-        on first access when development mode was disabled and background
-        compilation was enabled. Based on a patch by Jordi Llach. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <fix>
-        <bug>62731</bug>: Make the URI returned by
-        <code>HandshakeRequest.getRequestURI()</code> and
-        <code>Session.getRequestURI()</code> absolute so that the scheme, host
-        and port are accessible. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        <bug>62676</bug>: Expand the CORS filter documentation to make it clear
-        that explicit configuration is required to enable support for
-        cross-origin requests. (markt)
-      </fix>
-      <fix>
-        <bug>62712</bug>: Correct NPE in Manager application when attempting to
-        view configured certificates for an APR/native TLS connector. (markt)
-      </fix>
-      <fix>
-        <bug>62761</bug>: Correct the advanced CORS example in the Filter
-        documentation to use a valid configuration. (markt)
-      </fix>
-      <fix>
-        <bug>62786</bug>: Add a note to the Context documentation to explain
-        that, by default, settings for a Context element defined in server.xml
-        will be overwritten by settings specified in a default context file such
-        as <code>conf/context.xml</code>. (markt)
-      </fix>
-      <fix>
-        Create a little visual separation between the Undeploy button and the
-        other buttons in the Manager application. Patch provided by Łukasz
-        Jąder. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <add>
-        Add <code>setMembershipService</code> method to the
-        <code>MembershipProvider</code>. (kfujino)
-      </add>
-      <add>
-        Experimental Kubernetes aware cloud membership provider, based on code
-        by Maxime Beck. Contains code derived from jgroups. (remm/kfujino)
-      </add>
-      <fix>
-        Move the event notification <code>ThreadPoolExecutor</code> to
-        <code>MembershipProviderBase</code>. (kfujino)
-      </fix>
-      <fix>
-        Even if all members have already disappeared and PING can not be sent,
-        ensure that members will be expired. (kfujino)
-      </fix>
-      <fix>
-        Ensure that remove the member from suspect list when member added.
-        (kfujino)
-      </fix>
-      <add>
-        Add EncryptInterceptor to the portfolio of available clustering
-        interceptors. This adds symmetric encryption of session data
-        to Tomcat clustering regardless of the type of cluster manager
-        or membership being used. (schultz)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Port DBCP transaction synchronization registry fix
-        (commit d49d45e). (remm)
-      </fix>
-      <update>
-        Update the internal fork of Apache Commons Pool 2 to d4e0e88
-        (2018-09-12) to pick up some bug fixes and enhancements. (markt)
-      </update>
-      <add>
-        <bug>62705</bug>: Added a fail fast check for minimum required Apache
-        Ant version 1.9.8 when building Tomcat. (isapir)
-      </add>
-      <add>
-        Added ant target ide-intellij to create an IntelliJ IDEA project. (isapir)
-      </add>
-      <add>
-        Utility JSON parser generated from a public domain javacc grammar
-        written by Robert Fischer. (remm)
-      </add>
-      <update>
-        Update the packaged version of the Tomcat Native Library to 1.2.18 to
-        pick up the latest Windows binaries built with APR 1.6.5 and OpenSSL
-        1.1.1. (markt)
-      </update>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.12 (markt)" rtext="2018-09-10">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Improve the handling of path parameters when working with
-        RequestDispatcher objects. (markt)
-      </fix>
-      <fix>
-        <bug>62664</bug>: Process requests with content type
-        <code>multipart/form-data</code> to servlets with a
-        <code>@MultipartConfig</code> annotation regardless of HTTP method.
-        (markt)
-      </fix>
-      <fix>
-        <bug>62667</bug>: Add recursion to rewrite substitution parsing. (remm)
-      </fix>
-      <fix>
-        <bug>62669</bug>: When using the SSIFilter and a resource does not
-        specify a content type, do not force the content type to
-        <code>application/x-octet-stream</code>. (markt)
-      </fix>
-      <fix>
-        <bug>62670</bug>: Adjust the memory leak protection for the
-        <code>DriverManager</code> so that JDBC drivers located in
-        <code>$CATALINA_HOME/lib</code> and <code>$CATALINA_BASE/lib</code> are
-        loaded via the service loader mechanism when the protection is enabled.
-        (markt)
-      </fix>
-      <fix>
-        When generating a redirect to a directory in the Default Servlet, avoid
-        generating a protocol relative redirect. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Fix potential deadlocks when using asynchronous Servlet processing with
-        HTTP/2 connectors. (markt)
-      </fix>
-      <fix>
-        <bug>62620</bug>: Fix corruption of response bodies when writing large
-        bodies using asynchronous processing over HTTP/2. (markt)
-      </fix>
-      <fix>
-        <bug>62628</bug>: Additional fixes for output corruption of response
-        bodies when writing large bodies using asynchronous processing over
-        HTTP/2. (markt)
-      </fix>
-      <scode>
-        Support for Netware in the <code>org.apache.tomcat.jni</code> package
-        has been removed as there has not been a supported Netware platform for
-        a number of years. (markt)
-      </scode>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        Correct the JSP version in the X-PoweredBy HTTP header generated when
-        the xpoweredBy option is enabled. (markt)
-      </fix>
-      <fix>
-        <bug>62662</bug>: Fix the corruption of web.xml output during JSP
-        compilation caused by the fix for <bug>53492</bug>. Patch provided by
-        Bernhard Frauendienst. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <add>
-        Expand the information in the documentation web application regarding
-        the use of <code>CATALINA_HOME</code> and <code>CATALINA_BASE</code>.
-        Patch provided by Marek Czernek. (markt)
-      </add>
-      <fix>
-        <bug>62652</bug>: Make it clearer that the version of DBCP that is
-        packaged in Tomcat 9.0.x is DBCP 2. Correct the names of some DBCP 2
-        configuration attributes that changed between 1.x and 2.x. (markt)
-      </fix>
-      <add>
-        <bug>62666</bug>: Expand internationalisation support in the Manager
-        application to include the server status page and provide Russian
-        translations in addition to English. Patch provided by Artem Chebykin.
-        (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Switch the build script to use http for downloads from an ASF mirror
-        using the closer.lua script to avoid failures due to HTTPS to HTTP
-        redirects. (rjung)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.11 (markt)" rtext="2018-08-17">
-  <subsection name="Catalina">
-    <changelog>
-      <add>
-        Make the <code>isLocked()</code> method of the <code>LockOutRealm</code>
-        public and expose the method via JMX. (markt)
-      </add>
-      <add>
-        <bug>53387</bug>: Add support for regular expression capture groups to
-        the SSI servlet and filter. (markt)
-      </add>
-      <fix>
-        <bug>53411</bug>: Improve the handling of HTTP requests that do not
-        explicitly specify a host name when no default host is configured. Also
-        improve the tracking of changes to the default host as hosts are added
-        and removed while Tomcat is running. (markt)
-      </fix>
-      <fix>
-        Ensure that the HTTP Vary header is set correctly when using the CORS
-        filter and improve the cacheability of requests that pass through the
-        COPRS filter. (markt)
-      </fix>
-      <fix>
-        <bug>62527</bug>: Revert restriction of JNDI to the <code>java:</code>
-        namespace. (remm)
-      </fix>
-      <add>
-        Introduce a new class - <code>MultiThrowable</code> - to report
-        exceptions when multiple actions are taken where each action may throw
-        an exception but all actions are taken before any errors are reported.
-        Use this new class when reporting multiple container (e.g. web
-        application) failures during start. (markt)
-      </add>
-      <fix>
-        Correctly decode URL paths (<code>+</code> should not be decoded to a
-        space in the path) in the <code>RequestDispatcher</code> and the web
-        application class loader. (markt)
-      </fix>
-      <add>
-        Make logout more robust if JASPIC subject is unexpectedly unavailable.
-        (markt)
-      </add>
-      <fix>
-        <bug>62547</bug>: JASPIC <code>cleanSubject()</code> was not called on
-        logout when the authenticator was configured to cache the authenticated
-        Principal. Patch provided by Guillermo González de Agüero. (markt)
-      </fix>
-      <add>
-        <bug>62559</bug>: Add <code>jaxb-*.jar</code> to the list of JARs
-        ignored by <code>StandardJarScanner</code>. (markt)
-      </add>
-      <add>
-        <bug>62560</bug>: Add <code>oraclepki.jar</code> to the list of JARs
-        ignored by <code>StandardJarScanner</code>. (markt)
-      </add>
-      <add>
-        <bug>62607</bug>: Return a non-zero exit code from
-        <code>catalina.[bat|sh] run</code> if Tomcat fails to start. (markt)
-      </add>
-      <fix>
-        Use short circuit logic to prevent potential NPE in CorsFilter. (fschumacher)
-      </fix>
-      <scode>
-        Simplify construction of appName from container name in JAASRealm. (fschumacher)
-      </scode>
-      <scode>
-        Remove <code>ServletException</code> from declaration of
-        <code>Tomcat.addWebapp(String,String)</code> since it is never thrown.
-        Patch provided by Tzafrir. (markt)
-      </scode>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <scode>
-        Refactor HTTP date creation and parsing to reduce code duplication,
-        reduce the use of ThreadLocals and to increase the use of caching.
-        (markt)
-      </scode>
-      <fix>
-        <bug>56676</bug>: Add a default location for the native library, as
-        ${catalina.home}/bin, which the testsuite already uses. (remm)
-      </fix>
-      <update>
-        <bug>60560</bug>: Add support for using an inherited channel to
-        the NIO connector. Based on a patch submitted by Thomas Meyer with
-        testing and suggestions by Coty Sutherland. (remm)
-      </update>
-      <fix>
-        <bug>62507</bug>: Ensure that JSSE based TLS connectors work correctly
-        with a DKS keystore. (markt)
-      </fix>
-      <fix>
-        Refactor code that adds an additional header name to the
-        <code>Vary</code> HTTP response header to use a common utility method
-        that addresses several additional edge cases. (markt)
-      </fix>
-      <fix>
-        <bug>62515</bug>: When a connector is configured (via setting
-        <code>bindOnInit</code> to <code>false</code>) to bind/unbind the server
-        socket during start/stop, close the socket earlier in the stop process
-        so new connections do not sit in the TCP backlog during the shutdown
-        process only to be dropped as stop completes. In this scenario new
-        connections will now be refused immediately. (markt)
-      </fix>
-      <fix>
-        <bug>62526</bug>: Correctly handle PKCS12 format key stores when the key
-        store password is configured to be the empty string. (markt)
-      </fix>
-      <fix>
-        <bug>62605</bug>: Ensure <code>ReadListener.onDataAvailable()</code> is
-        called when the initial request body data arrives after the request
-        headers when using asynchronous processing over HTTP/2. (markt)
-      </fix>
-      <fix>
-        <bug>62614</bug>: Ensure that
-        <code>WriteListener.onWritePossible()</code> is called after
-        <code>isReady()</code> returns <code>false</code> and the window size is
-        subsequently incremented when using asynchronous processing over HTTP/2.
-        (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <add>
-        <bug>53492</bug>: Make the Java file generation process multi-threaded.
-        By default, one thread will be used per core. Based on a patch by Dan
-        Fabulich. (markt)
-      </add>
-      <add>
-        <bug>62453</bug>: Add a performance optimisation for using expressions
-        in tags that depend on uninitialised tag attributes with implied scope.
-        Generally, using an explicit scope with tag attributes in EL is the best
-        way to avoid various potential performance issues. (markt)
-      </add>
-      <fix>
-        Correctly decode URL paths (<code>+</code> should not be decoded to a
-        space in the path) in the Jasper class loader. (markt)
-      </fix>
-      <fix>
-        <bug>62603</bug>: Fix a potential race condition when development mode
-        is disabled and background compilation checks are enabled. It was
-        possible that some updates would not take effect and/or
-        <code>ClassNotFoundException</code>s would occur. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <fix>
-        <bug>62596</bug>: Remove the limit on the size of the initial HTTP
-        upgrade request used to establish the web socket connection. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <add>
-        <bug>62558</bug>: Add Russian translations for the Manager and Host
-        Manager web applications. Based on a patch by Ivan Krasnov. (markt)
-      </add>
-      <add>
-        Add documents for Static Membership service. (kfujino)
-      </add>
-      <add>
-        <bug>62561</bug>: Add advanced class loader configuration information
-        regarding the use of the Server and Shared class loaders to the
-        documentation web application. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <fix>
-        Ensures that the specified <code>rxBufSize</code> is correctly set to
-        receiver buffer size. (kfujino)
-      </fix>
-      <fix>
-        Correct the stop order of the Channel components. It stops in the
-        reverse order to that at startup. (kfujino)
-      </fix>
-      <add>
-        Added new StaticMembership implementation. This implementation does not
-        require any additional configuration of other
-        <code>ChannelInterceptors</code>. It works only with membership service.
-        (kfujino)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <update>
-        Support building with Java 9+ while preserving the Java 8 compatibility
-        at runtime (requires Ant 1.9.8 or later). (ebourg)
-      </update>
-      <update>
-        Update WSDL4J library to version 1.6.3 (from 1.6.2). (kkolinko)
-      </update>
-      <update>
-        Update JUnit library to version 4.12 (from 4.11). (kkolinko)
-      </update>
-      <update>
-        Downgrade CGLib library used for testing with EasyMock to version
-        2.2.2 (from 2.2.3) as version 2.2.3 is not available from Maven Central.
-        (markt)
-      </update>
-      <add>
-        Implement checksum checks when downloading dependencies that are used
-        to build Tomcat. (kkolinko)
-      </add>
-      <fix>
-        Fixed spelling. Patch provided by Jimmy Casey via GitHub. (violetagg)
-      </fix>
-      <update>
-        Update the internal fork of Apache Commons Pool 2 to 3e02523
-        (2018-08-09) to pick up some bug fixes and enhancements. (markt)
-      </update>
-      <update>
-        Update the internal fork of Apache Commons DBCP 2 to abc0484
-        (2018-08-09) to pick up some bug fixes and enhancements. (markt)
-      </update>
-      <fix>
-        Correct various spelling errors throughout the source code and
-        documentation. Patch provided by Kazuhiro Sera. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.10 (markt)" rtext="2018-06-25">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>62476</bug>: Use GMT timezone for the value of
-        <code>Expires</code> header as required by HTTP specification
-        (RFC 7231, 7234). (kkolinko)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.9 (markt)" rtext="not released">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Treat the <code>&lt;mapped-name&gt;</code> element of a
-        <code>&lt;env-entry&gt;</code> in web.xml in the same way as the
-        <code>mappedName</code> element of the equivalent <code>@Resource</code>
-        annotation. Both now attempt to set the <code>mappedName</code> property
-        of the resource. (markt)
-      </fix>
-      <fix>
-        Correct the processing of resources with
-        <code>&lt;injection-target&gt;</code>s defined in web.xml. First look
-        for a match using JavaBean property names and then, only if a match is
-        not found, look for a match using fields. (markt)
-      </fix>
-      <fix>
-        When restoring a saved request with a request body after FORM
-        authentication, ensure that calls to the <code>HttpServletRequest</code>
-        methods <code>getRequestURI()</code>, <code>getQueryString()</code> and
-        <code>getProtocol()</code> are not corrupted by the processing of the
-        saved request body. (markt)
-      </fix>
-      <fix>
-        JNDI resources that are defined with injection targets but no value are
-        now treated as if the resource is not defined. (markt)
-      </fix>
-      <fix>
-        Ensure that JNDI names used for <code>&lt;lookup-name&gt;</code> entries
-        in web.xml and for <code>lookup</code> elements of
-        <code>@Resource</code> annotations specify a name with an explicit
-        <code>java:</code> namespace. (markt)
-      </fix>
-      <fix>
-        <bug>50019</bug>: Add support for <code>&lt;lookup-name&gt;</code>.
-        Based on a patch by Gurkan Erdogdu. (markt)
-      </fix>
-      <add>
-        Add the <code>AuthenticatedUserRealm</code> for use with CLIENT-CERT and
-        SPNEGO when just the authenticated user name is required. (markt)
-      </add>
-      <fix>
-        <bug>50175</bug>: Add a new attribute to the standard context
-        implementation, <code>skipMemoryLeakChecksOnJvmShutdown</code>, that
-        allows the user to configure Tomcat to skip the memory leak checks
-        usually performed during web application stop if that stop is triggered
-        by a JVM shutdown. (markt)
-      </fix>
-      <add>
-        <bug>51497</bug>: Add an option, <code>ipv6Canonical</code>, to the
-        <code>AccessLogValve</code> that causes IPv6 addresses to be output in
-        canonical form defined by RFC 5952. (ognjen/markt)
-      </add>
-      <add>
-        <bug>51953</bug>: Add the <code>RemoteCIDRFilter</code> and
-        <code>RemoteCIDRValve</code> that can be used to allow/deny requests
-        based on IPv4 and/or IPv6 client address where the IP ranges are defined
-        using CIDR notation. Based on a patch by Francis Galiegue. (markt)
-      </add>
-      <fix>
-        <bug>62343</bug>: Make CORS filter defaults more secure. This is the fix
-        for CVE-2018-8014. (markt)
-      </fix>
-      <fix>
-        Ensure that the web application resources implementation does not
-        incorrectly cache results for resources that are only visible as class
-        loader resources. (markt)
-      </fix>
-      <fix>
-        <bug>62387</bug>: Do not log a warning message if the file based
-        persistent session store fails to delete the file for a session when the
-        session is invalidated because the file has not been created yet.
-        (markt)
-      </fix>
-      <fix>
-        Make all loggers associated with Tomcat provided Filters non-static to
-        ensure that log messages are not lost when a web application is
-        reloaded. (markt)
-      </fix>
-      <fix>
-        Correct the manifest for the annotations-api.jar. The JAR implements the
-        Common Annotations API 1.3 and the manifest should reflect that. (markt)
-      </fix>
-      <fix>
-        Switch to non-static loggers where there is a possibility of a logger
-        becoming associated with a web application class loader causing log
-        messages to be lost if the web application is stopped. (markt)
-      </fix>
-      <add>
-        <bug>62389</bug>: Add the IPv6 loopback address to the default
-        <code>internalProxies</code> regular expression. Patch by Craig Andrews.
-        (markt)
-      </add>
-      <fix>
-        In the <code>RemoteIpValve</code> and <code>RemoteIpFilter</code>,
-        correctly handle the case when the request passes through one or more
-        <code>trustedProxies</code> but no <code>internalProxies</code>. Based
-        on a patch by zhanhb. (markt)
-      </fix>
-      <fix>
-        Correct the logic in <code>MBeanFactory.removeConnector()</code> to
-        ensure that the correct Connector is removed when there are multiple
-        Connectors using different addresses but the same port. (markt)
-      </fix>
-      <fix>
-        Make <code>JAASRealm</code> mis-configuration more obvious by requiring
-        the authenticated Subject to include at least one Principal of a type
-        specified by <code>userClassNames</code>. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Correct a regression in the error page handling that prevented error
-        pages from issuing redirects or taking other action that required the
-        response status code to be changed. (markt)
-      </fix>
-      <fix>
-        Consistent exception propagation for NIO2 SSL close. (remm)
-      </fix>
-      <fix>
-        Followup sync fix for NIO2 async IO blocking read/writes. (remm)
-      </fix>
-      <fix>
-        Log an error message if the AJP connector detects that the reverse proxy
-        is sending AJP messages that are too large for the configured
-        <code>packetSize</code>. (markt)
-      </fix>
-      <fix>
-        Relax Host validation by removing the requirement that the final
-        component of a FQDN must be alphabetic. (markt)
-      </fix>
-      <fix>
-        <bug>62371</bug>: Improve logging of Host validation failures. (markt)
-      </fix>
-      <fix>
-        Fix a couple of unlikely edge cases in the shutting down of the
-        APR/native connector. (markt)
-      </fix>
-      <fix>
-        Add missing handshake timeout for NIO2. (remm)
-      </fix>
-      <fix>
-        Correctly handle a digest authorization header when the user name
-        contains an escaped character. (markt)
-      </fix>
-      <fix>
-        Correctly handle a digest authorization header when one of the hex
-        field values ends the header with in an invalid character. (markt)
-      </fix>
-      <fix>
-        Correctly handle an invalid quality value in an
-        <code>Accept-Language</code> header. (markt)
-      </fix>
-      <docs>
-        <bug>62423</bug>: Fix SSL docs CRL attribute typo. (remm)
-      </docs>
-      <fix>
-        Improve IPv6 validation by ensuring that IPv4-Mapped IPv6 addresses do
-        not contain leading zeros in the IPv4 part. Based on a patch by Katya
-        Stoycheva. (markt)
-      </fix>
-      <fix>
-        Fix <code>NullPointerException</code> thrown from <code>
-        replaceSystemProperties()</code> when trying to log messages. (csutherl)
-      </fix>
-      <fix>
-        Avoid unnecessary processing of async timeouts. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <add>
-        <bug>50234</bug>: Add the capability to generate a web-fragment.xml file
-        to JspC. (markt)
-      </add>
-      <fix>
-        <bug>62080</bug>: Ensure that all reads of the current thread's context
-        class loader made by the UEL API and implementation are performed via a
-        <code>PrivilegedAction</code> to ensure that a
-        <code>SecurityException</code> is not triggered when running under a
-        <code>SecurityManager</code>. (mark)
-      </fix>
-      <fix>
-        <bug>62350</bug>: Refactor
-        <code>org.apache.jasper.runtime.BodyContentImpl</code> so a
-        <code>SecurityException</code> is not thrown when running under a
-        SecurityManger and additional permissions are not required in the
-        <code>catalina.policy</code> file. This is a follow-up to the fix for
-        <bug>43925</bug>. (kkolinko/markt)
-      </fix>
-      <fix>
-        Enable JspC from Tomcat 9 to work with Maven JspC compiler plug-ins
-        written for Tomcat 8.5.x. Patch provided by Pavel Cibulka. (markt)
-      </fix>
-      <fix>
-        Update web.xml, web-fragment.xml and web.xml extracts generated by JspC
-        to use the Servlet 4.0 version of the relevant schemas. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Cluster">
-    <changelog>
-      <fix>
-        Remove duplicate calls when creating a replicated session to reduce the
-        time taken to create the session and thereby reduce the chances of a
-        subsequent session update message being ignored because the session does
-        not yet exist. (markt)
-      </fix>
-      <add>
-        Add the method to send a message with a specified sendOptions. (kfujino)
-      </add>
-      <fix>
-        When sending the <code>GET_ALL_SESSIONS</code> message, make sure that
-        sends with asynchronous option in order to avoid ack timeout. Waiting to
-        receive the <code>ALL_SESSION_DATA</code> message should be done with
-        <code>waitForSendAllSessions</code> instead of ACK. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <update>
-        Use NIO2 API for websockets writes. (remm)
-      </update>
-      <fix>
-        When decoding of path parameter failed, make sure to throw
-        <code>DecodeException</code> instead of throwing
-        <code>ArrayIndexOutOfBoundsException</code>. (kfujino)
-      </fix>
-      <fix>
-        Improve the handling of exceptions during TLS handshakes for the
-        WebSocket client. (markt)
-      </fix>
-      <fix>
-        Enable host name verification when using TLS with the WebSocket client.
-        (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        <bug>62395</bug>: Clarify the meaning of the connector attribute
-        <code>minSpareThreads</code> in the documentation web application.
-        (markt)
-      </fix>
-      <fix>
-        Correct the documentation for the <code>allowHostHeaderMismatch</code>
-        attribute of the standard HTTP Connector implementations. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <fix>
-        Ensure that the correct default value is returned when retrieve unset
-        properties in <code>McastService</code>. (kfujino)
-      </fix>
-      <add>
-        Make <code>MembershipService</code> more easily extensible. (kfujino)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="jdbc-pool">
-    <changelog>
-      <fix>
-        When <code>logValidationErrors</code> is set to true, the connection
-        validation error is logged as <code>SEVERE</code> instead of
-        <code>WARNING</code>. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Ensure that Apache Tomcat may be built from source with Java 11. (markt)
-      </fix>
-      <add>
-        <bug>52381</bug>: Add OSGi metadata to JAR files. (markt)
-      </add>
-      <fix>
-        <bug>62391</bug>: Remove references to <code>javaw.exe</code> as this
-        file is not required by Tomcat and the references prevent the use of the
-        Server JRE. (markt)
-      </fix>
-      <update>
-        Update the packaged version of the Tomcat Native Library to 1.2.17 to
-        pick up the latest Windows binaries built with APR 1.6.3 and OpenSSL
-        1.0.2o. (markt)
-      </update>
-      <update>
-        <bug>62458</bug>: Update the internal fork of Commons Pool 2 to dfef97b
-        (2018-06-18) to pick up some bug fixes and enhancements. (markt)
-      </update>
-      <update>
-        Update the internal fork of Commons DBCP 2 to 2.4.0. (markt)
-      </update>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.8 (markt)" rtext="2018-05-03">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>62263</bug>: Avoid a <code>NullPointerException</code> when the
-        <code>RemoteIpValve</code> processes a request for which no Context can
-        be found. (markt)
-      </fix>
-      <add>
-        <bug>62258</bug>: Don't trigger the standard error page mechanism when
-        the error has caused the connection to the client to be closed as no-one
-        will ever see the error page. (markt)
-      </add>
-      <fix>
-        Register MBean when DataSource Resource <code>
-        type="javax.sql.XADataSource"</code>. Patch provided by Masafumi Miura.
-        (csutherl)
-      </fix>
-      <fix>
-        Fix a rare edge case that is unlikely to occur in real usage. This edge
-        case meant that writing long streams of UTF-8 characters to the HTTP
-        response that consisted almost entirely of surrogate pairs could result
-        in one surrogate pair being dropped. (markt)
-      </fix>
-      <add>
-        Update the internal fork of Apache Commons BCEL to r1829827 to add early
-        access Java 11 support to the annotation scanning code. (markt)
-      </add>
-      <fix>
-        <bug>62297</bug>: Enable the <code>CrawlerSessionManagerValve</code> to
-        correctly handle bots that crawl multiple hosts and/or web applications
-        when the Valve is configured on a Host or an Engine. (fschumacher)
-      </fix>
-      <fix>
-        <bug>62309</bug>: Fix a <code>SecurityException</code> when using JASPIC
-        under a <code>SecurityManager</code> when authentication is not
-        mandatory. (markt)
-      </fix>
-      <fix>
-        <bug>62329</bug>: Correctly list resources in JAR files when directories
-        do not have dedicated entries. Patch provided by Meelis Müür. (markt)
-      </fix>
-      <add>
-        Collapse multiple leading <code>/</code> characters to a single
-        <code>/</code> in the return value of
-        <code>HttpServletRequest#getContextPath()</code> to avoid issues if the
-        value is used with <code>HttpServletResponse#sendRedirect()</code>. This
-        behaviour is enabled by default and configurable via the new Context
-        attribute <code>allowMultipleLeadingForwardSlashInPath</code>. (markt)
-      </add>
-      <fix>
-        Improve handling of overflow in the UTF-8 decoder with supplementary
-        characters. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Correct off-by-one error in thread pool that allowed thread pools to
-        increase in size to one more than the configured limit. Patch provided
-        by usc. (markt)
-      </fix>
-      <fix>
-        Prevent unexpected TLS handshake failures caused by errors during a
-        previous handshake that were not correctly cleaned-up when using the NIO
-        or NIO2 connector with the <code>OpenSSLImplementation</code>. (markt)
-      </fix>
-      <add>
-        <bug>62273</bug>: Implement configuration options to work-around
-        specification non-compliant user agents (including all the major
-        browsers) that do not correctly %nn encode URI paths and query strings
-        as required by RFC 7230 and RFC 3986. (markt)
-      </add>
-      <fix>
-        Fix sync for NIO2 async IO blocking read/writes. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <update>
-        Update the Eclipse Compiler for Java to 4.7.3a. (markt)
-      </update>
-      <update>
-        Allow <code>9</code> to be used to specify Java 9 as the compiler source
-        and/or compiler target for JSP compilation. The Early Access value of
-        <code>1.9</code> is still supported. (markt)
-      </update>
-      <add>
-        Add support for specifying Java 10 (with the value <code>10</code>) as
-        the compiler source and/or compiler target for JSP compilation. (markt)
-      </add>
-      <fix>
-        <bug>62287</bug>: Do not rely on hash codes to test instances of
-        <code>ValueExpressionImpl</code> for equality. Patch provided by Mark
-        Struberg. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <fix>
-        <bug>62301</bug>: Correct a regression in the fix for <bug>61491</bug>
-        that didn't correctly handle a final empty message part in all
-        circumstances when using <code>PerMessageDeflate</code>. (markt)
-      </fix>
-      <fix>
-        <bug>62332</bug>: Ensure WebSocket connections are closed after an I/O
-        error is experienced reading from the client. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Avoid warning when running under Cygwin when the
-        <code>JAVA_ENDORSED_DIRS</code> environment variable is not set. Patch
-        provided by Zemian Deng. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.7 (markt)" rtext="2018-04-07">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>51195</bug>: Avoid a false positive report of a web application
-        memory leak by clearing <code>ObjectStreamClass$Caches</code> of classes
-        loaded by the web application when the web application is stopped.
-        (markt)
-      </fix>
-      <fix>
-        <bug>52688</bug>: Add support for the <code>maxDays</code> attribute to
-        the <code>AccessLogValve</code> and <code>ExtendedAccessLogValve</code>.
-        This allows the maximum number of days for which rotated access logs
-        should be retained before deletion to be defined. (markt)
-      </fix>
-      <fix>
-        Ensure the MBean names for the <code>SSLHostConfig</code> and
-        <code>SSLHostConfigCertificate</code> are correctly formed when the
-        <code>Connector</code> is bound to a specific IP address. (markt)
-      </fix>
-      <fix>
-        <bug>62168</bug>: When using the <code>PersistentManager</code> honor a
-        value of <code>-1</code> for <code>minIdleSwap</code> and do not swap
-        out sessions to keep the number of active sessions under
-        <code>maxActive</code>. Patch provided by Holger Sunke. (markt)
-      </fix>
-      <fix>
-        <bug>62172</bug>: Improve Javadoc for
-        <code>org.apache.catalina.startup.Constants</code> and ensure that the
-        constants are correctly used. (markt)
-      </fix>
-      <fix>
-        <bug>62175</bug>: Avoid infinite recursion, when trying to validate
-        a session while loading it with <code>PersistentManager</code>.
-        (fschumacher)
-      </fix>
-      <fix>
-        Ensure that <code>NamingContextListener</code> instances are only
-        notified once of property changes on the associated naming resources.
-        (markt)
-      </fix>
-      <add>
-        <bug>62224</bug>: Disable the <code>forkJoinCommonPoolProtection</code>
-        of the <code>JreMemoryLeakPreventionListener</code> when running on Java
-        9 and above since the underlying JRE bug has been fixed. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Avoid potential loop in APR/Native poller. (markt)
-      </fix>
-      <fix>
-        Ensure streams that are received but not processed are excluded from the
-        tracking of maximum ID of processed streams. (markt)
-      </fix>
-      <fix>
-        Refactor the check for a paused connector to consistently prevent new
-        streams from being created after the connector has been paused. (markt)
-      </fix>
-      <fix>
-        Improve debug logging for HTTP/2 pushed streams. (markt)
-      </fix>
-      <fix>
-        The OpenSSL engine SSL session will now ignore invalid accesses. (remm)
-      </fix>
-      <fix>
-        <bug>62177</bug>: Correct two protocol errors with HTTP/2
-        <code>PUSH_PROMISE</code> frames. Firstly, the HTTP/2 protocol only
-        permits pushes to be sent on peer initiated requests. Secondly, pushes
-        must be sent in order of increasing stream ID. These restriction were
-        not being enforced leading to protocol errors at the client. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <add>
-        Add document for <code>FragmentationInterceptor</code>. (kfujino)
-      </add>
-      <add>
-        Document how the roles for an authenticated user are determined when the
-        <code>CombinedRealm</code> is used. (markt)
-      </add>
-      <fix>
-        <bug>62163</bug>: Correct the Tomcat Setup documentation that
-        incorrectly referred to Java 7 as the minimum version rather than Java
-        8. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <fix>
-        Add JMX support for <code>FragmentationInterceptor</code> in order to
-        prevent warning of startup. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="jdbc-pool">
-    <changelog>
-      <fix>
-        Ensure that <code>SQLWarning</code> has been cleared when connection
-        returns to the pool. (kfujino)
-      </fix>
-      <add>
-        Enable clearing of <code>SQLWarning</code> via JMX. (kfujino)
-      </add>
-      <fix>
-        Ensure that parameters have been cleared when
-        <code>PreparedStatement</code> and/or <code>CallableStatement</code> are
-        cached. (kfujino)
-      </fix>
-      <fix>
-        Enable PoolCleaner to be started even if <code>validationQuery</code>
-        is not set. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <update>
-        Update the build script so MD5 hashes are no longer generated for
-        releases as per the change in the ASF distribution policy. (markt)
-      </update>
-      <fix>
-        <bug>62164</bug>: Switch the build script to use TLS for downloads from
-        SourceForge and Maven Central to avoid failures due to HTTP to HTTPS
-        redirects. (markt)
-      </fix>
-      <add>
-        Always report the OS's umask when launching the JVM. (schultz)
-      </add>
-      <add>
-        Add managed connections package to the package renamed DBCP 2 to provide
-        a complete DBCP 2 in Tomcat. (remm)
-      </add>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.6 (markt)" rtext="2018-03-08">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>43866</bug>: Add additional attributes to the Manager to provide
-        control over which listeners are called when an attribute is added to
-        the session when it has already been added under the same name. This is
-        to aid clustering scenarios where <code>setAttribute()</code> is often
-        called to signal that the attribute value has been mutated and needs to
-        be replicated but it may not be required, or even desired, for the
-        associated listeners to be triggered. The default behaviour has not been
-        changed. (markt)
-      </fix>
-      <fix>
-        Minor optimization when calling class transformers. (rjung)
-      </fix>
-      <add>
-        Pass errors triggered by invalid requests or unavailable services to the
-        application provided error handling and/or the container provided error
-        handling (<code>ErrorReportValve</code>) as appropriate. (markt)
-      </add>
-      <add>
-        <bug>41007</bug>: Add the ability to specify static HTML responses for
-        specific error codes and/or exception types with the
-        <code>ErrorReportValve</code>. (markt)
-      </add>
-      <fix>
-        Prevent Tomcat from applying gzip compression to content that is already
-        compressed with brotli compression. Based on a patch provided by burka.
-        (markt)
-      </fix>
-      <fix>
-        <bug>62090</bug>: Null container names are not allowed. (remm)
-      </fix>
-      <fix>
-        <bug>62104</bug>: Fix programmatic login regression as the
-        NonLoginAuthenticator has to be set for it to work (if no login method
-        is specified). (remm)
-      </fix>
-      <fix>
-        <bug>62117</bug>: Improve error message in <code>catalina.sh</code> when
-        calling <code>kill -0 &lt;pid&gt;</code> fails. Based on a suggestion
-        from Mark Morschhaeuser. (markt)
-      </fix>
-      <fix>
-        <bug>62118</bug>: Correctly create a JNDI <code>ServiceRef</code> using
-        the specified interface rather than the concrete type. Based on a
-        suggestion by Ángel Álvarez Páscua. (markt)
-      </fix>
-      <fix>
-        Fix for <code>RequestDumperFilter</code> log attribute. Patch provided
-        by Kirill Romanov via Github. (violetagg)
-      </fix>
-      <fix>
-        <bug>62123</bug>: Avoid <code>ConcurrentModificationException</code>
-        when attempting to clean up application triggered RMI memory leaks on
-        web application stop. (markt)
-      </fix>
-      <add>
-        When a deployment descriptor is deployed that includes a
-        <code>path</code> attribute, log a warning that the <code>path</code>
-        attribute will be ignored. (markt)
-      </add>
-      <add>
-        When a deployment descriptor is deployed that references an external
-        <code>docBase</code> and, as a result, a <code>docBase</code> under the
-        <code>appBase</code> will be ignored, log a warning. (markt)
-      </add>
-      <fix>
-        Correct a regression in the fix for <bug>60276</bug> that meant that
-        compression was applied to all MIME types. Patch provided by Stefan
-        Knoblich. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <add>
-        Add async HTTP/2 parser for NIO2. (remm)
-      </add>
-      <fix>
-        Add minor HPACK fixes, based on fixes by Stuart Douglas. (remm)
-      </fix>
-      <fix>
-        <bug>61751</bug>: Follow up fix so that OpenSSL engine returns
-        underflow when unwrapping if no bytes were produced and the input is
-        empty. (remm)
-      </fix>
-      <fix>
-        Minor OpenSSL engine cleanups. (remm)
-      </fix>
-      <fix>
-        NIO SSL handshake should throw an exception on overflow status, like
-        NIO2 SSL. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <add>
-        <bug>47467</bug>: When deploying a web application via the manager
-        application and a path is not explicitly specified, derive it from the
-        provided deployment descriptor or, if that is not present, the WAR or
-        DIR. (markt)
-      </add>
-      <add>
-        <bug>48672</bug>: Add documentation for the Host Manager web
-        application. Patch provided by Marek Czernek. (markt)
-      </add>
-      <add>
-        Add support for specifying the application version when deploying an
-        application via the Manager application HTML interface. (markt)
-      </add>
-      <add>
-        Work-around a known, non-specification compliant behaviour in some
-        versions of IE that can allow XSS when the Manager application generates
-        a plain text response. Based on a suggestion from Muthukumar Marikani.
-        (markt)
-      </add>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.5 (markt)" rtext="2018-02-11">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Prevent a stack trace being written to standard out when running on Java
-        10 due to changes in the <code>LogManager</code> implementation. (markt)
-      </fix>
-      <fix>
-        Avoid duplicate load attempts if one has been made already. (remm)
-      </fix>
-      <fix>
-        Avoid NPE in ThreadLocalLeakPreventionListener if there is no Engine.
-        (remm)
-      </fix>
-      <fix>
-        <bug>62000</bug>: When a JNDI reference cannot be resolved, ensure that
-        the root cause exception is reported rather than swallowed. (markt)
-      </fix>
-      <fix>
-        <bug>62036</bug>: When caching an authenticated user Principal in the
-        session when the web application is configured with the
-        <code>NonLoginAuthenticator</code>, cache the internal Principal object
-        rather than the user facing Principal object as Tomcat requires the
-        internal object to correctly process later authorization checks. (markt)
-      </fix>
-      <add>
-        Refactor error handling to enable errors that occur before processing is
-        passed to the application to be handled by the application provided
-        error handling and/or the container provided error handling
-        (<code>ErrorReportValve</code>) as appropriate. (markt)
-      </add>
-      <add>
-        Pass 404 errors triggered by a missing ROOT web application to the
-        container error handling to generate the response body. (markt)
-      </add>
-      <add>
-        Pass 400 errors triggered by invalid request targets to the container
-        error handling to generate the response body. (markt)
-      </add>
-      <fix>
-        Provide a correct <code>Allow</code> header when responding to an HTTP
-        <code>TRACE</code> request for a JSP with a 405 status code. (markt)
-      </fix>
-      <fix>
-        When using Tomcat embedded, only perform Authenticator configuration
-        once during web application start. (markt)
-      </fix>
-      <fix>
-        <bug>62067</bug>: Correctly apply security constraints mapped to the
-        context root using a URL pattern of <code>&quot;&quot;</code>. (markt)
-      </fix>
-      <fix>
-        Process all <code>ServletSecurity</code> annotations at web application
-        start rather than at servlet load time to ensure constraints are applied
-        consistently. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        <bug>61751</bug>: Fix truncated request input streams when using NIO2
-        with TLS. (markt)
-      </fix>
-      <fix>
-        <bug>62023</bug>: Log error reporting multiple SSLHostConfig elements
-        when using the APR Connector instead of crashing Tomcat. (csutherl)
-      </fix>
-      <fix>
-        <bug>62032</bug>: Fix NullPointerException when certificateFile is not
-        defined on an SSLHostConfig and unify the behavior when a
-        certificateFile is defined but the file does not exist for both
-        JKS and PEM file types. (csutherl)
-      </fix>
-      <fix>
-        Ensure that the <code>toString()</code> method behaves consistently for
-        <code>ByteChunk</code> and <code>CharChunk</code> and that
-        <code>null</code> is returned when <code>toString()</code> is called
-        both on newly created objects and immediately after a call to
-        <code>recycle()</code>. This should not impact typical Tomcat users. It
-        may impact users who use these classes directly in their own code.
-        (markt)
-      </fix>
-      <fix>
-        Ensure that the <code>toString()</code>, <code>toBytes()</code> and
-        <code>toChars()</code> methods of <code>MessageBytes</code> behave
-        consistently and do not throw a <code>NullPointerException</code> both
-        on newly created objects and immediately after a call to
-        <code>recycle()</code>. This should not impact typical Tomcat users. It
-        may impact users who use these classes directly in their own code.
-        (markt)
-      </fix>
-      <fix>
-        When processing an HTTP 1.0 request in the HTTP connector and no host
-        information is provided in the request, obtain the server port from the
-        local port rather than the connector configuration since the configured
-        value maybe zero. (markt)
-      </fix>
-      <add>
-        Enable strict validation of the provided host name and port for all
-        connectors. Requests with invalid host names and/or ports will be
-        rejected with a 400 response. (markt)
-      </add>
-      <fix>
-        Update the host validation to permit host names and components of domain
-        names (excluding top-level domains) to start with a number and to ensure
-        that top-level domains are fully alphabetic. (markt)
-      </fix>
-      <fix>
-        <bug>62053</bug>: Fix NPE when writing push headers with HTTP/2 NIO2.
-        Patch submitted by Holger Sunke. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        Include an HTTP <code>Allow</code> header when a JSP generates a
-        405 response due to a request with an unsupported method. (markt)
-      </fix>
-      <add>
-        Add support for the HTTP <code>OPTION</code> method to JSPs. The
-        JSP specification explicitly states that the behaviour for this
-        method is undefined for JSPs so this is a Tomcat specific
-        behaviour. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <fix>
-        <bug>62024</bug>: When closing a connection with an abnormal close,
-        close the socket immediately rather than waiting for a close message
-        from the client that may never arrive. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Webapps">
-    <changelog>
-      <fix>
-        <bug>62049</bug>: Fix missing class from manager 404 JSP error page.
-        (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="jdbc-pool">
-    <changelog>
-      <add>
-        Enhance the JMX support for jdbc-pool in order to expose
-        <code>PooledConnection</code> and <code>JdbcInterceptors</code>.
-        (kfujino)
-      </add>
-      <add>
-        Add MBean for <code>PooledConnection</code>. (kfujino)
-      </add>
-      <add>
-        <bug>62011</bug>: Add MBean for <code>StatementCache</code>. (kfujino)
-      </add>
-      <add>
-        Expose the cache size for each connection via JMX in
-        <code>StatementCache</code>. (kfujino)
-      </add>
-      <add>
-        Add MBean for <code>ResetAbandonedTimer</code>. (kfujino)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <update>
-        Update the list with the public interfaces in the RELEASE-NOTES.
-        (violetagg)
-      </update>
-      <update>
-        Update the NSIS Installer used to build the Windows installer to version
-        3.03. (kkolinko)
-      </update>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.4 (markt)" rtext="2018-01-22">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Correct a regression in the previous fix for <bug>61916</bug> that meant
-        that any call to <code>addHeader()</code> would have been replaced with
-        a call to <code>setHeader()</code> for all requests mapped to the
-        <code>AddDefaultCharsetFilter</code>. (markt)
-      </fix>
-      <fix>
-        <bug>61999</bug>: maxSavePostSize set to 0 should disable saving POST
-        data during authentication. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Fix NIO2 HTTP/2 sendfile. (remm)
-      </fix>
-      <fix>
-        <bug>61993</bug>: Improve handling for <code>ByteChunk</code> and
-        <code>CharChunk</code> instances that grow close to the maximum size
-        allowed by the JRE. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <add>
-        <bug>43925</bug>: Add a new system property
-        (<code>org.apache.jasper.runtime.BodyContentImpl.BUFFER_SIZE</code>) to
-        control the size of the buffer used by Jasper when buffering tag bodies.
-        (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        <bug>62006</bug>: Document the new <code>JvmOptions9</code> command line
-        parameter for <code>tomcat9.exe</code>. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.3 (markt)" rtext="not released">
-  <subsection name="Catalina">
-    <changelog>
-      <add>
-        <bug>57619</bug>: Implement a small optimisation to how JAR URLs are
-        processed to reduce the storage of duplicate String objects in memory.
-        Patch provided by Dmitri Blinov. (markt)
-      </add>
-      <fix>
-        Add some missing NPEs to ServletContext. (remm)
-      </fix>
-      <fix>
-        Update the Java EE 8 XML schema to the released versions. (markt)
-      </fix>
-      <fix>
-        Minor HTTP/2 push fixes. (remm)
-      </fix>
-      <fix>
-        <bug>61916</bug>: Extend the <code>AddDefaultCharsetFilter</code> to add
-        a character set when the content type is set via
-        <code>setHeader()</code> or <code>addHeader()</code> as well as when it
-        is set via <code>setContentType()</code>. (markt)
-      </fix>
-      <fix>
-        When using WebDAV to copy a file resource to a destination that requires
-        a collection to be overwritten, ensure that the operation succeeds
-        rather than fails (with a 500 response). This enables Tomcat to pass two
-        additional tests from the Litmus WebDAV test suite. (markt)
-      </fix>
-      <update>
-        Modify the Default and WebDAV Servlets so that a 405 status code is
-        returned for <code>PUT</code> and <code>DELETE</code> requests when
-        disabled via the <code>readonly</code> initialisation parameter.
-      </update>
-      <fix>
-        Align the contents of the <code>Allow</code> header with the response
-        code for the Default and WebDAV Servlets. For any given resource a
-        method that returns a 405 status code will not be listed in the
-        <code>Allow</code> header and a method listed in the <code>Allow</code>
-        header will not return a 405 status code. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <add>
-        <bug>60276</bug>: Implement GZIP compression support for responses
-        served over HTTP/2. (markt)
-      </add>
-      <fix>
-        Do not call onDataAvailable without any data to read. (remm)
-      </fix>
-      <fix>
-        Correctly handle EOF when <code>ServletInputStream.isReady()</code> is
-        called. (markt)
-      </fix>
-      <fix>
-        <bug>61886</bug>: Log errors on non-container threads at
-        <code>DEBUG</code> rather than <code>INFO</code>. The exception will be
-        made available to the application via the asynchronous error handling
-        mechanism. (markt)
-      </fix>
-      <fix>
-        <bug>61914</bug>: Possible NPE with Java 9 when creating a SSL engine.
-        Patch submitted by Evgenij Ryazanov. (remm)
-      </fix>
-      <fix>
-        <bug>61918</bug>: Fix connectionLimitLatch counting when closing an
-        already closed socket. Based on a patch by Ryan Fong. (remm)
-      </fix>
-      <add>
-        Add support for the OpenSSL ARIA ciphers to the OpenSSL to JSSE
-        cipher mapping. (markt)
-      </add>
-      <fix>
-        <bug>61932</bug>: Allow a call to <code>AsyncContext.dispatch()</code>
-        to terminate non-blocking I/O. (markt)
-      </fix>
-      <fix>
-        <bug>61948</bug>: Improve the handling of malformed ClientHello messages
-        in the code that extracts the SNI information from a TLS handshake for
-        the JSSE based NIO and NIO2 connectors. (markt)
-      </fix>
-      <fix>
-        Fix NIO2 handshaking with a full input buffer. (remm)
-      </fix>
-      <add>
-        Return a simple, plain text error message if a client attempts to make a
-        plain text HTTP connection to a TLS enabled NIO or NIO2 Connector.
-        (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        <bug>61854</bug>: When using sets and/or maps in EL expressions, ensure
-        that Jasper correctly parses the expression. Patch provided by Ricardo
-        Martin Camarero. (markt)
-      </fix>
-      <fix>
-        Improve the handling of methods with varargs in EL expressions. In
-        particular, the calling of a varargs method with no parameters now works
-        correctly. Based on a patch by Nitkalya (Ing) Wiriyanuparb. (markt)
-      </fix>
-      <fix>
-        <bug>61945</bug>: Fix prototype mode used to compile tags. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <add>
-        <bug>61223</bug>: Add the mbeans-descriptors.dtd file to the custom
-        MBean documentation so users have a reference to use when constructing
-        mbeans-descriptors.xml files for custom components. (markt)
-      </add>
-      <add>
-        <bug>61565</bug>: Add the ability to trigger a reloading of TLS host
-        configuration (certificate and key files, server.xml is not re-parsed)
-        via the Manager web application. (markt)
-      </add>
-      <add>
-        <bug>61566</bug>: Expose the currently in use certificate chain and list
-        of trusted certificates for all virtual hosts configured using the JSSE
-        style (keystore) TLS configuration via the Manager web application.
-        (markt)
-      </add>
-      <fix>
-        Partial fix for <bug>61886</bug>. Ensure that multiple threads do not
-        attempt to complete the <code>AsyncContext</code> if an I/O error occurs
-        in the stock ticker example Servlet. (markt)
-      </fix>
-      <fix>
-        <bug>61886</bug>: Prevent <code>ConcurrentModificationException</code>
-        when running the asynchronous stock ticker in the examples web
-        application. (markt)
-      </fix>
-      <fix>
-        <bug>61886</bug>: Prevent <code>NullPointerException</code> and other
-        errors if the stock ticker example is running when the examples web
-        application is stopped. (markt)
-      </fix>
-      <fix>
-        <bug>61910</bug>: Clarify the meaning of the <code>allowLinking</code>
-        option in the documentation web application. (markt)
-      </fix>
-      <add>
-        Add OCSP configuration information to the SSL How-To. Patch provided by
-        Marek Czernek. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="jdbc-pool">
-    <changelog>
-      <fix>
-        <bug>61312</bug>: Prevent <code>NullPointerException</code> when using
-        the statement cache of connection that has been closed. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Add an additional system property for the system property replacement.
-        (remm)
-      </fix>
-      <fix>
-        Add missing SHA-512 hash for release artifacts to the build script.
-        (markt)
-      </fix>
-      <update>
-        Update the internal fork of Commons Pool 2 to 2.4.3. (markt)
-      </update>
-      <update>
-        Update the internal fork of Commons DBCP 2 to 8a71764 (2017-10-18) to
-        pick up some bug fixes and enhancements. (markt)
-      </update>
-      <update>
-        Update the internal fork of Commons FileUpload to 6c00d57 (2017-11-23)
-        to pick up some code clean-up. (markt)
-      </update>
-      <update>
-        Update the internal fork of Commons Codec to r1817136 to pick up some
-        code clean-up. (markt)
-      </update>
-      <fix>
-        The native source bundles (for Commons Daemon and Tomcat Native) are no
-        longer copied to the bin directory for the deploy target. They are now
-        only copied to the bin directory for the release target. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.2 (markt)" rtext="2017-11-30">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Fix possible <code>SecurityException</code> when using TLS related
-        request attributes. (markt)
-      </fix>
-      <fix>
-        <bug>61597</bug>: Extend the <code>StandardJarScanner</code> to scan
-        JARs on the module path when running on Java 9 and class path scanning
-        is enabled. (markt)
-      </fix>
-      <fix>
-        <bug>61601</bug>: Add support for multi-release JARs in JAR scanning and
-        web application class loading. (markt)
-      </fix>
-      <fix>
-        <bug>61681</bug>: Allow HTTP/2 push when using request wrapping. (remm)
-      </fix>
-      <add>
-        Provide the <code>SessionInitializerFilter</code> that can be used to
-        ensure that an HTTP session exists when initiating a WebSocket
-        connection. Patch provided by isapir. (markt)
-      </add>
-      <fix>
-        <bug>61682</bug>: When re-prioritising HTTP/2 streams, ensure that both
-        parent and children fields are correctly updated to avoid a possible
-        <code>StackOverflowError</code>. (markt)
-      </fix>
-      <fix>
-        Improve concurrency by reducing the scope of the synchronisation for
-        <code>javax.security.auth.message.config.AuthConfigFactory</code> in the
-        JASPIC API implementation. Based on a patch by Pavan Kumar. (markt)
-      </fix>
-      <fix>
-        Avoid a possible <code>NullPointerException</code> when timing out
-        <code>AsyncContext</code> instances during shut down. (markt)
-      </fix>
-      <fix>
-        <bug>61777</bug>: Avoid a <code>NullPointerException</code> when
-        detaching a JASPIC <code>RegistrationListener</code>. Patch provided by
-        Lazar. (markt)
-      </fix>
-      <fix>
-        <bug>61778</bug>: Correct the return value when detaching a JASPIC
-        <code>RegistrationListener</code>. Patch provided by Lazar. (markt)
-      </fix>
-      <fix>
-        <bug>61779</bug>: Avoid a <code>NullPointerException</code> when a
-        <code>null</code> <code>RegistrationListener</code> is passed to
-        <code>AuthConfigFactory.getConfigProvider()</code>. Patch provided by
-        Lazar. (markt)
-      </fix>
-      <fix>
-        <bug>61780</bug>: Only include the default JASPIC registration ID in the
-        return value for a call to
-        <code>AuthConfigFactory.getRegistrationIDs()</code> if a
-        <code>RegistrationContext</code> has been registered using the default
-        registration ID. Patch provided by Lazar. (markt)
-      </fix>
-      <fix>
-        <bug>61781</bug>: Enable JASPIC provider registrations to be persisted
-        when the layer and/or application context are <code>null</code>. Patch
-        provided by Lazar. (markt)
-      </fix>
-      <fix>
-        <bug>61782</bug>: When calling
-        <code>AuthConfigFactory.doRegisterConfigProvider()</code> and the
-        requested JASPIC config provider class is found by the web application
-        class loader, do not attempt to load the class with the class loader
-        that loaded the JASPIC API. Patch provided by Lazar. (markt)
-      </fix>
-      <fix>
-        <bug>61783</bug>: When calling
-        <code>AuthConfigFactory.removeRegistration()</code> and the registration
-        is persistent, it should be removed from the persistent store. Patch
-        provided by Lazar. (markt)
-      </fix>
-      <fix>
-        <bug>61784</bug>: Correctly handle the case when
-        <code>AuthConfigFactoryImpl.registerConfigProvider()</code> is called
-        with a provider name of <code>null</code>. Patch provided by Lazar.
-        (markt)
-      </fix>
-      <add>
-        <bug>61795</bug>: Add a property to the <code>Authenticator</code>
-        implementations to enable a custom JASPIC <code>CallbackHandler</code>
-        to be specified. Patch provided by Lazar. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        <bug>61568</bug>: Avoid a potential <code>SecurityException</code> when
-        using the NIO2 connector and a new thread is added to the pool. (markt)
-      </fix>
-      <fix>
-        <bug>61583</bug>: Correct a further regression in the fix to enable the
-        use of Java key stores that contained multiple keys that did not all
-        have the same password. This fixes PKCS11 key store handling with
-        multiple keys selected with an alias. (markt)
-      </fix>
-      <fix>
-        Improve NIO2 syncing for async IO operations. (remm)
-      </fix>
-      <add>
-        Sendfile support for HTTP/2 and NIO2. (remm)
-      </add>
-      <fix>
-        Reduce default HTTP/2 stream concurrent execution within a connection
-        from 200 to 20. (remm)
-      </fix>
-      <fix>
-        <bug>61668</bug>: Avoid a possible NPE when calling
-        <code>AbstractHttp11Protocol.getSSLProtocol()</code>. (markt)
-      </fix>
-      <fix>
-        <bug>61673</bug>: Avoid a possible
-        <code>ConcurrentModificationException</code> when working with the
-        streams associated with a connection. (markt)
-      </fix>
-      <fix>
-        <bug>61719</bug>: Avoid possible NPE calling
-        InputStream.setReadListener with HTTP/2. (remm)
-      </fix>
-      <fix>
-        <bug>61736</bug>: Improve performance of NIO connector when clients
-        leave large time gaps between network packets. Patch provided by Zilong
-        Song. (markt)
-      </fix>
-      <fix>
-        <bug>61740</bug>: Correct an off-by-one error in the Hpack header index
-        validation that caused intermittent request failures when using HTTP/2.
-        (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        <bug>61604</bug>: Fix SMAP generation for JSPs that generate no output.
-        (markt)
-      </fix>
-      <fix>
-        <bug>61816</bug>: Invalid expressions in attribute values or template
-        text should trigger a translation (compile time) error, not a run time
-        error. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <fix>
-        <bug>61604</bug>: Add support for authentication in the websocket
-        client. Patch submitted by J Fernandez. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        Correct Javadoc links to point to Java SE 8 and Java EE 8. (markt)
-      </fix>
-      <fix>
-        Enable Javadoc to be built with Java 9. (markt)
-      </fix>
-      <fix>
-        <bug>61603</bug>: Add XML filtering for the status servlet output where
-        needed. (remm)
-      </fix>
-      <fix>
-        Correct the description of how the CGI servlet maps a request to a
-        script in the CGI How-To. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <fix>
-        Fix incorrect behavior that attempts to resend channel messages more
-        than the actual setting value of <code>maxRetryAttempts</code>.
-        (kfujino)
-      </fix>
-      <fix>
-        Ensure that the remaining Sender can send channel messages by avoiding
-        unintended <code>ChannelException</code> caused by comparing the number
-        of failed members and the number of remaining Senders. (kfujino)
-      </fix>
-      <fix>
-        Ensure that remaining SelectionKeys that were not handled by throwing a
-        <code>ChannelException</code> during SelectionKey processing are
-        handled. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Improve the fix for <bug>61439</bug> and exclude the JPA, JAX-WS and EJB
-        annotations completely from the Tomcat distributions. (markt)
-      </fix>
-      <fix>
-        Improve handling of endorsed directories. The endorsed directory
-        mechanism will only be used if the <code>JAVA_ENDORSED_DIRS</code>
-        system property is explicitly set or if
-        <code>$CATALINA_HOME/endorsed</code> exists. When running on Java 9, any
-        such attempted use of the endorsed directory mechanism will trigger an
-        error and Tomcat will fail to start. (rjung)
-      </fix>
-      <add>
-        <bug>51496</bug>: When using the Windows installer, check if the
-        requested service name already exists and, if it does, prompt the user
-        to select an alternative service name. Patch provided by Ralph
-        Plawetzki. (markt)
-      </add>
-      <fix>
-        <bug>61590</bug>: Enable <code>service.bat</code> to recognise when
-        <code>JAVA_HOME</code> is configured for a Java 9 JDK. (markt)
-      </fix>
-      <fix>
-        <bug>61598</bug>: Update the Windows installer to search the new (as of
-        Java 9) registry locations when looking for a JRE. (markt)
-      </fix>
-      <add>
-        Add generation of a SHA-512 hash for release artifacts to the build
-        script. (markt)
-      </add>
-      <fix>
-        <bug>61658</bug>: Update MIME mappings for fonts to use
-        <code>font/*</code> as per RFC8081. (markt)
-      </fix>
-      <update>
-        Update the packaged version of the Tomcat Native Library to 1.2.16 to
-        pick up the latest Windows binaries built with APR 1.6.3 and OpenSSL
-        1.0.2m. (markt)
-      </update>
-      <update>
-        Update the NSIS Installer used to build the Windows installer to version
-        3.02.1. (kkolinko)
-      </update>
-      <update>
-        Update the Windows installer to use "The Apache Software Foundation" as
-        the Publisher when Tomcat is displayed in the list of installed
-        applications in Microsoft Windows. (kkolinko)
-      </update>
-      <fix>
-        <bug>61803</bug>: Remove outdated SSL information from the Security
-        documentation. (remm)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.1 (markt)" rtext="2017-09-30">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Use the correct path when loading the JVM <code>logging.properties</code>
-        file for Java 9. (rjung)
-      </fix>
-      <fix>
-        Add additional validation to the resource handling required to fix
-        CVE-2017-12617 on Windows. The checks were being performed elsewhere but
-        adding them to the resource handling ensures that the checks are always
-        performed. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        <bug>61563</bug>: Correct typos in Spanish translation. Patch provided by
-        Gonzalo Vásquez. (csutherl)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0 (markt)" rtext="not released">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>61542</bug>: Fix CVE-2017-12617 and prevent JSPs from being
-        uploaded via a specially crafted request when HTTP PUT was enabled.
-        (markt)
-      </fix>
-      <fix>
-        <bug>61554</bug>: Exclude test files in unusual encodings and markdown
-        files intended for display in GitHub from RAT analysis. Patch provided
-        by Chris Thistlethwaite. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <add>
-        <bug>60762</bug>: Add the ability to make changes to the TLS
-        configuration of a connector at runtime without having to restart the
-        Connector. (markt)
-      </add>
-      <add>
-        Add an option to reject requests that contain HTTP headers with invalid
-        (non-token) header names with a 400 response and reject such requests by
-        default. (markt)
-      </add>
-      <fix>
-        Implement the requirements of RFC 7230 (and RFC 2616) that HTTP/1.1
-        requests must include a <code>Host</code> header and any request that
-        does not must be rejected with a 400 response. (markt)
-      </fix>
-      <fix>
-        Implement the requirements of RFC 7230 that any HTTP/1.1 request that
-        specifies a host in the request line, must specify the same host in the
-        <code>Host</code> header and that any such request that does not, must
-        be rejected with a 400 response. This check is optional but enabled by
-        default. It may be disabled with the
-        <code>allowHostHeaderMismatch</code> attribute of the Connector. (markt)
-      </fix>
-      <fix>
-        Implement the requirements of RFC 7230 that any HTTP/1.1 request that
-        contains multiple <code>Host</code> headers is rejected with a 400
-        response. (markt)
-      </fix>
-      <update>
-        Add a way to set the property source in embedded mode. (remm)
-      </update>
-      <fix>
-        <bug>61557</bug>: Correct a further regression in the fix to enable the
-        use of Java key stores that contain multiple keys that do not all have
-        the same password. The regression broke support for some FIPS compliant
-        key stores. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="jdbc-pool">
-    <changelog>
-      <fix>
-        <bug>61545</bug>: Correctly handle invocations of methods defined in the
-        <code>PooledConnection</code> interface when using pooled XA
-        connections. Patch provided by Nils Winkler. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Update fix for <bug>59904</bug> so that values less than zero are accepted
-        instead of throwing a NegativeArraySizeException. (remm)
-      </fix>
-      <add>
-        Complete the implementation of the Servlet 4.0 specification. (markt)
-      </add>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M27 (markt)" rtext="2017-09-19">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Before generating an error page in the <code>ErrorReportValve</code>,
-        check to see if I/O is still permitted for the associated connection
-        before generating the error page so that the page generation can be
-        skipped if the page is never going to be sent. (markt)
-      </fix>
-      <add>
-        <bug>61189</bug>: Add the ability to set environment variables for
-        individual CGI scripts. Based on a patch by jm009. (markt)
-      </add>
-      <fix>
-        <bug>61210</bug>: When running under a SecurityManager, do not print a
-        warning about not being able to read a logging configuration file when
-        that file does not exist. (markt)
-      </fix>
-      <add>
-        <bug>61280</bug>: Add RFC 7617 support to the
-        <code>BasicAuthenticator</code>. Note that the default configuration
-        does not change the existing behaviour. (markt)
-      </add>
-      <fix>
-        <bug>61424</bug>: Avoid a possible <code>StackOverflowError</code> when
-        running under a <code>SecurityManager</code> and using
-        <code>Subject.doAs()</code>. (markt)
-      </fix>
-      <add>
-        When running under Java 9 or later, and the
-        <code>urlCacheProtection</code> option of the
-        <code>JreMemoryLeakPreventionListener</code> is enabled, use the API
-        added in Java 9 to only disable the caching for JAR URL connections.
-        (markt)
-      </add>
-      <add>
-        <bug>61489</bug>: When using the CGI servlet, make the generation of
-        command line arguments from the query string (as per section 4.4 of RFC
-        3875) optional and disabled by default. Based on a patch by jm009.
-        (markt)
-      </add>
-      <fix>
-        <bug>61503</bug>: This corrects a potential regression in the fix for
-        <bug>60940</bug> with an alternative solution that adds the
-        <code>JarEntry</code> objects normally skipped by a
-        <code>JarInputStream</code> only if those entries exist. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <update>
-        The minimum required Tomcat Native version has been increased to 1.2.14.
-        This version includes a new API needed for correct client certificate
-        support when using a Java connector with OpenSSL TLS implementation and
-        support for the <code>SSL_CONF</code> OpenSSL API. (rjung)
-      </update>
-      <add>
-        Add support for the OpenSSL <code>SSL_CONF</code> API when using
-        TLS with OpenSSL implementation. It can be used by adding
-        <code>OpenSSLConf</code> elements underneath <code>SSLHostConfig</code>.
-        The new element contains a list of <code>OpenSSLConfCmd</code> elements,
-        each with the attributes <code>name</code> and <code>value</code>.
-        (rjung)
-      </add>
-      <fix>
-        When using a Java connector in combination with the OpenSSL TLS
-        implementation, do not configure each SSL connection object via
-        the OpenSSLEngine. For OpenSSL the SSL object inherits its
-        settings from the SSL_CTX which we have already configured.
-        (rjung)
-      </fix>
-      <fix>
-        When using JSSE TLS configuration with the OpenSSL implementation and
-        client certificates: include client CA subjects in the TLS handshake
-        so that the client can choose an appropriate client certificate to
-        present. (rjung)
-      </fix>
-      <fix>
-        If an invalid option is specified for the
-        <code>certificateVerification</code> attribute of an
-        <code>SSLHostConfig</code> element, treat it as <code>required</code>
-        which is the most secure / restrictive option in addition to reporting
-        the configuration error. (markt)
-      </fix>
-      <fix>
-        Improve the handling of client disconnections during the TLS
-        renegotiation handshake. (markt)
-      </fix>
-      <fix>
-        Prevent exceptions being thrown during normal shutdown of NIO
-        connections. This enables TLS connections to close cleanly. (markt)
-      </fix>
-      <fix>
-        Fix possible race condition when setting IO listeners on an upgraded
-        connection. (remm)
-      </fix>
-      <fix>
-        Ensure that the APR/native connector uses blocking I/O for TLS
-        renegotiation. (markt)
-      </fix>
-      <fix>
-        <bug>48655</bug>: Enable Tomcat to shutdown cleanly when using sendfile,
-        the APR/native connector and a multi-part download is in progress.
-        (markt)
-      </fix>
-      <fix>
-        <bug>58244</bug>: Handle the case when OpenSSL resumes a TLS session
-        using a ticket and the full client certificate chain is not available.
-        In this case the client certificate without the chain will be presented
-        to the application. (markt)
-      </fix>
-      <fix>
-        Improve the warning message when JSSE and OpenSSL configuration styles
-        are mixed on the same <code>SSLHostConfig</code>. (markt)
-      </fix>
-      <fix>
-        <bug>61415</bug>: Fix TLS renegotiation with OpenSSL based connections
-        and session caching. (markt)
-      </fix>
-      <fix>
-        Delay checking that the configured attributes for an
-        <code>SSLHostConfig</code> instance are consistent with the configured
-        SSL implementation until <code>Connector</code> start to avoid incorrect
-        warnings when the SSL implementation changes during initialisation.
-        (markt)
-      </fix>
-      <fix>
-        <bug>61450</bug>: Fix default key alias algorithm. (remm)
-      </fix>
-      <fix>
-        <bug>61451</bug>: Correct a regression in the fix to enable the use of
-        Java key stores that contained multiple keys that did not all have the
-        same password. The regression broke support for any key store that did
-        not store keys in PKCS #8 format such as hardware key stores and Windows
-        key stores. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <fix>
-        <bug>60523</bug>: Reduce the number of packets used to send WebSocket
-        messages by not flushing between the header and the payload when the
-        two are written together. (markt)
-      </fix>
-      <fix>
-        <bug>61491</bug>: When using the <code>permessage-deflate</code>
-        extension, correctly handle the sending of empty messages after
-        non-empty messages to avoid the <code>IllegalArgumentException</code>.
-        (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        Show connector cipher list in the manager web application in the
-        correct cipher order. (rjung)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <fix>
-        To avoid unexpected session timeout notification from backup session,
-        update the access time when receiving the map member notification
-        message. (kfujino)
-      </fix>
-      <fix>
-        Add member info to the log message when the failure detection check
-        fails in <code>TcpFailureDetector</code>. (kfujino)
-      </fix>
-      <fix>
-        Avoid Ping timeout until the added map member by receiving
-        <code>MSG_START</code> message is completely started. (kfujino)
-      </fix>
-      <fix>
-        When sending a channel message, make sure that the Sender has connected.
-        (kfujino)
-      </fix>
-      <fix>
-        Correct the backup node selection logic that node 0 is returned twice
-        consecutively. (kfujino)
-      </fix>
-      <fix>
-        Fix race condition of <code>responseMap</code> in
-        <code>RpcChannel</code>. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="jdbc-pool">
-    <changelog>
-      <fix>
-        <bug>61391</bug>: Ensure that failed queries are logged if the
-        <code>SlowQueryReport</code> interceptor is configured to do so and the
-        connection has been abandoned. Patch provided by Craig Webb. (markt)
-      </fix>
-      <fix>
-        <bug>61425</bug>: Ensure that transaction of idle connection has
-        terminated  when the <code>testWhileIdle</code> is set to
-        <code>true</code> and <code>defaultAutoCommit</code> is set to
-        <code>false</code>. Patch provided by WangZheng. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        <bug>61419</bug>: Replace a Unix style comment in the DOS bat file
-        <code>catalina.bat</code> with the correct <code>rem</code> markup.
-        (rjung)
-      </fix>
-      <fix>
-        <bug>61439</bug>: Remove the Java Annotation API classes from
-        tomcat-embed-core.jar and package them in a separate JAR in the
-        embedded distribution to provide end users with greater flexibility to
-        handle potential conflicts with the JRE and/or other JARs. (markt)
-      </fix>
-      <fix>
-        <bug>61441</bug>: Improve the detection of <code>JAVA_HOME</code> by the
-        <code>daemon.sh</code> script when running on a platform where Java has
-        been installed from an RPM. (rjung)
-      </fix>
-      <update>
-        Update the packaged version of the Tomcat Native Library to 1.2.14 to
-        pick up the latest Windows binaries built with APR 1.6.2 and OpenSSL
-        1.0.2l. (markt)
-      </update>
-      <update>
-        <bug>61599</bug>: Update to Commons Daemon 1.1.0 for improved Java 9
-        support. (markt)
-      </update>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M26 (markt)" rtext="2017-08-08">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Correct multiple regressions in the fix for <bug>49464</bug> that could
-        corrupt static content served by the <code>DefaultServlet</code>.(markt)
-      </fix>
-      <fix>
-        Correct a bug in the <code>PushBuilder</code> implementation that
-        meant push URLs containing <code>%nn</code> sequences were not correctly
-        decoded. Identified by FindBugs. (markt)
-      </fix>
-      <add>
-        <bug>61164</bug>: Add support for the <code>%X</code> pattern in the
-        <code>AccessLogValve</code> that reports the connection status at the
-        end of the request. Patch provided by Zemian Deng. (markt)
-      </add>
-      <fix>
-        <bug>61351</bug>: Correctly handle %nn decoding of URL patterns in
-        web.xml and similar locations that may legitimately contain characters
-        that are not permitted by RFC 3986. (markt)
-      </fix>
-      <add>
-        <bug>61366</bug>: Add a new attribute, <code>localDataSource</code>, to
-        the <code>JDBCStore</code> that allows the Store to be configured to use
-        a DataSource defined by the web application rather than the default of
-        using a globally defined DataSource. Patch provided by Jonathan
-        Horowitz. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        <bug>61086</bug>: Ensure to explicitly signal an empty request body for
-        HTTP 205 responses. Additional fix to r1795278. Based on a patch
-        provided by Alexandr Saperov. (violetagg)
-      </fix>
-      <update>
-        <bug>61345</bug>: Add a server listener that can be used to do system
-        property replacement from the property source configured in the
-        digester. (remm)
-      </update>
-      <add>
-        Add additional logging to record problems that occur while waiting for
-        the NIO pollers to stop during the Connector stop process. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        <bug>61364</bug>: Ensure that files are closed after detecting encoding
-        of JSPs so that files do not remain locked by the file system. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <add>
-        <bug>57767</bug>: Add support to the WebSocket client for following
-        redirects when attempting to establish a WebSocket connection. Patch
-        provided by J Fernandez. (markt)
-      </add>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M25 (markt)" rtext="2017-07-28">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Performance improvements for service loader look-ups (and look-ups of
-        other class loader resources) when the web application is deployed in a
-        packed WAR file. (markt)
-      </fix>
-      <fix>
-        <bug>60963</bug>: Add <code>ExtractingRoot</code>, a new
-        <code>WebResourceRoot</code> implementation that extracts JARs to the
-        work directory for improved performance when deploying packed WAR files.
-        (markt)
-      </fix>
-      <fix>
-        <bug>61253</bug>: Add warn message when Digester.updateAttributes
-        throws an exception instead of ignoring it. (csutherl)
-      </fix>
-      <fix>
-        Correct a further regression in the fix for <bug>49464</bug> that could
-        cause an byte order mark character to appear at the start of content
-        included by the <code>DefaultServlet</code>. (markt)
-      </fix>
-      <fix>
-        <bug>61313</bug>: Make the read timeout configurable in the
-        <code>JNDIRealm</code> and ensure that a read timeout will result in an
-        attempt to fail over to the alternateURL. Based on patches by Peter
-        Maloney and Felix Schumacher. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        Correct the documentation for how <code>StandardRoot</code> is
-        configured. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        <bug>61316</bug>: Fix corruption of UTF-16 encoded source files in
-        released source distributions. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M24 (markt)" rtext="not released">
-  <subsection name="Catalina">
-    <changelog>
-      <add>
-        <bug>52924</bug>: Add support for a Tomcat specific deployment
-        descriptor, <code>/WEB-INF/tomcat-web.xml</code>. This descriptor has an
-        identical format to <code>/WEB-INF/web.xml</code>. The Tomcat descriptor
-        takes precedence over any settings in <code>conf/web.xml</code> but does
-        not take precedence over any settings in <code>/WEB-INF/web.xml</code>.
-        (markt)
-      </add>
-      <fix>
-        <bug>61232</bug>: When log rotation is disabled only one separator will
-        be used when generating the log file name. For example if the prefix is
-        <code>catalina.</code> and the suffix is <code>.log</code> then the log
-        file name will be <code>catalina.log</code> instead of
-        <code>catalina..log</code>. Patch provided by Katya Stoycheva.
-        (violetagg)
-      </fix>
-      <fix>
-        <bug>61264</bug>: Correct a regression in the refactoring to use
-        <code>Charset</code> rather than <code>String</code> to store request
-        character encoding that prevented <code>getReader()</code> throwing an
-        <code>UnsupportedEncodingException</code> if the user agent specifies
-        an unsupported character encoding. (markt)
-      </fix>
-      <fix>
-        Correct a regression in the fix for <bug>49464</bug> that could cause an
-        incorrect <code>Content-Length</code> header to be sent by the
-        <code>DefaultServlet</code> if the encoding of a static is not
-        consistent with the encoding of the response. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Enable TLS connectors to use Java key stores that contain multiple keys
-        where each key has a separate password. Based on a patch by Frank
-        Taffelt. (markt)
-      </fix>
-      <fix>
-        Improve the handling of HTTP/2 stream resets due to excessive headers
-        when a continuation frame is used. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <add>
-        <bug>53031</bug>: Add support for the <code>fork</code> option when
-        compiling JSPs with the Jasper Ant task and javac. (markt)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <add>
-        <bug>52791</bug>: Add the ability to set the defaults used by the
-        Windows installer from a configuration file. Patch provided by Sandra
-        Madden. (markt)
-      </add>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M23 (markt)" rtext="not released">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>49464</bug>: Improve the Default Servlet's handling of static files
-        when the file encoding is not compatible with the required response
-        encoding. (markt)
-      </fix>
-      <fix>
-        <bug>61214</bug>: Remove deleted attribute <code>servlets</code> from
-        the Context MBean description. Patch provided by Alexis Hassler. (markt)
-      </fix>
-      <fix>
-        <bug>61215</bug>: Correctly define <code>addConnectorPort</code> and
-        <code>invalidAuthenticationWhenDeny</code> in the
-        <code>mbean-descriptors.xml</code> file for the
-        <code>org.apache.catalina.valves</code> package so that the attributes
-        are accessible via JMX. (markt)
-      </fix>
-      <fix>
-        <bug>61216</bug>: Improve layout for <code>CompositeData</code> and
-        <code>TabularData</code> when viewing via the JMX proxy servlet. Patch
-        provided by Alexis Hassler. (markt)
-      </fix>
-      <fix>
-        Additional permission for deleting files is granted to JULI as it is
-        required by FileHandler when running under a Security Manager. The
-        thread that cleans the log files is marked as daemon thread.
-        (violetagg)
-      </fix>
-      <fix>
-        <bug>61229</bug>: Correct a regression in 9.0.0.M21 that broke WebDAV
-        handling for resources with names that included a <code>&amp;</code>
-        character. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Restore the ability to configure support for SSLv3. Enabling this
-        protocol will trigger a warning in the logs since it is known to be
-        insecure. (markt)
-      </fix>
-      <add>
-        Add LoadBalancerDrainingValve, a Valve designed to reduce the amount of
-        time required for a node to drain its authenticated users. (schultz)
-      </add>
-      <fix>
-        Do not log a warning when a <code>null</code> session is returned for an
-        OpenSSL based TLS session since this is expected when session tickets
-        are enabled. (markt)
-      </fix>
-      <fix>
-        When the access log valve logs a TLS related request attribute and the
-        NIO2 connector is used with OpenSSL, ensure that the TLS attributes are
-        available to the access log valve when the connection is closing.
-        (markt)
-      </fix>
-      <fix>
-        <bug>60461</bug>: Sync SSL session access for the APR connector. (remm)
-      </fix>
-      <fix>
-        <bug>61224</bug>: Make the <code>GlobalRequestProcessor</code> MBean
-        attributes read-only. Patch provided by Alexis Hassler. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        <bug>49176</bug>: When generating JSP runtime error messages that quote
-        the relevant JSP source code, switch from using the results of the JSP
-        page parsing process to using the JSR 045 source map data to identify
-        the correct part of the JSP source from the stack trace. This
-        significantly reduces the memory footprint of Jasper in development
-        mode, provides a small performance improvement for error page generation
-        and enables source quotes to continue to be provided after a Tomcat
-        restart. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        Remove references to the Loader attribute
-        <code>searchExternalFirst</code> from the documentation since the
-        attribute is no longer supported. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <add>
-        <bug>51513</bug>: Add support for the <code>compressionMinSize</code>
-        attribute to the <code>GzipInterceptor</code>, add optional statistics
-        collection and expose the Interceptor over JMX. Based on a patch by
-        Christian Stöber. (markt)
-      </add>
-      <add>
-        <bug>61127</bug>: Allow human-readable names for channelSendOptions and
-        mapSendOptions. Patch provided by Igal Sapir. (schultz)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <scode>
-        Restore the local definition of the web service annotations since the
-        JRE provided versions are deprecated and Java 9 does not provide them by
-        default. (markt)
-      </scode>
-      <fix>
-        Add necessary Java 9 configuration options to the startup scripts to
-        prevent warnings being generated on web application stop. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M22 (markt)" rtext="2017-06-26">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>48543</bug>: Add the option to specify an alternative file name for
-        the <code>catalina.config</code> system property. Also document that
-        relative, as well as absolute, URLs are permitted. (markt)
-      </fix>
-      <fix>
-        <bug>61072</bug>: Respect the documentation statements that allow
-        using the platform default secure random for session id generation.
-        (remm)
-      </fix>
-      <fix>
-        Correct the javadoc for
-        <code>o.a.c.connector.CoyoteAdapter#parseSessionCookiesId</code>.
-        Patch provided by John Andrew (XUZHOUWANG) via Github. (violetagg)
-      </fix>
-      <fix>
-        <bug>61101</bug>: CORS filter should set Vary header in response.
-        Submitted by Rick Riemer. (remm)
-      </fix>
-      <add>
-        <bug>61105</bug>: Add a new JULI FileHandler configuration for
-        specifying the maximum number of days to keep the log files. By default
-        the log files will be kept 90 days as configured in
-        <code>logging.properties</code>. (violetagg)
-      </add>
-      <update>
-        Update the Servlet 4.0 implementation to add support for setting
-        trailer fields for HTTP responses. (markt)
-      </update>
-      <fix>
-        <bug>61125</bug>: Ensure that <code>WarURLConnection</code> returns the
-        correct value for calls to <code>getLastModified()</code> as this is
-        required for the correct detection of JSP modifications when the JSP is
-        packaged in a WAR file. (markt)
-      </fix>
-      <fix>
-        Improve the <code>SSLValve</code> so it is able to handle client
-        certificate headers from Nginx. Based on a patch by Lucas Ventura Carro.
-        (markt)
-      </fix>
-      <fix>
-        <bug>61134</bug>: Do not use '[' and ']' symbols around substituted
-        text fragments when generating the default error pages. Patch provided
-        by Katya Todorova. (violetagg)
-      </fix>
-      <fix>
-        <bug>61154</bug>: Allow the Manager and Host Manager web applications to
-        start by default when running under a security manager. This was
-        accomplished by adding a custom permission,
-        <code>org.apache.catalina.security.DeployXmlPermission</code>, that
-        permits an application to use a <code>META-INF/context.xml</code> file
-        and then granting that permission to the Manager and Host Manager.
-        (markt)
-      </fix>
-      <fix>
-        <bug>61173</bug>: Polish the javadoc for
-        <code>o.a.catalina.startup.Tomcat</code>. Patch provided by
-        peterhansson_se. (violetagg)
-      </fix>
-      <add>
-        A new configuration property <code>crawlerIps</code> is added to the
-        <code>o.a.catalina.valves.CrawlerSessionManagerValve</code>. Using this
-        property one can specify a regular expression that will be used to
-        identify crawlers based on their IP address. Based on a patch provided
-        by Tetradeus. (violetagg)
-      </add>
-      <fix>
-        <bug>61180</bug>: Log a warning message rather than an information
-        message if it takes more than 100ms to initialised a
-        <code>SecureRandom</code> instance for a web application to use to
-        generate session identifiers. Patch provided by Piotr Chlebda. (markt)
-      </fix>
-      <fix>
-        <bug>61185</bug>: When an asynchronous request is dispatched via
-        <code>AsyncContext.dispatch()</code> ensure that
-        <code>getRequestURI()</code> for the dispatched request matches that of
-        the original request. (markt)
-      </fix>
-      <fix>
-        <bug>61197</bug>: Ensure that the charset name used in the
-        <code>Content-Type</code> header has exactly the same form as that
-        provided by the application. This reverts a behavioural change in
-        9.0.0.M21 that caused problems for some clients. (markt)
-      </fix>
-      <fix>
-        <bug>61201</bug>: Ensure that the <code>SCRIPT_NAME</code> environment
-        variable for CGI executables is populated in a consistent way regardless
-        of how the CGI servlet is mapped to a request. (markt)
-      </fix>
-      <fix>
-        Ensure to send a space between trailer field name and field value
-        for HTTP responses trailer fields. (huxing)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        <bug>61086</bug>: Explicitly signal an empty request body for HTTP 205
-        responses. (markt)
-      </fix>
-      <fix>
-        <bug>61120</bug>: Do not ignore path parameters when processing HTTP/2
-        requests. (markt)
-      </fix>
-      <fix>
-        Revert a change introduced in the fix for bug <bug>60718</bug> that
-        changed the status code recorded in the access log when the client
-        dropped the connection from 200 to 500. (markt)
-      </fix>
-      <fix>
-        Make asynchronous error handling more robust. In particular ensure that
-        <code>onError()</code> is called for any registered
-        <code>AsyncListener</code>s after an I/O error on a non-container
-        thread. (markt)
-      </fix>
-      <fix>
-        Add additional syncs to the SSL session object provided by the OpenSSL
-        engine so that a concurrent destruction cannot cause a JVM crash.
-        (remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        <bug>44787</bug>: Improve error message when JSP compiler configuration
-        options are not valid. (markt)
-      </fix>
-      <add>
-        <bug>45931</bug>: Extend Jasper's <code>timeSpaces</code> option to add
-        support for <code>single</code> which replaces template text that
-        consists entirely of whitespace with a single space character. Based on
-        a patch by Meetesh Karia. (markt)
-      </add>
-      <fix>
-        <bug>53011</bug>: When pre-compiling with JspC, report all compilation
-        errors rather than stopping after the first error. A new option
-        <code>-failFast</code> can be used to restore the previous behaviour of
-        stopping after the first error. Based on a patch provided by Marc Pompl.
-        (markt)
-      </fix>
-      <fix>
-        <bug>61137</bug>: <code>j.s.jsp.tagext.TagLibraryInfo#uri</code> and
-        <code>j.s.jsp.tagext.TagLibraryInfo#prefix</code> fields should not be
-        final. Patch provided by Katya Todorova. (violetagg)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <fix>
-        Correct the log message when a <code>MessageHandler</code> for
-        <code>PongMessage</code> does not implement
-        <code>MessageHandler.Whole</code>. (rjung)
-      </fix>
-      <fix>
-        Improve thread-safety of <code>Future</code>s used to report the result
-        of sending WebSocket messages. (markt)
-      </fix>
-      <fix>
-        <bug>61183</bug>: Correct a regression in the previous fix for
-        <bug>58624</bug> that could trigger a deadlock depending on the locking
-        strategy employed by the client code. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web applications">
-    <changelog>
-      <fix>
-        Better document the meaning of the trimSpaces option for Jasper. (markt)
-      </fix>
-      <fix>
-        <bug>61150</bug>: Configure the Manager and Host-Manager web
-        applications to permit serialization and deserialization of
-        CRSFPreventionFilter related session objects to avoid warning messages
-        and/or stack traces on web application stop and/or start when running
-        under a security manager. (markt)
-      </fix>
-      <fix>
-        Correct the TLS configuration documentation to remove SSLv2 and SSLv3
-        from the list of supported protocols. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <add>
-        <bug>45832</bug>: Add HTTP DIGEST authentication support to the Catalina
-        Ant tasks used to communicate with the Manager application. (markt)
-      </add>
-      <fix>
-        <bug>45879</bug>: Add the <code>RELEASE-NOTES</code> file to the root of
-        the installation created by the Tomcat installer for Windows to make it
-        easier for users to identify the installed Tomcat version. (markt)
-      </fix>
-      <fix>
-        <bug>61055</bug>: Clarify the code comments in the rewrite valve to make
-        clear that there are no plans to provide proxy support for this valve
-        since Tomcat does not have proxy capabilities. (markt)
-      </fix>
-      <fix>
-        <bug>61076</bug>: Document the <code>altDDName</code> attribute for the
-        <code>Context</code> element. (markt)
-      </fix>
-      <fix>
-        Correct typo in Jar Scan Filter Configuration Reference.
-        Issue reported via comments.apache.org. (violetagg)
-      </fix>
-      <fix>
-        Correct the requirement for the minimum Java SE version in Application
-        Developer's Guide. Issue reported via comments.apache.org. (violetagg)
-      </fix>
-      <fix>
-        <bug>61145</bug>: Add missing <code>@Documented</code> annotation to
-        annotations in the annotations API. Patch provided by Katya Todorova.
-        (markt)
-      </fix>
-      <fix>
-        <bug>61146</bug>: Add missing <code>lookup()</code> method to
-        <code>@EJB</code> annotation in the annotations API. Patch provided by
-        Katya Todorova. (markt)
-      </fix>
-      <fix>
-        Correct typo in Context Container Configuration Reference.
-        Patch provided by Katya Todorova. (violetagg)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M21 (markt)" rtext="2017-05-10">
-  <subsection name="General">
-    <changelog>
-      <add>
-        Allow to exclude JUnit test classes using the build property
-        <code>test.exclude</code> and document the property in
-        BUILDING.txt. (rjung)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        Review those places where Tomcat re-encodes a URI or URI component and
-        ensure that the correct encoding (path differs from query string) is
-        applied and that the encoding is applied consistently. (markt)
-      </fix>
-      <fix>
-        Avoid a <code>NullPointerException</code> when reading attributes for a
-        initialised HTTP connector where TLS is enabled. (markt)
-      </fix>
-      <fix>
-        Always quote the <code>hostName</code> of an <code>SSLHostConfig</code>
-        element when using it as part of the JMX object name to avoid errors that
-        prevent the associated TLS connector from starting if a wild card
-        <code>hostName</code> is configured (because <code>*</code> is a
-        reserved character for JMX object names). (markt)
-      </fix>
-      <update>
-        Update the default <code>URIEncoding</code> for a <code>Connector</code>
-        to <code>UTF-8</code> as required by the Servlet 4.0 specification.
-        (markt)
-      </update>
-      <scode>
-        Switch to using <code>Charset</code> rather than <code>String</code> to
-        store encoding settings (including for configuration and for the
-        <code>Content-Type header</code>) to reduce the number of places the
-        associated <code>Charset</code> needs to be looked up. (markt)
-      </scode>
-      <fix>
-        Use a more reliable mechanism for the <code>DefaultServlet</code> when
-        determining if the current request is for custom error page or not.
-        (markt)
-      </fix>
-      <fix>
-        Ensure that when the Default or WebDAV servlets process an error
-        dispatch that the error resource is processed via the
-        <code>doGet()</code> method irrespective of the method used for the
-        original request that triggered the error. (markt)
-      </fix>
-      <fix>
-        If a static custom error page is specified that does not exist or cannot
-        be read, ensure that the intended error status is returned rather than a
-        404 or 403. (markt)
-      </fix>
-      <fix>
-        When the WebDAV servlet is configured and an error dispatch is made to a
-        custom error page located below <code>WEB-INF</code>, ensure that the
-        target error page is displayed rather than a 404 response. (markt)
-      </fix>
-      <update>
-        Update the Servlet 4.0 implementation to add support for obtaining
-        trailer fields from chunked HTTP requests. (markt)
-      </update>
-      <add>
-        <bug>61047</bug>: Add MIME mapping for woff2 fonts in the default
-        web.xml. Patch provided by Justin Williamson. (violetagg)
-      </add>
-      <fix>
-        Correct the logic that selects the encoding to use to decode the query
-        string in the <code>SSIServletExternalResolver</code> so that the
-        <code>useBodyEncodingForURI</code> attribute of the
-        <code>Connector</code> is correctly taken into account. (markt)
-      </fix>
-      <fix>
-        Within the Expires filter, make the content type value specified with the
-        <code>ExpiresByType</code> parameter, case insensitive. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        When a <code>TrustManager</code> is configured that does not support
-        <code>certificateVerificationDepth</code> only log a warning about that
-        lack of support when <code>certificateVerificationDepth</code> has been
-        explicitly set. (markt)
-      </fix>
-      <fix>
-        <bug>60970</bug>: Extend the fix for large headers to push requests.
-        (markt)
-      </fix>
-      <fix>
-        Do not include a <code>Date</code> header in HTTP/2 responses with
-        status codes less than 200. (markt)
-      </fix>
-      <fix>
-        When sending an HTTP/2 push promise with the NIO2 connector, the pushed
-        stream ID should only be included with the initial push promise frame
-        and not any subsequent continuation frames. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        When no BOM is present and an encoding is detected, do not skip the
-        bytes used to detect the encoding since they are not part of a BOM.
-        (markt)
-      </fix>
-      <update>
-        <bug>61057</bug>: Update to Eclipse JDT Compiler 4.6.3. (violetagg)
-      </update>
-      <fix>
-        <bug>61065</bug>: Ensure that once the class is resolved by
-        <code>javax.el.ImportHandler#resolveClass</code> it will be cached with
-        the proper name. (violetagg)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="WebSocket">
-    <changelog>
-      <add>
-        Introduce new API <code>o.a.tomcat.websocket.WsSession#suspend</code>/
-        <code>o.a.tomcat.websocket.WsSession#resume</code> that can be used to
-        suspend/resume reading of the incoming messages. (violetagg)
-      </add>
-      <fix>
-        <bug>61003</bug>: Ensure the flags for reading/writing in
-        <code>o.a.t.websocket.AsyncChannelWrapperSecure</code> are correctly
-        reset even if some exceptions occurred during processing. (markt/violetagg)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Web Applications">
-    <changelog>
-      <add>
-        Add documents for <code>maxIdleTime</code> attribute to Channel Receiver
-        docs. (kfujino)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <add>
-        Add features to get the statistics of the thread pool of the
-        <code>Receiver</code> component and
-        <code>MessageDispatchInterceptor</code>. These statistics information
-        can be acquired via JMX. (kfujino)
-      </add>
-      <add>
-        Add <code>maxIdleTime</code> attribute to <code>NioReceiverMBean</code>
-        in order to expose to JMX. (kfujino)
-      </add>
-      <add>
-        Add JMX support for <code>Channel Interceptors</code>. The Interceptors
-        that implement JMX support are <code>TcpFailureDetector</code>,
-        <code>ThroughputInterceptor</code>, <code>TcpPingInterceptor</code>,
-        <code>StaticMembershipInterceptor</code>,
-        <code>MessageDispatchInterceptor</code> and
-        <code>DomainFilterInterceptor</code>. (kfujino)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <add>
-        Modify the Ant build script used to publish to a Maven repository so
-        that it no longer requires artifacts to be GPG signed. This is make it
-        possible for the CI system to upload snapshot builds to the ASF Maven
-        repository. (markt)
-      </add>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M20 (markt)" rtext="2017-04-18">
-  <subsection name="Catalina">
-    <changelog>
-      <update>
-        Update the Servlet 4.0 API implementation to reflect the change in
-        method name from <code>getPushBuilder()</code> to
-        <code>newPushBuilder()</code>. (markt)
-      </update>
-      <fix>
-        Correct various edge cases in the new HTTP Host header validation
-        parser. Patch provided by Katya Todorova. (martk)
-      </fix>
-      <fix>
-        Correct a regression in the X to comma refactoring that broke JMX
-        operations that take parameters. (markt)
-      </fix>
-      <fix>
-        Avoid a <code>NullPointerException</code> when reading attributes for a
-        running HTTP connector where TLS is not enabled. (markt)
-      </fix>
-      <fix>
-        <bug>47214</bug>: Refactor code so that explicitly referenced inner
-        classes are given explicit names rather than being anonymous. (markt)
-      </fix>
-      <fix>
-        <bug>59825</bug>: Log a message that lists the components in the
-        processing chain that do not support async processing when a call to
-        <code>ServletRequest.startAsync()</code> fails. (markt)
-      </fix>
-      <fix>
-        <bug>60940</bug>: Improve the handling of the <code>META-INF/</code> and
-        <code>META-INF/MANIFEST.MF</code> entries for Jar files located in
-        <code>/WEB-INF/lib</code> when running a web application from a packed
-        WAR file. (markt)
-      </fix>
-      <fix>
-        Pre-load the <code>ExceptionUtils</code> class. Since the class is used
-        extensively in error handling, it is prudent to pre-load it to avoid any
-        failure to load this class masking the true problem during error
-        handling. (markt)
-      </fix>
-      <fix>
-        Avoid potential <code>NullPointerException</code>s related to access
-        logging during shutdown, some of which have been observed when running
-        the unit tests. (markt)
-      </fix>
-      <fix>
-        When there is no <code>javax.servlet.WriteListener</code> registered
-        then a call to <code>javax.servlet.ServletOutputStream#isReady</code>
-        will return <code>false</code> instead of throwing
-        <code>IllegalStateException</code>. (violetagg)
-      </fix>
-      <fix>
-        When there is no <code>javax.servlet.ReadListener</code> registered
-        then a call to <code>javax.servlet.ServletInputStream#isReady</code>
-        will return <code>false</code> instead of throwing
-        <code>IllegalStateException</code>. (violetagg)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Align cipher configuration parsing with current OpenSSL master. (markt)
-      </fix>
-      <fix>
-        <bug>60970</bug>: Fix infinite loop if application tries to write a
-        large header to the response when using HTTP/2. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        <bug>47214</bug>: Refactor code so that explicitly referenced inner
-        classes are given explicit names rather than being anonymous. (markt)
-      </fix>
-      <fix>
-        <bug>60925</bug>: Improve the handling of access to properties defined
-        by interfaces when a <code>BeanELResolver</code> is used under a
-        <code>SecurityManager</code>. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Tribes">
-    <changelog>
-      <add>
-        Add JMX support for Tribes components. (kfujino)
-      </add>
-    </changelog>
-  </subsection>
-  <subsection name="jdbc-pool">
-    <changelog>
-      <scode>
-        Refactor the creating a constructor for a proxy class to reduce
-        duplicate code. (kfujino)
-      </scode>
-      <fix>
-        In <code>StatementFacade</code>, the method call on the statements that
-        have been closed throw <code>SQLException</code> rather than
-        <code>NullPointerException</code>. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        <bug>60932</bug>: Correctly escape single quotes when used in i18n
-        messages. Based on a patch by Michael Osipov. (markt)
-      </fix>
-      <scode>
-        Review i18n property files, remove unnecessary escaping and consistently
-        use <code>[...]</code> to delimit inserted values. (markt)
-      </scode>
-      <fix>
-        Update the custom Ant task that integrates with the Symantec code
-        signing service to use the now mandatory 2-factor authentication.
-        (markt)
-      </fix>
-      <scode>
-        Refactoring in preparation for Java 9. Refactor to avoid using some
-        methods that will be deprecated in Java 9 onwards. (markt)
-      </scode>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M19 (markt)" rtext="2017-03-30">
-  <subsection name="Catalina">
-    <changelog>
-      <add>
-        <bug>54618</bug>: Add support to the
-        <code>HttpHeaderSecurityFilter</code> for the HSTS preload parameter.
-        (markt)
-      </add>
-      <fix>
-        Correct a bug in the implementation of the Servlet 4.0 feature that
-        allows specifying a default request and/or response character encoding
-        per web application. <code>null</code> values passed via the
-        programmatic interface no longer trigger a
-        <code>NullPointerException</code>. (markt)
-      </fix>
-      <fix>
-        Correct a potential exception during shutdown when one or more
-        Containers are configured with a value of 1 for startStopThreads.
-        (markt)
-      </fix>
-      <fix>
-        <bug>60853</bug>: Expose the <code>SSLHostConfig</code> and
-        <code>SSLHostConfigCertificate</code> objects via JMX. (markt)
-      </fix>
-      <fix>
-        <bug>60876</bug>: Ensure that <code>Set-Cookie</code> headers generated
-        by the <code>Rfc6265CookieProcessor</code> are aligned with the
-        specification. Patch provided by Jim Griswold. (markt)
-      </fix>
-      <fix>
-        <bug>60882</bug>: Fix a <code>NullPointerException</code> when obtaining
-        a <code>RequestDispatcher</code> for a request that will not have any
-        pathInfo associated with it. This was a regression in the changes in
-        9.0.0.M18 for the Servlet 4.0 API changes. (markt)
-      </fix>
-      <update>
-        Align <code>PushBuilder</code> API with changes from the Servlet expert
-        group. (markt)
-      </update>
-      <update>
-        Align web.xml parsing rules with changes from the Servlet expert group
-        for <code>&lt;request-character-encoding&gt;</code> and
-        <code>&lt;response-character-encoding&gt;</code>. (markt)
-      </update>
-      <scode>
-        Refactor the various implementations of X to comma separated list to a
-        single utility class and update the code to use the new utility class.
-        (markt)
-      </scode>
-      <fix>
-        <bug>60911</bug>: Ensure NPE will not be thrown when looking for SSL
-        session ID. Based on a patch by Didier Gutacker. (violetagg)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Add async based IO groundwork for HTTP/2. (remm)
-      </fix>
-      <fix>
-        Fix HTTP/2 incorrect input unblocking on EOF. (remm)
-      </fix>
-      <fix>
-        Close the connection sooner if an event occurs for a current connection
-        that is not consistent with the current state of that connection.
-        (markt)
-      </fix>
-      <fix>
-        Speed up shutdown when using multiple acceptor threads by ensuring that
-        the code that unlocks the acceptor threads correctly handles the case
-        where there are multiple threads. (markt)
-      </fix>
-      <fix>
-        <bug>60851</bug>: Add <code>application/xml</code> and
-        <code>application/json</code> to the default list of compressible MIME
-        types. Patch by Michael Osipov. (markt)
-      </fix>
-      <fix>
-        <bug>60852</bug>: Correctly spell compressible when used in
-        configuration attributes and internal code. Based on a patch by Michael
-        Osipov. (markt)
-      </fix>
-      <fix>
-        <bug>60900</bug>: Avoid a <code>NullPointerException</code> in the APR
-        Poller if a connection is closed at the same time as new data arrives on
-        that connection. (markt)
-      </fix>
-      <fix>
-        Improve HPACK specification compliance by fixing some test failures
-        reported by the h2spec tool written by Moto Ishizawa. (markt)
-      </fix>
-      <fix>
-        Improve HTTP/2 specification compliance by fixing some test failures
-        reported by the h2spec tool written by Moto Ishizawa. (markt)
-      </fix>
-      <fix>
-        <bug>60918</bug>: Fix sendfile processing error that could lead to
-        subsequent requests experiencing an <code>IllegalStateException</code>.
-        (markt)
-      </fix>
-      <fix>
-        Improve sendfile handling when requests are pipelined. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <fix>
-        <bug>60844</bug>: Correctly handle the error when fewer parameter values
-        than required by the method are used to invoke an EL method expression.
-        Patch provided by Daniel Gray. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="jdbc-pool">
-    <changelog>
-      <fix>
-        <bug>60764</bug>: Implement <code>equals()</code> and
-        <code>hashCode()</code> in the <code>StatementFacade</code> in order to
-        enable these methods to be called on the closed statements if any
-        statement proxy is set. This behavior can be changed with
-        <code>useStatementFacade</code> attribute. (kfujino)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Other">
-    <changelog>
-      <fix>
-        Refactor the build script and the NSIS installer script so that either
-        NSIS 2.x or NSIS 3.x can be used to build the installer. This is
-        primarily to re-enable building the installer on the Linux based CI
-        system where the combination of NSIS 3.x and wine leads to failed
-        installer builds. (markt)
-      </fix>
-    </changelog>
-  </subsection>
-</section>
-<section name="Tomcat 9.0.0.M18 (markt)" rtext="2017-03-13">
-  <subsection name="Catalina">
-    <changelog>
-      <fix>
-        <bug>60469</bug>: Refactor <code>RealmBase</code> for better code re-use
-        when implementing Realms that use a custom <code>Principal</code>.
-        (markt)
-      </fix>
-      <fix>
-        <bug>60490</bug>: Various formatting and layout improvements for the
-        <code>ErrorReportValve</code>. Patch provided by Michael Osipov. (markt)
-      </fix>
-      <fix>
-        <bug>60573</bug>: Remove the reason phrase when sending a
-        <code>100</code> response status for consistency with other response
-        status lines. Patch provided by Michael Osipov. (markt)
-      </fix>
-      <update>
-        <bug>60596</bug>: Improve performance of DefaultServlet when sendfile
-        feature is disabled on connector. (kkolinko)
-      </update>
-      <scode>
-        Make it easier for sub-classes of <code>Tomcat</code> to modify the
-        default web.xml settings by over-riding
-        <code>getDefaultWebXmlListener()</code>. Patch provided by Aaron
-        Anderson. (markt)
-      </scode>
-      <fix>
-        Reduce the contention in the default <code>InstanceManager</code>
-        implementation when multiple threads are managing objects and need to
-        reference the annotation cache. (markt)
-      </fix>
-      <fix>
-        <bug>60623</bug>: When startStopThreads is 1 (or a special value that
-        is equivalent to 1) then rather than using an
-        <code>ExecutorService</code> to start the children of the current
-        component, the children will be started on the current thread. (markt)
-      </fix>
-      <scode>
-        <bug>60674</bug>: Remove <code>final</code> marker from
-        <code>CorsFilter</code> to enable sub-classing. (markt)
-      </scode>
-      <fix>
-        <bug>60683</bug>: Security manager failure causing NPEs when doing IO
-        on some JVMs. (csutherl)
-      </fix>
-      <fix>
-        <bug>60688</bug>: Update the internal fork of Apache Commons BCEL to
-        r1782855 to add early access Java 9 support to the annotation scanning
-        code. (markt)
-      </fix>
-      <fix>
-        <bug>60694</bug>: Prevent NPE during authentication when no JASPIC
-        <code>AuthConfigFactory</code> is available. (markt)
-      </fix>
-      <fix>
-        <bug>60697</bug>: When HTTP TRACE requests are disabled on the
-        Connector, ensure that the HTTP OPTIONS response from custom servlets
-        does not include TRACE in the returned Allow header. (markt)
-      </fix>
-      <fix>
-        <bug>60718</bug>: Improve error handling for asynchronous processing and
-        correct a number of cases where the <code>requestDestroyed()</code>
-        event was not being fired and an entry wasn't being made in the access
-        logs. (markt)
-      </fix>
-      <fix>
-        <bug>60720</bug>: Replace "WWW-Authenticate" literal with static final
-        AUTH_HEADER_NAME in SpnegoAuthenticator. Patch provided by Michael
-        Osipov. (violetagg)
-      </fix>
-      <fix>
-        The default JASPIC <code>AuthConfigFactory</code> now correctly notifies
-        registered <code>RegistrationListener</code>s when a new
-        <code>AuthConfigProvider</code> is registered. (markt)
-      </fix>
-      <scode>
-        Improve the performance of <code>AuthenticatorBase</code> when there is
-        no JASPIC configuration available. (violetagg)
-      </scode>
-      <fix>
-        When HTTP TRACE requests are disabled on the Connector, ensure that the
-        HTTP OPTIONS response from the WebDAV servlet does not include
-        TRACE in the returned Allow header. (markt)
-      </fix>
-      <fix>
-        <bug>60722</bug>: Take account of the
-        <strong>dispatchersUseEncodedPaths</strong> setting on the current
-        <strong>Context</strong> when generating paths for dispatches triggered
-        by <code>AsyncContext.dispatch()</code>. (markt)
-      </fix>
-      <fix>
-        <bug>60728</bug>: Make the separator Tomcat uses in the Tomcat specific
-        <code>war:file:...</code> URL protocol customizable via a system
-        property. The separator is equivalent to the use of the <code>!</code>
-        character in <code>jar:file:...</code> URLs. The default separator of
-        <code>*</code> remains unchanged. (markt)
-      </fix>
-      <update>
-        Update the Servlet 4.0 API implementation to align with the latest
-        proposals from the Servlet 4.0 expert group. This includes updates to
-        the new Servlet mapping API, new methods on the
-        <code>ServletContext</code> to make the available API more equivalent to
-        the deployment descriptor, updates to the HTTP push API and the ability
-        to set default request and response character encoding per web
-        application. Note that the Servlet 4.0 API is still a work in progress
-        and further changes are likely. (markt)
-      </update>
-      <fix>
-        <bug>60798</bug>: Correct a bug in the handling of JARs in unpacked WARs
-        that meant multiple attempts to read the same entry from a JAR in
-        succession would fail for the second and subsequent attempts. (markt)
-      </fix>
-      <fix>
-        <bug>60808</bug>: Ensure that the <code>Map</code> returned by
-        <code>ServletRequest.getParameterMap()</code> is fully immutable. Based
-        on a patch provided by woosan. (markt)
-      </fix>
-      <fix>
-        <bug>60824</bug>: Correctly cache the <code>Subject</code> in the
-        session - if there is a session - when running under a
-        <code>SecurityManager</code>. Patch provided by Jan Engehausen. (markt)
-      </fix>
-      <fix>
-        Ensure request and response facades are used when firing application
-        listeners. (markt/remm)
-      </fix>
-    </changelog>
-  </subsection>
-  <subsection name="Coyote">
-    <changelog>
-      <fix>
-        Improve handling of case when an HTTP/2 client sends more data that is
-        subject to flow control than the current window size allows. (markt)
-      </fix>
-      <fix>
-        Improve NIO2 look-ahead parsing of TLS client hello for SNI with large
-        client hello messages. (markt)
-      </fix>
-      <add>
-        Enable ALPN and also, therefore, HTTP/2 for the NIO and NIO2 HTTP
-        connectors when using the JSSE implementation for TLS when running on
-        Java 9. (markt)
-      </add>
-      <fix>
-        Restore Java 9 direct byte buffer compatibility. (remm)
-      </fix>
-      <fix>
-        <bug>59807</bug>: Provide a better error message when there is no
-        <strong>SSLHostConfig</strong> defined with a <code>hostName</code> that
-        matches the <code>defaultSSLHostConfigName</code> for the associated
-        <strong>Connector</strong>. (markt)
-      </fix>
-      <fix>
-        <bug>60627</bug>: Modify the <code>Rfc6265CookieProcessor</code> so that
-        in addition to cookie headers that start with an explicit RFC 2109
-        <code>$Version=1</code>, cookies that start with <code>$Version=0</code>
-        are also parsed as RFC 2109 cookies. (markt)
-      </fix>
-      <fix>
-        Include the value of <code>SslHostConfig.truststoreAlgorithm</code> when
-        warning that the algorithm does not support the
-        <code>certificateVerificationDepth</code> configuration option. (markt)
-      </fix>
-      <fix>
-        Ensure that executor thread pools used with connectors pre-start the
-        configured minimum number of idle threads. (markt)
-      </fix>
-      <fix>
-        <bug>60716</bug>: Add a new JSSE specific attribute,
-        <code>revocationEnabled</code>, to <code>SSLHostConfig</code> to permit
-        JSSE provider revocation checks to be enabled when no
-        <code>certificateRevocationListFile</code> has been configured. The
-        expectation is that configuration will be performed via a JSSE provider
-        specific mechanisms. (markt)
... 10303 lines suppressed ...


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


Re: [tomcat] branch master updated: Ensure dcoBase is configured using a canonical path

Posted by Mark Thomas <ma...@apache.org>.
On 12/04/2019 18:00, markt@apache.org wrote:
> This is an automated email from the ASF dual-hosted git repository.
> 
> markt pushed a commit to branch master
> in repository https://gitbox.apache.org/repos/asf/tomcat.git
> 
> 
> The following commit(s) were added to refs/heads/master by this push:
>      new 9d9bc8c  Ensure dcoBase is configured using a canonical path
> 9d9bc8c is described below
> 
> commit 9d9bc8ceed67a1a63ebeb86b09c1310f6fb405e3
> Author: Mark Thomas <ma...@apache.org>
> AuthorDate: Fri Apr 12 17:58:43 2019 +0100
> 
>     Ensure dcoBase is configured using a canonical path
>     
>     This should fix test failures on Windows when starting the tests from a
>     command prompt using a lower case drive letter.

Sorry. Looks like my Windows VM isn't configured correctly for line
endings. I'll fix this now.

Mark

> ---
>  .../org/apache/catalina/startup/ContextConfig.java |  5419 +++----
>  webapps/docs/changelog.xml                         | 14857 ++++++++++---------
>  2 files changed, 10145 insertions(+), 10131 deletions(-)
> 
> diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
> index 0c67af3..544b3b4 100644
> --- a/java/org/apache/catalina/startup/ContextConfig.java
> +++ b/java/org/apache/catalina/startup/ContextConfig.java
> @@ -1,2706 +1,2713 @@
> -/*
> - * 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.startup;
> -
> -import java.io.File;
> -import java.io.FileInputStream;
> -import java.io.FileNotFoundException;
> -import java.io.IOException;
> -import java.io.InputStream;
> -import java.net.MalformedURLException;
> -import java.net.URISyntaxException;
> -import java.net.URL;
> -import java.net.URLConnection;
> -import java.util.ArrayList;
> -import java.util.Collections;
> -import java.util.HashMap;
> -import java.util.HashSet;
> -import java.util.LinkedHashMap;
> -import java.util.LinkedHashSet;
> -import java.util.List;
> -import java.util.Locale;
> -import java.util.Map;
> -import java.util.Map.Entry;
> -import java.util.Properties;
> -import java.util.Set;
> -import java.util.concurrent.ConcurrentHashMap;
> -
> -import javax.servlet.MultipartConfigElement;
> -import javax.servlet.ServletContainerInitializer;
> -import javax.servlet.ServletContext;
> -import javax.servlet.SessionCookieConfig;
> -import javax.servlet.annotation.HandlesTypes;
> -
> -import org.apache.catalina.Authenticator;
> -import org.apache.catalina.Container;
> -import org.apache.catalina.Context;
> -import org.apache.catalina.Engine;
> -import org.apache.catalina.Globals;
> -import org.apache.catalina.Host;
> -import org.apache.catalina.Lifecycle;
> -import org.apache.catalina.LifecycleEvent;
> -import org.apache.catalina.LifecycleListener;
> -import org.apache.catalina.Pipeline;
> -import org.apache.catalina.Server;
> -import org.apache.catalina.Service;
> -import org.apache.catalina.Valve;
> -import org.apache.catalina.WebResource;
> -import org.apache.catalina.WebResourceRoot;
> -import org.apache.catalina.Wrapper;
> -import org.apache.catalina.core.StandardContext;
> -import org.apache.catalina.core.StandardHost;
> -import org.apache.catalina.util.ContextName;
> -import org.apache.catalina.util.Introspection;
> -import org.apache.juli.logging.Log;
> -import org.apache.juli.logging.LogFactory;
> -import org.apache.tomcat.Jar;
> -import org.apache.tomcat.JarScanType;
> -import org.apache.tomcat.JarScanner;
> -import org.apache.tomcat.util.ExceptionUtils;
> -import org.apache.tomcat.util.bcel.classfile.AnnotationElementValue;
> -import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;
> -import org.apache.tomcat.util.bcel.classfile.ArrayElementValue;
> -import org.apache.tomcat.util.bcel.classfile.ClassFormatException;
> -import org.apache.tomcat.util.bcel.classfile.ClassParser;
> -import org.apache.tomcat.util.bcel.classfile.ElementValue;
> -import org.apache.tomcat.util.bcel.classfile.ElementValuePair;
> -import org.apache.tomcat.util.bcel.classfile.JavaClass;
> -import org.apache.tomcat.util.buf.UriUtil;
> -import org.apache.tomcat.util.descriptor.InputSourceUtil;
> -import org.apache.tomcat.util.descriptor.XmlErrorHandler;
> -import org.apache.tomcat.util.descriptor.web.ContextEjb;
> -import org.apache.tomcat.util.descriptor.web.ContextEnvironment;
> -import org.apache.tomcat.util.descriptor.web.ContextLocalEjb;
> -import org.apache.tomcat.util.descriptor.web.ContextResource;
> -import org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef;
> -import org.apache.tomcat.util.descriptor.web.ContextService;
> -import org.apache.tomcat.util.descriptor.web.ErrorPage;
> -import org.apache.tomcat.util.descriptor.web.FilterDef;
> -import org.apache.tomcat.util.descriptor.web.FilterMap;
> -import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
> -import org.apache.tomcat.util.descriptor.web.JspPropertyGroup;
> -import org.apache.tomcat.util.descriptor.web.LoginConfig;
> -import org.apache.tomcat.util.descriptor.web.MessageDestinationRef;
> -import org.apache.tomcat.util.descriptor.web.MultipartDef;
> -import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
> -import org.apache.tomcat.util.descriptor.web.SecurityRoleRef;
> -import org.apache.tomcat.util.descriptor.web.ServletDef;
> -import org.apache.tomcat.util.descriptor.web.SessionConfig;
> -import org.apache.tomcat.util.descriptor.web.WebXml;
> -import org.apache.tomcat.util.descriptor.web.WebXmlParser;
> -import org.apache.tomcat.util.digester.Digester;
> -import org.apache.tomcat.util.digester.RuleSet;
> -import org.apache.tomcat.util.file.ConfigFileLoader;
> -import org.apache.tomcat.util.file.ConfigurationSource;
> -import org.apache.tomcat.util.res.StringManager;
> -import org.apache.tomcat.util.scan.JarFactory;
> -import org.xml.sax.InputSource;
> -import org.xml.sax.SAXParseException;
> -
> -/**
> - * Startup event listener for a <b>Context</b> that configures the properties
> - * of that Context, and the associated defined servlets.
> - *
> - * @author Craig R. McClanahan
> - */
> -public class ContextConfig implements LifecycleListener {
> -
> -    private static final Log log = LogFactory.getLog(ContextConfig.class);
> -
> -
> -    /**
> -     * The string resources for this package.
> -     */
> -    protected static final StringManager sm =
> -        StringManager.getManager(Constants.Package);
> -
> -
> -    protected static final LoginConfig DUMMY_LOGIN_CONFIG =
> -        new LoginConfig("NONE", null, null, null);
> -
> -
> -    /**
> -     * The set of Authenticators that we know how to configure.  The key is
> -     * the name of the implemented authentication method, and the value is
> -     * the fully qualified Java class name of the corresponding Valve.
> -     */
> -    protected static final Properties authenticators;
> -
> -    static {
> -        // Load our mapping properties for the standard authenticators
> -        Properties props = new Properties();
> -        try (InputStream is = ContextConfig.class.getClassLoader().getResourceAsStream(
> -                "org/apache/catalina/startup/Authenticators.properties")) {
> -            if (is != null) {
> -                props.load(is);
> -            }
> -        } catch (IOException ioe) {
> -            props = null;
> -        }
> -        authenticators = props;
> -    }
> -
> -    /**
> -     * Deployment count.
> -     */
> -    protected static long deploymentCount = 0L;
> -
> -
> -    /**
> -     * Cache of default web.xml fragments per Host
> -     */
> -    protected static final Map<Host,DefaultWebXmlCacheEntry> hostWebXmlCache =
> -            new ConcurrentHashMap<>();
> -
> -
> -    /**
> -     * Set used as the value for {@code JavaClassCacheEntry.sciSet} when there
> -     * are no SCIs associated with a class.
> -     */
> -    private static final Set<ServletContainerInitializer> EMPTY_SCI_SET = Collections.emptySet();
> -
> -
> -    // ----------------------------------------------------- Instance Variables
> -    /**
> -     * Custom mappings of login methods to authenticators
> -     */
> -    protected Map<String,Authenticator> customAuthenticators;
> -
> -
> -    /**
> -     * The Context we are associated with.
> -     */
> -    protected volatile Context context = null;
> -
> -
> -    /**
> -     * The default web application's deployment descriptor location.
> -     */
> -    protected String defaultWebXml = null;
> -
> -
> -    /**
> -     * Track any fatal errors during startup configuration processing.
> -     */
> -    protected boolean ok = false;
> -
> -
> -    /**
> -     * Original docBase.
> -     */
> -    protected String originalDocBase = null;
> -
> -
> -    /**
> -     * Anti-locking docBase. It is a path to a copy of the web application
> -     * in the java.io.tmpdir directory. This path is always an absolute one.
> -     */
> -    private File antiLockingDocBase = null;
> -
> -
> -    /**
> -     * Map of ServletContainerInitializer to classes they expressed interest in.
> -     */
> -    protected final Map<ServletContainerInitializer, Set<Class<?>>> initializerClassMap =
> -            new LinkedHashMap<>();
> -
> -    /**
> -     * Map of Types to ServletContainerInitializer that are interested in those
> -     * types.
> -     */
> -    protected final Map<Class<?>, Set<ServletContainerInitializer>> typeInitializerMap =
> -            new HashMap<>();
> -
> -    /**
> -     * Flag that indicates if at least one {@link HandlesTypes} entry is present
> -     * that represents an annotation.
> -     */
> -    protected boolean handlesTypesAnnotations = false;
> -
> -    /**
> -     * Flag that indicates if at least one {@link HandlesTypes} entry is present
> -     * that represents a non-annotation.
> -     */
> -    protected boolean handlesTypesNonAnnotations = false;
> -
> -
> -    // ------------------------------------------------------------- Properties
> -
> -    /**
> -     * Obtain the location of the default deployment descriptor.
> -     *
> -     * @return The path to the default web.xml. If not absolute, it is relative
> -     *         to CATALINA_BASE.
> -     */
> -    public String getDefaultWebXml() {
> -        if (defaultWebXml == null) {
> -            defaultWebXml = Constants.DefaultWebXml;
> -        }
> -        return defaultWebXml;
> -    }
> -
> -
> -    /**
> -     * Set the location of the default deployment descriptor.
> -     *
> -     * @param path The path to the default web.xml. If not absolute, it is
> -     *             relative to CATALINA_BASE.
> -     */
> -    public void setDefaultWebXml(String path) {
> -        this.defaultWebXml = path;
> -    }
> -
> -
> -    /**
> -     * Sets custom mappings of login methods to authenticators.
> -     *
> -     * @param customAuthenticators Custom mappings of login methods to
> -     * authenticators
> -     */
> -    public void setCustomAuthenticators(
> -            Map<String,Authenticator> customAuthenticators) {
> -        this.customAuthenticators = customAuthenticators;
> -    }
> -
> -
> -    // --------------------------------------------------------- Public Methods
> -
> -
> -    /**
> -     * Process events for an associated Context.
> -     *
> -     * @param event The lifecycle event that has occurred
> -     */
> -    @Override
> -    public void lifecycleEvent(LifecycleEvent event) {
> -
> -        // Identify the context we are associated with
> -        try {
> -            context = (Context) event.getLifecycle();
> -        } catch (ClassCastException e) {
> -            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
> -            return;
> -        }
> -
> -        // Process the event that has occurred
> -        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
> -            configureStart();
> -        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
> -            beforeStart();
> -        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
> -            // Restore docBase for management tools
> -            if (originalDocBase != null) {
> -                context.setDocBase(originalDocBase);
> -            }
> -        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
> -            configureStop();
> -        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
> -            init();
> -        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
> -            destroy();
> -        }
> -
> -    }
> -
> -
> -    // -------------------------------------------------------- protected Methods
> -
> -
> -    /**
> -     * Process the application classes annotations, if it exists.
> -     */
> -    protected void applicationAnnotationsConfig() {
> -
> -        long t1=System.currentTimeMillis();
> -
> -        WebAnnotationSet.loadApplicationAnnotations(context);
> -
> -        long t2=System.currentTimeMillis();
> -        if (context instanceof StandardContext) {
> -            ((StandardContext) context).setStartupTime(t2-t1+
> -                    ((StandardContext) context).getStartupTime());
> -        }
> -    }
> -
> -
> -    /**
> -     * Set up an Authenticator automatically if required, and one has not
> -     * already been configured.
> -     */
> -    protected void authenticatorConfig() {
> -
> -        LoginConfig loginConfig = context.getLoginConfig();
> -        if (loginConfig == null) {
> -            // Need an authenticator to support HttpServletRequest.login()
> -            loginConfig = DUMMY_LOGIN_CONFIG;
> -            context.setLoginConfig(loginConfig);
> -        }
> -
> -        // Has an authenticator been configured already?
> -        if (context.getAuthenticator() != null) {
> -            return;
> -        }
> -
> -        // Has a Realm been configured for us to authenticate against?
> -        if (context.getRealm() == null) {
> -            log.error(sm.getString("contextConfig.missingRealm"));
> -            ok = false;
> -            return;
> -        }
> -
> -        /*
> -         * First check to see if there is a custom mapping for the login
> -         * method. If so, use it. Otherwise, check if there is a mapping in
> -         * org/apache/catalina/startup/Authenticators.properties.
> -         */
> -        Valve authenticator = null;
> -        if (customAuthenticators != null) {
> -            authenticator = (Valve) customAuthenticators.get(loginConfig.getAuthMethod());
> -        }
> -
> -        if (authenticator == null) {
> -            if (authenticators == null) {
> -                log.error(sm.getString("contextConfig.authenticatorResources"));
> -                ok = false;
> -                return;
> -            }
> -
> -            // Identify the class name of the Valve we should configure
> -            String authenticatorName = authenticators.getProperty(loginConfig.getAuthMethod());
> -            if (authenticatorName == null) {
> -                log.error(sm.getString("contextConfig.authenticatorMissing",
> -                                 loginConfig.getAuthMethod()));
> -                ok = false;
> -                return;
> -            }
> -
> -            // Instantiate and install an Authenticator of the requested class
> -            try {
> -                Class<?> authenticatorClass = Class.forName(authenticatorName);
> -                authenticator = (Valve) authenticatorClass.getConstructor().newInstance();
> -            } catch (Throwable t) {
> -                ExceptionUtils.handleThrowable(t);
> -                log.error(sm.getString(
> -                                    "contextConfig.authenticatorInstantiate",
> -                                    authenticatorName),
> -                          t);
> -                ok = false;
> -            }
> -        }
> -
> -        if (authenticator != null) {
> -            Pipeline pipeline = context.getPipeline();
> -            if (pipeline != null) {
> -                pipeline.addValve(authenticator);
> -                if (log.isDebugEnabled()) {
> -                    log.debug(sm.getString(
> -                                    "contextConfig.authenticatorConfigured",
> -                                    loginConfig.getAuthMethod()));
> -                }
> -            }
> -        }
> -    }
> -
> -
> -    /**
> -     * Create (if necessary) and return a Digester configured to process the
> -     * context configuration descriptor for an application.
> -     * @return the digester for context.xml files
> -     */
> -    protected Digester createContextDigester() {
> -        Digester digester = new Digester();
> -        digester.setValidating(false);
> -        digester.setRulesValidation(true);
> -        Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
> -        List<String> objectAttrs = new ArrayList<>();
> -        objectAttrs.add("className");
> -        fakeAttributes.put(Object.class, objectAttrs);
> -        // Ignore attribute added by Eclipse for its internal tracking
> -        List<String> contextAttrs = new ArrayList<>();
> -        contextAttrs.add("source");
> -        fakeAttributes.put(StandardContext.class, contextAttrs);
> -        digester.setFakeAttributes(fakeAttributes);
> -        RuleSet contextRuleSet = new ContextRuleSet("", false);
> -        digester.addRuleSet(contextRuleSet);
> -        RuleSet namingRuleSet = new NamingRuleSet("Context/");
> -        digester.addRuleSet(namingRuleSet);
> -        return digester;
> -    }
> -
> -
> -    /**
> -     * Process the default configuration file, if it exists.
> -     * @param digester The digester that will be used for XML parsing
> -     */
> -    protected void contextConfig(Digester digester) {
> -
> -        String defaultContextXml = null;
> -
> -        // Open the default context.xml file, if it exists
> -        if (context instanceof StandardContext) {
> -            defaultContextXml = ((StandardContext)context).getDefaultContextXml();
> -        }
> -        // set the default if we don't have any overrides
> -        if (defaultContextXml == null) {
> -            defaultContextXml = Constants.DefaultContextXml;
> -        }
> -
> -        if (!context.getOverride()) {
> -            try (ConfigurationSource.Resource contextXmlResource =
> -                    ConfigFileLoader.getSource().getResource(defaultContextXml)) {
> -                URL defaultContextUrl = contextXmlResource.getURI().toURL();
> -                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
> -            } catch (MalformedURLException e) {
> -                log.error(sm.getString("contextConfig.badUrl", defaultContextXml), e);
> -            } catch (IOException e) {
> -                // Not found
> -            }
> -
> -            String hostContextFile = Container.getConfigPath(context, Constants.HostContextXml);
> -            try (ConfigurationSource.Resource contextXmlResource =
> -                    ConfigFileLoader.getSource().getConfResource(hostContextFile)) {
> -                URL defaultContextUrl = contextXmlResource.getURI().toURL();
> -                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
> -            } catch (MalformedURLException e) {
> -                log.error(sm.getString("contextConfig.badUrl", hostContextFile), e);
> -            } catch (IOException e) {
> -                // Not found
> -            }
> -        }
> -        if (context.getConfigFile() != null) {
> -            processContextConfig(digester, context.getConfigFile(), null);
> -        }
> -
> -    }
> -
> -
> -    /**
> -     * Process a context.xml.
> -     * @param digester The digester that will be used for XML parsing
> -     * @param contextXml The URL to the context.xml configuration
> -     * @param stream The XML resource stream
> -     */
> -    protected void processContextConfig(Digester digester, URL contextXml, InputStream stream) {
> -
> -        if (log.isDebugEnabled()) {
> -            log.debug("Processing context [" + context.getName()
> -                    + "] configuration file [" + contextXml + "]");
> -        }
> -
> -        InputSource source = null;
> -
> -        try {
> -            source = new InputSource(contextXml.toString());
> -            if (stream == null) {
> -                URLConnection xmlConn = contextXml.openConnection();
> -                xmlConn.setUseCaches(false);
> -                stream = xmlConn.getInputStream();
> -            }
> -        } catch (Exception e) {
> -            log.error(sm.getString("contextConfig.contextMissing",
> -                      contextXml) , e);
> -        }
> -
> -        if (source == null) {
> -            return;
> -        }
> -
> -        try {
> -            source.setByteStream(stream);
> -            digester.setClassLoader(this.getClass().getClassLoader());
> -            digester.setUseContextClassLoader(false);
> -            digester.push(context.getParent());
> -            digester.push(context);
> -            XmlErrorHandler errorHandler = new XmlErrorHandler();
> -            digester.setErrorHandler(errorHandler);
> -            digester.parse(source);
> -            if (errorHandler.getWarnings().size() > 0 ||
> -                    errorHandler.getErrors().size() > 0) {
> -                errorHandler.logFindings(log, contextXml.toString());
> -                ok = false;
> -            }
> -            if (log.isDebugEnabled()) {
> -                log.debug("Successfully processed context [" + context.getName()
> -                        + "] configuration file [" + contextXml + "]");
> -            }
> -        } catch (SAXParseException e) {
> -            log.error(sm.getString("contextConfig.contextParse",
> -                    context.getName()), e);
> -            log.error(sm.getString("contextConfig.defaultPosition",
> -                             "" + e.getLineNumber(),
> -                             "" + e.getColumnNumber()));
> -            ok = false;
> -        } catch (Exception e) {
> -            log.error(sm.getString("contextConfig.contextParse",
> -                    context.getName()), e);
> -            ok = false;
> -        } finally {
> -            try {
> -                if (stream != null) {
> -                    stream.close();
> -                }
> -            } catch (IOException e) {
> -                log.error(sm.getString("contextConfig.contextClose"), e);
> -            }
> -        }
> -    }
> -
> -
> -    /**
> -     * Adjust docBase.
> -     * @throws IOException cannot access the context base path
> -     */
> -    protected void fixDocBase() throws IOException {
> -
> -        Host host = (Host) context.getParent();
> -        File appBase = host.getAppBaseFile();
> -
> -        String docBase = context.getDocBase();
> -        if (docBase == null) {
> -            // Trying to guess the docBase according to the path
> -            String path = context.getPath();
> -            if (path == null) {
> -                return;
> -            }
> -            ContextName cn = new ContextName(path, context.getWebappVersion());
> -            docBase = cn.getBaseName();
> -        }
> -
> -        File file = new File(docBase);
> -        if (!file.isAbsolute()) {
> -            docBase = (new File(appBase, docBase)).getAbsolutePath();
> -        } else {
> -            docBase = file.getAbsolutePath();
> -        }
> -        file = new File(docBase);
> -        String origDocBase = docBase;
> -
> -        ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
> -        String pathName = cn.getBaseName();
> -
> -        boolean unpackWARs = true;
> -        if (host instanceof StandardHost) {
> -            unpackWARs = ((StandardHost) host).isUnpackWARs();
> -            if (unpackWARs && context instanceof StandardContext) {
> -                unpackWARs =  ((StandardContext) context).getUnpackWAR();
> -            }
> -        }
> -
> -        boolean docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
> -
> -        if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) {
> -            URL war = UriUtil.buildJarUrl(new File(docBase));
> -            if (unpackWARs) {
> -                docBase = ExpandWar.expand(host, war, pathName);
> -                file = new File(docBase);
> -                docBase = file.getCanonicalPath();
> -                if (context instanceof StandardContext) {
> -                    ((StandardContext) context).setOriginalDocBase(origDocBase);
> -                }
> -            } else {
> -                ExpandWar.validate(host, war, pathName);
> -            }
> -        } else {
> -            File docDir = new File(docBase);
> -            File warFile = new File(docBase + ".war");
> -            URL war = null;
> -            if (warFile.exists() && docBaseInAppBase) {
> -                war = UriUtil.buildJarUrl(warFile);
> -            }
> -            if (docDir.exists()) {
> -                if (war != null && unpackWARs) {
> -                    // Check if WAR needs to be re-expanded (e.g. if it has
> -                    // changed). Note: HostConfig.deployWar() takes care of
> -                    // ensuring that the correct XML file is used.
> -                    // This will be a NO-OP if the WAR is unchanged.
> -                    ExpandWar.expand(host, war, pathName);
> -                }
> -            } else {
> -                if (war != null) {
> -                    if (unpackWARs) {
> -                        docBase = ExpandWar.expand(host, war, pathName);
> -                        file = new File(docBase);
> -                        docBase = file.getCanonicalPath();
> -                    } else {
> -                        docBase = warFile.getCanonicalPath();
> -                        ExpandWar.validate(host, war, pathName);
> -                    }
> -                }
> -                if (context instanceof StandardContext) {
> -                    ((StandardContext) context).setOriginalDocBase(origDocBase);
> -                }
> -            }
> -        }
> -
> -        // Re-calculate now docBase is a canonical path
> -        docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
> -
> -        if (docBaseInAppBase) {
> -            docBase = docBase.substring(appBase.getPath().length());
> -            docBase = docBase.replace(File.separatorChar, '/');
> -            if (docBase.startsWith("/")) {
> -                docBase = docBase.substring(1);
> -            }
> -        } else {
> -            docBase = docBase.replace(File.separatorChar, '/');
> -        }
> -
> -        context.setDocBase(docBase);
> -    }
> -
> -
> -    protected void antiLocking() {
> -
> -        if ((context instanceof StandardContext)
> -            && ((StandardContext) context).getAntiResourceLocking()) {
> -
> -            Host host = (Host) context.getParent();
> -            String docBase = context.getDocBase();
> -            if (docBase == null) {
> -                return;
> -            }
> -            originalDocBase = docBase;
> -
> -            File docBaseFile = new File(docBase);
> -            if (!docBaseFile.isAbsolute()) {
> -                docBaseFile = new File(host.getAppBaseFile(), docBase);
> -            }
> -
> -            String path = context.getPath();
> -            if (path == null) {
> -                return;
> -            }
> -            ContextName cn = new ContextName(path, context.getWebappVersion());
> -            docBase = cn.getBaseName();
> -
> -            if (originalDocBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
> -                antiLockingDocBase = new File(
> -                        System.getProperty("java.io.tmpdir"),
> -                        deploymentCount++ + "-" + docBase + ".war");
> -            } else {
> -                antiLockingDocBase = new File(
> -                        System.getProperty("java.io.tmpdir"),
> -                        deploymentCount++ + "-" + docBase);
> -            }
> -            antiLockingDocBase = antiLockingDocBase.getAbsoluteFile();
> -
> -            if (log.isDebugEnabled()) {
> -                log.debug("Anti locking context[" + context.getName()
> -                        + "] setting docBase to " +
> -                        antiLockingDocBase.getPath());
> -            }
> -
> -            // Cleanup just in case an old deployment is lying around
> -            ExpandWar.delete(antiLockingDocBase);
> -            if (ExpandWar.copy(docBaseFile, antiLockingDocBase)) {
> -                context.setDocBase(antiLockingDocBase.getPath());
> -            }
> -        }
> -    }
> -
> -
> -    /**
> -     * Process a "init" event for this Context.
> -     */
> -    protected synchronized void init() {
> -        // Called from StandardContext.init()
> -
> -        Digester contextDigester = createContextDigester();
> -        contextDigester.getParser();
> -
> -        if (log.isDebugEnabled()) {
> -            log.debug(sm.getString("contextConfig.init"));
> -        }
> -        context.setConfigured(false);
> -        ok = true;
> -
> -        contextConfig(contextDigester);
> -    }
> -
> -
> -    /**
> -     * Process a "before start" event for this Context.
> -     */
> -    protected synchronized void beforeStart() {
> -
> -        try {
> -            fixDocBase();
> -        } catch (IOException e) {
> -            log.error(sm.getString(
> -                    "contextConfig.fixDocBase", context.getName()), e);
> -        }
> -
> -        antiLocking();
> -    }
> -
> -
> -    /**
> -     * Process a "contextConfig" event for this Context.
> -     */
> -    protected synchronized void configureStart() {
> -        // Called from StandardContext.start()
> -
> -        if (log.isDebugEnabled()) {
> -            log.debug(sm.getString("contextConfig.start"));
> -        }
> -
> -        if (log.isDebugEnabled()) {
> -            log.debug(sm.getString("contextConfig.xmlSettings",
> -                    context.getName(),
> -                    Boolean.valueOf(context.getXmlValidation()),
> -                    Boolean.valueOf(context.getXmlNamespaceAware())));
> -        }
> -
> -        webConfig();
> -
> -        if (!context.getIgnoreAnnotations()) {
> -            applicationAnnotationsConfig();
> -        }
> -        if (ok) {
> -            validateSecurityRoles();
> -        }
> -
> -        // Configure an authenticator if we need one
> -        if (ok) {
> -            authenticatorConfig();
> -        }
> -
> -        // Dump the contents of this pipeline if requested
> -        if (log.isDebugEnabled()) {
> -            log.debug("Pipeline Configuration:");
> -            Pipeline pipeline = context.getPipeline();
> -            Valve valves[] = null;
> -            if (pipeline != null) {
> -                valves = pipeline.getValves();
> -            }
> -            if (valves != null) {
> -                for (int i = 0; i < valves.length; i++) {
> -                    log.debug("  " + valves[i].getClass().getName());
> -                }
> -            }
> -            log.debug("======================");
> -        }
> -
> -        // Make our application available if no problems were encountered
> -        if (ok) {
> -            context.setConfigured(true);
> -        } else {
> -            log.error(sm.getString("contextConfig.unavailable"));
> -            context.setConfigured(false);
> -        }
> -
> -    }
> -
> -
> -    /**
> -     * Process a "stop" event for this Context.
> -     */
> -    protected synchronized void configureStop() {
> -
> -        if (log.isDebugEnabled()) {
> -            log.debug(sm.getString("contextConfig.stop"));
> -        }
> -
> -        int i;
> -
> -        // Removing children
> -        Container[] children = context.findChildren();
> -        for (i = 0; i < children.length; i++) {
> -            context.removeChild(children[i]);
> -        }
> -
> -        // Removing application parameters
> -        /*
> -        ApplicationParameter[] applicationParameters =
> -            context.findApplicationParameters();
> -        for (i = 0; i < applicationParameters.length; i++) {
> -            context.removeApplicationParameter
> -                (applicationParameters[i].getName());
> -        }
> -        */
> -
> -        // Removing security constraints
> -        SecurityConstraint[] securityConstraints = context.findConstraints();
> -        for (i = 0; i < securityConstraints.length; i++) {
> -            context.removeConstraint(securityConstraints[i]);
> -        }
> -
> -        // Removing Ejbs
> -        /*
> -        ContextEjb[] contextEjbs = context.findEjbs();
> -        for (i = 0; i < contextEjbs.length; i++) {
> -            context.removeEjb(contextEjbs[i].getName());
> -        }
> -        */
> -
> -        // Removing environments
> -        /*
> -        ContextEnvironment[] contextEnvironments = context.findEnvironments();
> -        for (i = 0; i < contextEnvironments.length; i++) {
> -            context.removeEnvironment(contextEnvironments[i].getName());
> -        }
> -        */
> -
> -        // Removing errors pages
> -        ErrorPage[] errorPages = context.findErrorPages();
> -        for (i = 0; i < errorPages.length; i++) {
> -            context.removeErrorPage(errorPages[i]);
> -        }
> -
> -        // Removing filter defs
> -        FilterDef[] filterDefs = context.findFilterDefs();
> -        for (i = 0; i < filterDefs.length; i++) {
> -            context.removeFilterDef(filterDefs[i]);
> -        }
> -
> -        // Removing filter maps
> -        FilterMap[] filterMaps = context.findFilterMaps();
> -        for (i = 0; i < filterMaps.length; i++) {
> -            context.removeFilterMap(filterMaps[i]);
> -        }
> -
> -        // Removing local ejbs
> -        /*
> -        ContextLocalEjb[] contextLocalEjbs = context.findLocalEjbs();
> -        for (i = 0; i < contextLocalEjbs.length; i++) {
> -            context.removeLocalEjb(contextLocalEjbs[i].getName());
> -        }
> -        */
> -
> -        // Removing Mime mappings
> -        String[] mimeMappings = context.findMimeMappings();
> -        for (i = 0; i < mimeMappings.length; i++) {
> -            context.removeMimeMapping(mimeMappings[i]);
> -        }
> -
> -        // Removing parameters
> -        String[] parameters = context.findParameters();
> -        for (i = 0; i < parameters.length; i++) {
> -            context.removeParameter(parameters[i]);
> -        }
> -
> -        // Removing resource env refs
> -        /*
> -        String[] resourceEnvRefs = context.findResourceEnvRefs();
> -        for (i = 0; i < resourceEnvRefs.length; i++) {
> -            context.removeResourceEnvRef(resourceEnvRefs[i]);
> -        }
> -        */
> -
> -        // Removing resource links
> -        /*
> -        ContextResourceLink[] contextResourceLinks =
> -            context.findResourceLinks();
> -        for (i = 0; i < contextResourceLinks.length; i++) {
> -            context.removeResourceLink(contextResourceLinks[i].getName());
> -        }
> -        */
> -
> -        // Removing resources
> -        /*
> -        ContextResource[] contextResources = context.findResources();
> -        for (i = 0; i < contextResources.length; i++) {
> -            context.removeResource(contextResources[i].getName());
> -        }
> -        */
> -
> -        // Removing security role
> -        String[] securityRoles = context.findSecurityRoles();
> -        for (i = 0; i < securityRoles.length; i++) {
> -            context.removeSecurityRole(securityRoles[i]);
> -        }
> -
> -        // Removing servlet mappings
> -        String[] servletMappings = context.findServletMappings();
> -        for (i = 0; i < servletMappings.length; i++) {
> -            context.removeServletMapping(servletMappings[i]);
> -        }
> -
> -        // FIXME : Removing status pages
> -
> -        // Removing welcome files
> -        String[] welcomeFiles = context.findWelcomeFiles();
> -        for (i = 0; i < welcomeFiles.length; i++) {
> -            context.removeWelcomeFile(welcomeFiles[i]);
> -        }
> -
> -        // Removing wrapper lifecycles
> -        String[] wrapperLifecycles = context.findWrapperLifecycles();
> -        for (i = 0; i < wrapperLifecycles.length; i++) {
> -            context.removeWrapperLifecycle(wrapperLifecycles[i]);
> -        }
> -
> -        // Removing wrapper listeners
> -        String[] wrapperListeners = context.findWrapperListeners();
> -        for (i = 0; i < wrapperListeners.length; i++) {
> -            context.removeWrapperListener(wrapperListeners[i]);
> -        }
> -
> -        // Remove (partially) folders and files created by antiLocking
> -        if (antiLockingDocBase != null) {
> -            // No need to log failure - it is expected in this case
> -            ExpandWar.delete(antiLockingDocBase, false);
> -        }
> -
> -        // Reset ServletContextInitializer scanning
> -        initializerClassMap.clear();
> -        typeInitializerMap.clear();
> -
> -        ok = true;
> -
> -    }
> -
> -
> -    /**
> -     * Process a "destroy" event for this Context.
> -     */
> -    protected synchronized void destroy() {
> -        // Called from StandardContext.destroy()
> -        if (log.isDebugEnabled()) {
> -            log.debug(sm.getString("contextConfig.destroy"));
> -        }
> -
> -        // Skip clearing the work directory if Tomcat is being shutdown
> -        Server s = getServer();
> -        if (s != null && !s.getState().isAvailable()) {
> -            return;
> -        }
> -
> -        // Changed to getWorkPath per Bugzilla 35819.
> -        if (context instanceof StandardContext) {
> -            String workDir = ((StandardContext) context).getWorkPath();
> -            if (workDir != null) {
> -                ExpandWar.delete(new File(workDir));
> -            }
> -        }
> -    }
> -
> -
> -    private Server getServer() {
> -        Container c = context;
> -        while (c != null && !(c instanceof Engine)) {
> -            c = c.getParent();
> -        }
> -
> -        if (c == null) {
> -            return null;
> -        }
> -
> -        Service s = ((Engine)c).getService();
> -
> -        if (s == null) {
> -            return null;
> -        }
> -
> -        return s.getServer();
> -    }
> -
> -    /**
> -     * Validate the usage of security role names in the web application
> -     * deployment descriptor.  If any problems are found, issue warning
> -     * messages (for backwards compatibility) and add the missing roles.
> -     * (To make these problems fatal instead, simply set the <code>ok</code>
> -     * instance variable to <code>false</code> as well).
> -     */
> -    protected void validateSecurityRoles() {
> -
> -        // Check role names used in <security-constraint> elements
> -        SecurityConstraint constraints[] = context.findConstraints();
> -        for (int i = 0; i < constraints.length; i++) {
> -            String roles[] = constraints[i].findAuthRoles();
> -            for (int j = 0; j < roles.length; j++) {
> -                if (!"*".equals(roles[j]) &&
> -                    !context.findSecurityRole(roles[j])) {
> -                    log.warn(sm.getString("contextConfig.role.auth", roles[j]));
> -                    context.addSecurityRole(roles[j]);
> -                }
> -            }
> -        }
> -
> -        // Check role names used in <servlet> elements
> -        Container wrappers[] = context.findChildren();
> -        for (int i = 0; i < wrappers.length; i++) {
> -            Wrapper wrapper = (Wrapper) wrappers[i];
> -            String runAs = wrapper.getRunAs();
> -            if ((runAs != null) && !context.findSecurityRole(runAs)) {
> -                log.warn(sm.getString("contextConfig.role.runas", runAs));
> -                context.addSecurityRole(runAs);
> -            }
> -            String names[] = wrapper.findSecurityReferences();
> -            for (int j = 0; j < names.length; j++) {
> -                String link = wrapper.findSecurityReference(names[j]);
> -                if ((link != null) && !context.findSecurityRole(link)) {
> -                    log.warn(sm.getString("contextConfig.role.link", link));
> -                    context.addSecurityRole(link);
> -                }
> -            }
> -        }
> -
> -    }
> -
> -
> -    protected File getHostConfigBase() {
> -        File file = null;
> -        if (context.getParent() instanceof Host) {
> -            file = ((Host)context.getParent()).getConfigBaseFile();
> -        }
> -        return file;
> -    }
> -
> -    /**
> -     * Scan the web.xml files that apply to the web application and merge them
> -     * using the rules defined in the spec. For the global web.xml files,
> -     * where there is duplicate configuration, the most specific level wins. ie
> -     * an application's web.xml takes precedence over the host level or global
> -     * web.xml file.
> -     */
> -    protected void webConfig() {
> -        /*
> -         * Anything and everything can override the global and host defaults.
> -         * This is implemented in two parts
> -         * - Handle as a web fragment that gets added after everything else so
> -         *   everything else takes priority
> -         * - Mark Servlets as overridable so SCI configuration can replace
> -         *   configuration from the defaults
> -         */
> -
> -        /*
> -         * The rules for annotation scanning are not as clear-cut as one might
> -         * think. Tomcat implements the following process:
> -         * - As per SRV.1.6.2, Tomcat will scan for annotations regardless of
> -         *   which Servlet spec version is declared in web.xml. The EG has
> -         *   confirmed this is the expected behaviour.
> -         * - As per http://java.net/jira/browse/SERVLET_SPEC-36, if the main
> -         *   web.xml is marked as metadata-complete, JARs are still processed
> -         *   for SCIs.
> -         * - If metadata-complete=true and an absolute ordering is specified,
> -         *   JARs excluded from the ordering are also excluded from the SCI
> -         *   processing.
> -         * - If an SCI has a @HandlesType annotation then all classes (except
> -         *   those in JARs excluded from an absolute ordering) need to be
> -         *   scanned to check if they match.
> -         */
> -        WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
> -                context.getXmlValidation(), context.getXmlBlockExternal());
> -
> -        Set<WebXml> defaults = new HashSet<>();
> -        defaults.add(getDefaultWebXmlFragment(webXmlParser));
> -
> -        Set<WebXml> tomcatWebXml = new HashSet<>();
> -        tomcatWebXml.add(getTomcatWebXmlFragment(webXmlParser));
> -
> -        WebXml webXml = createWebXml();
> -
> -        // Parse context level web.xml
> -        InputSource contextWebXml = getContextWebXmlSource();
> -        if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
> -            ok = false;
> -        }
> -
> -        ServletContext sContext = context.getServletContext();
> -
> -        // Ordering is important here
> -
> -        // Step 1. Identify all the JARs packaged with the application and those
> -        // provided by the container. If any of the application JARs have a
> -        // web-fragment.xml it will be parsed at this point. web-fragment.xml
> -        // files are ignored for container provided JARs.
> -        Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
> -
> -        // Step 2. Order the fragments.
> -        Set<WebXml> orderedFragments = null;
> -        orderedFragments =
> -                WebXml.orderWebFragments(webXml, fragments, sContext);
> -
> -        // Step 3. Look for ServletContainerInitializer implementations
> -        if (ok) {
> -            processServletContainerInitializers();
> -        }
> -
> -        if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
> -            // Steps 4 & 5.
> -            processClasses(webXml, orderedFragments);
> -        }
> -
> -        if (!webXml.isMetadataComplete()) {
> -            // Step 6. Merge web-fragment.xml files into the main web.xml
> -            // file.
> -            if (ok) {
> -                ok = webXml.merge(orderedFragments);
> -            }
> -
> -            // Step 7a
> -            // merge tomcat-web.xml
> -            webXml.merge(tomcatWebXml);
> -
> -            // Step 7b. Apply global defaults
> -            // Have to merge defaults before JSP conversion since defaults
> -            // provide JSP servlet definition.
> -            webXml.merge(defaults);
> -
> -            // Step 8. Convert explicitly mentioned jsps to servlets
> -            if (ok) {
> -                convertJsps(webXml);
> -            }
> -
> -            // Step 9. Apply merged web.xml to Context
> -            if (ok) {
> -                configureContext(webXml);
> -            }
> -        } else {
> -            webXml.merge(tomcatWebXml);
> -            webXml.merge(defaults);
> -            convertJsps(webXml);
> -            configureContext(webXml);
> -        }
> -
> -        if (context.getLogEffectiveWebXml()) {
> -            log.info(sm.getString("contextConfig.effectiveWebXml", webXml.toXml()));
> -        }
> -
> -        // Always need to look for static resources
> -        // Step 10. Look for static resources packaged in JARs
> -        if (ok) {
> -            // Spec does not define an order.
> -            // Use ordered JARs followed by remaining JARs
> -            Set<WebXml> resourceJars = new LinkedHashSet<>();
> -            for (WebXml fragment : orderedFragments) {
> -                resourceJars.add(fragment);
> -            }
> -            for (WebXml fragment : fragments.values()) {
> -                if (!resourceJars.contains(fragment)) {
> -                    resourceJars.add(fragment);
> -                }
> -            }
> -            processResourceJARs(resourceJars);
> -            // See also StandardContext.resourcesStart() for
> -            // WEB-INF/classes/META-INF/resources configuration
> -        }
> -
> -        // Step 11. Apply the ServletContainerInitializer config to the
> -        // context
> -        if (ok) {
> -            for (Map.Entry<ServletContainerInitializer,
> -                    Set<Class<?>>> entry :
> -                        initializerClassMap.entrySet()) {
> -                if (entry.getValue().isEmpty()) {
> -                    context.addServletContainerInitializer(
> -                            entry.getKey(), null);
> -                } else {
> -                    context.addServletContainerInitializer(
> -                            entry.getKey(), entry.getValue());
> -                }
> -            }
> -        }
> -    }
> -
> -
> -    protected void processClasses(WebXml webXml, Set<WebXml> orderedFragments) {
> -        // Step 4. Process /WEB-INF/classes for annotations and
> -        // @HandlesTypes matches
> -        Map<String, JavaClassCacheEntry> javaClassCache = new HashMap<>();
> -
> -        if (ok) {
> -            WebResource[] webResources =
> -                    context.getResources().listResources("/WEB-INF/classes");
> -
> -            for (WebResource webResource : webResources) {
> -                // Skip the META-INF directory from any JARs that have been
> -                // expanded in to WEB-INF/classes (sometimes IDEs do this).
> -                if ("META-INF".equals(webResource.getName())) {
> -                    continue;
> -                }
> -                processAnnotationsWebResource(webResource, webXml,
> -                        webXml.isMetadataComplete(), javaClassCache);
> -            }
> -        }
> -
> -        // Step 5. Process JARs for annotations and
> -        // @HandlesTypes matches - only need to process those fragments we
> -        // are going to use (remember orderedFragments includes any
> -        // container fragments)
> -        if (ok) {
> -            processAnnotations(
> -                    orderedFragments, webXml.isMetadataComplete(), javaClassCache);
> -        }
> -
> -        // Cache, if used, is no longer required so clear it
> -        javaClassCache.clear();
> -    }
> -
> -
> -    private void configureContext(WebXml webxml) {
> -        // As far as possible, process in alphabetical order so it is easy to
> -        // check everything is present
> -        // Some validation depends on correct public ID
> -        context.setPublicId(webxml.getPublicId());
> -
> -        // Everything else in order
> -        context.setEffectiveMajorVersion(webxml.getMajorVersion());
> -        context.setEffectiveMinorVersion(webxml.getMinorVersion());
> -
> -        for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
> -            context.addParameter(entry.getKey(), entry.getValue());
> -        }
> -        context.setDenyUncoveredHttpMethods(
> -                webxml.getDenyUncoveredHttpMethods());
> -        context.setDisplayName(webxml.getDisplayName());
> -        context.setDistributable(webxml.isDistributable());
> -        for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
> -            context.getNamingResources().addLocalEjb(ejbLocalRef);
> -        }
> -        for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
> -            context.getNamingResources().addEjb(ejbRef);
> -        }
> -        for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
> -            context.getNamingResources().addEnvironment(environment);
> -        }
> -        for (ErrorPage errorPage : webxml.getErrorPages().values()) {
> -            context.addErrorPage(errorPage);
> -        }
> -        for (FilterDef filter : webxml.getFilters().values()) {
> -            if (filter.getAsyncSupported() == null) {
> -                filter.setAsyncSupported("false");
> -            }
> -            context.addFilterDef(filter);
> -        }
> -        for (FilterMap filterMap : webxml.getFilterMappings()) {
> -            context.addFilterMap(filterMap);
> -        }
> -        context.setJspConfigDescriptor(webxml.getJspConfigDescriptor());
> -        for (String listener : webxml.getListeners()) {
> -            context.addApplicationListener(listener);
> -        }
> -        for (Entry<String, String> entry :
> -                webxml.getLocaleEncodingMappings().entrySet()) {
> -            context.addLocaleEncodingMappingParameter(entry.getKey(),
> -                    entry.getValue());
> -        }
> -        // Prevents IAE
> -        if (webxml.getLoginConfig() != null) {
> -            context.setLoginConfig(webxml.getLoginConfig());
> -        }
> -        for (MessageDestinationRef mdr :
> -                webxml.getMessageDestinationRefs().values()) {
> -            context.getNamingResources().addMessageDestinationRef(mdr);
> -        }
> -
> -        // messageDestinations were ignored in Tomcat 6, so ignore here
> -
> -        context.setIgnoreAnnotations(webxml.isMetadataComplete());
> -        for (Entry<String, String> entry :
> -                webxml.getMimeMappings().entrySet()) {
> -            context.addMimeMapping(entry.getKey(), entry.getValue());
> -        }
> -        context.setRequestCharacterEncoding(webxml.getRequestCharacterEncoding());
> -        // Name is just used for ordering
> -        for (ContextResourceEnvRef resource :
> -                webxml.getResourceEnvRefs().values()) {
> -            context.getNamingResources().addResourceEnvRef(resource);
> -        }
> -        for (ContextResource resource : webxml.getResourceRefs().values()) {
> -            context.getNamingResources().addResource(resource);
> -        }
> -        context.setResponseCharacterEncoding(webxml.getResponseCharacterEncoding());
> -        boolean allAuthenticatedUsersIsAppRole =
> -                webxml.getSecurityRoles().contains(
> -                        SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
> -        for (SecurityConstraint constraint : webxml.getSecurityConstraints()) {
> -            if (allAuthenticatedUsersIsAppRole) {
> -                constraint.treatAllAuthenticatedUsersAsApplicationRole();
> -            }
> -            context.addConstraint(constraint);
> -        }
> -        for (String role : webxml.getSecurityRoles()) {
> -            context.addSecurityRole(role);
> -        }
> -        for (ContextService service : webxml.getServiceRefs().values()) {
> -            context.getNamingResources().addService(service);
> -        }
> -        for (ServletDef servlet : webxml.getServlets().values()) {
> -            Wrapper wrapper = context.createWrapper();
> -            // Description is ignored
> -            // Display name is ignored
> -            // Icons are ignored
> -
> -            // jsp-file gets passed to the JSP Servlet as an init-param
> -
> -            if (servlet.getLoadOnStartup() != null) {
> -                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
> -            }
> -            if (servlet.getEnabled() != null) {
> -                wrapper.setEnabled(servlet.getEnabled().booleanValue());
> -            }
> -            wrapper.setName(servlet.getServletName());
> -            Map<String,String> params = servlet.getParameterMap();
> -            for (Entry<String, String> entry : params.entrySet()) {
> -                wrapper.addInitParameter(entry.getKey(), entry.getValue());
> -            }
> -            wrapper.setRunAs(servlet.getRunAs());
> -            Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
> -            for (SecurityRoleRef roleRef : roleRefs) {
> -                wrapper.addSecurityReference(
> -                        roleRef.getName(), roleRef.getLink());
> -            }
> -            wrapper.setServletClass(servlet.getServletClass());
> -            MultipartDef multipartdef = servlet.getMultipartDef();
> -            if (multipartdef != null) {
> -                if (multipartdef.getMaxFileSize() != null &&
> -                        multipartdef.getMaxRequestSize()!= null &&
> -                        multipartdef.getFileSizeThreshold() != null) {
> -                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
> -                            multipartdef.getLocation(),
> -                            Long.parseLong(multipartdef.getMaxFileSize()),
> -                            Long.parseLong(multipartdef.getMaxRequestSize()),
> -                            Integer.parseInt(
> -                                    multipartdef.getFileSizeThreshold())));
> -                } else {
> -                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
> -                            multipartdef.getLocation()));
> -                }
> -            }
> -            if (servlet.getAsyncSupported() != null) {
> -                wrapper.setAsyncSupported(
> -                        servlet.getAsyncSupported().booleanValue());
> -            }
> -            wrapper.setOverridable(servlet.isOverridable());
> -            context.addChild(wrapper);
> -        }
> -        for (Entry<String, String> entry :
> -                webxml.getServletMappings().entrySet()) {
> -            context.addServletMappingDecoded(entry.getKey(), entry.getValue());
> -        }
> -        SessionConfig sessionConfig = webxml.getSessionConfig();
> -        if (sessionConfig != null) {
> -            if (sessionConfig.getSessionTimeout() != null) {
> -                context.setSessionTimeout(
> -                        sessionConfig.getSessionTimeout().intValue());
> -            }
> -            SessionCookieConfig scc =
> -                context.getServletContext().getSessionCookieConfig();
> -            scc.setName(sessionConfig.getCookieName());
> -            scc.setDomain(sessionConfig.getCookieDomain());
> -            scc.setPath(sessionConfig.getCookiePath());
> -            scc.setComment(sessionConfig.getCookieComment());
> -            if (sessionConfig.getCookieHttpOnly() != null) {
> -                scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());
> -            }
> -            if (sessionConfig.getCookieSecure() != null) {
> -                scc.setSecure(sessionConfig.getCookieSecure().booleanValue());
> -            }
> -            if (sessionConfig.getCookieMaxAge() != null) {
> -                scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());
> -            }
> -            if (sessionConfig.getSessionTrackingModes().size() > 0) {
> -                context.getServletContext().setSessionTrackingModes(
> -                        sessionConfig.getSessionTrackingModes());
> -            }
> -        }
> -
> -        // Context doesn't use version directly
> -
> -        for (String welcomeFile : webxml.getWelcomeFiles()) {
> -            /*
> -             * The following will result in a welcome file of "" so don't add
> -             * that to the context
> -             * <welcome-file-list>
> -             *   <welcome-file/>
> -             * </welcome-file-list>
> -             */
> -            if (welcomeFile != null && welcomeFile.length() > 0) {
> -                context.addWelcomeFile(welcomeFile);
> -            }
> -        }
> -
> -        // Do this last as it depends on servlets
> -        for (JspPropertyGroup jspPropertyGroup :
> -                webxml.getJspPropertyGroups()) {
> -            String jspServletName = context.findServletMapping("*.jsp");
> -            if (jspServletName == null) {
> -                jspServletName = "jsp";
> -            }
> -            if (context.findChild(jspServletName) != null) {
> -                for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
> -                    context.addServletMappingDecoded(urlPattern, jspServletName, true);
> -                }
> -            } else {
> -                if(log.isDebugEnabled()) {
> -                    for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
> -                        log.debug("Skipping " + urlPattern + " , no servlet " +
> -                                jspServletName);
> -                    }
> -                }
> -            }
> -        }
> -
> -        for (Entry<String, String> entry :
> -                webxml.getPostConstructMethods().entrySet()) {
> -            context.addPostConstructMethod(entry.getKey(), entry.getValue());
> -        }
> -
> -        for (Entry<String, String> entry :
> -            webxml.getPreDestroyMethods().entrySet()) {
> -            context.addPreDestroyMethod(entry.getKey(), entry.getValue());
> -        }
> -    }
> -
> -
> -    private WebXml getTomcatWebXmlFragment(WebXmlParser webXmlParser) {
> -
> -        WebXml webXmlTomcatFragment = createWebXml();
> -        webXmlTomcatFragment.setOverridable(true);
> -
> -        // Set to distributable else every app will be prevented from being
> -        // distributable when the Tomcat fragment is merged with the main
> -        // web.xml
> -        webXmlTomcatFragment.setDistributable(true);
> -        // When merging, the default welcome files are only used if the app has
> -        // not defined any welcomes files.
> -        webXmlTomcatFragment.setAlwaysAddWelcomeFiles(false);
> -
> -        WebResource resource = context.getResources().getResource(Constants.TomcatWebXml);
> -        if (resource.isFile()) {
> -            try {
> -                InputSource source = new InputSource(resource.getURL().toURI().toString());
> -                source.setByteStream(resource.getInputStream());
> -                if (!webXmlParser.parseWebXml(source, webXmlTomcatFragment, false)) {
> -                    ok = false;
> -                }
> -            } catch (URISyntaxException e) {
> -                log.error(sm.getString("contextConfig.tomcatWebXmlError"), e);
> -            }
> -        }
> -        return webXmlTomcatFragment;
> -    }
> -
> -
> -    private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {
> -
> -        // Host should never be null
> -        Host host = (Host) context.getParent();
> -
> -        DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
> -
> -        InputSource globalWebXml = getGlobalWebXmlSource();
> -        InputSource hostWebXml = getHostWebXmlSource();
> -
> -        long globalTimeStamp = 0;
> -        long hostTimeStamp = 0;
> -
> -        if (globalWebXml != null) {
> -            URLConnection uc = null;
> -            try {
> -                URL url = new URL(globalWebXml.getSystemId());
> -                uc = url.openConnection();
> -                globalTimeStamp = uc.getLastModified();
> -            } catch (IOException e) {
> -                globalTimeStamp = -1;
> -            } finally {
> -                if (uc != null) {
> -                    try {
> -                        uc.getInputStream().close();
> -                    } catch (IOException e) {
> -                        ExceptionUtils.handleThrowable(e);
> -                        globalTimeStamp = -1;
> -                    }
> -                }
> -            }
> -        }
> -
> -        if (hostWebXml != null) {
> -            URLConnection uc = null;
> -            try {
> -                URL url = new URL(hostWebXml.getSystemId());
> -                uc = url.openConnection();
> -                hostTimeStamp = uc.getLastModified();
> -            } catch (IOException e) {
> -                hostTimeStamp = -1;
> -            } finally {
> -                if (uc != null) {
> -                    try {
> -                        uc.getInputStream().close();
> -                    } catch (IOException e) {
> -                        ExceptionUtils.handleThrowable(e);
> -                        hostTimeStamp = -1;
> -                    }
> -                }
> -            }
> -        }
> -
> -        if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
> -                entry.getHostTimeStamp() == hostTimeStamp) {
> -            InputSourceUtil.close(globalWebXml);
> -            InputSourceUtil.close(hostWebXml);
> -            return entry.getWebXml();
> -        }
> -
> -        // Parsing global web.xml is relatively expensive. Use a sync block to
> -        // make sure it only happens once. Use the pipeline since a lock will
> -        // already be held on the host by another thread
> -        synchronized (host.getPipeline()) {
> -            entry = hostWebXmlCache.get(host);
> -            if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
> -                    entry.getHostTimeStamp() == hostTimeStamp) {
> -                return entry.getWebXml();
> -            }
> -
> -            WebXml webXmlDefaultFragment = createWebXml();
> -            webXmlDefaultFragment.setOverridable(true);
> -            // Set to distributable else every app will be prevented from being
> -            // distributable when the default fragment is merged with the main
> -            // web.xml
> -            webXmlDefaultFragment.setDistributable(true);
> -            // When merging, the default welcome files are only used if the app has
> -            // not defined any welcomes files.
> -            webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
> -
> -            // Parse global web.xml if present
> -            if (globalWebXml == null) {
> -                // This is unusual enough to log
> -                log.info(sm.getString("contextConfig.defaultMissing"));
> -            } else {
> -                if (!webXmlParser.parseWebXml(
> -                        globalWebXml, webXmlDefaultFragment, false)) {
> -                    ok = false;
> -                }
> -            }
> -
> -            // Parse host level web.xml if present
> -            // Additive apart from welcome pages
> -            webXmlDefaultFragment.setReplaceWelcomeFiles(true);
> -
> -            if (!webXmlParser.parseWebXml(
> -                    hostWebXml, webXmlDefaultFragment, false)) {
> -                ok = false;
> -            }
> -
> -            // Don't update the cache if an error occurs
> -            if (globalTimeStamp != -1 && hostTimeStamp != -1) {
> -                entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
> -                        globalTimeStamp, hostTimeStamp);
> -                hostWebXmlCache.put(host, entry);
> -            }
> -
> -            return webXmlDefaultFragment;
> -        }
> -    }
> -
> -
> -    private void convertJsps(WebXml webXml) {
> -        Map<String,String> jspInitParams;
> -        ServletDef jspServlet = webXml.getServlets().get("jsp");
> -        if (jspServlet == null) {
> -            jspInitParams = new HashMap<>();
> -            Wrapper w = (Wrapper) context.findChild("jsp");
> -            if (w != null) {
> -                String[] params = w.findInitParameters();
> -                for (String param : params) {
> -                    jspInitParams.put(param, w.findInitParameter(param));
> -                }
> -            }
> -        } else {
> -            jspInitParams = jspServlet.getParameterMap();
> -        }
> -        for (ServletDef servletDef: webXml.getServlets().values()) {
> -            if (servletDef.getJspFile() != null) {
> -                convertJsp(servletDef, jspInitParams);
> -            }
> -        }
> -    }
> -
> -    private void convertJsp(ServletDef servletDef,
> -            Map<String,String> jspInitParams) {
> -        servletDef.setServletClass(org.apache.catalina.core.Constants.JSP_SERVLET_CLASS);
> -        String jspFile = servletDef.getJspFile();
> -        if ((jspFile != null) && !jspFile.startsWith("/")) {
> -            if (context.isServlet22()) {
> -                if(log.isDebugEnabled()) {
> -                    log.debug(sm.getString("contextConfig.jspFile.warning",
> -                                       jspFile));
> -                }
> -                jspFile = "/" + jspFile;
> -            } else {
> -                throw new IllegalArgumentException
> -                    (sm.getString("contextConfig.jspFile.error", jspFile));
> -            }
> -        }
> -        servletDef.getParameterMap().put("jspFile", jspFile);
> -        servletDef.setJspFile(null);
> -        for (Map.Entry<String, String> initParam: jspInitParams.entrySet()) {
> -            servletDef.addInitParameter(initParam.getKey(), initParam.getValue());
> -        }
> -    }
> -
> -    protected WebXml createWebXml() {
> -        return new WebXml();
> -    }
> -
> -    /**
> -     * Scan JARs for ServletContainerInitializer implementations.
> -     */
> -    protected void processServletContainerInitializers() {
> -
> -        List<ServletContainerInitializer> detectedScis;
> -        try {
> -            WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
> -            detectedScis = loader.load(ServletContainerInitializer.class);
> -        } catch (IOException e) {
> -            log.error(sm.getString(
> -                    "contextConfig.servletContainerInitializerFail",
> -                    context.getName()),
> -                e);
> -            ok = false;
> -            return;
> -        }
> -
> -        for (ServletContainerInitializer sci : detectedScis) {
> -            initializerClassMap.put(sci, new HashSet<Class<?>>());
> -
> -            HandlesTypes ht;
> -            try {
> -                ht = sci.getClass().getAnnotation(HandlesTypes.class);
> -            } catch (Exception e) {
> -                if (log.isDebugEnabled()) {
> -                    log.info(sm.getString("contextConfig.sci.debug",
> -                            sci.getClass().getName()),
> -                            e);
> -                } else {
> -                    log.info(sm.getString("contextConfig.sci.info",
> -                            sci.getClass().getName()));
> -                }
> -                continue;
> -            }
> -            if (ht == null) {
> -                continue;
> -            }
> -            Class<?>[] types = ht.value();
> -            if (types == null) {
> -                continue;
> -            }
> -
> -            for (Class<?> type : types) {
> -                if (type.isAnnotation()) {
> -                    handlesTypesAnnotations = true;
> -                } else {
> -                    handlesTypesNonAnnotations = true;
> -                }
> -                Set<ServletContainerInitializer> scis =
> -                        typeInitializerMap.get(type);
> -                if (scis == null) {
> -                    scis = new HashSet<>();
> -                    typeInitializerMap.put(type, scis);
> -                }
> -                scis.add(sci);
> -            }
> -        }
> -    }
> -
> -    /**
> -     * Scan JARs that contain web-fragment.xml files that will be used to
> -     * configure this application to see if they also contain static resources.
> -     * If static resources are found, add them to the context. Resources are
> -     * added in web-fragment.xml priority order.
> -     * @param fragments The set of fragments that will be scanned for
> -     *  static resources
> -     */
> -    protected void processResourceJARs(Set<WebXml> fragments) {
> -        for (WebXml fragment : fragments) {
> -            URL url = fragment.getURL();
> -            try {
> -                if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
> -                    try (Jar jar = JarFactory.newInstance(url)) {
> -                        jar.nextEntry();
> -                        String entryName = jar.getEntryName();
> -                        while (entryName != null) {
> -                            if (entryName.startsWith("META-INF/resources/")) {
> -                                context.getResources().createWebResourceSet(
> -                                        WebResourceRoot.ResourceSetType.RESOURCE_JAR,
> -                                        "/", url, "/META-INF/resources");
> -                                break;
> -                            }
> -                            jar.nextEntry();
> -                            entryName = jar.getEntryName();
> -                        }
> -                    }
> -                } else if ("file".equals(url.getProtocol())) {
> -                    File file = new File(url.toURI());
> -                    File resources = new File(file, "META-INF/resources/");
> -                    if (resources.isDirectory()) {
> -                        context.getResources().createWebResourceSet(
> -                                WebResourceRoot.ResourceSetType.RESOURCE_JAR,
> -                                "/", resources.getAbsolutePath(), null, "/");
> -                    }
> -                }
> -            } catch (IOException ioe) {
> -                log.error(sm.getString("contextConfig.resourceJarFail", url,
> -                        context.getName()));
> -            } catch (URISyntaxException e) {
> -                log.error(sm.getString("contextConfig.resourceJarFail", url,
> -                    context.getName()));
> -            }
> -        }
> -    }
> -
> -
> -    /**
> -     * Identify the default web.xml to be used and obtain an input source for
> -     * it.
> -     * @return an input source to the default web.xml
> -     */
> -    protected InputSource getGlobalWebXmlSource() {
> -        // Is a default web.xml specified for the Context?
> -        if (defaultWebXml == null && context instanceof StandardContext) {
> -            defaultWebXml = ((StandardContext) context).getDefaultWebXml();
> -        }
> -        // Set the default if we don't have any overrides
> -        if (defaultWebXml == null) {
> -            getDefaultWebXml();
> -        }
> -
> -        // Is it explicitly suppressed, e.g. in embedded environment?
> -        if (Constants.NoDefaultWebXml.equals(defaultWebXml)) {
> -            return null;
> -        }
> -        return getWebXmlSource(defaultWebXml, true);
> -    }
> -
> -    /**
> -     * Identify the host web.xml to be used and obtain an input source for
> -     * it.
> -     * @return an input source to the default per host web.xml
> -     */
> -    protected InputSource getHostWebXmlSource() {
> -        File hostConfigBase = getHostConfigBase();
> -        if (hostConfigBase == null)
> -            return null;
> -
> -        return getWebXmlSource(hostConfigBase.getPath(), false);
> -    }
> -
> -    /**
> -     * Identify the application web.xml to be used and obtain an input source
> -     * for it.
> -     * @return an input source to the context web.xml
> -     */
> -    protected InputSource getContextWebXmlSource() {
> -        InputStream stream = null;
> -        InputSource source = null;
> -        URL url = null;
> -
> -        String altDDName = null;
> -
> -        // Open the application web.xml file, if it exists
> -        ServletContext servletContext = context.getServletContext();
> -        try {
> -            if (servletContext != null) {
> -                altDDName = (String)servletContext.getAttribute(Globals.ALT_DD_ATTR);
> -                if (altDDName != null) {
> -                    try {
> -                        stream = new FileInputStream(altDDName);
> -                        url = new File(altDDName).toURI().toURL();
> -                    } catch (FileNotFoundException e) {
> -                        log.error(sm.getString("contextConfig.altDDNotFound",
> -                                               altDDName));
> -                    } catch (MalformedURLException e) {
> -                        log.error(sm.getString("contextConfig.applicationUrl"));
> -                    }
> -                }
> -                else {
> -                    stream = servletContext.getResourceAsStream
> -                        (Constants.ApplicationWebXml);
> -                    try {
> -                        url = servletContext.getResource(
> -                                Constants.ApplicationWebXml);
> -                    } catch (MalformedURLException e) {
> -                        log.error(sm.getString("contextConfig.applicationUrl"));
> -                    }
> -                }
> -            }
> -            if (stream == null || url == null) {
> -                if (log.isDebugEnabled()) {
> -                    log.debug(sm.getString("contextConfig.applicationMissing") + " " + context);
> -                }
> -            } else {
> -                source = new InputSource(url.toExternalForm());
> -                source.setByteStream(stream);
> -            }
> -        } finally {
> -            if (source == null && stream != null) {
> -                try {
> -                    stream.close();
> -                } catch (IOException e) {
> -                    // Ignore
> -                }
> -            }
> -        }
> -
> -        return source;
> -    }
> -
> -    public String getConfigBasePath() {
> -        String path = null;
> -        if (context.getParent() instanceof Host) {
> -            Host host = (Host) context.getParent();
> -            if (host.getXmlBase() != null) {
> -                path = host.getXmlBase();
> -            } else {
> -                StringBuilder xmlDir = new StringBuilder("conf");
> -                Container parent = host.getParent();
> -                if (parent instanceof Engine) {
> -                    xmlDir.append('/');
> -                    xmlDir.append(parent.getName());
> -                }
> -                xmlDir.append('/');
> -                xmlDir.append(host.getName());
> -                path = xmlDir.toString();
> -            }
> -        }
> -        return path;
> -    }
> -
> -    /**
> -     * Utility method to create an input source from the specified XML file.
> -     * @param filename  Name of the file (possibly with one or more leading path
> -     *                  segments) to read
> -     * @param global true if processing a shared resource, false if processing
> -     *        a host based resource
> -     * @return the input source
> -     */
> -    protected InputSource getWebXmlSource(String filename, boolean global) {
> -        ConfigurationSource.Resource webXmlResource = null;
> -        try {
> -            if (global) {
> -                if (Constants.DefaultWebXml.equals(filename)) {
> -                    webXmlResource = ConfigFileLoader.getSource().getSharedWebXml();
> -                } else {
> -                    webXmlResource = ConfigFileLoader.getSource().getResource(filename);
> -                }
> -            } else {
> -                String hostWebXml = Container.getConfigPath(context, Constants.HostWebXml);
> -                webXmlResource = ConfigFileLoader.getSource().getConfResource(hostWebXml);
> -            }
> -        } catch (IOException e) {
> -            // Ignore if not found
> -            return null;
> -        }
> -
> -        InputStream stream = null;
> -        InputSource source = null;
> -
> -        try {
> -            stream = webXmlResource.getInputStream();
> -            source = new InputSource(webXmlResource.getURI().toString());
> -            if (stream != null) {
> -                source.setByteStream(stream);
> -            }
> -        } catch (Exception e) {
> -            log.error(sm.getString("contextConfig.defaultError", filename, webXmlResource.getURI()), e);
> -        } finally {
> -            if (source == null && stream != null) {
> -                try {
> -                    stream.close();
> -                } catch (IOException e) {
> -                    // Ignore
> -                }
> -            }
> -        }
> -
> -        return source;
> -    }
> -
> -
> -    /**
> -     * Scan /WEB-INF/lib for JARs and for each one found add it and any
> -     * /META-INF/web-fragment.xml to the resulting Map. web-fragment.xml files
> -     * will be parsed before being added to the map. Every JAR will be added and
> -     * <code>null</code> will be used if no web-fragment.xml was found. Any JARs
> -     * known not contain fragments will be skipped.
> -     *
> -     * @param application The main web.xml metadata
> -     * @param webXmlParser The parser to use to process the web.xml file
> -     * @return A map of JAR name to processed web fragment (if any)
> -     */
> -    protected Map<String,WebXml> processJarsForWebFragments(WebXml application,
> -            WebXmlParser webXmlParser) {
> -
> -        JarScanner jarScanner = context.getJarScanner();
> -        boolean delegate = false;
> -        if (context instanceof StandardContext) {
> -            delegate = ((StandardContext) context).getDelegate();
> -        }
> -        boolean parseRequired = true;
> -        Set<String> absoluteOrder = application.getAbsoluteOrdering();
> -        if (absoluteOrder != null && absoluteOrder.isEmpty() &&
> -                !context.getXmlValidation()) {
> -            // Skip parsing when there is an empty absolute ordering and
> -            // validation is not enabled
> -            parseRequired = false;
> -        }
> -        FragmentJarScannerCallback callback =
> -                new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);
> -
> -        jarScanner.scan(JarScanType.PLUGGABILITY,
> -                context.getServletContext(), callback);
> -
> -        if (!callback.isOk()) {
> -            ok = false;
> -        }
> -        return callback.getFragments();
> -    }
> -
> -    protected void processAnnotations(Set<WebXml> fragments,
> -            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
> -        for(WebXml fragment : fragments) {
> -            // Only need to scan for @HandlesTypes matches if any of the
> -            // following are true:
> -            // - it has already been determined only @HandlesTypes is required
> -            //   (e.g. main web.xml has metadata-complete="true"
> -            // - this fragment is for a container JAR (Servlet 3.1 section 8.1)
> -            // - this fragment has metadata-complete="true"
> -            boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() ||
> -                    fragment.isMetadataComplete();
> -
> -            WebXml annotations = new WebXml();
> -            // no impact on distributable
> -            annotations.setDistributable(true);
> -            URL url = fragment.getURL();
> -            processAnnotationsUrl(url, annotations, htOnly, javaClassCache);
> -            Set<WebXml> set = new HashSet<>();
> -            set.add(annotations);
> -            // Merge annotations into fragment - fragment takes priority
> -            fragment.merge(set);
> -        }
> -    }
> -
> -    protected void processAnnotationsWebResource(WebResource webResource,
> -            WebXml fragment, boolean handlesTypesOnly,
> -            Map<String,JavaClassCacheEntry> javaClassCache) {
> -
> -        if (webResource.isDirectory()) {
> -            WebResource[] webResources =
> -                    webResource.getWebResourceRoot().listResources(
> -                            webResource.getWebappPath());
> -            if (webResources.length > 0) {
> -                if (log.isDebugEnabled()) {
> -                    log.debug(sm.getString(
> -                            "contextConfig.processAnnotationsWebDir.debug",
> -                            webResource.getURL()));
> -                }
> -                for (WebResource r : webResources) {
> -                    processAnnotationsWebResource(r, fragment, handlesTypesOnly, javaClassCache);
> -                }
> -            }
> -        } else if (webResource.isFile() &&
> -                webResource.getName().endsWith(".class")) {
> -            try (InputStream is = webResource.getInputStream()) {
> -                processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
> -            } catch (IOException e) {
> -                log.error(sm.getString("contextConfig.inputStreamWebResource",
> -                        webResource.getWebappPath()),e);
> -            } catch (ClassFormatException e) {
> -                log.error(sm.getString("contextConfig.inputStreamWebResource",
> -                        webResource.getWebappPath()),e);
> -            }
> -        }
> -    }
> -
> -
> -    protected void processAnnotationsUrl(URL url, WebXml fragment,
> -            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
> -        if (url == null) {
> -            // Nothing to do.
> -            return;
> -        } else if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
> -            processAnnotationsJar(url, fragment, handlesTypesOnly, javaClassCache);
> -        } else if ("file".equals(url.getProtocol())) {
> -            try {
> -                processAnnotationsFile(
> -                        new File(url.toURI()), fragment, handlesTypesOnly, javaClassCache);
> -            } catch (URISyntaxException e) {
> -                log.error(sm.getString("contextConfig.fileUrl", url), e);
> -            }
> -        } else {
> -            log.error(sm.getString("contextConfig.unknownUrlProtocol",
> -                    url.getProtocol(), url));
> -        }
> -
> -    }
> -
> -
> -    protected void processAnnotationsJar(URL url, WebXml fragment,
> -            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
> -
> -        try (Jar jar = JarFactory.newInstance(url)) {
> -            if (log.isDebugEnabled()) {
> -                log.debug(sm.getString(
> -                        "contextConfig.processAnnotationsJar.debug", url));
> -            }
> -
> -            jar.nextEntry();
> -            String entryName = jar.getEntryName();
> -            while (entryName != null) {
> -                if (entryName.endsWith(".class")) {
> -                    try (InputStream is = jar.getEntryInputStream()) {
> -                        processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
> -                    } catch (IOException e) {
> -                        log.error(sm.getString("contextConfig.inputStreamJar",
> -                                entryName, url),e);
> -                    } catch (ClassFormatException e) {
> -                        log.error(sm.getString("contextConfig.inputStreamJar",
> -                                entryName, url),e);
> -                    }
> -                }
> -                jar.nextEntry();
> -                entryName = jar.getEntryName();
> -            }
> -        } catch (IOException e) {
> -            log.error(sm.getString("contextConfig.jarFile", url), e);
> -        }
> -    }
> -
> -
> -    protected void processAnnotationsFile(File file, WebXml fragment,
> -            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
> -
> -        if (file.isDirectory()) {
> -            // Returns null if directory is not readable
> -            String[] dirs = file.list();
> -            if (dirs != null) {
> -                if (log.isDebugEnabled()) {
> -                    log.debug(sm.getString(
> -                            "contextConfig.processAnnotationsDir.debug", file));
> -                }
> -                for (String dir : dirs) {
> -                    processAnnotationsFile(
> -                            new File(file,dir), fragment, handlesTypesOnly, javaClassCache);
> -                }
> -            }
> -        } else if (file.getName().endsWith(".class") && file.canRead()) {
> -            try (FileInputStream fis = new FileInputStream(file)) {
> -                processAnnotationsStream(fis, fragment, handlesTypesOnly, javaClassCache);
> -            } catch (IOException e) {
> -                log.error(sm.getString("contextConfig.inputStreamFile",
> -                        file.getAbsolutePath()),e);
> -            } catch (ClassFormatException e) {
> -                log.error(sm.getString("contextConfig.inputStreamFile",
> -                        file.getAbsolutePath()),e);
> -            }
> -        }
> -    }
> -
> -
> -    protected void processAnnotationsStream(InputStream is, WebXml fragment,
> -            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache)
> -            throws ClassFormatException, IOException {
> -
> -        ClassParser parser = new ClassParser(is);
> -        JavaClass clazz = parser.parse();
> -        checkHandlesTypes(clazz, javaClassCache);
> -
> -        if (handlesTypesOnly) {
> -            return;
> -        }
> -
> -        processClass(fragment, clazz);
> -    }
> -
> -
> -    protected void processClass(WebXml fragment, JavaClass clazz) {
> -        AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
> -        if (annotationsEntries != null) {
> -            String className = clazz.getClassName();
> -            for (AnnotationEntry ae : annotationsEntries) {
> -                String type = ae.getAnnotationType();
> -                if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
> -                    processAnnotationWebServlet(className, ae, fragment);
> -                }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
> -                    processAnnotationWebFilter(className, ae, fragment);
> -                }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
> -                    fragment.addListener(className);
> -                } else {
> -                    // Unknown annotation - ignore
> -                }
> -            }
> -        }
> -    }
> -
> -
> -    /**
> -     * For classes packaged with the web application, the class and each
> -     * super class needs to be checked for a match with {@link HandlesTypes} or
> -     * for an annotation that matches {@link HandlesTypes}.
> -     * @param javaClass the class to check
> -     * @param javaClassCache a class cache
> -     */
> -    protected void checkHandlesTypes(JavaClass javaClass,
> -            Map<String,JavaClassCacheEntry> javaClassCache) {
> -
> -        // Skip this if we can
> -        if (typeInitializerMap.size() == 0) {
> -            return;
> -        }
> -
> -        if ((javaClass.getAccessFlags() &
> -                org.apache.tomcat.util.bcel.Const.ACC_ANNOTATION) != 0) {
> -            // Skip annotations.
> -            return;
> -        }
> -
> -        String className = javaClass.getClassName();
> -
> -        Class<?> clazz = null;
> -        if (handlesTypesNonAnnotations) {
> -            // This *might* be match for a HandlesType.
> -            populateJavaClassCache(className, javaClass, javaClassCache);
> -            JavaClassCacheEntry entry = javaClassCache.get(className);
> -            if (entry.getSciSet() == null) {
> -                try {
> -                    populateSCIsForCacheEntry(entry, javaClassCache);
> -                } catch (StackOverflowError soe) {
> -                    throw new IllegalStateException(sm.getString(
> -                            "contextConfig.annotationsStackOverflow",
> -                            context.getName(),
> -                            classHierarchyToString(className, entry, javaClassCache)));
> -                }
> -            }
> -            if (!entry.getSciSet().isEmpty()) {
> -                // Need to try and load the class
> -                clazz = Introspection.loadClass(context, className);
> -                if (clazz == null) {
> -                    // Can't load the class so no point continuing
> -                    return;
> -                }
> -
> -                for (ServletContainerInitializer sci : entry.getSciSet()) {
> -                    Set<Class<?>> classes = initializerClassMap.get(sci);
> -                    if (classes == null) {
> -                        classes = new HashSet<>();
> -                        initializerClassMap.put(sci, classes);
> -                    }
> -                    classes.add(clazz);
> -                }
> -            }
> -        }
> -
> -        if (handlesTypesAnnotations) {
> -            AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries();
> -            if (annotationEntries != null) {
> -                for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
> -                        typeInitializerMap.entrySet()) {
> -                    if (entry.getKey().isAnnotation()) {
> -                        String entryClassName = entry.getKey().getName();
> -                        for (AnnotationEntry annotationEntry : annotationEntries) {
> -                            if (entryClassName.equals(
> -                                    getClassName(annotationEntry.getAnnotationType()))) {
> -                                if (clazz == null) {
> -                                    clazz = Introspection.loadClass(
> -                                            context, className);
> -                                    if (clazz == null) {
> -                                        // Can't load the class so no point
> -                                        // continuing
> -                                        return;
> -                                    }
> -                                }
> -                                for (ServletContainerInitializer sci : entry.getValue()) {
> -                                    initializerClassMap.get(sci).add(clazz);
> -                                }
> -                                break;
> -                            }
> -                        }
> -                    }
> -                }
> -            }
> -        }
> -    }
> -
> -
> -    private String classHierarchyToString(String className,
> -            JavaClassCacheEntry entry, Map<String,JavaClassCacheEntry> javaClassCache) {
> -        JavaClassCacheEntry start = entry;
> -        StringBuilder msg = new StringBuilder(className);
> -        msg.append("->");
> -
> -        String parentName = entry.getSuperclassName();
> -        JavaClassCacheEntry parent = javaClassCache.get(parentName);
> -        int count = 0;
> -
> -        while (count < 100 && parent != null && parent != start) {
> -            msg.append(parentName);
> -            msg.append("->");
> -
> -            count ++;
> -            parentName = parent.getSuperclassName();
> -            parent = javaClassCache.get(parentName);
> -        }
> -
> -        msg.append(parentName);
> -
> -        return msg.toString();
> -    }
> -
> -    private void populateJavaClassCache(String className, JavaClass javaClass,
> -            Map<String,JavaClassCacheEntry> javaClassCache) {
> -        if (javaClassCache.containsKey(className)) {
> -            return;
> -        }
> -
> -        // Add this class to the cache
> -        javaClassCache.put(className, new JavaClassCacheEntry(javaClass));
> -
> -        populateJavaClassCache(javaClass.getSuperclassName(), javaClassCache);
> -
> -        for (String interfaceName : javaClass.getInterfaceNames()) {
> -            populateJavaClassCache(interfaceName, javaClassCache);
> -        }
> -    }
> -
> -    private void populateJavaClassCache(String className,
> -            Map<String,JavaClassCacheEntry> javaClassCache) {
> -        if (!javaClassCache.containsKey(className)) {
> -            String name = className.replace('.', '/') + ".class";
> -            try (InputStream is = context.getLoader().getClassLoader().getResourceAsStream(name)) {
> -                if (is == null) {
> -                    return;
> -                }
> -                ClassParser parser = new ClassParser(is);
> -                JavaClass clazz = parser.parse();
> -                populateJavaClassCache(clazz.getClassName(), clazz, javaClassCache);
> -            } catch (ClassFormatException e) {
> -                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
> -                        className), e);
> -            } catch (IOException e) {
> -                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
> -                        className), e);
> -            }
> -        }
> -    }
> -
> -    private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry,
> -            Map<String,JavaClassCacheEntry> javaClassCache) {
> -        Set<ServletContainerInitializer> result = new HashSet<>();
> -
> -        // Super class
> -        String superClassName = cacheEntry.getSuperclassName();
> -        JavaClassCacheEntry superClassCacheEntry =
> -                javaClassCache.get(superClassName);
> -
> -        // Avoid an infinite loop with java.lang.Object
> -        if (cacheEntry.equals(superClassCacheEntry)) {
> -            cacheEntry.setSciSet(EMPTY_SCI_SET);
> -            return;
> -        }
> -
> -        // May be null of the class is not present or could not be loaded.
> -        if (superClassCacheEntry != null) {
> -            if (superClassCacheEntry.getSciSet() == null) {
> -                populateSCIsForCacheEntry(superClassCacheEntry, javaClassCache);
> -            }
> -            result.addAll(superClassCacheEntry.getSciSet());
> -        }
> -        result.addAll(getSCIsForClass(superClassName));
> -
> -        // Interfaces
> -        for (String interfaceName : cacheEntry.getInterfaceNames()) {
> -            JavaClassCacheEntry interfaceEntry =
> -                    javaClassCache.get(interfaceName);
> -            // A null could mean that the class not present in application or
> -            // that there is nothing of interest. Either way, nothing to do here
> -            // so move along
> -            if (interfaceEntry != null) {
> -                if (interfaceEntry.getSciSet() == null) {
> -                    populateSCIsForCacheEntry(interfaceEntry, javaClassCache);
> -                }
> -                result.addAll(interfaceEntry.getSciSet());
> -            }
> -            result.addAll(getSCIsForClass(interfaceName));
> -        }
> -
> -        cacheEntry.setSciSet(result.isEmpty() ? EMPTY_SCI_SET : result);
> -    }
> -
> -    private Set<ServletContainerInitializer> getSCIsForClass(String className) {
> -        for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
> -                typeInitializerMap.entrySet()) {
> -            Class<?> clazz = entry.getKey();
> -            if (!clazz.isAnnotation()) {
> -                if (clazz.getName().equals(className)) {
> -                    return entry.getValue();
> -                }
> -            }
> -        }
> -        return EMPTY_SCI_SET;
> -    }
> -
> -    private static final String getClassName(String internalForm) {
> -        if (!internalForm.startsWith("L")) {
> -            return internalForm;
> -        }
> -
> -        // Assume starts with L, ends with ; and uses / rather than .
> -        return internalForm.substring(1,
> -                internalForm.length() - 1).replace('/', '.');
> -    }
> -
> -    protected void processAnnotationWebServlet(String className,
> -            AnnotationEntry ae, WebXml fragment) {
> -        String servletName = null;
> -        // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
> -        List<ElementValuePair> evps = ae.getElementValuePairs();
> -        for (ElementValuePair evp : evps) {
> -            String name = evp.getNameString();
> -            if ("name".equals(name)) {
> -                servletName = evp.getValue().stringifyValue();
> -                break;
> -            }
> -        }
> -        if (servletName == null) {
> -            // classname is default servletName as annotation has no name!
> -            servletName = className;
> -        }
> -        ServletDef servletDef = fragment.getServlets().get(servletName);
> -
> -        boolean isWebXMLservletDef;
> -        if (servletDef == null) {
> -            servletDef = new ServletDef();
> -            servletDef.setServletName(servletName);
> -            servletDef.setServletClass(className);
> -            isWebXMLservletDef = false;
> -        } else {
> -            isWebXMLservletDef = true;
> -        }
> -
> -        boolean urlPatternsSet = false;
> -        String[] urlPatterns = null;
> -
> -        // List<ElementValuePair> evps = ae.getElementValuePairs();
> -        for (ElementValuePair evp : evps) {
> -            String name = evp.getNameString();
> -            if ("value".equals(name) || "urlPatterns".equals(name)) {
> -                if (urlPatternsSet) {
> -                    throw new IllegalArgumentException(sm.getString(
> -                            "contextConfig.urlPatternValue", "WebServlet", className));
> -                }
> -                urlPatternsSet = true;
> -                urlPatterns = processAnnotationsStringArray(evp.getValue());
> -            } else if ("description".equals(name)) {
> -                if (servletDef.getDescription() == null) {
> -                    servletDef.setDescription(evp.getValue().stringifyValue());
> -                }
> -            } else if ("displayName".equals(name)) {
> -                if (servletDef.getDisplayName() == null) {
> -                    servletDef.setDisplayName(evp.getValue().stringifyValue());
> -                }
> -            } else if ("largeIcon".equals(name)) {
> -                if (servletDef.getLargeIcon() == null) {
> -                    servletDef.setLargeIcon(evp.getValue().stringifyValue());
> -                }
> -            } else if ("smallIcon".equals(name)) {
> -                if (servletDef.getSmallIcon() == null) {
> -                    servletDef.setSmallIcon(evp.getValue().stringifyValue());
> -                }
> -            } else if ("asyncSupported".equals(name)) {
> -                if (servletDef.getAsyncSupported() == null) {
> -                    servletDef.setAsyncSupported(evp.getValue()
> -                            .stringifyValue());
> -                }
> -            } else if ("loadOnStartup".equals(name)) {
> -                if (servletDef.getLoadOnStartup() == null) {
> -                    servletDef
> -                            .setLoadOnStartup(evp.getValue().stringifyValue());
> -                }
> -            } else if ("initParams".equals(name)) {
> -                Map<String, String> initParams = processAnnotationWebInitParams(evp
> -                        .getValue());
> -                if (isWebXMLservletDef) {
> -                    Map<String, String> webXMLInitParams = servletDef
> -                            .getParameterMap();
> -                    for (Map.Entry<String, String> entry : initParams
> -                            .entrySet()) {
> -                        if (webXMLInitParams.get(entry.getKey()) == null) {
> -                            servletDef.addInitParameter(entry.getKey(), entry
> -                                    .getValue());
> -                        }
> -                    }
> -                } else {
> -                    for (Map.Entry<String, String> entry : initParams
> -                            .entrySet()) {
> -                        servletDef.addInitParameter(entry.getKey(), entry
> -                                .getValue());
> -                    }
> -                }
> -            }
> -        }
> -        if (!isWebXMLservletDef && urlPatterns != null) {
> -            fragment.addServlet(servletDef);
> -        }
> -        if (urlPatterns != null) {
> -            if (!fragment.getServletMappings().containsValue(servletName)) {
> -                for (String urlPattern : urlPatterns) {
> -                    fragment.addServletMapping(urlPattern, servletName);
> -                }
> -            }
> -        }
> -
> -    }
> -
> -    /**
> -     * process filter annotation and merge with existing one!
> -     * FIXME: refactoring method too long and has redundant subroutines with
> -     *        processAnnotationWebServlet!
> -     * @param className The filter class name
> -     * @param ae The filter annotation
> -     * @param fragment The corresponding fragment
> -     */
> -    protected void processAnnotationWebFilter(String className,
> -            AnnotationEntry ae, WebXml fragment) {
> -        String filterName = null;
> -        // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
> -        List<ElementValuePair> evps = ae.getElementValuePairs();
> -        for (ElementValuePair evp : evps) {
> -            String name = evp.getNameString();
> -            if ("filterName".equals(name)) {
> -                filterName = evp.getValue().stringifyValue();
> -                break;
> -            }
> -        }
> -        if (filterName == null) {
> -            // classname is default filterName as annotation has no name!
> -            filterName = className;
> -        }
> -        FilterDef filterDef = fragment.getFilters().get(filterName);
> -        FilterMap filterMap = new FilterMap();
> -
> -        boolean isWebXMLfilterDef;
> -        if (filterDef == null) {
> -            filterDef = new FilterDef();
> -            filterDef.setFilterName(filterName);
> -            filterDef.setFilterClass(className);
> -            isWebXMLfilterDef = false;
> -        } else {
> -            isWebXMLfilterDef = true;
> -        }
> -
> -        boolean urlPatternsSet = false;
> -        boolean servletNamesSet = false;
> -        boolean dispatchTypesSet = false;
> -        String[] urlPatterns = null;
> -
> -        for (ElementValuePair evp : evps) {
> -            String name = evp.getNameString();
> -            if ("value".equals(name) || "urlPatterns".equals(name)) {
> -                if (urlPatternsSet) {
> -                    throw new IllegalArgumentException(sm.getString(
> -                            "contextConfig.urlPatternValue", "WebFilter", className));
> -                }
> -                urlPatterns = processAnnotationsStringArray(evp.getValue());
> -                urlPatternsSet = urlPatterns.length > 0;
> -                for (String urlPattern : urlPatterns) {
> -                    // % decoded (if required) using UTF-8
> -                    filterMap.addURLPattern(urlPattern);
> -                }
> -            } else if ("servletNames".equals(name)) {
> -                String[] servletNames = processAnnotationsStringArray(evp
> -                        .getValue());
> -                servletNamesSet = servletNames.length > 0;
> -                for (String servletName : servletNames) {
> -                    filterMap.addServletName(servletName);
> -                }
> -            } else if ("dispatcherTypes".equals(name)) {
> -                String[] dispatcherTypes = processAnnotationsStringArray(evp
> -                        .getValue());
> -                dispatchTypesSet = dispatcherTypes.length > 0;
> -                for (String dispatcherType : dispatcherTypes) {
> -                    filterMap.setDispatcher(dispatcherType);
> -                }
> -            } else if ("description".equals(name)) {
> -                if (filterDef.getDescription() == null) {
> -                    filterDef.setDescription(evp.getValue().stringifyValue());
> -                }
> -            } else if ("displayName".equals(name)) {
> -                if (filterDef.getDisplayName() == null) {
> -                    filterDef.setDisplayName(evp.getValue().stringifyValue());
> -                }
> -            } else if ("largeIcon".equals(name)) {
> -                if (filterDef.getLargeIcon() == null) {
> -                    filterDef.setLargeIcon(evp.getValue().stringifyValue());
> -                }
> -            } else if ("smallIcon".equals(name)) {
> -                if (filterDef.getSmallIcon() == null) {
> -                    filterDef.setSmallIcon(evp.getValue().stringifyValue());
> -                }
> -            } else if ("asyncSupported".equals(name)) {
> -                if (filterDef.getAsyncSupported() == null) {
> -                    filterDef
> -                            .setAsyncSupported(evp.getValue().stringifyValue());
> -                }
> -            } else if ("initParams".equals(name)) {
> -                Map<String, String> initParams = processAnnotationWebInitParams(evp
> -                        .getValue());
> -                if (isWebXMLfilterDef) {
> -                    Map<String, String> webXMLInitParams = filterDef
> -                            .getParameterMap();
> -                    for (Map.Entry<String, String> entry : initParams
> -                            .entrySet()) {
> -                        if (webXMLInitParams.get(entry.getKey()) == null) {
> -                            filterDef.addInitParameter(entry.getKey(), entry
> -                                    .getValue());
> -                        }
> -                    }
> -                } else {
> -                    for (Map.Entry<String, String> entry : initParams
> -                            .entrySet()) {
> -                        filterDef.addInitParameter(entry.getKey(), entry
> -                                .getValue());
> -                    }
> -                }
> -
> -            }
> -        }
> -        if (!isWebXMLfilterDef) {
> -            fragment.addFilter(filterDef);
> -            if (urlPatternsSet || servletNamesSet) {
> -                filterMap.setFilterName(filterName);
> -                fragment.addFilterMapping(filterMap);
> -            }
> -        }
> -        if (urlPatternsSet || dispatchTypesSet) {
> -            Set<FilterMap> fmap = fragment.getFilterMappings();
> -            FilterMap descMap = null;
> -            for (FilterMap map : fmap) {
> -                if (filterName.equals(map.getFilterName())) {
> -                    descMap = map;
> -                    break;
> -                }
> -            }
> -            if (descMap != null) {
> -                String[] urlsPatterns = descMap.getURLPatterns();
> -                if (urlPatternsSet
> -                        && (urlsPatterns == null || urlsPatterns.length == 0)) {
> -                    for (String urlPattern : filterMap.getURLPatterns()) {
> -                        // % decoded (if required) using UTF-8
> -                        descMap.addURLPattern(urlPattern);
> -                    }
> -                }
> -                String[] dispatcherNames = descMap.getDispatcherNames();
> -                if (dispatchTypesSet
> -                        && (dispatcherNames == null || dispatcherNames.length == 0)) {
> -                    for (String dis : filterMap.getDispatcherNames()) {
> -                        descMap.setDispatcher(dis);
> -                    }
> -                }
> -            }
> -        }
> -
> -    }
> -
> -    protected String[] processAnnotationsStringArray(ElementValue ev) {
> -        List<String> values = new ArrayList<>();
> -        if (ev instanceof ArrayElementValue) {
> -            ElementValue[] arrayValues =
> -                ((ArrayElementValue) ev).getElementValuesArray();
> -            for (ElementValue value : arrayValues) {
> -                values.add(value.stringifyValue());
> -            }
> -        } else {
> -            values.add(ev.stringifyValue());
> -        }
> -        String[] result = new String[values.size()];
> -        return values.toArray(result);
> -    }
> -
> -    protected Map<String,String> processAnnotationWebInitParams(
> -            ElementValue ev) {
> -        Map<String, String> result = new HashMap<>();
> -        if (ev instanceof ArrayElementValue) {
> -            ElementValue[] arrayValues =
> -                ((ArrayElementValue) ev).getElementValuesArray();
> -            for (ElementValue value : arrayValues) {
> -                if (value instanceof AnnotationElementValue) {
> -                    List<ElementValuePair> evps = ((AnnotationElementValue) value)
> -                            .getAnnotationEntry().getElementValuePairs();
> -                    String initParamName = null;
> -                    String initParamValue = null;
> -                    for (ElementValuePair evp : evps) {
> -                        if ("name".equals(evp.getNameString())) {
> -                            initParamName = evp.getValue().stringifyValue();
> -                        } else if ("value".equals(evp.getNameString())) {
> -                            initParamValue = evp.getValue().stringifyValue();
> -                        } else {
> -                            // Ignore
> -                        }
> -                    }
> -                    result.put(initParamName, initParamValue);
> -                }
> -            }
> -        }
> -        return result;
> -    }
> -
> -    private static class DefaultWebXmlCacheEntry {
> -        private final WebXml webXml;
> -        private final long globalTimeStamp;
> -        private final long hostTimeStamp;
> -
> -        public DefaultWebXmlCacheEntry(WebXml webXml, long globalTimeStamp,
> -                long hostTimeStamp) {
> -            this.webXml = webXml;
> -            this.globalTimeStamp = globalTimeStamp;
> -            this.hostTimeStamp = hostTimeStamp;
> -        }
> -
> -        public WebXml getWebXml() {
> -            return webXml;
> -        }
> -
> -        public long getGlobalTimeStamp() {
> -            return globalTimeStamp;
> -        }
> -
> -        public long getHostTimeStamp() {
> -            return hostTimeStamp;
> -        }
> -    }
> -
> -    static class JavaClassCacheEntry {
> -        public final String superclassName;
> -
> -        public final String[] interfaceNames;
> -
> -        private Set<ServletContainerInitializer> sciSet = null;
> -
> -        public JavaClassCacheEntry(JavaClass javaClass) {
> -            superclassName = javaClass.getSuperclassName();
> -            interfaceNames = javaClass.getInterfaceNames();
> -        }
> -
> -        public String getSuperclassName() {
> -            return superclassName;
> -        }
> -
> -        public String[] getInterfaceNames() {
> -            return interfaceNames;
> -        }
> -
> -        public Set<ServletContainerInitializer> getSciSet() {
> -            return sciSet;
> -        }
> -
> -        public void setSciSet(Set<ServletContainerInitializer> sciSet) {
> -            this.sciSet = sciSet;
> -        }
> -    }
> -}
> +/*
> + * 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.startup;
> +
> +import java.io.File;
> +import java.io.FileInputStream;
> +import java.io.FileNotFoundException;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.net.MalformedURLException;
> +import java.net.URISyntaxException;
> +import java.net.URL;
> +import java.net.URLConnection;
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.HashMap;
> +import java.util.HashSet;
> +import java.util.LinkedHashMap;
> +import java.util.LinkedHashSet;
> +import java.util.List;
> +import java.util.Locale;
> +import java.util.Map;
> +import java.util.Map.Entry;
> +import java.util.Properties;
> +import java.util.Set;
> +import java.util.concurrent.ConcurrentHashMap;
> +
> +import javax.servlet.MultipartConfigElement;
> +import javax.servlet.ServletContainerInitializer;
> +import javax.servlet.ServletContext;
> +import javax.servlet.SessionCookieConfig;
> +import javax.servlet.annotation.HandlesTypes;
> +
> +import org.apache.catalina.Authenticator;
> +import org.apache.catalina.Container;
> +import org.apache.catalina.Context;
> +import org.apache.catalina.Engine;
> +import org.apache.catalina.Globals;
> +import org.apache.catalina.Host;
> +import org.apache.catalina.Lifecycle;
> +import org.apache.catalina.LifecycleEvent;
> +import org.apache.catalina.LifecycleListener;
> +import org.apache.catalina.Pipeline;
> +import org.apache.catalina.Server;
> +import org.apache.catalina.Service;
> +import org.apache.catalina.Valve;
> +import org.apache.catalina.WebResource;
> +import org.apache.catalina.WebResourceRoot;
> +import org.apache.catalina.Wrapper;
> +import org.apache.catalina.core.StandardContext;
> +import org.apache.catalina.core.StandardHost;
> +import org.apache.catalina.util.ContextName;
> +import org.apache.catalina.util.Introspection;
> +import org.apache.juli.logging.Log;
> +import org.apache.juli.logging.LogFactory;
> +import org.apache.tomcat.Jar;
> +import org.apache.tomcat.JarScanType;
> +import org.apache.tomcat.JarScanner;
> +import org.apache.tomcat.util.ExceptionUtils;
> +import org.apache.tomcat.util.bcel.classfile.AnnotationElementValue;
> +import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;
> +import org.apache.tomcat.util.bcel.classfile.ArrayElementValue;
> +import org.apache.tomcat.util.bcel.classfile.ClassFormatException;
> +import org.apache.tomcat.util.bcel.classfile.ClassParser;
> +import org.apache.tomcat.util.bcel.classfile.ElementValue;
> +import org.apache.tomcat.util.bcel.classfile.ElementValuePair;
> +import org.apache.tomcat.util.bcel.classfile.JavaClass;
> +import org.apache.tomcat.util.buf.UriUtil;
> +import org.apache.tomcat.util.descriptor.InputSourceUtil;
> +import org.apache.tomcat.util.descriptor.XmlErrorHandler;
> +import org.apache.tomcat.util.descriptor.web.ContextEjb;
> +import org.apache.tomcat.util.descriptor.web.ContextEnvironment;
> +import org.apache.tomcat.util.descriptor.web.ContextLocalEjb;
> +import org.apache.tomcat.util.descriptor.web.ContextResource;
> +import org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef;
> +import org.apache.tomcat.util.descriptor.web.ContextService;
> +import org.apache.tomcat.util.descriptor.web.ErrorPage;
> +import org.apache.tomcat.util.descriptor.web.FilterDef;
> +import org.apache.tomcat.util.descriptor.web.FilterMap;
> +import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
> +import org.apache.tomcat.util.descriptor.web.JspPropertyGroup;
> +import org.apache.tomcat.util.descriptor.web.LoginConfig;
> +import org.apache.tomcat.util.descriptor.web.MessageDestinationRef;
> +import org.apache.tomcat.util.descriptor.web.MultipartDef;
> +import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
> +import org.apache.tomcat.util.descriptor.web.SecurityRoleRef;
> +import org.apache.tomcat.util.descriptor.web.ServletDef;
> +import org.apache.tomcat.util.descriptor.web.SessionConfig;
> +import org.apache.tomcat.util.descriptor.web.WebXml;
> +import org.apache.tomcat.util.descriptor.web.WebXmlParser;
> +import org.apache.tomcat.util.digester.Digester;
> +import org.apache.tomcat.util.digester.RuleSet;
> +import org.apache.tomcat.util.file.ConfigFileLoader;
> +import org.apache.tomcat.util.file.ConfigurationSource;
> +import org.apache.tomcat.util.res.StringManager;
> +import org.apache.tomcat.util.scan.JarFactory;
> +import org.xml.sax.InputSource;
> +import org.xml.sax.SAXParseException;
> +
> +/**
> + * Startup event listener for a <b>Context</b> that configures the properties
> + * of that Context, and the associated defined servlets.
> + *
> + * @author Craig R. McClanahan
> + */
> +public class ContextConfig implements LifecycleListener {
> +
> +    private static final Log log = LogFactory.getLog(ContextConfig.class);
> +
> +
> +    /**
> +     * The string resources for this package.
> +     */
> +    protected static final StringManager sm =
> +        StringManager.getManager(Constants.Package);
> +
> +
> +    protected static final LoginConfig DUMMY_LOGIN_CONFIG =
> +        new LoginConfig("NONE", null, null, null);
> +
> +
> +    /**
> +     * The set of Authenticators that we know how to configure.  The key is
> +     * the name of the implemented authentication method, and the value is
> +     * the fully qualified Java class name of the corresponding Valve.
> +     */
> +    protected static final Properties authenticators;
> +
> +    static {
> +        // Load our mapping properties for the standard authenticators
> +        Properties props = new Properties();
> +        try (InputStream is = ContextConfig.class.getClassLoader().getResourceAsStream(
> +                "org/apache/catalina/startup/Authenticators.properties")) {
> +            if (is != null) {
> +                props.load(is);
> +            }
> +        } catch (IOException ioe) {
> +            props = null;
> +        }
> +        authenticators = props;
> +    }
> +
> +    /**
> +     * Deployment count.
> +     */
> +    protected static long deploymentCount = 0L;
> +
> +
> +    /**
> +     * Cache of default web.xml fragments per Host
> +     */
> +    protected static final Map<Host,DefaultWebXmlCacheEntry> hostWebXmlCache =
> +            new ConcurrentHashMap<>();
> +
> +
> +    /**
> +     * Set used as the value for {@code JavaClassCacheEntry.sciSet} when there
> +     * are no SCIs associated with a class.
> +     */
> +    private static final Set<ServletContainerInitializer> EMPTY_SCI_SET = Collections.emptySet();
> +
> +
> +    // ----------------------------------------------------- Instance Variables
> +    /**
> +     * Custom mappings of login methods to authenticators
> +     */
> +    protected Map<String,Authenticator> customAuthenticators;
> +
> +
> +    /**
> +     * The Context we are associated with.
> +     */
> +    protected volatile Context context = null;
> +
> +
> +    /**
> +     * The default web application's deployment descriptor location.
> +     */
> +    protected String defaultWebXml = null;
> +
> +
> +    /**
> +     * Track any fatal errors during startup configuration processing.
> +     */
> +    protected boolean ok = false;
> +
> +
> +    /**
> +     * Original docBase.
> +     */
> +    protected String originalDocBase = null;
> +
> +
> +    /**
> +     * Anti-locking docBase. It is a path to a copy of the web application
> +     * in the java.io.tmpdir directory. This path is always an absolute one.
> +     */
> +    private File antiLockingDocBase = null;
> +
> +
> +    /**
> +     * Map of ServletContainerInitializer to classes they expressed interest in.
> +     */
> +    protected final Map<ServletContainerInitializer, Set<Class<?>>> initializerClassMap =
> +            new LinkedHashMap<>();
> +
> +    /**
> +     * Map of Types to ServletContainerInitializer that are interested in those
> +     * types.
> +     */
> +    protected final Map<Class<?>, Set<ServletContainerInitializer>> typeInitializerMap =
> +            new HashMap<>();
> +
> +    /**
> +     * Flag that indicates if at least one {@link HandlesTypes} entry is present
> +     * that represents an annotation.
> +     */
> +    protected boolean handlesTypesAnnotations = false;
> +
> +    /**
> +     * Flag that indicates if at least one {@link HandlesTypes} entry is present
> +     * that represents a non-annotation.
> +     */
> +    protected boolean handlesTypesNonAnnotations = false;
> +
> +
> +    // ------------------------------------------------------------- Properties
> +
> +    /**
> +     * Obtain the location of the default deployment descriptor.
> +     *
> +     * @return The path to the default web.xml. If not absolute, it is relative
> +     *         to CATALINA_BASE.
> +     */
> +    public String getDefaultWebXml() {
> +        if (defaultWebXml == null) {
> +            defaultWebXml = Constants.DefaultWebXml;
> +        }
> +        return defaultWebXml;
> +    }
> +
> +
> +    /**
> +     * Set the location of the default deployment descriptor.
> +     *
> +     * @param path The path to the default web.xml. If not absolute, it is
> +     *             relative to CATALINA_BASE.
> +     */
> +    public void setDefaultWebXml(String path) {
> +        this.defaultWebXml = path;
> +    }
> +
> +
> +    /**
> +     * Sets custom mappings of login methods to authenticators.
> +     *
> +     * @param customAuthenticators Custom mappings of login methods to
> +     * authenticators
> +     */
> +    public void setCustomAuthenticators(
> +            Map<String,Authenticator> customAuthenticators) {
> +        this.customAuthenticators = customAuthenticators;
> +    }
> +
> +
> +    // --------------------------------------------------------- Public Methods
> +
> +
> +    /**
> +     * Process events for an associated Context.
> +     *
> +     * @param event The lifecycle event that has occurred
> +     */
> +    @Override
> +    public void lifecycleEvent(LifecycleEvent event) {
> +
> +        // Identify the context we are associated with
> +        try {
> +            context = (Context) event.getLifecycle();
> +        } catch (ClassCastException e) {
> +            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
> +            return;
> +        }
> +
> +        // Process the event that has occurred
> +        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
> +            configureStart();
> +        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
> +            beforeStart();
> +        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
> +            // Restore docBase for management tools
> +            if (originalDocBase != null) {
> +                context.setDocBase(originalDocBase);
> +            }
> +        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
> +            configureStop();
> +        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
> +            init();
> +        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
> +            destroy();
> +        }
> +
> +    }
> +
> +
> +    // -------------------------------------------------------- protected Methods
> +
> +
> +    /**
> +     * Process the application classes annotations, if it exists.
> +     */
> +    protected void applicationAnnotationsConfig() {
> +
> +        long t1=System.currentTimeMillis();
> +
> +        WebAnnotationSet.loadApplicationAnnotations(context);
> +
> +        long t2=System.currentTimeMillis();
> +        if (context instanceof StandardContext) {
> +            ((StandardContext) context).setStartupTime(t2-t1+
> +                    ((StandardContext) context).getStartupTime());
> +        }
> +    }
> +
> +
> +    /**
> +     * Set up an Authenticator automatically if required, and one has not
> +     * already been configured.
> +     */
> +    protected void authenticatorConfig() {
> +
> +        LoginConfig loginConfig = context.getLoginConfig();
> +        if (loginConfig == null) {
> +            // Need an authenticator to support HttpServletRequest.login()
> +            loginConfig = DUMMY_LOGIN_CONFIG;
> +            context.setLoginConfig(loginConfig);
> +        }
> +
> +        // Has an authenticator been configured already?
> +        if (context.getAuthenticator() != null) {
> +            return;
> +        }
> +
> +        // Has a Realm been configured for us to authenticate against?
> +        if (context.getRealm() == null) {
> +            log.error(sm.getString("contextConfig.missingRealm"));
> +            ok = false;
> +            return;
> +        }
> +
> +        /*
> +         * First check to see if there is a custom mapping for the login
> +         * method. If so, use it. Otherwise, check if there is a mapping in
> +         * org/apache/catalina/startup/Authenticators.properties.
> +         */
> +        Valve authenticator = null;
> +        if (customAuthenticators != null) {
> +            authenticator = (Valve) customAuthenticators.get(loginConfig.getAuthMethod());
> +        }
> +
> +        if (authenticator == null) {
> +            if (authenticators == null) {
> +                log.error(sm.getString("contextConfig.authenticatorResources"));
> +                ok = false;
> +                return;
> +            }
> +
> +            // Identify the class name of the Valve we should configure
> +            String authenticatorName = authenticators.getProperty(loginConfig.getAuthMethod());
> +            if (authenticatorName == null) {
> +                log.error(sm.getString("contextConfig.authenticatorMissing",
> +                                 loginConfig.getAuthMethod()));
> +                ok = false;
> +                return;
> +            }
> +
> +            // Instantiate and install an Authenticator of the requested class
> +            try {
> +                Class<?> authenticatorClass = Class.forName(authenticatorName);
> +                authenticator = (Valve) authenticatorClass.getConstructor().newInstance();
> +            } catch (Throwable t) {
> +                ExceptionUtils.handleThrowable(t);
> +                log.error(sm.getString(
> +                                    "contextConfig.authenticatorInstantiate",
> +                                    authenticatorName),
> +                          t);
> +                ok = false;
> +            }
> +        }
> +
> +        if (authenticator != null) {
> +            Pipeline pipeline = context.getPipeline();
> +            if (pipeline != null) {
> +                pipeline.addValve(authenticator);
> +                if (log.isDebugEnabled()) {
> +                    log.debug(sm.getString(
> +                                    "contextConfig.authenticatorConfigured",
> +                                    loginConfig.getAuthMethod()));
> +                }
> +            }
> +        }
> +    }
> +
> +
> +    /**
> +     * Create (if necessary) and return a Digester configured to process the
> +     * context configuration descriptor for an application.
> +     * @return the digester for context.xml files
> +     */
> +    protected Digester createContextDigester() {
> +        Digester digester = new Digester();
> +        digester.setValidating(false);
> +        digester.setRulesValidation(true);
> +        Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
> +        List<String> objectAttrs = new ArrayList<>();
> +        objectAttrs.add("className");
> +        fakeAttributes.put(Object.class, objectAttrs);
> +        // Ignore attribute added by Eclipse for its internal tracking
> +        List<String> contextAttrs = new ArrayList<>();
> +        contextAttrs.add("source");
> +        fakeAttributes.put(StandardContext.class, contextAttrs);
> +        digester.setFakeAttributes(fakeAttributes);
> +        RuleSet contextRuleSet = new ContextRuleSet("", false);
> +        digester.addRuleSet(contextRuleSet);
> +        RuleSet namingRuleSet = new NamingRuleSet("Context/");
> +        digester.addRuleSet(namingRuleSet);
> +        return digester;
> +    }
> +
> +
> +    /**
> +     * Process the default configuration file, if it exists.
> +     * @param digester The digester that will be used for XML parsing
> +     */
> +    protected void contextConfig(Digester digester) {
> +
> +        String defaultContextXml = null;
> +
> +        // Open the default context.xml file, if it exists
> +        if (context instanceof StandardContext) {
> +            defaultContextXml = ((StandardContext)context).getDefaultContextXml();
> +        }
> +        // set the default if we don't have any overrides
> +        if (defaultContextXml == null) {
> +            defaultContextXml = Constants.DefaultContextXml;
> +        }
> +
> +        if (!context.getOverride()) {
> +            try (ConfigurationSource.Resource contextXmlResource =
> +                    ConfigFileLoader.getSource().getResource(defaultContextXml)) {
> +                URL defaultContextUrl = contextXmlResource.getURI().toURL();
> +                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
> +            } catch (MalformedURLException e) {
> +                log.error(sm.getString("contextConfig.badUrl", defaultContextXml), e);
> +            } catch (IOException e) {
> +                // Not found
> +            }
> +
> +            String hostContextFile = Container.getConfigPath(context, Constants.HostContextXml);
> +            try (ConfigurationSource.Resource contextXmlResource =
> +                    ConfigFileLoader.getSource().getConfResource(hostContextFile)) {
> +                URL defaultContextUrl = contextXmlResource.getURI().toURL();
> +                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
> +            } catch (MalformedURLException e) {
> +                log.error(sm.getString("contextConfig.badUrl", hostContextFile), e);
> +            } catch (IOException e) {
> +                // Not found
> +            }
> +        }
> +        if (context.getConfigFile() != null) {
> +            processContextConfig(digester, context.getConfigFile(), null);
> +        }
> +
> +    }
> +
> +
> +    /**
> +     * Process a context.xml.
> +     * @param digester The digester that will be used for XML parsing
> +     * @param contextXml The URL to the context.xml configuration
> +     * @param stream The XML resource stream
> +     */
> +    protected void processContextConfig(Digester digester, URL contextXml, InputStream stream) {
> +
> +        if (log.isDebugEnabled()) {
> +            log.debug("Processing context [" + context.getName()
> +                    + "] configuration file [" + contextXml + "]");
> +        }
> +
> +        InputSource source = null;
> +
> +        try {
> +            source = new InputSource(contextXml.toString());
> +            if (stream == null) {
> +                URLConnection xmlConn = contextXml.openConnection();
> +                xmlConn.setUseCaches(false);
> +                stream = xmlConn.getInputStream();
> +            }
> +        } catch (Exception e) {
> +            log.error(sm.getString("contextConfig.contextMissing",
> +                      contextXml) , e);
> +        }
> +
> +        if (source == null) {
> +            return;
> +        }
> +
> +        try {
> +            source.setByteStream(stream);
> +            digester.setClassLoader(this.getClass().getClassLoader());
> +            digester.setUseContextClassLoader(false);
> +            digester.push(context.getParent());
> +            digester.push(context);
> +            XmlErrorHandler errorHandler = new XmlErrorHandler();
> +            digester.setErrorHandler(errorHandler);
> +            digester.parse(source);
> +            if (errorHandler.getWarnings().size() > 0 ||
> +                    errorHandler.getErrors().size() > 0) {
> +                errorHandler.logFindings(log, contextXml.toString());
> +                ok = false;
> +            }
> +            if (log.isDebugEnabled()) {
> +                log.debug("Successfully processed context [" + context.getName()
> +                        + "] configuration file [" + contextXml + "]");
> +            }
> +        } catch (SAXParseException e) {
> +            log.error(sm.getString("contextConfig.contextParse",
> +                    context.getName()), e);
> +            log.error(sm.getString("contextConfig.defaultPosition",
> +                             "" + e.getLineNumber(),
> +                             "" + e.getColumnNumber()));
> +            ok = false;
> +        } catch (Exception e) {
> +            log.error(sm.getString("contextConfig.contextParse",
> +                    context.getName()), e);
> +            ok = false;
> +        } finally {
> +            try {
> +                if (stream != null) {
> +                    stream.close();
> +                }
> +            } catch (IOException e) {
> +                log.error(sm.getString("contextConfig.contextClose"), e);
> +            }
> +        }
> +    }
> +
> +
> +    /**
> +     * Adjust docBase.
> +     * @throws IOException cannot access the context base path
> +     */
> +    protected void fixDocBase() throws IOException {
> +
> +        Host host = (Host) context.getParent();
> +        File appBase = host.getAppBaseFile();
> +
> +        // This could be blank, relative, absolute or canonical
> +        String docBaseConfigured = context.getDocBase();
> +        // If there is no explicit docBase, derive it from the path and version
> +        if (docBaseConfigured == null) {
> +            // Trying to guess the docBase according to the path
> +            String path = context.getPath();
> +            if (path == null) {
> +                return;
> +            }
> +            ContextName cn = new ContextName(path, context.getWebappVersion());
> +            docBaseConfigured = cn.getBaseName();
> +        }
> +
> +        // Obtain the absolute docBase in String and File form
> +        String docBaseAbsolute;
> +        File docBaseConfiguredFile = new File(docBaseConfigured);
> +        if (!docBaseConfiguredFile.isAbsolute()) {
> +            docBaseAbsolute = (new File(appBase, docBaseConfigured)).getAbsolutePath();
> +        } else {
> +            docBaseAbsolute = docBaseConfiguredFile.getAbsolutePath();
> +        }
> +        File docBaseAbsoluteFile = new File(docBaseAbsolute);
> +        String originalDocBase = docBaseAbsolute;
> +
> +        ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
> +        String pathName = cn.getBaseName();
> +
> +        boolean unpackWARs = true;
> +        if (host instanceof StandardHost) {
> +            unpackWARs = ((StandardHost) host).isUnpackWARs();
> +            if (unpackWARs && context instanceof StandardContext) {
> +                unpackWARs =  ((StandardContext) context).getUnpackWAR();
> +            }
> +        }
> +
> +        // At this point we need to determine if we have a WAR file in the
> +        // appBase that needs to be expanded. Therefore we consider the absolute
> +        // docBase NOT the canonical docBase. This is because some users symlink
> +        // WAR files into the appBase and we want this to work correctly.
> +        boolean docBaseAbsoluteInAppBase = docBaseAbsolute.startsWith(appBase.getPath() + File.separatorChar);
> +        if (docBaseAbsolute.toLowerCase(Locale.ENGLISH).endsWith(".war") && !docBaseAbsoluteFile.isDirectory()) {
> +            URL war = UriUtil.buildJarUrl(docBaseAbsoluteFile);
> +            if (unpackWARs) {
> +                docBaseAbsolute = ExpandWar.expand(host, war, pathName);
> +                docBaseAbsoluteFile = new File(docBaseAbsolute);
> +                if (context instanceof StandardContext) {
> +                    ((StandardContext) context).setOriginalDocBase(originalDocBase);
> +                }
> +            } else {
> +                ExpandWar.validate(host, war, pathName);
> +            }
> +        } else {
> +            File docBaseAbsoluteFileWar = new File(docBaseAbsolute + ".war");
> +            URL war = null;
> +            if (docBaseAbsoluteFileWar.exists() && docBaseAbsoluteInAppBase) {
> +                war = UriUtil.buildJarUrl(docBaseAbsoluteFileWar);
> +            }
> +            if (docBaseAbsoluteFile.exists()) {
> +                if (war != null && unpackWARs) {
> +                    // Check if WAR needs to be re-expanded (e.g. if it has
> +                    // changed). Note: HostConfig.deployWar() takes care of
> +                    // ensuring that the correct XML file is used.
> +                    // This will be a NO-OP if the WAR is unchanged.
> +                    ExpandWar.expand(host, war, pathName);
> +                }
> +            } else {
> +                if (war != null) {
> +                    if (unpackWARs) {
> +                        docBaseAbsolute = ExpandWar.expand(host, war, pathName);
> +                        docBaseAbsoluteFile = new File(docBaseAbsolute);
> +                    } else {
> +                        docBaseAbsolute = docBaseAbsoluteFileWar.getAbsolutePath();
> +                        docBaseAbsoluteFile = docBaseAbsoluteFileWar;
> +                        ExpandWar.validate(host, war, pathName);
> +                    }
> +                }
> +                if (context instanceof StandardContext) {
> +                    ((StandardContext) context).setOriginalDocBase(originalDocBase);
> +                }
> +            }
> +        }
> +
> +        String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath();
> +
> +        // Re-calculate now docBase is a canonical path
> +        boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar);
> +        String docBase;
> +        if (docBaseCanonicalInAppBase) {
> +            docBase = docBaseCanonical.substring(appBase.getPath().length());
> +            docBase = docBase.replace(File.separatorChar, '/');
> +            if (docBase.startsWith("/")) {
> +                docBase = docBase.substring(1);
> +            }
> +        } else {
> +            docBase = docBaseCanonical.replace(File.separatorChar, '/');
> +        }
> +
> +        context.setDocBase(docBase);
> +    }
> +
> +
> +    protected void antiLocking() {
> +
> +        if ((context instanceof StandardContext)
> +            && ((StandardContext) context).getAntiResourceLocking()) {
> +
> +            Host host = (Host) context.getParent();
> +            String docBase = context.getDocBase();
> +            if (docBase == null) {
> +                return;
> +            }
> +            originalDocBase = docBase;
> +
> +            File docBaseFile = new File(docBase);
> +            if (!docBaseFile.isAbsolute()) {
> +                docBaseFile = new File(host.getAppBaseFile(), docBase);
> +            }
> +
> +            String path = context.getPath();
> +            if (path == null) {
> +                return;
> +            }
> +            ContextName cn = new ContextName(path, context.getWebappVersion());
> +            docBase = cn.getBaseName();
> +
> +            if (originalDocBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
> +                antiLockingDocBase = new File(
> +                        System.getProperty("java.io.tmpdir"),
> +                        deploymentCount++ + "-" + docBase + ".war");
> +            } else {
> +                antiLockingDocBase = new File(
> +                        System.getProperty("java.io.tmpdir"),
> +                        deploymentCount++ + "-" + docBase);
> +            }
> +            antiLockingDocBase = antiLockingDocBase.getAbsoluteFile();
> +
> +            if (log.isDebugEnabled()) {
> +                log.debug("Anti locking context[" + context.getName()
> +                        + "] setting docBase to " +
> +                        antiLockingDocBase.getPath());
> +            }
> +
> +            // Cleanup just in case an old deployment is lying around
> +            ExpandWar.delete(antiLockingDocBase);
> +            if (ExpandWar.copy(docBaseFile, antiLockingDocBase)) {
> +                context.setDocBase(antiLockingDocBase.getPath());
> +            }
> +        }
> +    }
> +
> +
> +    /**
> +     * Process a "init" event for this Context.
> +     */
> +    protected synchronized void init() {
> +        // Called from StandardContext.init()
> +
> +        Digester contextDigester = createContextDigester();
> +        contextDigester.getParser();
> +
> +        if (log.isDebugEnabled()) {
> +            log.debug(sm.getString("contextConfig.init"));
> +        }
> +        context.setConfigured(false);
> +        ok = true;
> +
> +        contextConfig(contextDigester);
> +    }
> +
> +
> +    /**
> +     * Process a "before start" event for this Context.
> +     */
> +    protected synchronized void beforeStart() {
> +
> +        try {
> +            fixDocBase();
> +        } catch (IOException e) {
> +            log.error(sm.getString(
> +                    "contextConfig.fixDocBase", context.getName()), e);
> +        }
> +
> +        antiLocking();
> +    }
> +
> +
> +    /**
> +     * Process a "contextConfig" event for this Context.
> +     */
> +    protected synchronized void configureStart() {
> +        // Called from StandardContext.start()
> +
> +        if (log.isDebugEnabled()) {
> +            log.debug(sm.getString("contextConfig.start"));
> +        }
> +
> +        if (log.isDebugEnabled()) {
> +            log.debug(sm.getString("contextConfig.xmlSettings",
> +                    context.getName(),
> +                    Boolean.valueOf(context.getXmlValidation()),
> +                    Boolean.valueOf(context.getXmlNamespaceAware())));
> +        }
> +
> +        webConfig();
> +
> +        if (!context.getIgnoreAnnotations()) {
> +            applicationAnnotationsConfig();
> +        }
> +        if (ok) {
> +            validateSecurityRoles();
> +        }
> +
> +        // Configure an authenticator if we need one
> +        if (ok) {
> +            authenticatorConfig();
> +        }
> +
> +        // Dump the contents of this pipeline if requested
> +        if (log.isDebugEnabled()) {
> +            log.debug("Pipeline Configuration:");
> +            Pipeline pipeline = context.getPipeline();
> +            Valve valves[] = null;
> +            if (pipeline != null) {
> +                valves = pipeline.getValves();
> +            }
> +            if (valves != null) {
> +                for (int i = 0; i < valves.length; i++) {
> +                    log.debug("  " + valves[i].getClass().getName());
> +                }
> +            }
> +            log.debug("======================");
> +        }
> +
> +        // Make our application available if no problems were encountered
> +        if (ok) {
> +            context.setConfigured(true);
> +        } else {
> +            log.error(sm.getString("contextConfig.unavailable"));
> +            context.setConfigured(false);
> +        }
> +
> +    }
> +
> +
> +    /**
> +     * Process a "stop" event for this Context.
> +     */
> +    protected synchronized void configureStop() {
> +
> +        if (log.isDebugEnabled()) {
> +            log.debug(sm.getString("contextConfig.stop"));
> +        }
> +
> +        int i;
> +
> +        // Removing children
> +        Container[] children = context.findChildren();
> +        for (i = 0; i < children.length; i++) {
> +            context.removeChild(children[i]);
> +        }
> +
> +        // Removing application parameters
> +        /*
> +        ApplicationParameter[] applicationParameters =
> +            context.findApplicationParameters();
> +        for (i = 0; i < applicationParameters.length; i++) {
> +            context.removeApplicationParameter
> +                (applicationParameters[i].getName());
> +        }
> +        */
> +
> +        // Removing security constraints
> +        SecurityConstraint[] securityConstraints = context.findConstraints();
> +        for (i = 0; i < securityConstraints.length; i++) {
> +            context.removeConstraint(securityConstraints[i]);
> +        }
> +
> +        // Removing Ejbs
> +        /*
> +        ContextEjb[] contextEjbs = context.findEjbs();
> +        for (i = 0; i < contextEjbs.length; i++) {
> +            context.removeEjb(contextEjbs[i].getName());
> +        }
> +        */
> +
> +        // Removing environments
> +        /*
> +        ContextEnvironment[] contextEnvironments = context.findEnvironments();
> +        for (i = 0; i < contextEnvironments.length; i++) {
> +            context.removeEnvironment(contextEnvironments[i].getName());
> +        }
> +        */
> +
> +        // Removing errors pages
> +        ErrorPage[] errorPages = context.findErrorPages();
> +        for (i = 0; i < errorPages.length; i++) {
> +            context.removeErrorPage(errorPages[i]);
> +        }
> +
> +        // Removing filter defs
> +        FilterDef[] filterDefs = context.findFilterDefs();
> +        for (i = 0; i < filterDefs.length; i++) {
> +            context.removeFilterDef(filterDefs[i]);
> +        }
> +
> +        // Removing filter maps
> +        FilterMap[] filterMaps = context.findFilterMaps();
> +        for (i = 0; i < filterMaps.length; i++) {
> +            context.removeFilterMap(filterMaps[i]);
> +        }
> +
> +        // Removing local ejbs
> +        /*
> +        ContextLocalEjb[] contextLocalEjbs = context.findLocalEjbs();
> +        for (i = 0; i < contextLocalEjbs.length; i++) {
> +            context.removeLocalEjb(contextLocalEjbs[i].getName());
> +        }
> +        */
> +
> +        // Removing Mime mappings
> +        String[] mimeMappings = context.findMimeMappings();
> +        for (i = 0; i < mimeMappings.length; i++) {
> +            context.removeMimeMapping(mimeMappings[i]);
> +        }
> +
> +        // Removing parameters
> +        String[] parameters = context.findParameters();
> +        for (i = 0; i < parameters.length; i++) {
> +            context.removeParameter(parameters[i]);
> +        }
> +
> +        // Removing resource env refs
> +        /*
> +        String[] resourceEnvRefs = context.findResourceEnvRefs();
> +        for (i = 0; i < resourceEnvRefs.length; i++) {
> +            context.removeResourceEnvRef(resourceEnvRefs[i]);
> +        }
> +        */
> +
> +        // Removing resource links
> +        /*
> +        ContextResourceLink[] contextResourceLinks =
> +            context.findResourceLinks();
> +        for (i = 0; i < contextResourceLinks.length; i++) {
> +            context.removeResourceLink(contextResourceLinks[i].getName());
> +        }
> +        */
> +
> +        // Removing resources
> +        /*
> +        ContextResource[] contextResources = context.findResources();
> +        for (i = 0; i < contextResources.length; i++) {
> +            context.removeResource(contextResources[i].getName());
> +        }
> +        */
> +
> +        // Removing security role
> +        String[] securityRoles = context.findSecurityRoles();
> +        for (i = 0; i < securityRoles.length; i++) {
> +            context.removeSecurityRole(securityRoles[i]);
> +        }
> +
> +        // Removing servlet mappings
> +        String[] servletMappings = context.findServletMappings();
> +        for (i = 0; i < servletMappings.length; i++) {
> +            context.removeServletMapping(servletMappings[i]);
> +        }
> +
> +        // FIXME : Removing status pages
> +
> +        // Removing welcome files
> +        String[] welcomeFiles = context.findWelcomeFiles();
> +        for (i = 0; i < welcomeFiles.length; i++) {
> +            context.removeWelcomeFile(welcomeFiles[i]);
> +        }
> +
> +        // Removing wrapper lifecycles
> +        String[] wrapperLifecycles = context.findWrapperLifecycles();
> +        for (i = 0; i < wrapperLifecycles.length; i++) {
> +            context.removeWrapperLifecycle(wrapperLifecycles[i]);
> +        }
> +
> +        // Removing wrapper listeners
> +        String[] wrapperListeners = context.findWrapperListeners();
> +        for (i = 0; i < wrapperListeners.length; i++) {
> +            context.removeWrapperListener(wrapperListeners[i]);
> +        }
> +
> +        // Remove (partially) folders and files created by antiLocking
> +        if (antiLockingDocBase != null) {
> +            // No need to log failure - it is expected in this case
> +            ExpandWar.delete(antiLockingDocBase, false);
> +        }
> +
> +        // Reset ServletContextInitializer scanning
> +        initializerClassMap.clear();
> +        typeInitializerMap.clear();
> +
> +        ok = true;
> +
> +    }
> +
> +
> +    /**
> +     * Process a "destroy" event for this Context.
> +     */
> +    protected synchronized void destroy() {
> +        // Called from StandardContext.destroy()
> +        if (log.isDebugEnabled()) {
> +            log.debug(sm.getString("contextConfig.destroy"));
> +        }
> +
> +        // Skip clearing the work directory if Tomcat is being shutdown
> +        Server s = getServer();
> +        if (s != null && !s.getState().isAvailable()) {
> +            return;
> +        }
> +
> +        // Changed to getWorkPath per Bugzilla 35819.
> +        if (context instanceof StandardContext) {
> +            String workDir = ((StandardContext) context).getWorkPath();
> +            if (workDir != null) {
> +                ExpandWar.delete(new File(workDir));
> +            }
> +        }
> +    }
> +
> +
> +    private Server getServer() {
> +        Container c = context;
> +        while (c != null && !(c instanceof Engine)) {
> +            c = c.getParent();
> +        }
> +
> +        if (c == null) {
> +            return null;
> +        }
> +
> +        Service s = ((Engine)c).getService();
> +
> +        if (s == null) {
> +            return null;
> +        }
> +
> +        return s.getServer();
> +    }
> +
> +    /**
> +     * Validate the usage of security role names in the web application
> +     * deployment descriptor.  If any problems are found, issue warning
> +     * messages (for backwards compatibility) and add the missing roles.
> +     * (To make these problems fatal instead, simply set the <code>ok</code>
> +     * instance variable to <code>false</code> as well).
> +     */
> +    protected void validateSecurityRoles() {
> +
> +        // Check role names used in <security-constraint> elements
> +        SecurityConstraint constraints[] = context.findConstraints();
> +        for (int i = 0; i < constraints.length; i++) {
> +            String roles[] = constraints[i].findAuthRoles();
> +            for (int j = 0; j < roles.length; j++) {
> +                if (!"*".equals(roles[j]) &&
> +                    !context.findSecurityRole(roles[j])) {
> +                    log.warn(sm.getString("contextConfig.role.auth", roles[j]));
> +                    context.addSecurityRole(roles[j]);
> +                }
> +            }
> +        }
> +
> +        // Check role names used in <servlet> elements
> +        Container wrappers[] = context.findChildren();
> +        for (int i = 0; i < wrappers.length; i++) {
> +            Wrapper wrapper = (Wrapper) wrappers[i];
> +            String runAs = wrapper.getRunAs();
> +            if ((runAs != null) && !context.findSecurityRole(runAs)) {
> +                log.warn(sm.getString("contextConfig.role.runas", runAs));
> +                context.addSecurityRole(runAs);
> +            }
> +            String names[] = wrapper.findSecurityReferences();
> +            for (int j = 0; j < names.length; j++) {
> +                String link = wrapper.findSecurityReference(names[j]);
> +                if ((link != null) && !context.findSecurityRole(link)) {
> +                    log.warn(sm.getString("contextConfig.role.link", link));
> +                    context.addSecurityRole(link);
> +                }
> +            }
> +        }
> +
> +    }
> +
> +
> +    protected File getHostConfigBase() {
> +        File file = null;
> +        if (context.getParent() instanceof Host) {
> +            file = ((Host)context.getParent()).getConfigBaseFile();
> +        }
> +        return file;
> +    }
> +
> +    /**
> +     * Scan the web.xml files that apply to the web application and merge them
> +     * using the rules defined in the spec. For the global web.xml files,
> +     * where there is duplicate configuration, the most specific level wins. ie
> +     * an application's web.xml takes precedence over the host level or global
> +     * web.xml file.
> +     */
> +    protected void webConfig() {
> +        /*
> +         * Anything and everything can override the global and host defaults.
> +         * This is implemented in two parts
> +         * - Handle as a web fragment that gets added after everything else so
> +         *   everything else takes priority
> +         * - Mark Servlets as overridable so SCI configuration can replace
> +         *   configuration from the defaults
> +         */
> +
> +        /*
> +         * The rules for annotation scanning are not as clear-cut as one might
> +         * think. Tomcat implements the following process:
> +         * - As per SRV.1.6.2, Tomcat will scan for annotations regardless of
> +         *   which Servlet spec version is declared in web.xml. The EG has
> +         *   confirmed this is the expected behaviour.
> +         * - As per http://java.net/jira/browse/SERVLET_SPEC-36, if the main
> +         *   web.xml is marked as metadata-complete, JARs are still processed
> +         *   for SCIs.
> +         * - If metadata-complete=true and an absolute ordering is specified,
> +         *   JARs excluded from the ordering are also excluded from the SCI
> +         *   processing.
> +         * - If an SCI has a @HandlesType annotation then all classes (except
> +         *   those in JARs excluded from an absolute ordering) need to be
> +         *   scanned to check if they match.
> +         */
> +        WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
> +                context.getXmlValidation(), context.getXmlBlockExternal());
> +
> +        Set<WebXml> defaults = new HashSet<>();
> +        defaults.add(getDefaultWebXmlFragment(webXmlParser));
> +
> +        Set<WebXml> tomcatWebXml = new HashSet<>();
> +        tomcatWebXml.add(getTomcatWebXmlFragment(webXmlParser));
> +
> +        WebXml webXml = createWebXml();
> +
> +        // Parse context level web.xml
> +        InputSource contextWebXml = getContextWebXmlSource();
> +        if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
> +            ok = false;
> +        }
> +
> +        ServletContext sContext = context.getServletContext();
> +
> +        // Ordering is important here
> +
> +        // Step 1. Identify all the JARs packaged with the application and those
> +        // provided by the container. If any of the application JARs have a
> +        // web-fragment.xml it will be parsed at this point. web-fragment.xml
> +        // files are ignored for container provided JARs.
> +        Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
> +
> +        // Step 2. Order the fragments.
> +        Set<WebXml> orderedFragments = null;
> +        orderedFragments =
> +                WebXml.orderWebFragments(webXml, fragments, sContext);
> +
> +        // Step 3. Look for ServletContainerInitializer implementations
> +        if (ok) {
> +            processServletContainerInitializers();
> +        }
> +
> +        if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
> +            // Steps 4 & 5.
> +            processClasses(webXml, orderedFragments);
> +        }
> +
> +        if (!webXml.isMetadataComplete()) {
> +            // Step 6. Merge web-fragment.xml files into the main web.xml
> +            // file.
> +            if (ok) {
> +                ok = webXml.merge(orderedFragments);
> +            }
> +
> +            // Step 7a
> +            // merge tomcat-web.xml
> +            webXml.merge(tomcatWebXml);
> +
> +            // Step 7b. Apply global defaults
> +            // Have to merge defaults before JSP conversion since defaults
> +            // provide JSP servlet definition.
> +            webXml.merge(defaults);
> +
> +            // Step 8. Convert explicitly mentioned jsps to servlets
> +            if (ok) {
> +                convertJsps(webXml);
> +            }
> +
> +            // Step 9. Apply merged web.xml to Context
> +            if (ok) {
> +                configureContext(webXml);
> +            }
> +        } else {
> +            webXml.merge(tomcatWebXml);
> +            webXml.merge(defaults);
> +            convertJsps(webXml);
> +            configureContext(webXml);
> +        }
> +
> +        if (context.getLogEffectiveWebXml()) {
> +            log.info(sm.getString("contextConfig.effectiveWebXml", webXml.toXml()));
> +        }
> +
> +        // Always need to look for static resources
> +        // Step 10. Look for static resources packaged in JARs
> +        if (ok) {
> +            // Spec does not define an order.
> +            // Use ordered JARs followed by remaining JARs
> +            Set<WebXml> resourceJars = new LinkedHashSet<>();
> +            for (WebXml fragment : orderedFragments) {
> +                resourceJars.add(fragment);
> +            }
> +            for (WebXml fragment : fragments.values()) {
> +                if (!resourceJars.contains(fragment)) {
> +                    resourceJars.add(fragment);
> +                }
> +            }
> +            processResourceJARs(resourceJars);
> +            // See also StandardContext.resourcesStart() for
> +            // WEB-INF/classes/META-INF/resources configuration
> +        }
> +
> +        // Step 11. Apply the ServletContainerInitializer config to the
> +        // context
> +        if (ok) {
> +            for (Map.Entry<ServletContainerInitializer,
> +                    Set<Class<?>>> entry :
> +                        initializerClassMap.entrySet()) {
> +                if (entry.getValue().isEmpty()) {
> +                    context.addServletContainerInitializer(
> +                            entry.getKey(), null);
> +                } else {
> +                    context.addServletContainerInitializer(
> +                            entry.getKey(), entry.getValue());
> +                }
> +            }
> +        }
> +    }
> +
> +
> +    protected void processClasses(WebXml webXml, Set<WebXml> orderedFragments) {
> +        // Step 4. Process /WEB-INF/classes for annotations and
> +        // @HandlesTypes matches
> +        Map<String, JavaClassCacheEntry> javaClassCache = new HashMap<>();
> +
> +        if (ok) {
> +            WebResource[] webResources =
> +                    context.getResources().listResources("/WEB-INF/classes");
> +
> +            for (WebResource webResource : webResources) {
> +                // Skip the META-INF directory from any JARs that have been
> +                // expanded in to WEB-INF/classes (sometimes IDEs do this).
> +                if ("META-INF".equals(webResource.getName())) {
> +                    continue;
> +                }
> +                processAnnotationsWebResource(webResource, webXml,
> +                        webXml.isMetadataComplete(), javaClassCache);
> +            }
> +        }
> +
> +        // Step 5. Process JARs for annotations and
> +        // @HandlesTypes matches - only need to process those fragments we
> +        // are going to use (remember orderedFragments includes any
> +        // container fragments)
> +        if (ok) {
> +            processAnnotations(
> +                    orderedFragments, webXml.isMetadataComplete(), javaClassCache);
> +        }
> +
> +        // Cache, if used, is no longer required so clear it
> +        javaClassCache.clear();
> +    }
> +
> +
> +    private void configureContext(WebXml webxml) {
> +        // As far as possible, process in alphabetical order so it is easy to
> +        // check everything is present
> +        // Some validation depends on correct public ID
> +        context.setPublicId(webxml.getPublicId());
> +
> +        // Everything else in order
> +        context.setEffectiveMajorVersion(webxml.getMajorVersion());
> +        context.setEffectiveMinorVersion(webxml.getMinorVersion());
> +
> +        for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
> +            context.addParameter(entry.getKey(), entry.getValue());
> +        }
> +        context.setDenyUncoveredHttpMethods(
> +                webxml.getDenyUncoveredHttpMethods());
> +        context.setDisplayName(webxml.getDisplayName());
> +        context.setDistributable(webxml.isDistributable());
> +        for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
> +            context.getNamingResources().addLocalEjb(ejbLocalRef);
> +        }
> +        for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
> +            context.getNamingResources().addEjb(ejbRef);
> +        }
> +        for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
> +            context.getNamingResources().addEnvironment(environment);
> +        }
> +        for (ErrorPage errorPage : webxml.getErrorPages().values()) {
> +            context.addErrorPage(errorPage);
> +        }
> +        for (FilterDef filter : webxml.getFilters().values()) {
> +            if (filter.getAsyncSupported() == null) {
> +                filter.setAsyncSupported("false");
> +            }
> +            context.addFilterDef(filter);
> +        }
> +        for (FilterMap filterMap : webxml.getFilterMappings()) {
> +            context.addFilterMap(filterMap);
> +        }
> +        context.setJspConfigDescriptor(webxml.getJspConfigDescriptor());
> +        for (String listener : webxml.getListeners()) {
> +            context.addApplicationListener(listener);
> +        }
> +        for (Entry<String, String> entry :
> +                webxml.getLocaleEncodingMappings().entrySet()) {
> +            context.addLocaleEncodingMappingParameter(entry.getKey(),
> +                    entry.getValue());
> +        }
> +        // Prevents IAE
> +        if (webxml.getLoginConfig() != null) {
> +            context.setLoginConfig(webxml.getLoginConfig());
> +        }
> +        for (MessageDestinationRef mdr :
> +                webxml.getMessageDestinationRefs().values()) {
> +            context.getNamingResources().addMessageDestinationRef(mdr);
> +        }
> +
> +        // messageDestinations were ignored in Tomcat 6, so ignore here
> +
> +        context.setIgnoreAnnotations(webxml.isMetadataComplete());
> +        for (Entry<String, String> entry :
> +                webxml.getMimeMappings().entrySet()) {
> +            context.addMimeMapping(entry.getKey(), entry.getValue());
> +        }
> +        context.setRequestCharacterEncoding(webxml.getRequestCharacterEncoding());
> +        // Name is just used for ordering
> +        for (ContextResourceEnvRef resource :
> +                webxml.getResourceEnvRefs().values()) {
> +            context.getNamingResources().addResourceEnvRef(resource);
> +        }
> +        for (ContextResource resource : webxml.getResourceRefs().values()) {
> +            context.getNamingResources().addResource(resource);
> +        }
> +        context.setResponseCharacterEncoding(webxml.getResponseCharacterEncoding());
> +        boolean allAuthenticatedUsersIsAppRole =
> +                webxml.getSecurityRoles().contains(
> +                        SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
> +        for (SecurityConstraint constraint : webxml.getSecurityConstraints()) {
> +            if (allAuthenticatedUsersIsAppRole) {
> +                constraint.treatAllAuthenticatedUsersAsApplicationRole();
> +            }
> +            context.addConstraint(constraint);
> +        }
> +        for (String role : webxml.getSecurityRoles()) {
> +            context.addSecurityRole(role);
> +        }
> +        for (ContextService service : webxml.getServiceRefs().values()) {
> +            context.getNamingResources().addService(service);
> +        }
> +        for (ServletDef servlet : webxml.getServlets().values()) {
> +            Wrapper wrapper = context.createWrapper();
> +            // Description is ignored
> +            // Display name is ignored
> +            // Icons are ignored
> +
> +            // jsp-file gets passed to the JSP Servlet as an init-param
> +
> +            if (servlet.getLoadOnStartup() != null) {
> +                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
> +            }
> +            if (servlet.getEnabled() != null) {
> +                wrapper.setEnabled(servlet.getEnabled().booleanValue());
> +            }
> +            wrapper.setName(servlet.getServletName());
> +            Map<String,String> params = servlet.getParameterMap();
> +            for (Entry<String, String> entry : params.entrySet()) {
> +                wrapper.addInitParameter(entry.getKey(), entry.getValue());
> +            }
> +            wrapper.setRunAs(servlet.getRunAs());
> +            Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
> +            for (SecurityRoleRef roleRef : roleRefs) {
> +                wrapper.addSecurityReference(
> +                        roleRef.getName(), roleRef.getLink());
> +            }
> +            wrapper.setServletClass(servlet.getServletClass());
> +            MultipartDef multipartdef = servlet.getMultipartDef();
> +            if (multipartdef != null) {
> +                if (multipartdef.getMaxFileSize() != null &&
> +                        multipartdef.getMaxRequestSize()!= null &&
> +                        multipartdef.getFileSizeThreshold() != null) {
> +                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
> +                            multipartdef.getLocation(),
> +                            Long.parseLong(multipartdef.getMaxFileSize()),
> +                            Long.parseLong(multipartdef.getMaxRequestSize()),
> +                            Integer.parseInt(
> +                                    multipartdef.getFileSizeThreshold())));
> +                } else {
> +                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
> +                            multipartdef.getLocation()));
> +                }
> +            }
> +            if (servlet.getAsyncSupported() != null) {
> +                wrapper.setAsyncSupported(
> +                        servlet.getAsyncSupported().booleanValue());
> +            }
> +            wrapper.setOverridable(servlet.isOverridable());
> +            context.addChild(wrapper);
> +        }
> +        for (Entry<String, String> entry :
> +                webxml.getServletMappings().entrySet()) {
> +            context.addServletMappingDecoded(entry.getKey(), entry.getValue());
> +        }
> +        SessionConfig sessionConfig = webxml.getSessionConfig();
> +        if (sessionConfig != null) {
> +            if (sessionConfig.getSessionTimeout() != null) {
> +                context.setSessionTimeout(
> +                        sessionConfig.getSessionTimeout().intValue());
> +            }
> +            SessionCookieConfig scc =
> +                context.getServletContext().getSessionCookieConfig();
> +            scc.setName(sessionConfig.getCookieName());
> +            scc.setDomain(sessionConfig.getCookieDomain());
> +            scc.setPath(sessionConfig.getCookiePath());
> +            scc.setComment(sessionConfig.getCookieComment());
> +            if (sessionConfig.getCookieHttpOnly() != null) {
> +                scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());
> +            }
> +            if (sessionConfig.getCookieSecure() != null) {
> +                scc.setSecure(sessionConfig.getCookieSecure().booleanValue());
> +            }
> +            if (sessionConfig.getCookieMaxAge() != null) {
> +                scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());
> +            }
> +            if (sessionConfig.getSessionTrackingModes().size() > 0) {
> +                context.getServletContext().setSessionTrackingModes(
> +                        sessionConfig.getSessionTrackingModes());
> +            }
> +        }
> +
> +        // Context doesn't use version directly
> +
> +        for (String welcomeFile : webxml.getWelcomeFiles()) {
> +            /*
> +             * The following will result in a welcome file of "" so don't add
> +             * that to the context
> +             * <welcome-file-list>
> +             *   <welcome-file/>
> +             * </welcome-file-list>
> +             */
> +            if (welcomeFile != null && welcomeFile.length() > 0) {
> +                context.addWelcomeFile(welcomeFile);
> +            }
> +        }
> +
> +        // Do this last as it depends on servlets
> +        for (JspPropertyGroup jspPropertyGroup :
> +                webxml.getJspPropertyGroups()) {
> +            String jspServletName = context.findServletMapping("*.jsp");
> +            if (jspServletName == null) {
> +                jspServletName = "jsp";
> +            }
> +            if (context.findChild(jspServletName) != null) {
> +                for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
> +                    context.addServletMappingDecoded(urlPattern, jspServletName, true);
> +                }
> +            } else {
> +                if(log.isDebugEnabled()) {
> +                    for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
> +                        log.debug("Skipping " + urlPattern + " , no servlet " +
> +                                jspServletName);
> +                    }
> +                }
> +            }
> +        }
> +
> +        for (Entry<String, String> entry :
> +                webxml.getPostConstructMethods().entrySet()) {
> +            context.addPostConstructMethod(entry.getKey(), entry.getValue());
> +        }
> +
> +        for (Entry<String, String> entry :
> +            webxml.getPreDestroyMethods().entrySet()) {
> +            context.addPreDestroyMethod(entry.getKey(), entry.getValue());
> +        }
> +    }
> +
> +
> +    private WebXml getTomcatWebXmlFragment(WebXmlParser webXmlParser) {
> +
> +        WebXml webXmlTomcatFragment = createWebXml();
> +        webXmlTomcatFragment.setOverridable(true);
> +
> +        // Set to distributable else every app will be prevented from being
> +        // distributable when the Tomcat fragment is merged with the main
> +        // web.xml
> +        webXmlTomcatFragment.setDistributable(true);
> +        // When merging, the default welcome files are only used if the app has
> +        // not defined any welcomes files.
> +        webXmlTomcatFragment.setAlwaysAddWelcomeFiles(false);
> +
> +        WebResource resource = context.getResources().getResource(Constants.TomcatWebXml);
> +        if (resource.isFile()) {
> +            try {
> +                InputSource source = new InputSource(resource.getURL().toURI().toString());
> +                source.setByteStream(resource.getInputStream());
> +                if (!webXmlParser.parseWebXml(source, webXmlTomcatFragment, false)) {
> +                    ok = false;
> +                }
> +            } catch (URISyntaxException e) {
> +                log.error(sm.getString("contextConfig.tomcatWebXmlError"), e);
> +            }
> +        }
> +        return webXmlTomcatFragment;
> +    }
> +
> +
> +    private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {
> +
> +        // Host should never be null
> +        Host host = (Host) context.getParent();
> +
> +        DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
> +
> +        InputSource globalWebXml = getGlobalWebXmlSource();
> +        InputSource hostWebXml = getHostWebXmlSource();
> +
> +        long globalTimeStamp = 0;
> +        long hostTimeStamp = 0;
> +
> +        if (globalWebXml != null) {
> +            URLConnection uc = null;
> +            try {
> +                URL url = new URL(globalWebXml.getSystemId());
> +                uc = url.openConnection();
> +                globalTimeStamp = uc.getLastModified();
> +            } catch (IOException e) {
> +                globalTimeStamp = -1;
> +            } finally {
> +                if (uc != null) {
> +                    try {
> +                        uc.getInputStream().close();
> +                    } catch (IOException e) {
> +                        ExceptionUtils.handleThrowable(e);
> +                        globalTimeStamp = -1;
> +                    }
> +                }
> +            }
> +        }
> +
> +        if (hostWebXml != null) {
> +            URLConnection uc = null;
> +            try {
> +                URL url = new URL(hostWebXml.getSystemId());
> +                uc = url.openConnection();
> +                hostTimeStamp = uc.getLastModified();
> +            } catch (IOException e) {
> +                hostTimeStamp = -1;
> +            } finally {
> +                if (uc != null) {
> +                    try {
> +                        uc.getInputStream().close();
> +                    } catch (IOException e) {
> +                        ExceptionUtils.handleThrowable(e);
> +                        hostTimeStamp = -1;
> +                    }
> +                }
> +            }
> +        }
> +
> +        if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
> +                entry.getHostTimeStamp() == hostTimeStamp) {
> +            InputSourceUtil.close(globalWebXml);
> +            InputSourceUtil.close(hostWebXml);
> +            return entry.getWebXml();
> +        }
> +
> +        // Parsing global web.xml is relatively expensive. Use a sync block to
> +        // make sure it only happens once. Use the pipeline since a lock will
> +        // already be held on the host by another thread
> +        synchronized (host.getPipeline()) {
> +            entry = hostWebXmlCache.get(host);
> +            if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
> +                    entry.getHostTimeStamp() == hostTimeStamp) {
> +                return entry.getWebXml();
> +            }
> +
> +            WebXml webXmlDefaultFragment = createWebXml();
> +            webXmlDefaultFragment.setOverridable(true);
> +            // Set to distributable else every app will be prevented from being
> +            // distributable when the default fragment is merged with the main
> +            // web.xml
> +            webXmlDefaultFragment.setDistributable(true);
> +            // When merging, the default welcome files are only used if the app has
> +            // not defined any welcomes files.
> +            webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
> +
> +            // Parse global web.xml if present
> +            if (globalWebXml == null) {
> +                // This is unusual enough to log
> +                log.info(sm.getString("contextConfig.defaultMissing"));
> +            } else {
> +                if (!webXmlParser.parseWebXml(
> +                        globalWebXml, webXmlDefaultFragment, false)) {
> +                    ok = false;
> +                }
> +            }
> +
> +            // Parse host level web.xml if present
> +            // Additive apart from welcome pages
> +            webXmlDefaultFragment.setReplaceWelcomeFiles(true);
> +
> +            if (!webXmlParser.parseWebXml(
> +                    hostWebXml, webXmlDefaultFragment, false)) {
> +                ok = false;
> +            }
> +
> +            // Don't update the cache if an error occurs
> +            if (globalTimeStamp != -1 && hostTimeStamp != -1) {
> +                entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
> +                        globalTimeStamp, hostTimeStamp);
> +                hostWebXmlCache.put(host, entry);
> +            }
> +
> +            return webXmlDefaultFragment;
> +        }
> +    }
> +
> +
> +    private void convertJsps(WebXml webXml) {
> +        Map<String,String> jspInitParams;
> +        ServletDef jspServlet = webXml.getServlets().get("jsp");
> +        if (jspServlet == null) {
> +            jspInitParams = new HashMap<>();
> +            Wrapper w = (Wrapper) context.findChild("jsp");
> +            if (w != null) {
> +                String[] params = w.findInitParameters();
> +                for (String param : params) {
> +                    jspInitParams.put(param, w.findInitParameter(param));
> +                }
> +            }
> +        } else {
> +            jspInitParams = jspServlet.getParameterMap();
> +        }
> +        for (ServletDef servletDef: webXml.getServlets().values()) {
> +            if (servletDef.getJspFile() != null) {
> +                convertJsp(servletDef, jspInitParams);
> +            }
> +        }
> +    }
> +
> +    private void convertJsp(ServletDef servletDef,
> +            Map<String,String> jspInitParams) {
> +        servletDef.setServletClass(org.apache.catalina.core.Constants.JSP_SERVLET_CLASS);
> +        String jspFile = servletDef.getJspFile();
> +        if ((jspFile != null) && !jspFile.startsWith("/")) {
> +            if (context.isServlet22()) {
> +                if(log.isDebugEnabled()) {
> +                    log.debug(sm.getString("contextConfig.jspFile.warning",
> +                                       jspFile));
> +                }
> +                jspFile = "/" + jspFile;
> +            } else {
> +                throw new IllegalArgumentException
> +                    (sm.getString("contextConfig.jspFile.error", jspFile));
> +            }
> +        }
> +        servletDef.getParameterMap().put("jspFile", jspFile);
> +        servletDef.setJspFile(null);
> +        for (Map.Entry<String, String> initParam: jspInitParams.entrySet()) {
> +            servletDef.addInitParameter(initParam.getKey(), initParam.getValue());
> +        }
> +    }
> +
> +    protected WebXml createWebXml() {
> +        return new WebXml();
> +    }
> +
> +    /**
> +     * Scan JARs for ServletContainerInitializer implementations.
> +     */
> +    protected void processServletContainerInitializers() {
> +
> +        List<ServletContainerInitializer> detectedScis;
> +        try {
> +            WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
> +            detectedScis = loader.load(ServletContainerInitializer.class);
> +        } catch (IOException e) {
> +            log.error(sm.getString(
> +                    "contextConfig.servletContainerInitializerFail",
> +                    context.getName()),
> +                e);
> +            ok = false;
> +            return;
> +        }
> +
> +        for (ServletContainerInitializer sci : detectedScis) {
> +            initializerClassMap.put(sci, new HashSet<Class<?>>());
> +
> +            HandlesTypes ht;
> +            try {
> +                ht = sci.getClass().getAnnotation(HandlesTypes.class);
> +            } catch (Exception e) {
> +                if (log.isDebugEnabled()) {
> +                    log.info(sm.getString("contextConfig.sci.debug",
> +                            sci.getClass().getName()),
> +                            e);
> +                } else {
> +                    log.info(sm.getString("contextConfig.sci.info",
> +                            sci.getClass().getName()));
> +                }
> +                continue;
> +            }
> +            if (ht == null) {
> +                continue;
> +            }
> +            Class<?>[] types = ht.value();
> +            if (types == null) {
> +                continue;
> +            }
> +
> +            for (Class<?> type : types) {
> +                if (type.isAnnotation()) {
> +                    handlesTypesAnnotations = true;
> +                } else {
> +                    handlesTypesNonAnnotations = true;
> +                }
> +                Set<ServletContainerInitializer> scis =
> +                        typeInitializerMap.get(type);
> +                if (scis == null) {
> +                    scis = new HashSet<>();
> +                    typeInitializerMap.put(type, scis);
> +                }
> +                scis.add(sci);
> +            }
> +        }
> +    }
> +
> +    /**
> +     * Scan JARs that contain web-fragment.xml files that will be used to
> +     * configure this application to see if they also contain static resources.
> +     * If static resources are found, add them to the context. Resources are
> +     * added in web-fragment.xml priority order.
> +     * @param fragments The set of fragments that will be scanned for
> +     *  static resources
> +     */
> +    protected void processResourceJARs(Set<WebXml> fragments) {
> +        for (WebXml fragment : fragments) {
> +            URL url = fragment.getURL();
> +            try {
> +                if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
> +                    try (Jar jar = JarFactory.newInstance(url)) {
> +                        jar.nextEntry();
> +                        String entryName = jar.getEntryName();
> +                        while (entryName != null) {
> +                            if (entryName.startsWith("META-INF/resources/")) {
> +                                context.getResources().createWebResourceSet(
> +                                        WebResourceRoot.ResourceSetType.RESOURCE_JAR,
> +                                        "/", url, "/META-INF/resources");
> +                                break;
> +                            }
> +                            jar.nextEntry();
> +                            entryName = jar.getEntryName();
> +                        }
> +                    }
> +                } else if ("file".equals(url.getProtocol())) {
> +                    File file = new File(url.toURI());
> +                    File resources = new File(file, "META-INF/resources/");
> +                    if (resources.isDirectory()) {
> +                        context.getResources().createWebResourceSet(
> +                                WebResourceRoot.ResourceSetType.RESOURCE_JAR,
> +                                "/", resources.getAbsolutePath(), null, "/");
> +                    }
> +                }
> +            } catch (IOException ioe) {
> +                log.error(sm.getString("contextConfig.resourceJarFail", url,
> +                        context.getName()));
> +            } catch (URISyntaxException e) {
> +                log.error(sm.getString("contextConfig.resourceJarFail", url,
> +                    context.getName()));
> +            }
> +        }
> +    }
> +
> +
> +    /**
> +     * Identify the default web.xml to be used and obtain an input source for
> +     * it.
> +     * @return an input source to the default web.xml
> +     */
> +    protected InputSource getGlobalWebXmlSource() {
> +        // Is a default web.xml specified for the Context?
> +        if (defaultWebXml == null && context instanceof StandardContext) {
> +            defaultWebXml = ((StandardContext) context).getDefaultWebXml();
> +        }
> +        // Set the default if we don't have any overrides
> +        if (defaultWebXml == null) {
> +            getDefaultWebXml();
> +        }
> +
> +        // Is it explicitly suppressed, e.g. in embedded environment?
> +        if (Constants.NoDefaultWebXml.equals(defaultWebXml)) {
> +            return null;
> +        }
> +        return getWebXmlSource(defaultWebXml, true);
> +    }
> +
> +    /**
> +     * Identify the host web.xml to be used and obtain an input source for
> +     * it.
> +     * @return an input source to the default per host web.xml
> +     */
> +    protected InputSource getHostWebXmlSource() {
> +        File hostConfigBase = getHostConfigBase();
> +        if (hostConfigBase == null)
> +            return null;
> +
> +        return getWebXmlSource(hostConfigBase.getPath(), false);
> +    }
> +
> +    /**
> +     * Identify the application web.xml to be used and obtain an input source
> +     * for it.
> +     * @return an input source to the context web.xml
> +     */
> +    protected InputSource getContextWebXmlSource() {
> +        InputStream stream = null;
> +        InputSource source = null;
> +        URL url = null;
> +
> +        String altDDName = null;
> +
> +        // Open the application web.xml file, if it exists
> +        ServletContext servletContext = context.getServletContext();
> +        try {
> +            if (servletContext != null) {
> +                altDDName = (String)servletContext.getAttribute(Globals.ALT_DD_ATTR);
> +                if (altDDName != null) {
> +                    try {
> +                        stream = new FileInputStream(altDDName);
> +                        url = new File(altDDName).toURI().toURL();
> +                    } catch (FileNotFoundException e) {
> +                        log.error(sm.getString("contextConfig.altDDNotFound",
> +                                               altDDName));
> +                    } catch (MalformedURLException e) {
> +                        log.error(sm.getString("contextConfig.applicationUrl"));
> +                    }
> +                }
> +                else {
> +                    stream = servletContext.getResourceAsStream
> +                        (Constants.ApplicationWebXml);
> +                    try {
> +                        url = servletContext.getResource(
> +                                Constants.ApplicationWebXml);
> +                    } catch (MalformedURLException e) {
> +                        log.error(sm.getString("contextConfig.applicationUrl"));
> +                    }
> +                }
> +            }
> +            if (stream == null || url == null) {
> +                if (log.isDebugEnabled()) {
> +                    log.debug(sm.getString("contextConfig.applicationMissing") + " " + context);
> +                }
> +            } else {
> +                source = new InputSource(url.toExternalForm());
> +                source.setByteStream(stream);
> +            }
> +        } finally {
> +            if (source == null && stream != null) {
> +                try {
> +                    stream.close();
> +                } catch (IOException e) {
> +                    // Ignore
> +                }
> +            }
> +        }
> +
> +        return source;
> +    }
> +
> +    public String getConfigBasePath() {
> +        String path = null;
> +        if (context.getParent() instanceof Host) {
> +            Host host = (Host) context.getParent();
> +            if (host.getXmlBase() != null) {
> +                path = host.getXmlBase();
> +            } else {
> +                StringBuilder xmlDir = new StringBuilder("conf");
> +                Container parent = host.getParent();
> +                if (parent instanceof Engine) {
> +                    xmlDir.append('/');
> +                    xmlDir.append(parent.getName());
> +                }
> +                xmlDir.append('/');
> +                xmlDir.append(host.getName());
> +                path = xmlDir.toString();
> +            }
> +        }
> +        return path;
> +    }
> +
> +    /**
> +     * Utility method to create an input source from the specified XML file.
> +     * @param filename  Name of the file (possibly with one or more leading path
> +     *                  segments) to read
> +     * @param global true if processing a shared resource, false if processing
> +     *        a host based resource
> +     * @return the input source
> +     */
> +    protected InputSource getWebXmlSource(String filename, boolean global) {
> +        ConfigurationSource.Resource webXmlResource = null;
> +        try {
> +            if (global) {
> +                if (Constants.DefaultWebXml.equals(filename)) {
> +                    webXmlResource = ConfigFileLoader.getSource().getSharedWebXml();
> +                } else {
> +                    webXmlResource = ConfigFileLoader.getSource().getResource(filename);
> +                }
> +            } else {
> +                String hostWebXml = Container.getConfigPath(context, Constants.HostWebXml);
> +                webXmlResource = ConfigFileLoader.getSource().getConfResource(hostWebXml);
> +            }
> +        } catch (IOException e) {
> +            // Ignore if not found
> +            return null;
> +        }
> +
> +        InputStream stream = null;
> +        InputSource source = null;
> +
> +        try {
> +            stream = webXmlResource.getInputStream();
> +            source = new InputSource(webXmlResource.getURI().toString());
> +            if (stream != null) {
> +                source.setByteStream(stream);
> +            }
> +        } catch (Exception e) {
> +            log.error(sm.getString("contextConfig.defaultError", filename, webXmlResource.getURI()), e);
> +        } finally {
> +            if (source == null && stream != null) {
> +                try {
> +                    stream.close();
> +                } catch (IOException e) {
> +                    // Ignore
> +                }
> +            }
> +        }
> +
> +        return source;
> +    }
> +
> +
> +    /**
> +     * Scan /WEB-INF/lib for JARs and for each one found add it and any
> +     * /META-INF/web-fragment.xml to the resulting Map. web-fragment.xml files
> +     * will be parsed before being added to the map. Every JAR will be added and
> +     * <code>null</code> will be used if no web-fragment.xml was found. Any JARs
> +     * known not contain fragments will be skipped.
> +     *
> +     * @param application The main web.xml metadata
> +     * @param webXmlParser The parser to use to process the web.xml file
> +     * @return A map of JAR name to processed web fragment (if any)
> +     */
> +    protected Map<String,WebXml> processJarsForWebFragments(WebXml application,
> +            WebXmlParser webXmlParser) {
> +
> +        JarScanner jarScanner = context.getJarScanner();
> +        boolean delegate = false;
> +        if (context instanceof StandardContext) {
> +            delegate = ((StandardContext) context).getDelegate();
> +        }
> +        boolean parseRequired = true;
> +        Set<String> absoluteOrder = application.getAbsoluteOrdering();
> +        if (absoluteOrder != null && absoluteOrder.isEmpty() &&
> +                !context.getXmlValidation()) {
> +            // Skip parsing when there is an empty absolute ordering and
> +            // validation is not enabled
> +            parseRequired = false;
> +        }
> +        FragmentJarScannerCallback callback =
> +                new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);
> +
> +        jarScanner.scan(JarScanType.PLUGGABILITY,
> +                context.getServletContext(), callback);
> +
> +        if (!callback.isOk()) {
> +            ok = false;
> +        }
> +        return callback.getFragments();
> +    }
> +
> +    protected void processAnnotations(Set<WebXml> fragments,
> +            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
> +        for(WebXml fragment : fragments) {
> +            // Only need to scan for @HandlesTypes matches if any of the
> +            // following are true:
> +            // - it has already been determined only @HandlesTypes is required
> +            //   (e.g. main web.xml has metadata-complete="true"
> +            // - this fragment is for a container JAR (Servlet 3.1 section 8.1)
> +            // - this fragment has metadata-complete="true"
> +            boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() ||
> +                    fragment.isMetadataComplete();
> +
> +            WebXml annotations = new WebXml();
> +            // no impact on distributable
> +            annotations.setDistributable(true);
> +            URL url = fragment.getURL();
> +            processAnnotationsUrl(url, annotations, htOnly, javaClassCache);
> +            Set<WebXml> set = new HashSet<>();
> +            set.add(annotations);
> +            // Merge annotations into fragment - fragment takes priority
> +            fragment.merge(set);
> +        }
> +    }
> +
> +    protected void processAnnotationsWebResource(WebResource webResource,
> +            WebXml fragment, boolean handlesTypesOnly,
> +            Map<String,JavaClassCacheEntry> javaClassCache) {
> +
> +        if (webResource.isDirectory()) {
> +            WebResource[] webResources =
> +                    webResource.getWebResourceRoot().listResources(
> +                            webResource.getWebappPath());
> +            if (webResources.length > 0) {
> +                if (log.isDebugEnabled()) {
> +                    log.debug(sm.getString(
> +                            "contextConfig.processAnnotationsWebDir.debug",
> +                            webResource.getURL()));
> +                }
> +                for (WebResource r : webResources) {
> +                    processAnnotationsWebResource(r, fragment, handlesTypesOnly, javaClassCache);
> +                }
> +            }
> +        } else if (webResource.isFile() &&
> +                webResource.getName().endsWith(".class")) {
> +            try (InputStream is = webResource.getInputStream()) {
> +                processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
> +            } catch (IOException e) {
> +                log.error(sm.getString("contextConfig.inputStreamWebResource",
> +                        webResource.getWebappPath()),e);
> +            } catch (ClassFormatException e) {
> +                log.error(sm.getString("contextConfig.inputStreamWebResource",
> +                        webResource.getWebappPath()),e);
> +            }
> +        }
> +    }
> +
> +
> +    protected void processAnnotationsUrl(URL url, WebXml fragment,
> +            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
> +        if (url == null) {
> +            // Nothing to do.
> +            return;
> +        } else if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
> +            processAnnotationsJar(url, fragment, handlesTypesOnly, javaClassCache);
> +        } else if ("file".equals(url.getProtocol())) {
> +            try {
> +                processAnnotationsFile(
> +                        new File(url.toURI()), fragment, handlesTypesOnly, javaClassCache);
> +            } catch (URISyntaxException e) {
> +                log.error(sm.getString("contextConfig.fileUrl", url), e);
> +            }
> +        } else {
> +            log.error(sm.getString("contextConfig.unknownUrlProtocol",
> +                    url.getProtocol(), url));
> +        }
> +
> +    }
> +
> +
> +    protected void processAnnotationsJar(URL url, WebXml fragment,
> +            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
> +
> +        try (Jar jar = JarFactory.newInstance(url)) {
> +            if (log.isDebugEnabled()) {
> +                log.debug(sm.getString(
> +                        "contextConfig.processAnnotationsJar.debug", url));
> +            }
> +
> +            jar.nextEntry();
> +            String entryName = jar.getEntryName();
> +            while (entryName != null) {
> +                if (entryName.endsWith(".class")) {
> +                    try (InputStream is = jar.getEntryInputStream()) {
> +                        processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
> +                    } catch (IOException e) {
> +                        log.error(sm.getString("contextConfig.inputStreamJar",
> +                                entryName, url),e);
> +                    } catch (ClassFormatException e) {
> +                        log.error(sm.getString("contextConfig.inputStreamJar",
> +                                entryName, url),e);
> +                    }
> +                }
> +                jar.nextEntry();
> +                entryName = jar.getEntryName();
> +            }
> +        } catch (IOException e) {
> +            log.error(sm.getString("contextConfig.jarFile", url), e);
> +        }
> +    }
> +
> +
> +    protected void processAnnotationsFile(File file, WebXml fragment,
> +            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
> +
> +        if (file.isDirectory()) {
> +            // Returns null if directory is not readable
> +            String[] dirs = file.list();
> +            if (dirs != null) {
> +                if (log.isDebugEnabled()) {
> +                    log.debug(sm.getString(
> +                            "contextConfig.processAnnotationsDir.debug", file));
> +                }
> +                for (String dir : dirs) {
> +                    processAnnotationsFile(
> +                            new File(file,dir), fragment, handlesTypesOnly, javaClassCache);
> +                }
> +            }
> +        } else if (file.getName().endsWith(".class") && file.canRead()) {
> +            try (FileInputStream fis = new FileInputStream(file)) {
> +                processAnnotationsStream(fis, fragment, handlesTypesOnly, javaClassCache);
> +            } catch (IOException e) {
> +                log.error(sm.getString("contextConfig.inputStreamFile",
> +                        file.getAbsolutePath()),e);
> +            } catch (ClassFormatException e) {
> +                log.error(sm.getString("contextConfig.inputStreamFile",
> +                        file.getAbsolutePath()),e);
> +            }
> +        }
> +    }
> +
> +
> +    protected void processAnnotationsStream(InputStream is, WebXml fragment,
> +            boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache)
> +            throws ClassFormatException, IOException {
> +
> +        ClassParser parser = new ClassParser(is);
> +        JavaClass clazz = parser.parse();
> +        checkHandlesTypes(clazz, javaClassCache);
> +
> +        if (handlesTypesOnly) {
> +            return;
> +        }
> +
> +        processClass(fragment, clazz);
> +    }
> +
> +
> +    protected void processClass(WebXml fragment, JavaClass clazz) {
> +        AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
> +        if (annotationsEntries != null) {
> +            String className = clazz.getClassName();
> +            for (AnnotationEntry ae : annotationsEntries) {
> +                String type = ae.getAnnotationType();
> +                if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
> +                    processAnnotationWebServlet(className, ae, fragment);
> +                }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
> +                    processAnnotationWebFilter(className, ae, fragment);
> +                }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
> +                    fragment.addListener(className);
> +                } else {
> +                    // Unknown annotation - ignore
> +                }
> +            }
> +        }
> +    }
> +
> +
> +    /**
> +     * For classes packaged with the web application, the class and each
> +     * super class needs to be checked for a match with {@link HandlesTypes} or
> +     * for an annotation that matches {@link HandlesTypes}.
> +     * @param javaClass the class to check
> +     * @param javaClassCache a class cache
> +     */
> +    protected void checkHandlesTypes(JavaClass javaClass,
> +            Map<String,JavaClassCacheEntry> javaClassCache) {
> +
> +        // Skip this if we can
> +        if (typeInitializerMap.size() == 0) {
> +            return;
> +        }
> +
> +        if ((javaClass.getAccessFlags() &
> +                org.apache.tomcat.util.bcel.Const.ACC_ANNOTATION) != 0) {
> +            // Skip annotations.
> +            return;
> +        }
> +
> +        String className = javaClass.getClassName();
> +
> +        Class<?> clazz = null;
> +        if (handlesTypesNonAnnotations) {
> +            // This *might* be match for a HandlesType.
> +            populateJavaClassCache(className, javaClass, javaClassCache);
> +            JavaClassCacheEntry entry = javaClassCache.get(className);
> +            if (entry.getSciSet() == null) {
> +                try {
> +                    populateSCIsForCacheEntry(entry, javaClassCache);
> +                } catch (StackOverflowError soe) {
> +                    throw new IllegalStateException(sm.getString(
> +                            "contextConfig.annotationsStackOverflow",
> +                            context.getName(),
> +                            classHierarchyToString(className, entry, javaClassCache)));
> +                }
> +            }
> +            if (!entry.getSciSet().isEmpty()) {
> +                // Need to try and load the class
> +                clazz = Introspection.loadClass(context, className);
> +                if (clazz == null) {
> +                    // Can't load the class so no point continuing
> +                    return;
> +                }
> +
> +                for (ServletContainerInitializer sci : entry.getSciSet()) {
> +                    Set<Class<?>> classes = initializerClassMap.get(sci);
> +                    if (classes == null) {
> +                        classes = new HashSet<>();
> +                        initializerClassMap.put(sci, classes);
> +                    }
> +                    classes.add(clazz);
> +                }
> +            }
> +        }
> +
> +        if (handlesTypesAnnotations) {
> +            AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries();
> +            if (annotationEntries != null) {
> +                for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
> +                        typeInitializerMap.entrySet()) {
> +                    if (entry.getKey().isAnnotation()) {
> +                        String entryClassName = entry.getKey().getName();
> +                        for (AnnotationEntry annotationEntry : annotationEntries) {
> +                            if (entryClassName.equals(
> +                                    getClassName(annotationEntry.getAnnotationType()))) {
> +                                if (clazz == null) {
> +                                    clazz = Introspection.loadClass(
> +                                            context, className);
> +                                    if (clazz == null) {
> +                                        // Can't load the class so no point
> +                                        // continuing
> +                                        return;
> +                                    }
> +                                }
> +                                for (ServletContainerInitializer sci : entry.getValue()) {
> +                                    initializerClassMap.get(sci).add(clazz);
> +                                }
> +                                break;
> +                            }
> +                        }
> +                    }
> +                }
> +            }
> +        }
> +    }
> +
> +
> +    private String classHierarchyToString(String className,
> +            JavaClassCacheEntry entry, Map<String,JavaClassCacheEntry> javaClassCache) {
> +        JavaClassCacheEntry start = entry;
> +        StringBuilder msg = new StringBuilder(className);
> +        msg.append("->");
> +
> +        String parentName = entry.getSuperclassName();
> +        JavaClassCacheEntry parent = javaClassCache.get(parentName);
> +        int count = 0;
> +
> +        while (count < 100 && parent != null && parent != start) {
> +            msg.append(parentName);
> +            msg.append("->");
> +
> +            count ++;
> +            parentName = parent.getSuperclassName();
> +            parent = javaClassCache.get(parentName);
> +        }
> +
> +        msg.append(parentName);
> +
> +        return msg.toString();
> +    }
> +
> +    private void populateJavaClassCache(String className, JavaClass javaClass,
> +            Map<String,JavaClassCacheEntry> javaClassCache) {
> +        if (javaClassCache.containsKey(className)) {
> +            return;
> +        }
> +
> +        // Add this class to the cache
> +        javaClassCache.put(className, new JavaClassCacheEntry(javaClass));
> +
> +        populateJavaClassCache(javaClass.getSuperclassName(), javaClassCache);
> +
> +        for (String interfaceName : javaClass.getInterfaceNames()) {
> +            populateJavaClassCache(interfaceName, javaClassCache);
> +        }
> +    }
> +
> +    private void populateJavaClassCache(String className,
> +            Map<String,JavaClassCacheEntry> javaClassCache) {
> +        if (!javaClassCache.containsKey(className)) {
> +            String name = className.replace('.', '/') + ".class";
> +            try (InputStream is = context.getLoader().getClassLoader().getResourceAsStream(name)) {
> +                if (is == null) {
> +                    return;
> +                }
> +                ClassParser parser = new ClassParser(is);
> +                JavaClass clazz = parser.parse();
> +                populateJavaClassCache(clazz.getClassName(), clazz, javaClassCache);
> +            } catch (ClassFormatException e) {
> +                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
> +                        className), e);
> +            } catch (IOException e) {
> +                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
> +                        className), e);
> +            }
> +        }
> +    }
> +
> +    private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry,
> +            Map<String,JavaClassCacheEntry> javaClassCache) {
> +        Set<ServletContainerInitializer> result = new HashSet<>();
> +
> +        // Super class
> +        String superClassName = cacheEntry.getSuperclassName();
> +        JavaClassCacheEntry superClassCacheEntry =
> +                javaClassCache.get(superClassName);
> +
> +        // Avoid an infinite loop with java.lang.Object
> +        if (cacheEntry.equals(superClassCacheEntry)) {
> +            cacheEntry.setSciSet(EMPTY_SCI_SET);
> +            return;
> +        }
> +
> +        // May be null of the class is not present or could not be loaded.
> +        if (superClassCacheEntry != null) {
> +            if (superClassCacheEntry.getSciSet() == null) {
> +                populateSCIsForCacheEntry(superClassCacheEntry, javaClassCache);
> +            }
> +            result.addAll(superClassCacheEntry.getSciSet());
> +        }
> +        result.addAll(getSCIsForClass(superClassName));
> +
> +        // Interfaces
> +        for (String interfaceName : cacheEntry.getInterfaceNames()) {
> +            JavaClassCacheEntry interfaceEntry =
> +                    javaClassCache.get(interfaceName);
> +            // A null could mean that the class not present in application or
> +            // that there is nothing of interest. Either way, nothing to do here
> +            // so move along
> +            if (interfaceEntry != null) {
> +                if (interfaceEntry.getSciSet() == null) {
> +                    populateSCIsForCacheEntry(interfaceEntry, javaClassCache);
> +                }
> +                result.addAll(interfaceEntry.getSciSet());
> +            }
> +            result.addAll(getSCIsForClass(interfaceName));
> +        }
> +
> +        cacheEntry.setSciSet(result.isEmpty() ? EMPTY_SCI_SET : result);
> +    }
> +
> +    private Set<ServletContainerInitializer> getSCIsForClass(String className) {
> +        for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
> +                typeInitializerMap.entrySet()) {
> +            Class<?> clazz = entry.getKey();
> +            if (!clazz.isAnnotation()) {
> +                if (clazz.getName().equals(className)) {
> +                    return entry.getValue();
> +                }
> +            }
> +        }
> +        return EMPTY_SCI_SET;
> +    }
> +
> +    private static final String getClassName(String internalForm) {
> +        if (!internalForm.startsWith("L")) {
> +            return internalForm;
> +        }
> +
> +        // Assume starts with L, ends with ; and uses / rather than .
> +        return internalForm.substring(1,
> +                internalForm.length() - 1).replace('/', '.');
> +    }
> +
> +    protected void processAnnotationWebServlet(String className,
> +            AnnotationEntry ae, WebXml fragment) {
> +        String servletName = null;
> +        // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
> +        List<ElementValuePair> evps = ae.getElementValuePairs();
> +        for (ElementValuePair evp : evps) {
> +            String name = evp.getNameString();
> +            if ("name".equals(name)) {
> +                servletName = evp.getValue().stringifyValue();
> +                break;
> +            }
> +        }
> +        if (servletName == null) {
> +            // classname is default servletName as annotation has no name!
> +            servletName = className;
> +        }
> +        ServletDef servletDef = fragment.getServlets().get(servletName);
> +
> +        boolean isWebXMLservletDef;
> +        if (servletDef == null) {
> +            servletDef = new ServletDef();
> +            servletDef.setServletName(servletName);
> +            servletDef.setServletClass(className);
> +            isWebXMLservletDef = false;
> +        } else {
> +            isWebXMLservletDef = true;
> +        }
> +
> +        boolean urlPatternsSet = false;
> +        String[] urlPatterns = null;
> +
> +        // List<ElementValuePair> evps = ae.getElementValuePairs();
> +        for (ElementValuePair evp : evps) {
> +            String name = evp.getNameString();
> +            if ("value".equals(name) || "urlPatterns".equals(name)) {
> +                if (urlPatternsSet) {
> +                    throw new IllegalArgumentException(sm.getString(
> +                            "contextConfig.urlPatternValue", "WebServlet", className));
> +                }
> +                urlPatternsSet = true;
> +                urlPatterns = processAnnotationsStringArray(evp.getValue());
> +            } else if ("description".equals(name)) {
> +                if (servletDef.getDescription() == null) {
> +                    servletDef.setDescription(evp.getValue().stringifyValue());
> +                }
> +            } else if ("displayName".equals(name)) {
> +                if (servletDef.getDisplayName() == null) {
> +                    servletDef.setDisplayName(evp.getValue().stringifyValue());
> +                }
> +            } else if ("largeIcon".equals(name)) {
> +                if (servletDef.getLargeIcon() == null) {
> +                    servletDef.setLargeIcon(evp.getValue().stringifyValue());
> +                }
> +            } else if ("smallIcon".equals(name)) {
> +                if (servletDef.getSmallIcon() == null) {
> +                    servletDef.setSmallIcon(evp.getValue().stringifyValue());
> +                }
> +            } else if ("asyncSupported".equals(name)) {
> +                if (servletDef.getAsyncSupported() == null) {
> +                    servletDef.setAsyncSupported(evp.getValue()
> +                            .stringifyValue());
> +                }
> +            } else if ("loadOnStartup".equals(name)) {
> +                if (servletDef.getLoadOnStartup() == null) {
> +                    servletDef
> +                            .setLoadOnStartup(evp.getValue().stringifyValue());
> +                }
> +            } else if ("initParams".equals(name)) {
> +                Map<String, String> initParams = processAnnotationWebInitParams(evp
> +                        .getValue());
> +                if (isWebXMLservletDef) {
> +                    Map<String, String> webXMLInitParams = servletDef
> +                            .getParameterMap();
> +                    for (Map.Entry<String, String> entry : initParams
> +                            .entrySet()) {
> +                        if (webXMLInitParams.get(entry.getKey()) == null) {
> +                            servletDef.addInitParameter(entry.getKey(), entry
> +                                    .getValue());
> +                        }
> +                    }
> +                } else {
> +                    for (Map.Entry<String, String> entry : initParams
> +                            .entrySet()) {
> +                        servletDef.addInitParameter(entry.getKey(), entry
> +                                .getValue());
> +                    }
> +                }
> +            }
> +        }
> +        if (!isWebXMLservletDef && urlPatterns != null) {
> +            fragment.addServlet(servletDef);
> +        }
> +        if (urlPatterns != null) {
> +            if (!fragment.getServletMappings().containsValue(servletName)) {
> +                for (String urlPattern : urlPatterns) {
> +                    fragment.addServletMapping(urlPattern, servletName);
> +                }
> +            }
> +        }
> +
> +    }
> +
> +    /**
> +     * process filter annotation and merge with existing one!
> +     * FIXME: refactoring method too long and has redundant subroutines with
> +     *        processAnnotationWebServlet!
> +     * @param className The filter class name
> +     * @param ae The filter annotation
> +     * @param fragment The corresponding fragment
> +     */
> +    protected void processAnnotationWebFilter(String className,
> +            AnnotationEntry ae, WebXml fragment) {
> +        String filterName = null;
> +        // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
> +        List<ElementValuePair> evps = ae.getElementValuePairs();
> +        for (ElementValuePair evp : evps) {
> +            String name = evp.getNameString();
> +            if ("filterName".equals(name)) {
> +                filterName = evp.getValue().stringifyValue();
> +                break;
> +            }
> +        }
> +        if (filterName == null) {
> +            // classname is default filterName as annotation has no name!
> +            filterName = className;
> +        }
> +        FilterDef filterDef = fragment.getFilters().get(filterName);
> +        FilterMap filterMap = new FilterMap();
> +
> +        boolean isWebXMLfilterDef;
> +        if (filterDef == null) {
> +            filterDef = new FilterDef();
> +            filterDef.setFilterName(filterName);
> +            filterDef.setFilterClass(className);
> +            isWebXMLfilterDef = false;
> +        } else {
> +            isWebXMLfilterDef = true;
> +        }
> +
> +        boolean urlPatternsSet = false;
> +        boolean servletNamesSet = false;
> +        boolean dispatchTypesSet = false;
> +        String[] urlPatterns = null;
> +
> +        for (ElementValuePair evp : evps) {
> +            String name = evp.getNameString();
> +            if ("value".equals(name) || "urlPatterns".equals(name)) {
> +                if (urlPatternsSet) {
> +                    throw new IllegalArgumentException(sm.getString(
> +                            "contextConfig.urlPatternValue", "WebFilter", className));
> +                }
> +                urlPatterns = processAnnotationsStringArray(evp.getValue());
> +                urlPatternsSet = urlPatterns.length > 0;
> +                for (String urlPattern : urlPatterns) {
> +                    // % decoded (if required) using UTF-8
> +                    filterMap.addURLPattern(urlPattern);
> +                }
> +            } else if ("servletNames".equals(name)) {
> +                String[] servletNames = processAnnotationsStringArray(evp
> +                        .getValue());
> +                servletNamesSet = servletNames.length > 0;
> +                for (String servletName : servletNames) {
> +                    filterMap.addServletName(servletName);
> +                }
> +            } else if ("dispatcherTypes".equals(name)) {
> +                String[] dispatcherTypes = processAnnotationsStringArray(evp
> +                        .getValue());
> +                dispatchTypesSet = dispatcherTypes.length > 0;
> +                for (String dispatcherType : dispatcherTypes) {
> +                    filterMap.setDispatcher(dispatcherType);
> +                }
> +            } else if ("description".equals(name)) {
> +                if (filterDef.getDescription() == null) {
> +                    filterDef.setDescription(evp.getValue().stringifyValue());
> +                }
> +            } else if ("displayName".equals(name)) {
> +                if (filterDef.getDisplayName() == null) {
> +                    filterDef.setDisplayName(evp.getValue().stringifyValue());
> +                }
> +            } else if ("largeIcon".equals(name)) {
> +                if (filterDef.getLargeIcon() == null) {
> +                    filterDef.setLargeIcon(evp.getValue().stringifyValue());
> +                }
> +            } else if ("smallIcon".equals(name)) {
> +                if (filterDef.getSmallIcon() == null) {
> +                    filterDef.setSmallIcon(evp.getValue().stringifyValue());
> +                }
> +            } else if ("asyncSupported".equals(name)) {
> +                if (filterDef.getAsyncSupported() == null) {
> +                    filterDef
> +                            .setAsyncSupported(evp.getValue().stringifyValue());
> +                }
> +            } else if ("initParams".equals(name)) {
> +                Map<String, String> initParams = processAnnotationWebInitParams(evp
> +                        .getValue());
> +                if (isWebXMLfilterDef) {
> +                    Map<String, String> webXMLInitParams = filterDef
> +                            .getParameterMap();
> +                    for (Map.Entry<String, String> entry : initParams
> +                            .entrySet()) {
> +                        if (webXMLInitParams.get(entry.getKey()) == null) {
> +                            filterDef.addInitParameter(entry.getKey(), entry
> +                                    .getValue());
> +                        }
> +                    }
> +                } else {
> +                    for (Map.Entry<String, String> entry : initParams
> +                            .entrySet()) {
> +                        filterDef.addInitParameter(entry.getKey(), entry
> +                                .getValue());
> +                    }
> +                }
> +
> +            }
> +        }
> +        if (!isWebXMLfilterDef) {
> +            fragment.addFilter(filterDef);
> +            if (urlPatternsSet || servletNamesSet) {
> +                filterMap.setFilterName(filterName);
> +                fragment.addFilterMapping(filterMap);
> +            }
> +        }
> +        if (urlPatternsSet || dispatchTypesSet) {
> +            Set<FilterMap> fmap = fragment.getFilterMappings();
> +            FilterMap descMap = null;
> +            for (FilterMap map : fmap) {
> +                if (filterName.equals(map.getFilterName())) {
> +                    descMap = map;
> +                    break;
> +                }
> +            }
> +            if (descMap != null) {
> +                String[] urlsPatterns = descMap.getURLPatterns();
> +                if (urlPatternsSet
> +                        && (urlsPatterns == null || urlsPatterns.length == 0)) {
> +                    for (String urlPattern : filterMap.getURLPatterns()) {
> +                        // % decoded (if required) using UTF-8
> +                        descMap.addURLPattern(urlPattern);
> +                    }
> +                }
> +                String[] dispatcherNames = descMap.getDispatcherNames();
> +                if (dispatchTypesSet
> +                        && (dispatcherNames == null || dispatcherNames.length == 0)) {
> +                    for (String dis : filterMap.getDispatcherNames()) {
> +                        descMap.setDispatcher(dis);
> +                    }
> +                }
> +            }
> +        }
> +
> +    }
> +
> +    protected String[] processAnnotationsStringArray(ElementValue ev) {
> +        List<String> values = new ArrayList<>();
> +        if (ev instanceof ArrayElementValue) {
> +            ElementValue[] arrayValues =
> +                ((ArrayElementValue) ev).getElementValuesArray();
> +            for (ElementValue value : arrayValues) {
> +                values.add(value.stringifyValue());
> +            }
> +        } else {
> +            values.add(ev.stringifyValue());
> +        }
> +        String[] result = new String[values.size()];
> +        return values.toArray(result);
> +    }
> +
> +    protected Map<String,String> processAnnotationWebInitParams(
> +            ElementValue ev) {
> +        Map<String, String> result = new HashMap<>();
> +        if (ev instanceof ArrayElementValue) {
> +            ElementValue[] arrayValues =
> +                ((ArrayElementValue) ev).getElementValuesArray();
> +            for (ElementValue value : arrayValues) {
> +                if (value instanceof AnnotationElementValue) {
> +                    List<ElementValuePair> evps = ((AnnotationElementValue) value)
> +                            .getAnnotationEntry().getElementValuePairs();
> +                    String initParamName = null;
> +                    String initParamValue = null;
> +                    for (ElementValuePair evp : evps) {
> +                        if ("name".equals(evp.getNameString())) {
> +                            initParamName = evp.getValue().stringifyValue();
> +                        } else if ("value".equals(evp.getNameString())) {
> +                            initParamValue = evp.getValue().stringifyValue();
> +                        } else {
> +                            // Ignore
> +                        }
> +                    }
> +                    result.put(initParamName, initParamValue);
> +                }
> +            }
> +        }
> +        return result;
> +    }
> +
> +    private static class DefaultWebXmlCacheEntry {
> +        private final WebXml webXml;
> +        private final long globalTimeStamp;
> +        private final long hostTimeStamp;
> +
> +        public DefaultWebXmlCacheEntry(WebXml webXml, long globalTimeStamp,
> +                long hostTimeStamp) {
> +            this.webXml = webXml;
> +            this.globalTimeStamp = globalTimeStamp;
> +            this.hostTimeStamp = hostTimeStamp;
> +        }
> +
> +        public WebXml getWebXml() {
> +            return webXml;
> +        }
> +
> +        public long getGlobalTimeStamp() {
> +            return globalTimeStamp;
> +        }
> +
> +        public long getHostTimeStamp() {
> +            return hostTimeStamp;
> +        }
> +    }
> +
> +    static class JavaClassCacheEntry {
> +        public final String superclassName;
> +
> +        public final String[] interfaceNames;
> +
> +        private Set<ServletContainerInitializer> sciSet = null;
> +
> +        public JavaClassCacheEntry(JavaClass javaClass) {
> +            superclassName = javaClass.getSuperclassName();
> +            interfaceNames = javaClass.getInterfaceNames();
> +        }
> +
> +        public String getSuperclassName() {
> +            return superclassName;
> +        }
> +
> +        public String[] getInterfaceNames() {
> +            return interfaceNames;
> +        }
> +
> +        public Set<ServletContainerInitializer> getSciSet() {
> +            return sciSet;
> +        }
> +
> +        public void setSciSet(Set<ServletContainerInitializer> sciSet) {
> +            this.sciSet = sciSet;
> +        }
> +    }
> +}
> diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
> index 2542985..354aff7 100644
> --- a/webapps/docs/changelog.xml
> +++ b/webapps/docs/changelog.xml
> @@ -1,7425 +1,7432 @@
> -<?xml version="1.0" encoding="UTF-8"?>
> -<!--
> -  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.
> --->
> -<!DOCTYPE document [
> -  <!ENTITY project SYSTEM "project.xml">
> -]>
> -<?xml-stylesheet type="text/xsl" href="tomcat-docs.xsl"?>
> -<document url="changelog.html">
> -
> -  &project;
> -
> -  <properties>
> -    <title>Changelog</title>
> -    <no-comments />
> -  </properties>
> -
> -<body>
> -<!--
> -  Subsection ordering:
> -  General, Catalina, Coyote, Jasper, Cluster, WebSocket, Web applications,
> -  Extras, Tribes, jdbc-pool, Other
> -
> -  Item Ordering:
> -
> -  Fixes having an issue number are sorted by their number, ascending.
> -
> -  There is no ordering by add/update/fix/scode.
> -
> -  Other fixed issues are added to the end of the list, chronologically.
> -  They eventually become mixed with the numbered issues (i.e., numbered
> -  issues do not "pop up" wrt. others).
> --->
> -<section name="Tomcat 9.0.20 (markt)" rtext="in development">
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        The <code>useAsyncIO</code> boolean attribute on the Connector element
> -        value now defaults to <code>true</code>. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.19 (markt)" rtext="release in progress">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Fix wrong JMX registration regression in 9.0.18. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <update>
> -        Add vectoring for NIO in the base and SSL channels. (remm)
> -      </update>
> -      <add>
> -        Add asynchronous IO from NIO2 to the NIO connector, with support for
> -        the async IO implementations for HTTP/2 and Websockets. The
> -        <code>useAsyncIO</code> boolean attribute on the Connector element
> -        allows enabling use of the asynchronous IO API. (remm)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Ensure that the correct files are included in the source distribution
> -        for javacc based parsers depending on whether jjtree is used or not.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Ensure that text files in the source distribution have the correct line
> -        endings for the target platform. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.18 (markt)" rtext="not released">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>63196</bug>: Provide a default (<code>X-Forwarded-Proto</code>) for
> -        the <code>protocolHeader</code> attribute of the
> -        <code>RemoteIpFilter</code> and <code>RemoteIpValve</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63235</bug>: Refactor Charset cache to reduce start time. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63249</bug>: Use a consistent log level (<code>WARN</code>) when
> -        logging the failure to register or deregister a JMX Bean. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63249</bug>: Use a consistent log level (<code>ERROR</code>) when
> -        logging the <code>LifecycleException</code> associated with the failure
> -        to start or stop a component. (markt)
> -      </fix>
> -      <fix>
> -        When the SSI directive <code>fsize</code> is used with an invalid
> -        target, return a file size of <code>-</code> rather than
> -        <code>1k</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63251</bug>: Implement a work-around for a known JRE bug (<a
> -        href="https://bugs.openjdk.java.net/browse/JDK-8194653">JDK-8194653</a>)
> -        that may cause a dead-lock when Tomcat starts. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63275</bug>: When using a <code>RequestDispatcher</code> ensure
> -        that <code>HttpServletRequest.getContextPath()</code> returns an encoded
> -        path in the dispatched request. (markt)
> -      </fix>
> -      <update>
> -        Add optional listeners for Server/Listener, as a slight variant of
> -        a standard listener. The difference is that loading is not fatal when
> -        it fails. This would allow adding example configuration to the standard
> -        server.xml if deemed useful. Storeconfig will not attempt to persist
> -        the new listener. (remm)
> -      </update>
> -      <fix>
> -        <bug>63286</bug>: Document the differences in behaviour between the
> -        <code>LogFormat</code> directive in httpd and the <code>pattern</code>
> -        attribute in the <code>AccessLogValve</code> for <code>%D</code> and
> -        <code>%T</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63287</bug>: Make logging levels more consistent for similar issues
> -        of similar severity. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63311</bug>: Add support for https URLs to the local resolver within
> -        Tomcat used to resolve standard XML DTDs and schemas when Tomcat is
> -        configured to validate XML configuration files such as web.xml. (markt)
> -      </fix>
> -      <fix>
> -        Encode the output of the SSI <code>printenv</code> command. (markt)
> -      </fix>
> -      <scode>
> -        Use constants for SSI encoding values. (markt)
> -      </scode>
> -      <add>
> -        When the CGI Servlet is configured with
> -        <code>enableCmdLineArguments</code> set to true, limit the encoded form
> -        of the individual command line arguments to those values allowed by RFC
> -        3875. This restriction may be relaxed by the use of the new
> -        initialisation parameter <code>cmdLineArgumentsEncoded</code>. (markt)
> -      </add>
> -      <add>
> -        When the CGI Servlet is configured with
> -        <code>enableCmdLineArguments</code> set to true, limit the decoded form
> -        of the individual command line arguments to known safe values when
> -        running on Windows. This restriction may be relaxed by the use of the
> -        new initialisation parameter <code>cmdLineArgumentsDecoded</code>. This
> -        is the fix for CVE-2019-0232. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Fix bad interaction between NIO2 async read API and the regular read.
> -        (remm)
> -      </fix>
> -      <fix>
> -        Refactor NIO2 write pending strategy for the classic IO API. (remm)
> -      </fix>
> -      <fix>
> -        Restore original maxConnections default for NIO2 as the underlying
> -        close issues have been fixed. (remm)
> -      </fix>
> -      <fix>
> -        Harmonize NIO2 isReadyForWrite with isReadyForRead code. (remm)
> -      </fix>
> -      <fix>
> -        When using a JSSE TLS connector that supported ALPN (Java 9 onwards) and
> -        a protocol was not negotiated, Tomcat failed to fallback to HTTP/1.1 and
> -        instead dropped the connection. (markt)
> -      </fix>
> -      <fix>
> -        Correct a regression in the TLS connector refactoring in Tomcat 9.0.17
> -        that prevented the use of PKCS#8 private keys with OpenSSL based
> -        connectors. (markt)
> -      </fix>
> -      <fix>
> -        Fix NIO2 SSL edge cases. (remm)
> -      </fix>
> -      <fix>
> -        When performing an upgrade from HTTP/1.1 to HTTP/2, ensure that any
> -        query string present in the original HTTP/1.1 request is passed to the
> -        HTTP/2 request processing. (markt)
> -      </fix>
> -      <fix>
> -        When Tomcat writes a final response without reading all of an HTTP/2
> -        request, reset the stream to inform the client that the remaining
> -        request body is not required. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <add>
> -        Add support for specifying Java 11 (with the value <code>11</code>) as
> -        the compiler source and/or compiler target for JSP compilation. (markt)
> -      </add>
> -      <add>
> -        Add support for specifying Java 12 (with the value <code>12</code>) and
> -        Java 13 (with the value <code>13</code>) as the compiler source and/or
> -        compiler target for JSP compilation. If used with an ECJ version that
> -        does not support these values, a warning will be logged and the latest
> -        supported version will used. Based on a patch by Thomas Collignon.
> -        (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        <bug>63184</bug>: Expand the SSI documentation to provide more
> -        information on the supported directives and their attributes. Patch
> -        provided by nightwatchcyber. (markt)
> -      </fix>
> -      <add>
> -        Add a note to the documentation about the risk of DoS with poorly
> -        written regular expressions and the <code>RewriteValve</code>. Patch
> -        provided by salgattas. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="jdbc-pool">
> -    <changelog>
> -      <fix>
> -        Improved maxAge handling. Add support for age check on idle connections.
> -        Connection that expired reconnects rather than closes it. Patch provided
> -        by toby1984. (kfujino)
> -      </fix>
> -      <fix>
> -        <bug>63320</bug>: Ensure that <code>StatementCache</code> caches
> -        statements that include arrays in arguments. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <update>
> -        Update to the Eclipse JDT compiler 4.10. (markt)
> -      </update>
> -      <add>
> -        Expand the coverage and quality of the Spanish translations provided
> -        with Apache Tomcat. Includes contributions by Ulises Gonzalez Horta.
> -        (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Czech translations provided
> -        with Apache Tomcat. Includes contributions by Arnošt Havelka. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Chinese translations provided
> -        with Apache Tomcat. Includes contributions by winsonzhao and wjt.
> -        (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Russian translations provided
> -        with Apache Tomcat. (kkolinko)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Japanese translations provided
> -        with Apache Tomcat. (kfujino)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Korean translations provided
> -        with Apache Tomcat. (woonsan)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the German translations provided
> -        with Apache Tomcat. (fschumacher)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the French translations provided
> -        with Apache Tomcat. (remm)
> -      </add>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.17 (markt)" rtext="2019-03-18">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Refactor how cookies are transferred from the base request to a
> -        <code>PushBuilder</code> so that they are accessible, and may be edited,
> -        via the standard <code>PushBuilder</code> methods for working with HTTP
> -        headers. (markt)
> -      </fix>
> -      <update>
> -        Simplify the value of <code>jarsToSkip</code> property in
> -        <code>catalina.properties</code> file for tomcat-i18n jar files.
> -        Use prefix pattern instead of listing each language. (kkolinko)
> -      </update>
> -      <fix>
> -        Restore the getter and setter for the access log valve attribute
> -        <code>maxLogMessageBufferSize</code> that were accidentally removed.
> -        (markt)
> -      </fix>
> -      <add>
> -        <bug>63206</bug>: Add a new attribute to <code>Context</code> -
> -        <code>createUploadTargets</code> which, if <code>true</code> enables
> -        Tomcat to create the temporary upload location used by a Servlet if the
> -        location specified by the Servlet does not already exist. The default
> -        value is <code>false</code>. (markt)
> -      </add>
> -      <fix>
> -        <bug>63210</bug>: Ensure that the Apache Commons DBCP 2 based default
> -        connection pool is correctly shutdown when it is no longer required.
> -        This ensures that a non-daemon thread is not left running that will
> -        prevent Tomcat from shutting down cleanly. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63213</bug>: Ensure the correct escaping of group names when
> -        searching for nested groups when the JNDIRealm is configured with
> -        <code>roleNested</code> set to <code>true</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63236</bug>: Use <code>String.intern()</code> as suggested by
> -        Phillip Webb to reduce memory wasted due to String duplication. This
> -        changes saves ~245k when starting a clean installation. With additional
> -        thanks to YourKit Java profiler for helping to track down the wasted
> -        memory and the root causes. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63246</bug>: Fix a potential <code>NullPointerException</code> when
> -        calling <code>AsyncContext.dispatch()</code>. (markt)
> -      </fix>
> -      <fix>
> -        Always use the absolute path of the <code>docBase</code> during the
> -        deployment process to determine the Context name, deployment type,
> -        whether the <code>docBase</code> is located within the
> -        <code>appBase</code> etc. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        When performing an HTTP/1.1 upgrade to HTTP/2 (h2c) ensure that the hostname
> -        and port from the HTTP/1.1 Host header of the upgraded request are made
> -        available via the standard methods
> -        <code>ServletRequest.getServerName()</code> and
> -        <code>ServletRequest.getServerPort()</code>. (markt)
> -      </fix>
> -      <fix>
> -        Refactor the APR/Native endpoint TLS configuration code to enable JSSE
> -        style configuration - including JKS keystores - to be used with the
> -        APR/Native connector. (markt)
> -      </fix>
> -      <add>
> -        With the TLS configuration refactoring, the configuration attributes
> -        <code>sessionCacheSize</code> and <code>sessionTimeout</code> are no
> -        longer limited to JSSE implementations. They may now be used with
> -        OpenSSL implementations as well. (markt)
> -      </add>
> -      <fix>
> -        Refactor NIO2 read pending strategy for the classic IO API. (remm)
> -      </fix>
> -      <fix>
> -        <bug>63182</bug>: Avoid extra read notifications for HTTP/1.1 with
> -        NIO2 when using asynchronous threads. (remm)
> -      </fix>
> -      <add>
> -        <bug>63205</bug>: Add a work-around for a known
> -        <a href="https://bugs.openjdk.java.net/browse/JDK-8157404">JRE KeyStore
> -        loading bug</a>. (markt)
> -      </add>
> -      <fix>
> -        NIO2 should try to use SocketTimeoutException everywhere rather than a
> -        mix of it and InterruptedByTimeout. (remm)
> -      </fix>
> -      <fix>
> -        Correct an error in the request validation that meant that HTTP/2 push
> -        requests always resulted in a 400 response. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63223</bug>: Correctly account for push requests when tracking
> -        currently active HTTP/2 streams. (markt)
> -      </fix>
> -      <fix>
> -        Ensure enough buffer space when using TLS with NIO2 by using the main
> -        read buffer to store additional decrypted data. (remm)
> -      </fix>
> -      <fix>
> -        Verify HTTP/2 stream is still writable before assuming a timeout
> -        occurred. (remm)
> -      </fix>
> -      <fix>
> -        Avoid some overflow cases with OpenSSL to improve efficiency, as the
> -        OpenSSL engine has an internal buffer. (remm)
> -      </fix>
> -      <fix>
> -        Harmonize HTTP/1.1 NIO2 keepalive code. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <scode>
> -        Remove the <code>STREAMS_DROP_EMPTY_MESSAGES</code> system property that
> -        was introduced to work-around four failing TCK tests. An alternative
> -        solution has been implemented. Sending messages via
> -        <code>getSendStream()</code> and <code>getSendWriter()</code> will now
> -        only result in messages on the wire if data is written to the
> -        <code>OutputStream</code> or <code>Writer</code>. Writing zero length
> -        data will result in an empty message. Note that sending a message via an
> -        <code>Encoder</code> may result in the message being send via
> -        <code>getSendStream()</code> or <code>getSendWriter()</code>. (markt)
> -      </scode>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        Fix messages used by Manager and Host Manager web applications.
> -        Disambiguate message keys used when adding or removing a host.
> -        Improve display of summary values on the status page: separate
> -        terms and values with a whitespace. Improve wording of messages
> -        for expire sessions command. (kkolinko)
> -      </fix>
> -      <fix>
> -        Do not add CSRF nonce parameter and suppress Referer header for external
> -        links in Manager and Host Manager web applications. (kkolinko)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <add>
> -        Add feature that discover local member from the static member list.
> -        (kfujino)
> -      </add>
> -      <fix>
> -        Ensure that members registered in the addSuspects list are static
> -        members. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <add>
> -        Expand the coverage and quality of the French translations provided
> -        with Apache Tomcat. (remm)
> -      </add>
> -      <fix>
> -        <bug>63041</bug>: Revert the changes for <bug>53930</bug> that added
> -        support for the <code>CATALINA_OUT_CMD</code> environment variable as
> -        they prevented correct operation with systemd configurations that did
> -        not explicitly specify a PID file. (markt)
> -      </fix>
> -      <add>
> -        Expand the coverage and quality of the Russian translations provided
> -        with Apache Tomcat. (kkolinko)
> -      </add>
> -      <fix>
> -        Fix the artifactId of <code>tomcat-i18n-cs</code>. (rjung)
> -      </fix>
> -      <add>
> -        Expand the coverage and quality of the Korean translations provided
> -        with Apache Tomcat. (woonsan)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Chinese translations provided
> -        with Apache Tomcat. Includes contributions by winsonzhao. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Czech translations provided
> -        with Apache Tomcat. Includes contributions by Arnošt Havelka. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Spanish translations provided
> -        with Apache Tomcat. Includes contributions by Ulises Gonzalez Horta.
> -        (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.16 (markt)" rtext="2019-02-08">
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        Use client's preferred language for the Server Status page of the
> -        Manager web application. Review and fix several cases when the
> -        client's language preference was not respected in Manager and
> -        Host Manager web applications. (kkolinko)
> -      </fix>
> -      <fix>
> -        <bug>63141</bug>: Ensure that translated manager response strings still
> -        start with <code>OK -</code> where expected by the associated Ant tasks.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>63143</bug>: Ensure that the Manager web application respects the
> -        language preferences of the user as configured in the browser when the
> -        language of the default system locale is not English. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <fix>
> -        Remove unnecessary shutdown for executor. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <update>
> -        Update the NSIS Installer used to build the Windows installer to version
> -        3.04. (markt)
> -      </update>
> -      <add>
> -        Add Czech translations to Apache Tomcat. Includes contributions from
> -        Arnošt Havelka and Alice. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Spanish translations provided
> -        with Apache Tomcat. Includes contributions from Ulises Gonzalez Horta.
> -        (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the French translations provided
> -        with Apache Tomcat. (remm)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Korean translations provided
> -        with Apache Tomcat. (woonsan)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Japanese translations provided
> -        with Apache Tomcat. Includes contributions from Yujiorama. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Chinese translations provided
> -        with Apache Tomcat. Includes contributions from zheng. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Russian translations provided
> -        with Apache Tomcat. (kkolinko)
> -      </add>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.15 (markt)" rtext="not released">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>54741</bug>: Add a new method,
> -        <code>Tomcat.addWebapp(String,URL)</code>, that allows a web application
> -        to be deployed from a URL when using Tomcat in embedded mode. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63002</bug>: Fix setting rewrite qsdiscard flag. (remm)
> -      </fix>
> -      <fix>
> -        Implement the requirements of section 8.2.2 2c of the Servlet
> -        specification and prevent a web application from deploying if it has
> -        fragments with duplicate names and is configured to use relative
> -        ordering of fragments. (markt)
> -      </fix>
> -      <fix>
> -        Ensure that the HEAD response is consistent with the GET response when
> -        <code>HttpServlet</code> is relied upon to generate the HEAD response
> -        and the GET response uses chunking. (markt)
> -      </fix>
> -      <fix>
> -        Ensure that the <code>ServletOutputStream</code> implementation is
> -        consistent with the requirements of asynchronous I/O and that all of the
> -        write methods use a single write rather than multiple writes. (markt)
> -      </fix>
> -      <fix>
> -        Correct the Javadoc for <code>Context.getDocBase()</code> and
> -        <code>Context.setDocBase()</code> and remove text that indicates that a
> -        URL may be used for the <code>docBase</code> as this has not been the
> -        case for quite some time. (markt)
> -      </fix>
> -      <update>
> -        Add basic health check valve. (remm)
> -      </update>
> -      <fix>
> -        Correct a bug exposed in 9.0.14 and ensure that the Tomcat terminates in
> -        a timely manner when running as a service. (markt)
> -      </fix>
> -      <fix>
> -        Log a message when using a Connector that requires Apr without enabling
> -        the AprLifecycleListener first. (csutherl)
> -      </fix>
> -      <fix>
> -        Utility thread count for special negative or zero values will again be
> -        based on Runtime.getRuntime().availableProcessors(). (remm)
> -      </fix>
> -      <scode>
> -        Treat I/O errors during request body reads the same way as I/O errors
> -        during response body writes. The errors are treated as client side
> -        errors rather than server side errors and only logged at debug level.
> -        (markt)
> -      </scode>
> -      <fix>
> -        <bug>63038</bug>: Ensure that a <code>ClassNotFoundException</code> is
> -        thrown when attempting to load a class from a corrupted JAR file.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>63078</bug>: Ensure the utility thread pool is at least two, as the
> -        deployer uses a blocking pattern. (remm, markt)
> -      </fix>
> -      <add>
> -        Make the removal of leading and trailing whitespace from credentials
> -        passed to BASIC authentication configurable via a new attribute,
> -        <code>trimCredentials</code> on the <code>BasicAuthenticator</code>.
> -        (markt)
> -      </add>
> -      <fix>
> -        <bug>63003</bug>: Extend the <code>unloadDelay</code> attribute on a
> -        <code>Context</code> to include in-flight asynchronous requests. (markt)
> -      </fix>
> -      <add>
> -        <bug>63026</bug>: Add a new attribute, <code>forceDnHexEscape</code>, to
> -        the <code>JNDIRealm</code> that forces escaping in the String
> -        representation of a distinguished name to use the <code>\nn</code> form.
> -        This may avoid issues with realms using Active Directory which appears
> -        to be more tolerant of optional escaping when the <code>\nn</code> form
> -        is used. (markt)
> -      </add>
> -      <fix>
> -        Avoid a swallowed (and therefore ignored) access failure during web
> -        application class loading when running under a
> -        <code>SecurityManager</code>. (markt)
> -      </fix>
> -      <update>
> -        Add SSL configuration options to the JMX remote listener using the
> -        <code>SSLHostConfig</code> framework. (remm)
> -      </update>
> -      <update>
> -        Update the recommended minimum Tomcat Native version to 1.2.21. (markt)
> -      </update>
> -      <fix>
> -        <bug>63137</bug>: If the resources for a web application have been
> -        configured with multiple locations mapped to
> -        <code>/WEB-INF/classes</code>, ensure that all of those locations are
> -        used when building the web application class path. Patch provided by
> -        Marcin Gołębski. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <add>
> -        <bug>63009</bug>: Include the optional <code>content-length</code>
> -        header in HTTP/2 responses where an appropriate value is available.
> -        (markt)
> -      </add>
> -      <fix>
> -        <bug>63022</bug>: Do not use the socket open state when using the
> -        wrapper isClosed method for NIO and NIO2, as it will disable all
> -        further processing. (remm)
> -      </fix>
> -      <fix>
> -        Fix socket close discrepancies for NIO2, now the wrapper close
> -        is used everywhere except for socket accept problems. (remm)
> -      </fix>
> -      <fix>
> -        Fix use of write timeout instead of read timeout for HTTP/2 NIO2
> -        frame read. (remm)
> -      </fix>
> -      <fix>
> -        Fix incorrect APR sendfile thread stop. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        <bug>63056</bug>: Correct a regression in the fix for <bug>53737</bug>
> -        that did not correctly scan the web application directory structure for
> -        JSPs. (markt)
> -      </fix>
> -      <fix>
> -        Update the performance optimisation for using expressions in tags that
> -        depend on uninitialised tag attributes with implied scope to make the
> -        performance optimisation aware of the new public class
> -        (<code>java.lang.Enum$EnumDesc</code>) added in Java 12. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <fix>
> -        <bug>57974</bug>: Ensure implementation of
> -        <code>Session.getOpenSessions()</code> returns correct value for both
> -        client-side and server-side calls. (markt)
> -      </fix>
> -      <fix>
> -        <bug>63019</bug>: Use payload remaining bytes rather than limit when
> -        writing. Submitted by Benoit Courtilly. (remm)
> -      </fix>
> -      <fix>
> -        When running under a <code>SecurityManager</code>, ensure that the
> -        <code>ServiceLoader</code> look-up for the default
> -        <code>javax.websocket.server.ServerEndpointConfig.Configurator</code>
> -        implementation completes correctly rather than silently using the
> -        hard-coded fall-back. (markt)
> -      </fix>
> -      <fix>
> -        Ensure that the network connection is closed if the client receives an
> -        I/O error trying to communicate with the server. (markt)
> -      </fix>
> -      <fix>
> -        Ignore synthetic methods when scanning POJO methods. (markt)
> -      </fix>
> -      <fix>
> -        Implement the requirements of section 5.2.1 of the WebSocket 1.1
> -        specification and ensure that if the deployment of one Endpoint fails,
> -        no Endpoints are deployed for that web application. (markt)
> -      </fix>
> -      <fix>
> -        Implement the requirements of section 4.3 of the WebSocket 1.1
> -        specification and ensure that the deployment of an Endpoint fails if
> -        <code>@PathParam</code> is used with an invalid parameter type. (markt)
> -      </fix>
> -      <fix>
> -        Ensure a <code>DeploymentException</code> rather than an
> -        <code>IllegalArgumentException</code> is thrown if a method annotated
> -        with <code>@OnMessage</code> does not conform to the requirements set
> -        out in the Javadoc. (markt)
> -      </fix>
> -      <fix>
> -        Improve algorithm that determines if two <code>@OnMessage</code>
> -        annotations have been added for the same message type. Prior to this
> -        change some matches were missed. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        <bug>63103</bug>: Remove the unused source.jsp file and associated tag
> -        from the examples web application as it is no longer used. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <update>
> -        Add dns-ping support to enumerate cluster members. This is much simpler
> -        than getting the pod list but it does not indicate pod status.
> -        Submitted by Maxime Beck. (remm)
> -      </update>
> -      <fix>
> -        Never expire the local member from a Membership. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <update>
> -        Update container image with monitoring contraptions. (remm)
> -      </update>
> -      <add>
> -        Expand the coverage and quality of the Korean translations provided with
> -        Apache Tomcat. Includes contributions from woonsan and Chris Cho.
> -        (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Japanese translations provided
> -        with Apache Tomcat. Includes contributions from kfujino, Yujiorama and
> -        motohashi.yuki. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the French translations provided with
> -        Apache Tomcat. Includes contributions from remm, Ludovic Pénet and
> -        evernat. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the German translations provided
> -        with Apache Tomcat. Includes contributions from fschumacher, Stefan and
> -        burghard. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Chinese (simplified) translations
> -        provided with Apache Tomcat. Includes contributions from winsonzhao,
> -        Lanranzi, shawn, Winsonzhoa, JinXiqian, RichardHo, qingshi huang,
> -        Greenman0007, Jim Ma, huxing, 袁宇杰 and evernat. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Spanish translations provided
> -        with Apache Tomcat. Includes contributions from Ulises Gonzalez Horta,
> -        Israel, Eduardo Quintanilla and Miguel Ortega. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Russian translations provided
> -        with Apache Tomcat. Includes contributions from Andrei Maiseyenka and
> -        solomax. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Brazilian Portuguese translations
> -        provided with Apache Tomcat. Includes contributions from Victor Caetano
> -        and Dabilo. (markt)
> -      </add>
> -      <fix>
> -        <bug>63041</bug>: Correct a regression in the fix for <bug>53930</bug>
> -        that prevented Tomcat from working correctly with systemd. Patch
> -        provided by Patrik S. (markt)
> -      </fix>
> -      <update>
> -        <fix>63072</fix>: Remove extras (JMX remote listener and webservices
> -        object factories) and merge them back into the core build.
> -        (remm)
> -      </update>
> -      <add>
> -        Update the internal fork of Apache Commons FileUpload to pick up the
> -        changes in the Apache Commons FileUpload 1.4 release. (markt)
> -      </add>
> -      <update>
> -        Update the internal fork of Apache Commons DBCP 2 to de20b77
> -        (2019-01-29) to pick up some bug fixes and enhancements. (markt)
> -      </update>
> -      <update>
> -        Update the packaged version of the Tomcat Native Library to 1.2.21 to
> -        pick up the memory leak fixes when using NIO/NIO2 with OpenSSL. (markt)
> -      </update>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.14 (markt)" rtext="2018-12-12">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>62788</bug>: Add explicit logging configuration to write log files
> -        using UTF-8 to align with Tomcat&apos;s use of UTF-8 by default
> -        elsewhere. (markt)
> -      </fix>
> -      <fix>
> -        The default Servlet should not override a previously set content-type.
> -        (remm)
> -      </fix>
> -      <fix>
> -        Fix storeconfig for the cluster encryption interceptor key attribute.
> -        (remm)
> -      </fix>
> -      <add>
> -        Add a scheduled executor to the Server, which can be used to
> -        process periodic utility tasks. The utility threads are non daemon
> -        by default. (remm)
> -      </add>
> -      <update>
> -        Refactor container background processor using the Server executor, and
> -        add monitoring to reschedule it in case of an unexpected error. (remm)
> -      </update>
> -      <update>
> -        Refactor parallel deployment threads using the Server executor. (remm)
> -      </update>
> -      <add>
> -        Introduce a ConfigurationSource API to standardize access to the core
> -        configuration resources of Tomcat. (remm)
> -      </add>
> -      <update>
> -        Update the Tomcat embedded API by allowing to set a configuration
> -        source, which will allow processing of core configuration. (remm)
> -      </update>
> -      <update>
> -        Refactor processing of server.xml, web.xml, context.xml, other
> -        configuration files and resources using the ConfigurationSource API.
> -        JASPIC persistent providers load and store remains file based.
> -        StoreConfig Tomcat configuration files storing remains file based
> -        at their previous default locations. (remm)
> -      </update>
> -      <add>
> -        <bug>62897</bug>: Provide a property
> -        (<code>clearReferencesThreadLocals</code>) on the standard
> -        <code>Context</code> implementation that enables the check for memory
> -        leaks via <code>ThreadLocal</code>s to be disabled because this check
> -        depends on the use of an API that has been deprecated in later versions
> -        of Java. (markt)
> -      </add>
> -      <fix>
> -        Fix more storeconfig issues with duplicated SSL attributes. (remm)
> -      </fix>
> -      <fix>
> -        <bug>62924</bug>: Fix file descriptor leak introduced in the code that
> -        monitors <code>tomcat-users.xml</code> for modifications. (markt)
> -      </fix>
> -      <update>
> -        Add periodic event notification for lifecycle listeners configured on
> -        the Server. (remm)
> -      </update>
> -      <fix>
> -        <bug>62968</bug>: Avoid unnecessary (and relatively expensive)
> -        <code>getResources()</code> call in the Mapper when processing rule 7.
> -        (markt)
> -      </fix>
> -      <update>
> -        Update the recommended minimum Tomcat Native version to 1.2.19. (markt)
> -      </update>
> -      <fix>
> -        <bug>62978</bug>: Update the RemoteIpValve to handle multiple values in
> -        the <code>x-forwarded-proto</code> header. Patch provided by Tom Groot.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Update the RemoteIpFilter to handle multiple values in the
> -        <code>x-forwarded-proto</code> header. Based on a patch provided by Tom
> -        Groot. (markt)
> -      </fix>
> -      <scode>
> -        <bug>62986</bug>: Refactor the code that performs class scanning during
> -        web application start to make integration simpler for downstream users.
> -        Patch provided by rmannibucau. (markt)
> -      </scode>
> -      <fix>
> -        Filter out tomcat-web.xml from the watched resources list in
> -        storeconfig. (remm)
> -      </fix>
> -      <fix>
> -        <bug>62988</bug>: Fix the <code>LoadBalancerDrainingValve</code> so it
> -        works when the session cookie configuration is not explicitly declared.
> -        Based on a patch provided by Andreas Kurth. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <update>
> -        Refactor connector async timeout threads using a scheduled executor.
> -        (remm)
> -      </update>
> -      <update>
> -        Avoid using a dedicated thread for accept on the NIO2 connector, it is
> -        always less efficient. (remm)
> -      </update>
> -      <update>
> -        Load SSL configuration resources for JSSE using the ConfigurationSource
> -        API. OpenSSL use requires actual files. (remm)
> -      </update>
> -      <fix>
> -        <bug>62899</bug>: Prevent the incorrect timing out of connections when
> -        Servlet non-blocking I/O is used to read a request body over an HTTP/2
> -        stream. (markt)
> -      </fix>
> -      <fix>
> -        Avoid bad SSLHostConfig JMX registrations before init. (remm)
> -      </fix>
> -      <fix>
> -        Avoid a potential hang when a client connects using TLS 1.0 to a Tomcat
> -        HTTPS connector configured to use NIO or NIO2 with OpenSSL 1.1.1 or
> -        later. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <update>
> -        Update the Eclipse Compiler for Java to 4.9. Additional patch by Lukasz
> -        Jader. (markt)
> -      </update>
> -      <add>
> -        <bug>53737</bug>: Extend JspC, the precompilation tool, to include
> -        support for resource JARs. (markt)
> -      </add>
> -      <fix>
> -        <bug>62976</bug>: Avoid an <code>IllegalStateException</code> when using
> -        background compilation when tag files are packaged in JAR files. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <add>
> -        <bug>53553</bug>: Add the ability to specify a context.xml from the
> -        server to use when uploading a web application for deployment with the
> -        Manager web application. Patch provided by Anton Lindström. (markt)
> -      </add>
> -      <fix>
> -        <bug>62918</bug>: Filter out subtype mbeans to avoid breaking the
> -        connector status page. (remm)
> -      </fix>
> -      <fix>
> -        Unify letter case of the word 'How-To' in the webapps (csutherl)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <update>
> -        Refactor various operations performed in tribes using a scheduled
> -        executor. When tribes is not running standalone, it will use the
> -        executor from the Catalina Server. If running independently, the
> -        Channel will provide the executor. (remm)
> -      </update>
> -      <fix>
> -        Make EncryptInterceptor thread-safe. This makes this interceptor
> -        actually usable. (schultz/markt)
> -      </fix>
> -      <add>
> -        Add support for GCM mode to EncryptInterceptor. (schultz)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Prevent an error when running in a Cygwin shell and the
> -        <code>JAVA_ENDORSED_DIRS</code> system property is empty. Patch provided
> -        by Zemian Deng. (markt)
> -      </fix>
> -      <add>
> -        Expand the coverage and quality of the French translations provided with
> -        Apache Tomcat. Includes contributions from remm, soliplaya, Ludovic
> -        Pénet, David, NicolasG and bdelacretaz. (markt)
> -      </add>
> -      <add>
> -        Add Simplified Chinese translations to the translations to Apache
> -        Tomcat. Includes contributions from Darren Luo, syseal, Winsonzhao,
> -        袁宇杰, Lanranzi, ZhangJieWen, Jerry, yinzhili001, 安柏诚, shawn, lavender,
> -        Zheng Feng, zengwc, RichardHo, mm, gingshi huang, Bob, geekwang, zheng,
> -        Deanzhg, Tianfengjingjing, Panblack, oking, Dave Newman, Cnfnss, Jim Ma,
> -        852394875, huxing and Greenman0007. (markt)
> -      </add>
> -      <add>
> -        Add Korean translations to Apache Tomcat. Includes contributions from
> -        woonsan, JunSang Park, song choe and OhChan. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Spanish translations provided
> -        with Apache Tomcat. Includes contributions from Ulises Gonzalez Horta,
> -        Israel, Eduardo Quintanilla and Miguel suarez. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Russian translations provided
> -        with Apache Tomcat. Includes contributions from solomax, Rafael Sachakov
> -        and Andrei Maiseyenka. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the German translations provided
> -        with Apache Tomcat. Includes contributions from Matk80, burghard,
> -        Daniel Wehringer and Felix Schumacher. (markt)
> -      </add>
> -      <add>
> -        Expand the coverage and quality of the Japanese translations provided
> -        with Apache Tomcat. Includes contributions from Yujiorama,
> -        motohashi.yuki and kfujino. (markt)
> -      </add>
> -      <add>
> -        Add Brazilian Portuguese translations to Apache Tomcat. Includes
> -        contributions from geraldo netto. (markt)
> -      </add>
> -      <fix>
> -        Include Brazilian Portuguese translations in the standard Tomcat
> -        distribution. (markt)
> -      </fix>
> -      <fix>
> -        Include Simplified Chinese translations in the standard Tomcat
> -        distribution. (markt)
> -      </fix>
> -      <fix>
> -        Include Korean translations in the standard Tomcat distribution. (markt)
> -      </fix>
> -      <add>
> -        Add a packaging method for Tomcat using Maven, as well as a container
> -        build file for it. (remm)
> -      </add>
> -      <fix>
> -        Add XML Namespace to the project element of all POM files so that the
> -        XML files are Well Formed and Valid. (csutherl)
> -      </fix>
> -      <add>
> -        <bug>53930</bug>: Add support for the <code>CATALINA_OUT_CMD</code>
> -        environment variable that defines a command to which captured stdout and
> -        stderr will be redirected. Patch provided by Casey Lucas. (markt)
> -      </add>
> -      <update>
> -        Update the packaged version of the Tomcat Native Library to 1.2.19 to
> -        pick up the latest Windows binaries built with APR 1.6.5 and OpenSSL
> -        1.1.1a. (markt)
> -      </update>
> -      <update>
> -        Add i18n to many strings that lacked it. (remm)
> -      </update>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.13 (markt)" rtext="2018-11-07">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <add>
> -        <bug>58590</bug>: Add the ability for a UserDatabase to monitor the
> -        backing XML file for changes and reload the source file if a change in
> -        the last modified time is detected. This is enabled by default meaning
> -        that changes to <code>$CATALINA_BASE/conf/tomcat-users.xml</code> will
> -        now take effect a short time after the file is saved. (markt)
> -      </add>
> -      <add>
> -        <bug>61171</bug>: Add the <code>portOffset</code> attribute to the
> -        <code>Server</code> element which is added to the configured shutdown
> -        and <code>Connector</code> ports. Based on a patch by Marek Czernek.
> -        (markt)
> -      </add>
> -      <add>
> -        <bug>61692</bug>: Add the ability to control which HTTP methods are
> -        handled by the CGI Servlet via a new initialization parameter
> -        <code>cgiMethods</code>. (markt)
> -      </add>
> -      <fix>
> -        <bug>62687</bug>: Expose content length information for resources
> -        when using a compressed war. (remm)
> -      </fix>
> -      <fix>
> -        <bug>62737</bug>: Fix rewrite substitutions parsing of {} nesting.
> -        (remm)
> -      </fix>
> -      <fix>
> -        Add rewrite flags output when getting the rewrite configuration back.
> -        (remm)
> -      </fix>
> -      <fix>
> -        Add missing qsdiscard flag to the rewrite flags as a cleaner way to
> -        discard the query string. (remm)
> -      </fix>
> -      <add>
> -        <bug>62755</bug>: Add ability to opt out of adding the default web.xml
> -        config when embedding Tomcat and adding a context via
> -        <code>addWebapp()</code>. Call
> -        <code>setAddDefaultWebXmlToWebapp(false)</code> to prevent the automatic
> -        config. (isapir)
> -      </add>
> -      <fix>
> -        Add documentation about the files <code>context.xml.default</code> and
> -        <code>web.xml.default</code> that can be used to customize
> -        <code>conf/context.xml</code> and <code>conf/web.xml</code> on a per
> -        host basis. (fschumacher)
> -      </fix>
> -      <fix>
> -        Ensure that a canonical path is always used for the docBase of a Context
> -        to ensure consistent behaviour. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62803</bug>: Fix SSL connector configuration processing
> -        in storeconfig. (remm)
> -      </fix>
> -      <fix>
> -        <bug>62797</bug>: Pass throwable to keep client aborts with status 200
> -        rather than 500. Patch submitted by zikfat. (remm)
> -      </fix>
> -      <fix>
> -        <bug>62802</bug>: Restore the <code>appContextProtection</code>
> -        attribute to the <code>JreMemoryLeakPreventionListener</code> as
> -        application code may still trigger this memory leak. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62809</bug>: Correct a regression in the implementation of DIGEST
> -        authentication support for the Deployer Ant tasks (bug <bug>45832</bug>)
> -        that prevented the <code>DeployTask</code> from working when
> -        authentication was required. (markt)
> -      </fix>
> -      <update>
> -        Update the recommended minimum Tomcat Native version to 1.2.18. (markt)
> -      </update>
> -      <add>
> -        Ignore an attribute named <code>source</code> on <code>Context</code>
> -        elements provided by <code>StandardContext</code>. This is to suppress
> -        warnings generated by the Eclipse / Tomcat integration provided by
> -        Eclipse. Based on a patch by mdfst13. (markt)
> -      </add>
> -      <add>
> -        <bug>62830</bug>: Added <code>JniLifeCycleListener</code> and static
> -        methods <code>Library.loadLibrary(libraryName)</code> and
> -        <code>Library.load(filename)</code> to load a native library by a
> -        shared class loader so that more than one Webapp can use it. (isapir)
> -      </add>
> -      <scode>
> -        Refactor the <code>Connector</code> so that the port is obtained from
> -        the <code>Endpoint</code> rather than a local field that could end up
> -        out of sync. (markt)
> -      </scode>
> -      <fix>
> -        Correct a typo in the Spanish resource files. Patch provided by Diego
> -        Agulló. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62868</bug>: Order the <code>Enumeration&lt;URL&gt;</code> provided
> -        by <code>WebappClassLoaderBase.getResources(String)</code> according to
> -        the setting of the delegate flag. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <add>
> -        Add TLSv1.3 to the default protocols and to the <code>all</code>
> -        alias for JSSE based TLS connectors when running on a JVM that
> -        supports TLS version 1.3. One such JVM is OpenJDK version 11. (rjung)
> -      </add>
> -      <fix>
> -        <bug>62685</bug>: Correct an error in host name validation parsing that
> -        did not allow a fully qualified domain name to terminate with a period.
> -        Patch provided by AG. (markt)
> -      </fix>
> -      <fix>
> -        Make PEM file parser a public utility class. (remm)
> -      </fix>
> -      <fix>
> -        <bug>62739</bug>: Do not reject requests with an empty HTTP Host header.
> -        Such requests are unusual but not invalid. Patch provided by Michael
> -        Orr. (markt)
> -      </fix>
> -      <add>
> -        <bug>62748</bug>: Add TLS 1.3 support for the APR/Native connector and
> -        the NIO/NIO2 connector when using the OpenSSL backed JSSE
> -        implementation. (schultz/markt)
> -      </add>
> -      <fix>
> -        <bug>62791</bug>: Remove an unnecessary check in the NIO TLS
> -        implementation that prevented from secure WebSocket connections from
> -        being established. (markt)
> -      </fix>
> -      <fix>
> -        Fix server initiated TLS renegotiation to obtain a client certificate
> -        when using NIO/NIO2 and the OpenSSL backed JSSE TLS implementation.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Ensure open sockets etc. are cleaned up if the socket binding process
> -        fails. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62871</bug>: Improve MBeans for Endpoint instances (type
> -        <code>ThreadPool</code> in JMX) by using explicit declaration of
> -        attributes and operations rather than relying on introspection. Add a
> -        new MBean to expose the <code>Socketproperties</code> values. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        Correct parsing of XML whitespace in TLD function signatures that
> -        incorrectly only looked for the space character. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62674</bug>: Correct a regression in the stand-alone JSP compiler
> -        utility, <code>JspC</code>, caused by the fix for <bug>53492</bug>, that
> -        caused the JSP compiler to hang. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62721</bug>: Correct generation of web.xml header when using JspC.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>62757</bug>: Correct a regression in the fix for <bug>62603</bug>
> -        that caused <code>NullPointerException</code>s when compiling tag files
> -        on first access when development mode was disabled and background
> -        compilation was enabled. Based on a patch by Jordi Llach. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <fix>
> -        <bug>62731</bug>: Make the URI returned by
> -        <code>HandshakeRequest.getRequestURI()</code> and
> -        <code>Session.getRequestURI()</code> absolute so that the scheme, host
> -        and port are accessible. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        <bug>62676</bug>: Expand the CORS filter documentation to make it clear
> -        that explicit configuration is required to enable support for
> -        cross-origin requests. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62712</bug>: Correct NPE in Manager application when attempting to
> -        view configured certificates for an APR/native TLS connector. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62761</bug>: Correct the advanced CORS example in the Filter
> -        documentation to use a valid configuration. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62786</bug>: Add a note to the Context documentation to explain
> -        that, by default, settings for a Context element defined in server.xml
> -        will be overwritten by settings specified in a default context file such
> -        as <code>conf/context.xml</code>. (markt)
> -      </fix>
> -      <fix>
> -        Create a little visual separation between the Undeploy button and the
> -        other buttons in the Manager application. Patch provided by Łukasz
> -        Jąder. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <add>
> -        Add <code>setMembershipService</code> method to the
> -        <code>MembershipProvider</code>. (kfujino)
> -      </add>
> -      <add>
> -        Experimental Kubernetes aware cloud membership provider, based on code
> -        by Maxime Beck. Contains code derived from jgroups. (remm/kfujino)
> -      </add>
> -      <fix>
> -        Move the event notification <code>ThreadPoolExecutor</code> to
> -        <code>MembershipProviderBase</code>. (kfujino)
> -      </fix>
> -      <fix>
> -        Even if all members have already disappeared and PING can not be sent,
> -        ensure that members will be expired. (kfujino)
> -      </fix>
> -      <fix>
> -        Ensure that remove the member from suspect list when member added.
> -        (kfujino)
> -      </fix>
> -      <add>
> -        Add EncryptInterceptor to the portfolio of available clustering
> -        interceptors. This adds symmetric encryption of session data
> -        to Tomcat clustering regardless of the type of cluster manager
> -        or membership being used. (schultz)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Port DBCP transaction synchronization registry fix
> -        (commit d49d45e). (remm)
> -      </fix>
> -      <update>
> -        Update the internal fork of Apache Commons Pool 2 to d4e0e88
> -        (2018-09-12) to pick up some bug fixes and enhancements. (markt)
> -      </update>
> -      <add>
> -        <bug>62705</bug>: Added a fail fast check for minimum required Apache
> -        Ant version 1.9.8 when building Tomcat. (isapir)
> -      </add>
> -      <add>
> -        Added ant target ide-intellij to create an IntelliJ IDEA project. (isapir)
> -      </add>
> -      <add>
> -        Utility JSON parser generated from a public domain javacc grammar
> -        written by Robert Fischer. (remm)
> -      </add>
> -      <update>
> -        Update the packaged version of the Tomcat Native Library to 1.2.18 to
> -        pick up the latest Windows binaries built with APR 1.6.5 and OpenSSL
> -        1.1.1. (markt)
> -      </update>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.12 (markt)" rtext="2018-09-10">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Improve the handling of path parameters when working with
> -        RequestDispatcher objects. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62664</bug>: Process requests with content type
> -        <code>multipart/form-data</code> to servlets with a
> -        <code>@MultipartConfig</code> annotation regardless of HTTP method.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>62667</bug>: Add recursion to rewrite substitution parsing. (remm)
> -      </fix>
> -      <fix>
> -        <bug>62669</bug>: When using the SSIFilter and a resource does not
> -        specify a content type, do not force the content type to
> -        <code>application/x-octet-stream</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62670</bug>: Adjust the memory leak protection for the
> -        <code>DriverManager</code> so that JDBC drivers located in
> -        <code>$CATALINA_HOME/lib</code> and <code>$CATALINA_BASE/lib</code> are
> -        loaded via the service loader mechanism when the protection is enabled.
> -        (markt)
> -      </fix>
> -      <fix>
> -        When generating a redirect to a directory in the Default Servlet, avoid
> -        generating a protocol relative redirect. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Fix potential deadlocks when using asynchronous Servlet processing with
> -        HTTP/2 connectors. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62620</bug>: Fix corruption of response bodies when writing large
> -        bodies using asynchronous processing over HTTP/2. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62628</bug>: Additional fixes for output corruption of response
> -        bodies when writing large bodies using asynchronous processing over
> -        HTTP/2. (markt)
> -      </fix>
> -      <scode>
> -        Support for Netware in the <code>org.apache.tomcat.jni</code> package
> -        has been removed as there has not been a supported Netware platform for
> -        a number of years. (markt)
> -      </scode>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        Correct the JSP version in the X-PoweredBy HTTP header generated when
> -        the xpoweredBy option is enabled. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62662</bug>: Fix the corruption of web.xml output during JSP
> -        compilation caused by the fix for <bug>53492</bug>. Patch provided by
> -        Bernhard Frauendienst. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <add>
> -        Expand the information in the documentation web application regarding
> -        the use of <code>CATALINA_HOME</code> and <code>CATALINA_BASE</code>.
> -        Patch provided by Marek Czernek. (markt)
> -      </add>
> -      <fix>
> -        <bug>62652</bug>: Make it clearer that the version of DBCP that is
> -        packaged in Tomcat 9.0.x is DBCP 2. Correct the names of some DBCP 2
> -        configuration attributes that changed between 1.x and 2.x. (markt)
> -      </fix>
> -      <add>
> -        <bug>62666</bug>: Expand internationalisation support in the Manager
> -        application to include the server status page and provide Russian
> -        translations in addition to English. Patch provided by Artem Chebykin.
> -        (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Switch the build script to use http for downloads from an ASF mirror
> -        using the closer.lua script to avoid failures due to HTTPS to HTTP
> -        redirects. (rjung)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.11 (markt)" rtext="2018-08-17">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <add>
> -        Make the <code>isLocked()</code> method of the <code>LockOutRealm</code>
> -        public and expose the method via JMX. (markt)
> -      </add>
> -      <add>
> -        <bug>53387</bug>: Add support for regular expression capture groups to
> -        the SSI servlet and filter. (markt)
> -      </add>
> -      <fix>
> -        <bug>53411</bug>: Improve the handling of HTTP requests that do not
> -        explicitly specify a host name when no default host is configured. Also
> -        improve the tracking of changes to the default host as hosts are added
> -        and removed while Tomcat is running. (markt)
> -      </fix>
> -      <fix>
> -        Ensure that the HTTP Vary header is set correctly when using the CORS
> -        filter and improve the cacheability of requests that pass through the
> -        COPRS filter. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62527</bug>: Revert restriction of JNDI to the <code>java:</code>
> -        namespace. (remm)
> -      </fix>
> -      <add>
> -        Introduce a new class - <code>MultiThrowable</code> - to report
> -        exceptions when multiple actions are taken where each action may throw
> -        an exception but all actions are taken before any errors are reported.
> -        Use this new class when reporting multiple container (e.g. web
> -        application) failures during start. (markt)
> -      </add>
> -      <fix>
> -        Correctly decode URL paths (<code>+</code> should not be decoded to a
> -        space in the path) in the <code>RequestDispatcher</code> and the web
> -        application class loader. (markt)
> -      </fix>
> -      <add>
> -        Make logout more robust if JASPIC subject is unexpectedly unavailable.
> -        (markt)
> -      </add>
> -      <fix>
> -        <bug>62547</bug>: JASPIC <code>cleanSubject()</code> was not called on
> -        logout when the authenticator was configured to cache the authenticated
> -        Principal. Patch provided by Guillermo González de Agüero. (markt)
> -      </fix>
> -      <add>
> -        <bug>62559</bug>: Add <code>jaxb-*.jar</code> to the list of JARs
> -        ignored by <code>StandardJarScanner</code>. (markt)
> -      </add>
> -      <add>
> -        <bug>62560</bug>: Add <code>oraclepki.jar</code> to the list of JARs
> -        ignored by <code>StandardJarScanner</code>. (markt)
> -      </add>
> -      <add>
> -        <bug>62607</bug>: Return a non-zero exit code from
> -        <code>catalina.[bat|sh] run</code> if Tomcat fails to start. (markt)
> -      </add>
> -      <fix>
> -        Use short circuit logic to prevent potential NPE in CorsFilter. (fschumacher)
> -      </fix>
> -      <scode>
> -        Simplify construction of appName from container name in JAASRealm. (fschumacher)
> -      </scode>
> -      <scode>
> -        Remove <code>ServletException</code> from declaration of
> -        <code>Tomcat.addWebapp(String,String)</code> since it is never thrown.
> -        Patch provided by Tzafrir. (markt)
> -      </scode>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <scode>
> -        Refactor HTTP date creation and parsing to reduce code duplication,
> -        reduce the use of ThreadLocals and to increase the use of caching.
> -        (markt)
> -      </scode>
> -      <fix>
> -        <bug>56676</bug>: Add a default location for the native library, as
> -        ${catalina.home}/bin, which the testsuite already uses. (remm)
> -      </fix>
> -      <update>
> -        <bug>60560</bug>: Add support for using an inherited channel to
> -        the NIO connector. Based on a patch submitted by Thomas Meyer with
> -        testing and suggestions by Coty Sutherland. (remm)
> -      </update>
> -      <fix>
> -        <bug>62507</bug>: Ensure that JSSE based TLS connectors work correctly
> -        with a DKS keystore. (markt)
> -      </fix>
> -      <fix>
> -        Refactor code that adds an additional header name to the
> -        <code>Vary</code> HTTP response header to use a common utility method
> -        that addresses several additional edge cases. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62515</bug>: When a connector is configured (via setting
> -        <code>bindOnInit</code> to <code>false</code>) to bind/unbind the server
> -        socket during start/stop, close the socket earlier in the stop process
> -        so new connections do not sit in the TCP backlog during the shutdown
> -        process only to be dropped as stop completes. In this scenario new
> -        connections will now be refused immediately. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62526</bug>: Correctly handle PKCS12 format key stores when the key
> -        store password is configured to be the empty string. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62605</bug>: Ensure <code>ReadListener.onDataAvailable()</code> is
> -        called when the initial request body data arrives after the request
> -        headers when using asynchronous processing over HTTP/2. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62614</bug>: Ensure that
> -        <code>WriteListener.onWritePossible()</code> is called after
> -        <code>isReady()</code> returns <code>false</code> and the window size is
> -        subsequently incremented when using asynchronous processing over HTTP/2.
> -        (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <add>
> -        <bug>53492</bug>: Make the Java file generation process multi-threaded.
> -        By default, one thread will be used per core. Based on a patch by Dan
> -        Fabulich. (markt)
> -      </add>
> -      <add>
> -        <bug>62453</bug>: Add a performance optimisation for using expressions
> -        in tags that depend on uninitialised tag attributes with implied scope.
> -        Generally, using an explicit scope with tag attributes in EL is the best
> -        way to avoid various potential performance issues. (markt)
> -      </add>
> -      <fix>
> -        Correctly decode URL paths (<code>+</code> should not be decoded to a
> -        space in the path) in the Jasper class loader. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62603</bug>: Fix a potential race condition when development mode
> -        is disabled and background compilation checks are enabled. It was
> -        possible that some updates would not take effect and/or
> -        <code>ClassNotFoundException</code>s would occur. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <fix>
> -        <bug>62596</bug>: Remove the limit on the size of the initial HTTP
> -        upgrade request used to establish the web socket connection. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <add>
> -        <bug>62558</bug>: Add Russian translations for the Manager and Host
> -        Manager web applications. Based on a patch by Ivan Krasnov. (markt)
> -      </add>
> -      <add>
> -        Add documents for Static Membership service. (kfujino)
> -      </add>
> -      <add>
> -        <bug>62561</bug>: Add advanced class loader configuration information
> -        regarding the use of the Server and Shared class loaders to the
> -        documentation web application. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <fix>
> -        Ensures that the specified <code>rxBufSize</code> is correctly set to
> -        receiver buffer size. (kfujino)
> -      </fix>
> -      <fix>
> -        Correct the stop order of the Channel components. It stops in the
> -        reverse order to that at startup. (kfujino)
> -      </fix>
> -      <add>
> -        Added new StaticMembership implementation. This implementation does not
> -        require any additional configuration of other
> -        <code>ChannelInterceptors</code>. It works only with membership service.
> -        (kfujino)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <update>
> -        Support building with Java 9+ while preserving the Java 8 compatibility
> -        at runtime (requires Ant 1.9.8 or later). (ebourg)
> -      </update>
> -      <update>
> -        Update WSDL4J library to version 1.6.3 (from 1.6.2). (kkolinko)
> -      </update>
> -      <update>
> -        Update JUnit library to version 4.12 (from 4.11). (kkolinko)
> -      </update>
> -      <update>
> -        Downgrade CGLib library used for testing with EasyMock to version
> -        2.2.2 (from 2.2.3) as version 2.2.3 is not available from Maven Central.
> -        (markt)
> -      </update>
> -      <add>
> -        Implement checksum checks when downloading dependencies that are used
> -        to build Tomcat. (kkolinko)
> -      </add>
> -      <fix>
> -        Fixed spelling. Patch provided by Jimmy Casey via GitHub. (violetagg)
> -      </fix>
> -      <update>
> -        Update the internal fork of Apache Commons Pool 2 to 3e02523
> -        (2018-08-09) to pick up some bug fixes and enhancements. (markt)
> -      </update>
> -      <update>
> -        Update the internal fork of Apache Commons DBCP 2 to abc0484
> -        (2018-08-09) to pick up some bug fixes and enhancements. (markt)
> -      </update>
> -      <fix>
> -        Correct various spelling errors throughout the source code and
> -        documentation. Patch provided by Kazuhiro Sera. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.10 (markt)" rtext="2018-06-25">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>62476</bug>: Use GMT timezone for the value of
> -        <code>Expires</code> header as required by HTTP specification
> -        (RFC 7231, 7234). (kkolinko)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.9 (markt)" rtext="not released">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Treat the <code>&lt;mapped-name&gt;</code> element of a
> -        <code>&lt;env-entry&gt;</code> in web.xml in the same way as the
> -        <code>mappedName</code> element of the equivalent <code>@Resource</code>
> -        annotation. Both now attempt to set the <code>mappedName</code> property
> -        of the resource. (markt)
> -      </fix>
> -      <fix>
> -        Correct the processing of resources with
> -        <code>&lt;injection-target&gt;</code>s defined in web.xml. First look
> -        for a match using JavaBean property names and then, only if a match is
> -        not found, look for a match using fields. (markt)
> -      </fix>
> -      <fix>
> -        When restoring a saved request with a request body after FORM
> -        authentication, ensure that calls to the <code>HttpServletRequest</code>
> -        methods <code>getRequestURI()</code>, <code>getQueryString()</code> and
> -        <code>getProtocol()</code> are not corrupted by the processing of the
> -        saved request body. (markt)
> -      </fix>
> -      <fix>
> -        JNDI resources that are defined with injection targets but no value are
> -        now treated as if the resource is not defined. (markt)
> -      </fix>
> -      <fix>
> -        Ensure that JNDI names used for <code>&lt;lookup-name&gt;</code> entries
> -        in web.xml and for <code>lookup</code> elements of
> -        <code>@Resource</code> annotations specify a name with an explicit
> -        <code>java:</code> namespace. (markt)
> -      </fix>
> -      <fix>
> -        <bug>50019</bug>: Add support for <code>&lt;lookup-name&gt;</code>.
> -        Based on a patch by Gurkan Erdogdu. (markt)
> -      </fix>
> -      <add>
> -        Add the <code>AuthenticatedUserRealm</code> for use with CLIENT-CERT and
> -        SPNEGO when just the authenticated user name is required. (markt)
> -      </add>
> -      <fix>
> -        <bug>50175</bug>: Add a new attribute to the standard context
> -        implementation, <code>skipMemoryLeakChecksOnJvmShutdown</code>, that
> -        allows the user to configure Tomcat to skip the memory leak checks
> -        usually performed during web application stop if that stop is triggered
> -        by a JVM shutdown. (markt)
> -      </fix>
> -      <add>
> -        <bug>51497</bug>: Add an option, <code>ipv6Canonical</code>, to the
> -        <code>AccessLogValve</code> that causes IPv6 addresses to be output in
> -        canonical form defined by RFC 5952. (ognjen/markt)
> -      </add>
> -      <add>
> -        <bug>51953</bug>: Add the <code>RemoteCIDRFilter</code> and
> -        <code>RemoteCIDRValve</code> that can be used to allow/deny requests
> -        based on IPv4 and/or IPv6 client address where the IP ranges are defined
> -        using CIDR notation. Based on a patch by Francis Galiegue. (markt)
> -      </add>
> -      <fix>
> -        <bug>62343</bug>: Make CORS filter defaults more secure. This is the fix
> -        for CVE-2018-8014. (markt)
> -      </fix>
> -      <fix>
> -        Ensure that the web application resources implementation does not
> -        incorrectly cache results for resources that are only visible as class
> -        loader resources. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62387</bug>: Do not log a warning message if the file based
> -        persistent session store fails to delete the file for a session when the
> -        session is invalidated because the file has not been created yet.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Make all loggers associated with Tomcat provided Filters non-static to
> -        ensure that log messages are not lost when a web application is
> -        reloaded. (markt)
> -      </fix>
> -      <fix>
> -        Correct the manifest for the annotations-api.jar. The JAR implements the
> -        Common Annotations API 1.3 and the manifest should reflect that. (markt)
> -      </fix>
> -      <fix>
> -        Switch to non-static loggers where there is a possibility of a logger
> -        becoming associated with a web application class loader causing log
> -        messages to be lost if the web application is stopped. (markt)
> -      </fix>
> -      <add>
> -        <bug>62389</bug>: Add the IPv6 loopback address to the default
> -        <code>internalProxies</code> regular expression. Patch by Craig Andrews.
> -        (markt)
> -      </add>
> -      <fix>
> -        In the <code>RemoteIpValve</code> and <code>RemoteIpFilter</code>,
> -        correctly handle the case when the request passes through one or more
> -        <code>trustedProxies</code> but no <code>internalProxies</code>. Based
> -        on a patch by zhanhb. (markt)
> -      </fix>
> -      <fix>
> -        Correct the logic in <code>MBeanFactory.removeConnector()</code> to
> -        ensure that the correct Connector is removed when there are multiple
> -        Connectors using different addresses but the same port. (markt)
> -      </fix>
> -      <fix>
> -        Make <code>JAASRealm</code> mis-configuration more obvious by requiring
> -        the authenticated Subject to include at least one Principal of a type
> -        specified by <code>userClassNames</code>. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Correct a regression in the error page handling that prevented error
> -        pages from issuing redirects or taking other action that required the
> -        response status code to be changed. (markt)
> -      </fix>
> -      <fix>
> -        Consistent exception propagation for NIO2 SSL close. (remm)
> -      </fix>
> -      <fix>
> -        Followup sync fix for NIO2 async IO blocking read/writes. (remm)
> -      </fix>
> -      <fix>
> -        Log an error message if the AJP connector detects that the reverse proxy
> -        is sending AJP messages that are too large for the configured
> -        <code>packetSize</code>. (markt)
> -      </fix>
> -      <fix>
> -        Relax Host validation by removing the requirement that the final
> -        component of a FQDN must be alphabetic. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62371</bug>: Improve logging of Host validation failures. (markt)
> -      </fix>
> -      <fix>
> -        Fix a couple of unlikely edge cases in the shutting down of the
> -        APR/native connector. (markt)
> -      </fix>
> -      <fix>
> -        Add missing handshake timeout for NIO2. (remm)
> -      </fix>
> -      <fix>
> -        Correctly handle a digest authorization header when the user name
> -        contains an escaped character. (markt)
> -      </fix>
> -      <fix>
> -        Correctly handle a digest authorization header when one of the hex
> -        field values ends the header with in an invalid character. (markt)
> -      </fix>
> -      <fix>
> -        Correctly handle an invalid quality value in an
> -        <code>Accept-Language</code> header. (markt)
> -      </fix>
> -      <docs>
> -        <bug>62423</bug>: Fix SSL docs CRL attribute typo. (remm)
> -      </docs>
> -      <fix>
> -        Improve IPv6 validation by ensuring that IPv4-Mapped IPv6 addresses do
> -        not contain leading zeros in the IPv4 part. Based on a patch by Katya
> -        Stoycheva. (markt)
> -      </fix>
> -      <fix>
> -        Fix <code>NullPointerException</code> thrown from <code>
> -        replaceSystemProperties()</code> when trying to log messages. (csutherl)
> -      </fix>
> -      <fix>
> -        Avoid unnecessary processing of async timeouts. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <add>
> -        <bug>50234</bug>: Add the capability to generate a web-fragment.xml file
> -        to JspC. (markt)
> -      </add>
> -      <fix>
> -        <bug>62080</bug>: Ensure that all reads of the current thread's context
> -        class loader made by the UEL API and implementation are performed via a
> -        <code>PrivilegedAction</code> to ensure that a
> -        <code>SecurityException</code> is not triggered when running under a
> -        <code>SecurityManager</code>. (mark)
> -      </fix>
> -      <fix>
> -        <bug>62350</bug>: Refactor
> -        <code>org.apache.jasper.runtime.BodyContentImpl</code> so a
> -        <code>SecurityException</code> is not thrown when running under a
> -        SecurityManger and additional permissions are not required in the
> -        <code>catalina.policy</code> file. This is a follow-up to the fix for
> -        <bug>43925</bug>. (kkolinko/markt)
> -      </fix>
> -      <fix>
> -        Enable JspC from Tomcat 9 to work with Maven JspC compiler plug-ins
> -        written for Tomcat 8.5.x. Patch provided by Pavel Cibulka. (markt)
> -      </fix>
> -      <fix>
> -        Update web.xml, web-fragment.xml and web.xml extracts generated by JspC
> -        to use the Servlet 4.0 version of the relevant schemas. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Cluster">
> -    <changelog>
> -      <fix>
> -        Remove duplicate calls when creating a replicated session to reduce the
> -        time taken to create the session and thereby reduce the chances of a
> -        subsequent session update message being ignored because the session does
> -        not yet exist. (markt)
> -      </fix>
> -      <add>
> -        Add the method to send a message with a specified sendOptions. (kfujino)
> -      </add>
> -      <fix>
> -        When sending the <code>GET_ALL_SESSIONS</code> message, make sure that
> -        sends with asynchronous option in order to avoid ack timeout. Waiting to
> -        receive the <code>ALL_SESSION_DATA</code> message should be done with
> -        <code>waitForSendAllSessions</code> instead of ACK. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <update>
> -        Use NIO2 API for websockets writes. (remm)
> -      </update>
> -      <fix>
> -        When decoding of path parameter failed, make sure to throw
> -        <code>DecodeException</code> instead of throwing
> -        <code>ArrayIndexOutOfBoundsException</code>. (kfujino)
> -      </fix>
> -      <fix>
> -        Improve the handling of exceptions during TLS handshakes for the
> -        WebSocket client. (markt)
> -      </fix>
> -      <fix>
> -        Enable host name verification when using TLS with the WebSocket client.
> -        (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        <bug>62395</bug>: Clarify the meaning of the connector attribute
> -        <code>minSpareThreads</code> in the documentation web application.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Correct the documentation for the <code>allowHostHeaderMismatch</code>
> -        attribute of the standard HTTP Connector implementations. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <fix>
> -        Ensure that the correct default value is returned when retrieve unset
> -        properties in <code>McastService</code>. (kfujino)
> -      </fix>
> -      <add>
> -        Make <code>MembershipService</code> more easily extensible. (kfujino)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="jdbc-pool">
> -    <changelog>
> -      <fix>
> -        When <code>logValidationErrors</code> is set to true, the connection
> -        validation error is logged as <code>SEVERE</code> instead of
> -        <code>WARNING</code>. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Ensure that Apache Tomcat may be built from source with Java 11. (markt)
> -      </fix>
> -      <add>
> -        <bug>52381</bug>: Add OSGi metadata to JAR files. (markt)
> -      </add>
> -      <fix>
> -        <bug>62391</bug>: Remove references to <code>javaw.exe</code> as this
> -        file is not required by Tomcat and the references prevent the use of the
> -        Server JRE. (markt)
> -      </fix>
> -      <update>
> -        Update the packaged version of the Tomcat Native Library to 1.2.17 to
> -        pick up the latest Windows binaries built with APR 1.6.3 and OpenSSL
> -        1.0.2o. (markt)
> -      </update>
> -      <update>
> -        <bug>62458</bug>: Update the internal fork of Commons Pool 2 to dfef97b
> -        (2018-06-18) to pick up some bug fixes and enhancements. (markt)
> -      </update>
> -      <update>
> -        Update the internal fork of Commons DBCP 2 to 2.4.0. (markt)
> -      </update>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.8 (markt)" rtext="2018-05-03">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>62263</bug>: Avoid a <code>NullPointerException</code> when the
> -        <code>RemoteIpValve</code> processes a request for which no Context can
> -        be found. (markt)
> -      </fix>
> -      <add>
> -        <bug>62258</bug>: Don't trigger the standard error page mechanism when
> -        the error has caused the connection to the client to be closed as no-one
> -        will ever see the error page. (markt)
> -      </add>
> -      <fix>
> -        Register MBean when DataSource Resource <code>
> -        type="javax.sql.XADataSource"</code>. Patch provided by Masafumi Miura.
> -        (csutherl)
> -      </fix>
> -      <fix>
> -        Fix a rare edge case that is unlikely to occur in real usage. This edge
> -        case meant that writing long streams of UTF-8 characters to the HTTP
> -        response that consisted almost entirely of surrogate pairs could result
> -        in one surrogate pair being dropped. (markt)
> -      </fix>
> -      <add>
> -        Update the internal fork of Apache Commons BCEL to r1829827 to add early
> -        access Java 11 support to the annotation scanning code. (markt)
> -      </add>
> -      <fix>
> -        <bug>62297</bug>: Enable the <code>CrawlerSessionManagerValve</code> to
> -        correctly handle bots that crawl multiple hosts and/or web applications
> -        when the Valve is configured on a Host or an Engine. (fschumacher)
> -      </fix>
> -      <fix>
> -        <bug>62309</bug>: Fix a <code>SecurityException</code> when using JASPIC
> -        under a <code>SecurityManager</code> when authentication is not
> -        mandatory. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62329</bug>: Correctly list resources in JAR files when directories
> -        do not have dedicated entries. Patch provided by Meelis Müür. (markt)
> -      </fix>
> -      <add>
> -        Collapse multiple leading <code>/</code> characters to a single
> -        <code>/</code> in the return value of
> -        <code>HttpServletRequest#getContextPath()</code> to avoid issues if the
> -        value is used with <code>HttpServletResponse#sendRedirect()</code>. This
> -        behaviour is enabled by default and configurable via the new Context
> -        attribute <code>allowMultipleLeadingForwardSlashInPath</code>. (markt)
> -      </add>
> -      <fix>
> -        Improve handling of overflow in the UTF-8 decoder with supplementary
> -        characters. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Correct off-by-one error in thread pool that allowed thread pools to
> -        increase in size to one more than the configured limit. Patch provided
> -        by usc. (markt)
> -      </fix>
> -      <fix>
> -        Prevent unexpected TLS handshake failures caused by errors during a
> -        previous handshake that were not correctly cleaned-up when using the NIO
> -        or NIO2 connector with the <code>OpenSSLImplementation</code>. (markt)
> -      </fix>
> -      <add>
> -        <bug>62273</bug>: Implement configuration options to work-around
> -        specification non-compliant user agents (including all the major
> -        browsers) that do not correctly %nn encode URI paths and query strings
> -        as required by RFC 7230 and RFC 3986. (markt)
> -      </add>
> -      <fix>
> -        Fix sync for NIO2 async IO blocking read/writes. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <update>
> -        Update the Eclipse Compiler for Java to 4.7.3a. (markt)
> -      </update>
> -      <update>
> -        Allow <code>9</code> to be used to specify Java 9 as the compiler source
> -        and/or compiler target for JSP compilation. The Early Access value of
> -        <code>1.9</code> is still supported. (markt)
> -      </update>
> -      <add>
> -        Add support for specifying Java 10 (with the value <code>10</code>) as
> -        the compiler source and/or compiler target for JSP compilation. (markt)
> -      </add>
> -      <fix>
> -        <bug>62287</bug>: Do not rely on hash codes to test instances of
> -        <code>ValueExpressionImpl</code> for equality. Patch provided by Mark
> -        Struberg. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <fix>
> -        <bug>62301</bug>: Correct a regression in the fix for <bug>61491</bug>
> -        that didn't correctly handle a final empty message part in all
> -        circumstances when using <code>PerMessageDeflate</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62332</bug>: Ensure WebSocket connections are closed after an I/O
> -        error is experienced reading from the client. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Avoid warning when running under Cygwin when the
> -        <code>JAVA_ENDORSED_DIRS</code> environment variable is not set. Patch
> -        provided by Zemian Deng. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.7 (markt)" rtext="2018-04-07">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>51195</bug>: Avoid a false positive report of a web application
> -        memory leak by clearing <code>ObjectStreamClass$Caches</code> of classes
> -        loaded by the web application when the web application is stopped.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>52688</bug>: Add support for the <code>maxDays</code> attribute to
> -        the <code>AccessLogValve</code> and <code>ExtendedAccessLogValve</code>.
> -        This allows the maximum number of days for which rotated access logs
> -        should be retained before deletion to be defined. (markt)
> -      </fix>
> -      <fix>
> -        Ensure the MBean names for the <code>SSLHostConfig</code> and
> -        <code>SSLHostConfigCertificate</code> are correctly formed when the
> -        <code>Connector</code> is bound to a specific IP address. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62168</bug>: When using the <code>PersistentManager</code> honor a
> -        value of <code>-1</code> for <code>minIdleSwap</code> and do not swap
> -        out sessions to keep the number of active sessions under
> -        <code>maxActive</code>. Patch provided by Holger Sunke. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62172</bug>: Improve Javadoc for
> -        <code>org.apache.catalina.startup.Constants</code> and ensure that the
> -        constants are correctly used. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62175</bug>: Avoid infinite recursion, when trying to validate
> -        a session while loading it with <code>PersistentManager</code>.
> -        (fschumacher)
> -      </fix>
> -      <fix>
> -        Ensure that <code>NamingContextListener</code> instances are only
> -        notified once of property changes on the associated naming resources.
> -        (markt)
> -      </fix>
> -      <add>
> -        <bug>62224</bug>: Disable the <code>forkJoinCommonPoolProtection</code>
> -        of the <code>JreMemoryLeakPreventionListener</code> when running on Java
> -        9 and above since the underlying JRE bug has been fixed. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Avoid potential loop in APR/Native poller. (markt)
> -      </fix>
> -      <fix>
> -        Ensure streams that are received but not processed are excluded from the
> -        tracking of maximum ID of processed streams. (markt)
> -      </fix>
> -      <fix>
> -        Refactor the check for a paused connector to consistently prevent new
> -        streams from being created after the connector has been paused. (markt)
> -      </fix>
> -      <fix>
> -        Improve debug logging for HTTP/2 pushed streams. (markt)
> -      </fix>
> -      <fix>
> -        The OpenSSL engine SSL session will now ignore invalid accesses. (remm)
> -      </fix>
> -      <fix>
> -        <bug>62177</bug>: Correct two protocol errors with HTTP/2
> -        <code>PUSH_PROMISE</code> frames. Firstly, the HTTP/2 protocol only
> -        permits pushes to be sent on peer initiated requests. Secondly, pushes
> -        must be sent in order of increasing stream ID. These restriction were
> -        not being enforced leading to protocol errors at the client. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <add>
> -        Add document for <code>FragmentationInterceptor</code>. (kfujino)
> -      </add>
> -      <add>
> -        Document how the roles for an authenticated user are determined when the
> -        <code>CombinedRealm</code> is used. (markt)
> -      </add>
> -      <fix>
> -        <bug>62163</bug>: Correct the Tomcat Setup documentation that
> -        incorrectly referred to Java 7 as the minimum version rather than Java
> -        8. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <fix>
> -        Add JMX support for <code>FragmentationInterceptor</code> in order to
> -        prevent warning of startup. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="jdbc-pool">
> -    <changelog>
> -      <fix>
> -        Ensure that <code>SQLWarning</code> has been cleared when connection
> -        returns to the pool. (kfujino)
> -      </fix>
> -      <add>
> -        Enable clearing of <code>SQLWarning</code> via JMX. (kfujino)
> -      </add>
> -      <fix>
> -        Ensure that parameters have been cleared when
> -        <code>PreparedStatement</code> and/or <code>CallableStatement</code> are
> -        cached. (kfujino)
> -      </fix>
> -      <fix>
> -        Enable PoolCleaner to be started even if <code>validationQuery</code>
> -        is not set. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <update>
> -        Update the build script so MD5 hashes are no longer generated for
> -        releases as per the change in the ASF distribution policy. (markt)
> -      </update>
> -      <fix>
> -        <bug>62164</bug>: Switch the build script to use TLS for downloads from
> -        SourceForge and Maven Central to avoid failures due to HTTP to HTTPS
> -        redirects. (markt)
> -      </fix>
> -      <add>
> -        Always report the OS's umask when launching the JVM. (schultz)
> -      </add>
> -      <add>
> -        Add managed connections package to the package renamed DBCP 2 to provide
> -        a complete DBCP 2 in Tomcat. (remm)
> -      </add>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.6 (markt)" rtext="2018-03-08">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>43866</bug>: Add additional attributes to the Manager to provide
> -        control over which listeners are called when an attribute is added to
> -        the session when it has already been added under the same name. This is
> -        to aid clustering scenarios where <code>setAttribute()</code> is often
> -        called to signal that the attribute value has been mutated and needs to
> -        be replicated but it may not be required, or even desired, for the
> -        associated listeners to be triggered. The default behaviour has not been
> -        changed. (markt)
> -      </fix>
> -      <fix>
> -        Minor optimization when calling class transformers. (rjung)
> -      </fix>
> -      <add>
> -        Pass errors triggered by invalid requests or unavailable services to the
> -        application provided error handling and/or the container provided error
> -        handling (<code>ErrorReportValve</code>) as appropriate. (markt)
> -      </add>
> -      <add>
> -        <bug>41007</bug>: Add the ability to specify static HTML responses for
> -        specific error codes and/or exception types with the
> -        <code>ErrorReportValve</code>. (markt)
> -      </add>
> -      <fix>
> -        Prevent Tomcat from applying gzip compression to content that is already
> -        compressed with brotli compression. Based on a patch provided by burka.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>62090</bug>: Null container names are not allowed. (remm)
> -      </fix>
> -      <fix>
> -        <bug>62104</bug>: Fix programmatic login regression as the
> -        NonLoginAuthenticator has to be set for it to work (if no login method
> -        is specified). (remm)
> -      </fix>
> -      <fix>
> -        <bug>62117</bug>: Improve error message in <code>catalina.sh</code> when
> -        calling <code>kill -0 &lt;pid&gt;</code> fails. Based on a suggestion
> -        from Mark Morschhaeuser. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62118</bug>: Correctly create a JNDI <code>ServiceRef</code> using
> -        the specified interface rather than the concrete type. Based on a
> -        suggestion by Ángel Álvarez Páscua. (markt)
> -      </fix>
> -      <fix>
> -        Fix for <code>RequestDumperFilter</code> log attribute. Patch provided
> -        by Kirill Romanov via Github. (violetagg)
> -      </fix>
> -      <fix>
> -        <bug>62123</bug>: Avoid <code>ConcurrentModificationException</code>
> -        when attempting to clean up application triggered RMI memory leaks on
> -        web application stop. (markt)
> -      </fix>
> -      <add>
> -        When a deployment descriptor is deployed that includes a
> -        <code>path</code> attribute, log a warning that the <code>path</code>
> -        attribute will be ignored. (markt)
> -      </add>
> -      <add>
> -        When a deployment descriptor is deployed that references an external
> -        <code>docBase</code> and, as a result, a <code>docBase</code> under the
> -        <code>appBase</code> will be ignored, log a warning. (markt)
> -      </add>
> -      <fix>
> -        Correct a regression in the fix for <bug>60276</bug> that meant that
> -        compression was applied to all MIME types. Patch provided by Stefan
> -        Knoblich. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <add>
> -        Add async HTTP/2 parser for NIO2. (remm)
> -      </add>
> -      <fix>
> -        Add minor HPACK fixes, based on fixes by Stuart Douglas. (remm)
> -      </fix>
> -      <fix>
> -        <bug>61751</bug>: Follow up fix so that OpenSSL engine returns
> -        underflow when unwrapping if no bytes were produced and the input is
> -        empty. (remm)
> -      </fix>
> -      <fix>
> -        Minor OpenSSL engine cleanups. (remm)
> -      </fix>
> -      <fix>
> -        NIO SSL handshake should throw an exception on overflow status, like
> -        NIO2 SSL. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <add>
> -        <bug>47467</bug>: When deploying a web application via the manager
> -        application and a path is not explicitly specified, derive it from the
> -        provided deployment descriptor or, if that is not present, the WAR or
> -        DIR. (markt)
> -      </add>
> -      <add>
> -        <bug>48672</bug>: Add documentation for the Host Manager web
> -        application. Patch provided by Marek Czernek. (markt)
> -      </add>
> -      <add>
> -        Add support for specifying the application version when deploying an
> -        application via the Manager application HTML interface. (markt)
> -      </add>
> -      <add>
> -        Work-around a known, non-specification compliant behaviour in some
> -        versions of IE that can allow XSS when the Manager application generates
> -        a plain text response. Based on a suggestion from Muthukumar Marikani.
> -        (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.5 (markt)" rtext="2018-02-11">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Prevent a stack trace being written to standard out when running on Java
> -        10 due to changes in the <code>LogManager</code> implementation. (markt)
> -      </fix>
> -      <fix>
> -        Avoid duplicate load attempts if one has been made already. (remm)
> -      </fix>
> -      <fix>
> -        Avoid NPE in ThreadLocalLeakPreventionListener if there is no Engine.
> -        (remm)
> -      </fix>
> -      <fix>
> -        <bug>62000</bug>: When a JNDI reference cannot be resolved, ensure that
> -        the root cause exception is reported rather than swallowed. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62036</bug>: When caching an authenticated user Principal in the
> -        session when the web application is configured with the
> -        <code>NonLoginAuthenticator</code>, cache the internal Principal object
> -        rather than the user facing Principal object as Tomcat requires the
> -        internal object to correctly process later authorization checks. (markt)
> -      </fix>
> -      <add>
> -        Refactor error handling to enable errors that occur before processing is
> -        passed to the application to be handled by the application provided
> -        error handling and/or the container provided error handling
> -        (<code>ErrorReportValve</code>) as appropriate. (markt)
> -      </add>
> -      <add>
> -        Pass 404 errors triggered by a missing ROOT web application to the
> -        container error handling to generate the response body. (markt)
> -      </add>
> -      <add>
> -        Pass 400 errors triggered by invalid request targets to the container
> -        error handling to generate the response body. (markt)
> -      </add>
> -      <fix>
> -        Provide a correct <code>Allow</code> header when responding to an HTTP
> -        <code>TRACE</code> request for a JSP with a 405 status code. (markt)
> -      </fix>
> -      <fix>
> -        When using Tomcat embedded, only perform Authenticator configuration
> -        once during web application start. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62067</bug>: Correctly apply security constraints mapped to the
> -        context root using a URL pattern of <code>&quot;&quot;</code>. (markt)
> -      </fix>
> -      <fix>
> -        Process all <code>ServletSecurity</code> annotations at web application
> -        start rather than at servlet load time to ensure constraints are applied
> -        consistently. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        <bug>61751</bug>: Fix truncated request input streams when using NIO2
> -        with TLS. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62023</bug>: Log error reporting multiple SSLHostConfig elements
> -        when using the APR Connector instead of crashing Tomcat. (csutherl)
> -      </fix>
> -      <fix>
> -        <bug>62032</bug>: Fix NullPointerException when certificateFile is not
> -        defined on an SSLHostConfig and unify the behavior when a
> -        certificateFile is defined but the file does not exist for both
> -        JKS and PEM file types. (csutherl)
> -      </fix>
> -      <fix>
> -        Ensure that the <code>toString()</code> method behaves consistently for
> -        <code>ByteChunk</code> and <code>CharChunk</code> and that
> -        <code>null</code> is returned when <code>toString()</code> is called
> -        both on newly created objects and immediately after a call to
> -        <code>recycle()</code>. This should not impact typical Tomcat users. It
> -        may impact users who use these classes directly in their own code.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Ensure that the <code>toString()</code>, <code>toBytes()</code> and
> -        <code>toChars()</code> methods of <code>MessageBytes</code> behave
> -        consistently and do not throw a <code>NullPointerException</code> both
> -        on newly created objects and immediately after a call to
> -        <code>recycle()</code>. This should not impact typical Tomcat users. It
> -        may impact users who use these classes directly in their own code.
> -        (markt)
> -      </fix>
> -      <fix>
> -        When processing an HTTP 1.0 request in the HTTP connector and no host
> -        information is provided in the request, obtain the server port from the
> -        local port rather than the connector configuration since the configured
> -        value maybe zero. (markt)
> -      </fix>
> -      <add>
> -        Enable strict validation of the provided host name and port for all
> -        connectors. Requests with invalid host names and/or ports will be
> -        rejected with a 400 response. (markt)
> -      </add>
> -      <fix>
> -        Update the host validation to permit host names and components of domain
> -        names (excluding top-level domains) to start with a number and to ensure
> -        that top-level domains are fully alphabetic. (markt)
> -      </fix>
> -      <fix>
> -        <bug>62053</bug>: Fix NPE when writing push headers with HTTP/2 NIO2.
> -        Patch submitted by Holger Sunke. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        Include an HTTP <code>Allow</code> header when a JSP generates a
> -        405 response due to a request with an unsupported method. (markt)
> -      </fix>
> -      <add>
> -        Add support for the HTTP <code>OPTION</code> method to JSPs. The
> -        JSP specification explicitly states that the behaviour for this
> -        method is undefined for JSPs so this is a Tomcat specific
> -        behaviour. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <fix>
> -        <bug>62024</bug>: When closing a connection with an abnormal close,
> -        close the socket immediately rather than waiting for a close message
> -        from the client that may never arrive. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Webapps">
> -    <changelog>
> -      <fix>
> -        <bug>62049</bug>: Fix missing class from manager 404 JSP error page.
> -        (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="jdbc-pool">
> -    <changelog>
> -      <add>
> -        Enhance the JMX support for jdbc-pool in order to expose
> -        <code>PooledConnection</code> and <code>JdbcInterceptors</code>.
> -        (kfujino)
> -      </add>
> -      <add>
> -        Add MBean for <code>PooledConnection</code>. (kfujino)
> -      </add>
> -      <add>
> -        <bug>62011</bug>: Add MBean for <code>StatementCache</code>. (kfujino)
> -      </add>
> -      <add>
> -        Expose the cache size for each connection via JMX in
> -        <code>StatementCache</code>. (kfujino)
> -      </add>
> -      <add>
> -        Add MBean for <code>ResetAbandonedTimer</code>. (kfujino)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <update>
> -        Update the list with the public interfaces in the RELEASE-NOTES.
> -        (violetagg)
> -      </update>
> -      <update>
> -        Update the NSIS Installer used to build the Windows installer to version
> -        3.03. (kkolinko)
> -      </update>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.4 (markt)" rtext="2018-01-22">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Correct a regression in the previous fix for <bug>61916</bug> that meant
> -        that any call to <code>addHeader()</code> would have been replaced with
> -        a call to <code>setHeader()</code> for all requests mapped to the
> -        <code>AddDefaultCharsetFilter</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61999</bug>: maxSavePostSize set to 0 should disable saving POST
> -        data during authentication. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Fix NIO2 HTTP/2 sendfile. (remm)
> -      </fix>
> -      <fix>
> -        <bug>61993</bug>: Improve handling for <code>ByteChunk</code> and
> -        <code>CharChunk</code> instances that grow close to the maximum size
> -        allowed by the JRE. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <add>
> -        <bug>43925</bug>: Add a new system property
> -        (<code>org.apache.jasper.runtime.BodyContentImpl.BUFFER_SIZE</code>) to
> -        control the size of the buffer used by Jasper when buffering tag bodies.
> -        (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        <bug>62006</bug>: Document the new <code>JvmOptions9</code> command line
> -        parameter for <code>tomcat9.exe</code>. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.3 (markt)" rtext="not released">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <add>
> -        <bug>57619</bug>: Implement a small optimisation to how JAR URLs are
> -        processed to reduce the storage of duplicate String objects in memory.
> -        Patch provided by Dmitri Blinov. (markt)
> -      </add>
> -      <fix>
> -        Add some missing NPEs to ServletContext. (remm)
> -      </fix>
> -      <fix>
> -        Update the Java EE 8 XML schema to the released versions. (markt)
> -      </fix>
> -      <fix>
> -        Minor HTTP/2 push fixes. (remm)
> -      </fix>
> -      <fix>
> -        <bug>61916</bug>: Extend the <code>AddDefaultCharsetFilter</code> to add
> -        a character set when the content type is set via
> -        <code>setHeader()</code> or <code>addHeader()</code> as well as when it
> -        is set via <code>setContentType()</code>. (markt)
> -      </fix>
> -      <fix>
> -        When using WebDAV to copy a file resource to a destination that requires
> -        a collection to be overwritten, ensure that the operation succeeds
> -        rather than fails (with a 500 response). This enables Tomcat to pass two
> -        additional tests from the Litmus WebDAV test suite. (markt)
> -      </fix>
> -      <update>
> -        Modify the Default and WebDAV Servlets so that a 405 status code is
> -        returned for <code>PUT</code> and <code>DELETE</code> requests when
> -        disabled via the <code>readonly</code> initialisation parameter.
> -      </update>
> -      <fix>
> -        Align the contents of the <code>Allow</code> header with the response
> -        code for the Default and WebDAV Servlets. For any given resource a
> -        method that returns a 405 status code will not be listed in the
> -        <code>Allow</code> header and a method listed in the <code>Allow</code>
> -        header will not return a 405 status code. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <add>
> -        <bug>60276</bug>: Implement GZIP compression support for responses
> -        served over HTTP/2. (markt)
> -      </add>
> -      <fix>
> -        Do not call onDataAvailable without any data to read. (remm)
> -      </fix>
> -      <fix>
> -        Correctly handle EOF when <code>ServletInputStream.isReady()</code> is
> -        called. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61886</bug>: Log errors on non-container threads at
> -        <code>DEBUG</code> rather than <code>INFO</code>. The exception will be
> -        made available to the application via the asynchronous error handling
> -        mechanism. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61914</bug>: Possible NPE with Java 9 when creating a SSL engine.
> -        Patch submitted by Evgenij Ryazanov. (remm)
> -      </fix>
> -      <fix>
> -        <bug>61918</bug>: Fix connectionLimitLatch counting when closing an
> -        already closed socket. Based on a patch by Ryan Fong. (remm)
> -      </fix>
> -      <add>
> -        Add support for the OpenSSL ARIA ciphers to the OpenSSL to JSSE
> -        cipher mapping. (markt)
> -      </add>
> -      <fix>
> -        <bug>61932</bug>: Allow a call to <code>AsyncContext.dispatch()</code>
> -        to terminate non-blocking I/O. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61948</bug>: Improve the handling of malformed ClientHello messages
> -        in the code that extracts the SNI information from a TLS handshake for
> -        the JSSE based NIO and NIO2 connectors. (markt)
> -      </fix>
> -      <fix>
> -        Fix NIO2 handshaking with a full input buffer. (remm)
> -      </fix>
> -      <add>
> -        Return a simple, plain text error message if a client attempts to make a
> -        plain text HTTP connection to a TLS enabled NIO or NIO2 Connector.
> -        (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        <bug>61854</bug>: When using sets and/or maps in EL expressions, ensure
> -        that Jasper correctly parses the expression. Patch provided by Ricardo
> -        Martin Camarero. (markt)
> -      </fix>
> -      <fix>
> -        Improve the handling of methods with varargs in EL expressions. In
> -        particular, the calling of a varargs method with no parameters now works
> -        correctly. Based on a patch by Nitkalya (Ing) Wiriyanuparb. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61945</bug>: Fix prototype mode used to compile tags. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <add>
> -        <bug>61223</bug>: Add the mbeans-descriptors.dtd file to the custom
> -        MBean documentation so users have a reference to use when constructing
> -        mbeans-descriptors.xml files for custom components. (markt)
> -      </add>
> -      <add>
> -        <bug>61565</bug>: Add the ability to trigger a reloading of TLS host
> -        configuration (certificate and key files, server.xml is not re-parsed)
> -        via the Manager web application. (markt)
> -      </add>
> -      <add>
> -        <bug>61566</bug>: Expose the currently in use certificate chain and list
> -        of trusted certificates for all virtual hosts configured using the JSSE
> -        style (keystore) TLS configuration via the Manager web application.
> -        (markt)
> -      </add>
> -      <fix>
> -        Partial fix for <bug>61886</bug>. Ensure that multiple threads do not
> -        attempt to complete the <code>AsyncContext</code> if an I/O error occurs
> -        in the stock ticker example Servlet. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61886</bug>: Prevent <code>ConcurrentModificationException</code>
> -        when running the asynchronous stock ticker in the examples web
> -        application. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61886</bug>: Prevent <code>NullPointerException</code> and other
> -        errors if the stock ticker example is running when the examples web
> -        application is stopped. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61910</bug>: Clarify the meaning of the <code>allowLinking</code>
> -        option in the documentation web application. (markt)
> -      </fix>
> -      <add>
> -        Add OCSP configuration information to the SSL How-To. Patch provided by
> -        Marek Czernek. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="jdbc-pool">
> -    <changelog>
> -      <fix>
> -        <bug>61312</bug>: Prevent <code>NullPointerException</code> when using
> -        the statement cache of connection that has been closed. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Add an additional system property for the system property replacement.
> -        (remm)
> -      </fix>
> -      <fix>
> -        Add missing SHA-512 hash for release artifacts to the build script.
> -        (markt)
> -      </fix>
> -      <update>
> -        Update the internal fork of Commons Pool 2 to 2.4.3. (markt)
> -      </update>
> -      <update>
> -        Update the internal fork of Commons DBCP 2 to 8a71764 (2017-10-18) to
> -        pick up some bug fixes and enhancements. (markt)
> -      </update>
> -      <update>
> -        Update the internal fork of Commons FileUpload to 6c00d57 (2017-11-23)
> -        to pick up some code clean-up. (markt)
> -      </update>
> -      <update>
> -        Update the internal fork of Commons Codec to r1817136 to pick up some
> -        code clean-up. (markt)
> -      </update>
> -      <fix>
> -        The native source bundles (for Commons Daemon and Tomcat Native) are no
> -        longer copied to the bin directory for the deploy target. They are now
> -        only copied to the bin directory for the release target. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.2 (markt)" rtext="2017-11-30">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Fix possible <code>SecurityException</code> when using TLS related
> -        request attributes. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61597</bug>: Extend the <code>StandardJarScanner</code> to scan
> -        JARs on the module path when running on Java 9 and class path scanning
> -        is enabled. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61601</bug>: Add support for multi-release JARs in JAR scanning and
> -        web application class loading. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61681</bug>: Allow HTTP/2 push when using request wrapping. (remm)
> -      </fix>
> -      <add>
> -        Provide the <code>SessionInitializerFilter</code> that can be used to
> -        ensure that an HTTP session exists when initiating a WebSocket
> -        connection. Patch provided by isapir. (markt)
> -      </add>
> -      <fix>
> -        <bug>61682</bug>: When re-prioritising HTTP/2 streams, ensure that both
> -        parent and children fields are correctly updated to avoid a possible
> -        <code>StackOverflowError</code>. (markt)
> -      </fix>
> -      <fix>
> -        Improve concurrency by reducing the scope of the synchronisation for
> -        <code>javax.security.auth.message.config.AuthConfigFactory</code> in the
> -        JASPIC API implementation. Based on a patch by Pavan Kumar. (markt)
> -      </fix>
> -      <fix>
> -        Avoid a possible <code>NullPointerException</code> when timing out
> -        <code>AsyncContext</code> instances during shut down. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61777</bug>: Avoid a <code>NullPointerException</code> when
> -        detaching a JASPIC <code>RegistrationListener</code>. Patch provided by
> -        Lazar. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61778</bug>: Correct the return value when detaching a JASPIC
> -        <code>RegistrationListener</code>. Patch provided by Lazar. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61779</bug>: Avoid a <code>NullPointerException</code> when a
> -        <code>null</code> <code>RegistrationListener</code> is passed to
> -        <code>AuthConfigFactory.getConfigProvider()</code>. Patch provided by
> -        Lazar. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61780</bug>: Only include the default JASPIC registration ID in the
> -        return value for a call to
> -        <code>AuthConfigFactory.getRegistrationIDs()</code> if a
> -        <code>RegistrationContext</code> has been registered using the default
> -        registration ID. Patch provided by Lazar. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61781</bug>: Enable JASPIC provider registrations to be persisted
> -        when the layer and/or application context are <code>null</code>. Patch
> -        provided by Lazar. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61782</bug>: When calling
> -        <code>AuthConfigFactory.doRegisterConfigProvider()</code> and the
> -        requested JASPIC config provider class is found by the web application
> -        class loader, do not attempt to load the class with the class loader
> -        that loaded the JASPIC API. Patch provided by Lazar. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61783</bug>: When calling
> -        <code>AuthConfigFactory.removeRegistration()</code> and the registration
> -        is persistent, it should be removed from the persistent store. Patch
> -        provided by Lazar. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61784</bug>: Correctly handle the case when
> -        <code>AuthConfigFactoryImpl.registerConfigProvider()</code> is called
> -        with a provider name of <code>null</code>. Patch provided by Lazar.
> -        (markt)
> -      </fix>
> -      <add>
> -        <bug>61795</bug>: Add a property to the <code>Authenticator</code>
> -        implementations to enable a custom JASPIC <code>CallbackHandler</code>
> -        to be specified. Patch provided by Lazar. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        <bug>61568</bug>: Avoid a potential <code>SecurityException</code> when
> -        using the NIO2 connector and a new thread is added to the pool. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61583</bug>: Correct a further regression in the fix to enable the
> -        use of Java key stores that contained multiple keys that did not all
> -        have the same password. This fixes PKCS11 key store handling with
> -        multiple keys selected with an alias. (markt)
> -      </fix>
> -      <fix>
> -        Improve NIO2 syncing for async IO operations. (remm)
> -      </fix>
> -      <add>
> -        Sendfile support for HTTP/2 and NIO2. (remm)
> -      </add>
> -      <fix>
> -        Reduce default HTTP/2 stream concurrent execution within a connection
> -        from 200 to 20. (remm)
> -      </fix>
> -      <fix>
> -        <bug>61668</bug>: Avoid a possible NPE when calling
> -        <code>AbstractHttp11Protocol.getSSLProtocol()</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61673</bug>: Avoid a possible
> -        <code>ConcurrentModificationException</code> when working with the
> -        streams associated with a connection. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61719</bug>: Avoid possible NPE calling
> -        InputStream.setReadListener with HTTP/2. (remm)
> -      </fix>
> -      <fix>
> -        <bug>61736</bug>: Improve performance of NIO connector when clients
> -        leave large time gaps between network packets. Patch provided by Zilong
> -        Song. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61740</bug>: Correct an off-by-one error in the Hpack header index
> -        validation that caused intermittent request failures when using HTTP/2.
> -        (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        <bug>61604</bug>: Fix SMAP generation for JSPs that generate no output.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>61816</bug>: Invalid expressions in attribute values or template
> -        text should trigger a translation (compile time) error, not a run time
> -        error. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <fix>
> -        <bug>61604</bug>: Add support for authentication in the websocket
> -        client. Patch submitted by J Fernandez. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        Correct Javadoc links to point to Java SE 8 and Java EE 8. (markt)
> -      </fix>
> -      <fix>
> -        Enable Javadoc to be built with Java 9. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61603</bug>: Add XML filtering for the status servlet output where
> -        needed. (remm)
> -      </fix>
> -      <fix>
> -        Correct the description of how the CGI servlet maps a request to a
> -        script in the CGI How-To. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <fix>
> -        Fix incorrect behavior that attempts to resend channel messages more
> -        than the actual setting value of <code>maxRetryAttempts</code>.
> -        (kfujino)
> -      </fix>
> -      <fix>
> -        Ensure that the remaining Sender can send channel messages by avoiding
> -        unintended <code>ChannelException</code> caused by comparing the number
> -        of failed members and the number of remaining Senders. (kfujino)
> -      </fix>
> -      <fix>
> -        Ensure that remaining SelectionKeys that were not handled by throwing a
> -        <code>ChannelException</code> during SelectionKey processing are
> -        handled. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Improve the fix for <bug>61439</bug> and exclude the JPA, JAX-WS and EJB
> -        annotations completely from the Tomcat distributions. (markt)
> -      </fix>
> -      <fix>
> -        Improve handling of endorsed directories. The endorsed directory
> -        mechanism will only be used if the <code>JAVA_ENDORSED_DIRS</code>
> -        system property is explicitly set or if
> -        <code>$CATALINA_HOME/endorsed</code> exists. When running on Java 9, any
> -        such attempted use of the endorsed directory mechanism will trigger an
> -        error and Tomcat will fail to start. (rjung)
> -      </fix>
> -      <add>
> -        <bug>51496</bug>: When using the Windows installer, check if the
> -        requested service name already exists and, if it does, prompt the user
> -        to select an alternative service name. Patch provided by Ralph
> -        Plawetzki. (markt)
> -      </add>
> -      <fix>
> -        <bug>61590</bug>: Enable <code>service.bat</code> to recognise when
> -        <code>JAVA_HOME</code> is configured for a Java 9 JDK. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61598</bug>: Update the Windows installer to search the new (as of
> -        Java 9) registry locations when looking for a JRE. (markt)
> -      </fix>
> -      <add>
> -        Add generation of a SHA-512 hash for release artifacts to the build
> -        script. (markt)
> -      </add>
> -      <fix>
> -        <bug>61658</bug>: Update MIME mappings for fonts to use
> -        <code>font/*</code> as per RFC8081. (markt)
> -      </fix>
> -      <update>
> -        Update the packaged version of the Tomcat Native Library to 1.2.16 to
> -        pick up the latest Windows binaries built with APR 1.6.3 and OpenSSL
> -        1.0.2m. (markt)
> -      </update>
> -      <update>
> -        Update the NSIS Installer used to build the Windows installer to version
> -        3.02.1. (kkolinko)
> -      </update>
> -      <update>
> -        Update the Windows installer to use "The Apache Software Foundation" as
> -        the Publisher when Tomcat is displayed in the list of installed
> -        applications in Microsoft Windows. (kkolinko)
> -      </update>
> -      <fix>
> -        <bug>61803</bug>: Remove outdated SSL information from the Security
> -        documentation. (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.1 (markt)" rtext="2017-09-30">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Use the correct path when loading the JVM <code>logging.properties</code>
> -        file for Java 9. (rjung)
> -      </fix>
> -      <fix>
> -        Add additional validation to the resource handling required to fix
> -        CVE-2017-12617 on Windows. The checks were being performed elsewhere but
> -        adding them to the resource handling ensures that the checks are always
> -        performed. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        <bug>61563</bug>: Correct typos in Spanish translation. Patch provided by
> -        Gonzalo Vásquez. (csutherl)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0 (markt)" rtext="not released">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>61542</bug>: Fix CVE-2017-12617 and prevent JSPs from being
> -        uploaded via a specially crafted request when HTTP PUT was enabled.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>61554</bug>: Exclude test files in unusual encodings and markdown
> -        files intended for display in GitHub from RAT analysis. Patch provided
> -        by Chris Thistlethwaite. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <add>
> -        <bug>60762</bug>: Add the ability to make changes to the TLS
> -        configuration of a connector at runtime without having to restart the
> -        Connector. (markt)
> -      </add>
> -      <add>
> -        Add an option to reject requests that contain HTTP headers with invalid
> -        (non-token) header names with a 400 response and reject such requests by
> -        default. (markt)
> -      </add>
> -      <fix>
> -        Implement the requirements of RFC 7230 (and RFC 2616) that HTTP/1.1
> -        requests must include a <code>Host</code> header and any request that
> -        does not must be rejected with a 400 response. (markt)
> -      </fix>
> -      <fix>
> -        Implement the requirements of RFC 7230 that any HTTP/1.1 request that
> -        specifies a host in the request line, must specify the same host in the
> -        <code>Host</code> header and that any such request that does not, must
> -        be rejected with a 400 response. This check is optional but enabled by
> -        default. It may be disabled with the
> -        <code>allowHostHeaderMismatch</code> attribute of the Connector. (markt)
> -      </fix>
> -      <fix>
> -        Implement the requirements of RFC 7230 that any HTTP/1.1 request that
> -        contains multiple <code>Host</code> headers is rejected with a 400
> -        response. (markt)
> -      </fix>
> -      <update>
> -        Add a way to set the property source in embedded mode. (remm)
> -      </update>
> -      <fix>
> -        <bug>61557</bug>: Correct a further regression in the fix to enable the
> -        use of Java key stores that contain multiple keys that do not all have
> -        the same password. The regression broke support for some FIPS compliant
> -        key stores. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="jdbc-pool">
> -    <changelog>
> -      <fix>
> -        <bug>61545</bug>: Correctly handle invocations of methods defined in the
> -        <code>PooledConnection</code> interface when using pooled XA
> -        connections. Patch provided by Nils Winkler. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Update fix for <bug>59904</bug> so that values less than zero are accepted
> -        instead of throwing a NegativeArraySizeException. (remm)
> -      </fix>
> -      <add>
> -        Complete the implementation of the Servlet 4.0 specification. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M27 (markt)" rtext="2017-09-19">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Before generating an error page in the <code>ErrorReportValve</code>,
> -        check to see if I/O is still permitted for the associated connection
> -        before generating the error page so that the page generation can be
> -        skipped if the page is never going to be sent. (markt)
> -      </fix>
> -      <add>
> -        <bug>61189</bug>: Add the ability to set environment variables for
> -        individual CGI scripts. Based on a patch by jm009. (markt)
> -      </add>
> -      <fix>
> -        <bug>61210</bug>: When running under a SecurityManager, do not print a
> -        warning about not being able to read a logging configuration file when
> -        that file does not exist. (markt)
> -      </fix>
> -      <add>
> -        <bug>61280</bug>: Add RFC 7617 support to the
> -        <code>BasicAuthenticator</code>. Note that the default configuration
> -        does not change the existing behaviour. (markt)
> -      </add>
> -      <fix>
> -        <bug>61424</bug>: Avoid a possible <code>StackOverflowError</code> when
> -        running under a <code>SecurityManager</code> and using
> -        <code>Subject.doAs()</code>. (markt)
> -      </fix>
> -      <add>
> -        When running under Java 9 or later, and the
> -        <code>urlCacheProtection</code> option of the
> -        <code>JreMemoryLeakPreventionListener</code> is enabled, use the API
> -        added in Java 9 to only disable the caching for JAR URL connections.
> -        (markt)
> -      </add>
> -      <add>
> -        <bug>61489</bug>: When using the CGI servlet, make the generation of
> -        command line arguments from the query string (as per section 4.4 of RFC
> -        3875) optional and disabled by default. Based on a patch by jm009.
> -        (markt)
> -      </add>
> -      <fix>
> -        <bug>61503</bug>: This corrects a potential regression in the fix for
> -        <bug>60940</bug> with an alternative solution that adds the
> -        <code>JarEntry</code> objects normally skipped by a
> -        <code>JarInputStream</code> only if those entries exist. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <update>
> -        The minimum required Tomcat Native version has been increased to 1.2.14.
> -        This version includes a new API needed for correct client certificate
> -        support when using a Java connector with OpenSSL TLS implementation and
> -        support for the <code>SSL_CONF</code> OpenSSL API. (rjung)
> -      </update>
> -      <add>
> -        Add support for the OpenSSL <code>SSL_CONF</code> API when using
> -        TLS with OpenSSL implementation. It can be used by adding
> -        <code>OpenSSLConf</code> elements underneath <code>SSLHostConfig</code>.
> -        The new element contains a list of <code>OpenSSLConfCmd</code> elements,
> -        each with the attributes <code>name</code> and <code>value</code>.
> -        (rjung)
> -      </add>
> -      <fix>
> -        When using a Java connector in combination with the OpenSSL TLS
> -        implementation, do not configure each SSL connection object via
> -        the OpenSSLEngine. For OpenSSL the SSL object inherits its
> -        settings from the SSL_CTX which we have already configured.
> -        (rjung)
> -      </fix>
> -      <fix>
> -        When using JSSE TLS configuration with the OpenSSL implementation and
> -        client certificates: include client CA subjects in the TLS handshake
> -        so that the client can choose an appropriate client certificate to
> -        present. (rjung)
> -      </fix>
> -      <fix>
> -        If an invalid option is specified for the
> -        <code>certificateVerification</code> attribute of an
> -        <code>SSLHostConfig</code> element, treat it as <code>required</code>
> -        which is the most secure / restrictive option in addition to reporting
> -        the configuration error. (markt)
> -      </fix>
> -      <fix>
> -        Improve the handling of client disconnections during the TLS
> -        renegotiation handshake. (markt)
> -      </fix>
> -      <fix>
> -        Prevent exceptions being thrown during normal shutdown of NIO
> -        connections. This enables TLS connections to close cleanly. (markt)
> -      </fix>
> -      <fix>
> -        Fix possible race condition when setting IO listeners on an upgraded
> -        connection. (remm)
> -      </fix>
> -      <fix>
> -        Ensure that the APR/native connector uses blocking I/O for TLS
> -        renegotiation. (markt)
> -      </fix>
> -      <fix>
> -        <bug>48655</bug>: Enable Tomcat to shutdown cleanly when using sendfile,
> -        the APR/native connector and a multi-part download is in progress.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>58244</bug>: Handle the case when OpenSSL resumes a TLS session
> -        using a ticket and the full client certificate chain is not available.
> -        In this case the client certificate without the chain will be presented
> -        to the application. (markt)
> -      </fix>
> -      <fix>
> -        Improve the warning message when JSSE and OpenSSL configuration styles
> -        are mixed on the same <code>SSLHostConfig</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61415</bug>: Fix TLS renegotiation with OpenSSL based connections
> -        and session caching. (markt)
> -      </fix>
> -      <fix>
> -        Delay checking that the configured attributes for an
> -        <code>SSLHostConfig</code> instance are consistent with the configured
> -        SSL implementation until <code>Connector</code> start to avoid incorrect
> -        warnings when the SSL implementation changes during initialisation.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>61450</bug>: Fix default key alias algorithm. (remm)
> -      </fix>
> -      <fix>
> -        <bug>61451</bug>: Correct a regression in the fix to enable the use of
> -        Java key stores that contained multiple keys that did not all have the
> -        same password. The regression broke support for any key store that did
> -        not store keys in PKCS #8 format such as hardware key stores and Windows
> -        key stores. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <fix>
> -        <bug>60523</bug>: Reduce the number of packets used to send WebSocket
> -        messages by not flushing between the header and the payload when the
> -        two are written together. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61491</bug>: When using the <code>permessage-deflate</code>
> -        extension, correctly handle the sending of empty messages after
> -        non-empty messages to avoid the <code>IllegalArgumentException</code>.
> -        (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        Show connector cipher list in the manager web application in the
> -        correct cipher order. (rjung)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <fix>
> -        To avoid unexpected session timeout notification from backup session,
> -        update the access time when receiving the map member notification
> -        message. (kfujino)
> -      </fix>
> -      <fix>
> -        Add member info to the log message when the failure detection check
> -        fails in <code>TcpFailureDetector</code>. (kfujino)
> -      </fix>
> -      <fix>
> -        Avoid Ping timeout until the added map member by receiving
> -        <code>MSG_START</code> message is completely started. (kfujino)
> -      </fix>
> -      <fix>
> -        When sending a channel message, make sure that the Sender has connected.
> -        (kfujino)
> -      </fix>
> -      <fix>
> -        Correct the backup node selection logic that node 0 is returned twice
> -        consecutively. (kfujino)
> -      </fix>
> -      <fix>
> -        Fix race condition of <code>responseMap</code> in
> -        <code>RpcChannel</code>. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="jdbc-pool">
> -    <changelog>
> -      <fix>
> -        <bug>61391</bug>: Ensure that failed queries are logged if the
> -        <code>SlowQueryReport</code> interceptor is configured to do so and the
> -        connection has been abandoned. Patch provided by Craig Webb. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61425</bug>: Ensure that transaction of idle connection has
> -        terminated  when the <code>testWhileIdle</code> is set to
> -        <code>true</code> and <code>defaultAutoCommit</code> is set to
> -        <code>false</code>. Patch provided by WangZheng. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        <bug>61419</bug>: Replace a Unix style comment in the DOS bat file
> -        <code>catalina.bat</code> with the correct <code>rem</code> markup.
> -        (rjung)
> -      </fix>
> -      <fix>
> -        <bug>61439</bug>: Remove the Java Annotation API classes from
> -        tomcat-embed-core.jar and package them in a separate JAR in the
> -        embedded distribution to provide end users with greater flexibility to
> -        handle potential conflicts with the JRE and/or other JARs. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61441</bug>: Improve the detection of <code>JAVA_HOME</code> by the
> -        <code>daemon.sh</code> script when running on a platform where Java has
> -        been installed from an RPM. (rjung)
> -      </fix>
> -      <update>
> -        Update the packaged version of the Tomcat Native Library to 1.2.14 to
> -        pick up the latest Windows binaries built with APR 1.6.2 and OpenSSL
> -        1.0.2l. (markt)
> -      </update>
> -      <update>
> -        <bug>61599</bug>: Update to Commons Daemon 1.1.0 for improved Java 9
> -        support. (markt)
> -      </update>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M26 (markt)" rtext="2017-08-08">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Correct multiple regressions in the fix for <bug>49464</bug> that could
> -        corrupt static content served by the <code>DefaultServlet</code>.(markt)
> -      </fix>
> -      <fix>
> -        Correct a bug in the <code>PushBuilder</code> implementation that
> -        meant push URLs containing <code>%nn</code> sequences were not correctly
> -        decoded. Identified by FindBugs. (markt)
> -      </fix>
> -      <add>
> -        <bug>61164</bug>: Add support for the <code>%X</code> pattern in the
> -        <code>AccessLogValve</code> that reports the connection status at the
> -        end of the request. Patch provided by Zemian Deng. (markt)
> -      </add>
> -      <fix>
> -        <bug>61351</bug>: Correctly handle %nn decoding of URL patterns in
> -        web.xml and similar locations that may legitimately contain characters
> -        that are not permitted by RFC 3986. (markt)
> -      </fix>
> -      <add>
> -        <bug>61366</bug>: Add a new attribute, <code>localDataSource</code>, to
> -        the <code>JDBCStore</code> that allows the Store to be configured to use
> -        a DataSource defined by the web application rather than the default of
> -        using a globally defined DataSource. Patch provided by Jonathan
> -        Horowitz. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        <bug>61086</bug>: Ensure to explicitly signal an empty request body for
> -        HTTP 205 responses. Additional fix to r1795278. Based on a patch
> -        provided by Alexandr Saperov. (violetagg)
> -      </fix>
> -      <update>
> -        <bug>61345</bug>: Add a server listener that can be used to do system
> -        property replacement from the property source configured in the
> -        digester. (remm)
> -      </update>
> -      <add>
> -        Add additional logging to record problems that occur while waiting for
> -        the NIO pollers to stop during the Connector stop process. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        <bug>61364</bug>: Ensure that files are closed after detecting encoding
> -        of JSPs so that files do not remain locked by the file system. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <add>
> -        <bug>57767</bug>: Add support to the WebSocket client for following
> -        redirects when attempting to establish a WebSocket connection. Patch
> -        provided by J Fernandez. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M25 (markt)" rtext="2017-07-28">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Performance improvements for service loader look-ups (and look-ups of
> -        other class loader resources) when the web application is deployed in a
> -        packed WAR file. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60963</bug>: Add <code>ExtractingRoot</code>, a new
> -        <code>WebResourceRoot</code> implementation that extracts JARs to the
> -        work directory for improved performance when deploying packed WAR files.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>61253</bug>: Add warn message when Digester.updateAttributes
> -        throws an exception instead of ignoring it. (csutherl)
> -      </fix>
> -      <fix>
> -        Correct a further regression in the fix for <bug>49464</bug> that could
> -        cause an byte order mark character to appear at the start of content
> -        included by the <code>DefaultServlet</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61313</bug>: Make the read timeout configurable in the
> -        <code>JNDIRealm</code> and ensure that a read timeout will result in an
> -        attempt to fail over to the alternateURL. Based on patches by Peter
> -        Maloney and Felix Schumacher. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        Correct the documentation for how <code>StandardRoot</code> is
> -        configured. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        <bug>61316</bug>: Fix corruption of UTF-16 encoded source files in
> -        released source distributions. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M24 (markt)" rtext="not released">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <add>
> -        <bug>52924</bug>: Add support for a Tomcat specific deployment
> -        descriptor, <code>/WEB-INF/tomcat-web.xml</code>. This descriptor has an
> -        identical format to <code>/WEB-INF/web.xml</code>. The Tomcat descriptor
> -        takes precedence over any settings in <code>conf/web.xml</code> but does
> -        not take precedence over any settings in <code>/WEB-INF/web.xml</code>.
> -        (markt)
> -      </add>
> -      <fix>
> -        <bug>61232</bug>: When log rotation is disabled only one separator will
> -        be used when generating the log file name. For example if the prefix is
> -        <code>catalina.</code> and the suffix is <code>.log</code> then the log
> -        file name will be <code>catalina.log</code> instead of
> -        <code>catalina..log</code>. Patch provided by Katya Stoycheva.
> -        (violetagg)
> -      </fix>
> -      <fix>
> -        <bug>61264</bug>: Correct a regression in the refactoring to use
> -        <code>Charset</code> rather than <code>String</code> to store request
> -        character encoding that prevented <code>getReader()</code> throwing an
> -        <code>UnsupportedEncodingException</code> if the user agent specifies
> -        an unsupported character encoding. (markt)
> -      </fix>
> -      <fix>
> -        Correct a regression in the fix for <bug>49464</bug> that could cause an
> -        incorrect <code>Content-Length</code> header to be sent by the
> -        <code>DefaultServlet</code> if the encoding of a static is not
> -        consistent with the encoding of the response. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Enable TLS connectors to use Java key stores that contain multiple keys
> -        where each key has a separate password. Based on a patch by Frank
> -        Taffelt. (markt)
> -      </fix>
> -      <fix>
> -        Improve the handling of HTTP/2 stream resets due to excessive headers
> -        when a continuation frame is used. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <add>
> -        <bug>53031</bug>: Add support for the <code>fork</code> option when
> -        compiling JSPs with the Jasper Ant task and javac. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <add>
> -        <bug>52791</bug>: Add the ability to set the defaults used by the
> -        Windows installer from a configuration file. Patch provided by Sandra
> -        Madden. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M23 (markt)" rtext="not released">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>49464</bug>: Improve the Default Servlet's handling of static files
> -        when the file encoding is not compatible with the required response
> -        encoding. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61214</bug>: Remove deleted attribute <code>servlets</code> from
> -        the Context MBean description. Patch provided by Alexis Hassler. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61215</bug>: Correctly define <code>addConnectorPort</code> and
> -        <code>invalidAuthenticationWhenDeny</code> in the
> -        <code>mbean-descriptors.xml</code> file for the
> -        <code>org.apache.catalina.valves</code> package so that the attributes
> -        are accessible via JMX. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61216</bug>: Improve layout for <code>CompositeData</code> and
> -        <code>TabularData</code> when viewing via the JMX proxy servlet. Patch
> -        provided by Alexis Hassler. (markt)
> -      </fix>
> -      <fix>
> -        Additional permission for deleting files is granted to JULI as it is
> -        required by FileHandler when running under a Security Manager. The
> -        thread that cleans the log files is marked as daemon thread.
> -        (violetagg)
> -      </fix>
> -      <fix>
> -        <bug>61229</bug>: Correct a regression in 9.0.0.M21 that broke WebDAV
> -        handling for resources with names that included a <code>&amp;</code>
> -        character. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Restore the ability to configure support for SSLv3. Enabling this
> -        protocol will trigger a warning in the logs since it is known to be
> -        insecure. (markt)
> -      </fix>
> -      <add>
> -        Add LoadBalancerDrainingValve, a Valve designed to reduce the amount of
> -        time required for a node to drain its authenticated users. (schultz)
> -      </add>
> -      <fix>
> -        Do not log a warning when a <code>null</code> session is returned for an
> -        OpenSSL based TLS session since this is expected when session tickets
> -        are enabled. (markt)
> -      </fix>
> -      <fix>
> -        When the access log valve logs a TLS related request attribute and the
> -        NIO2 connector is used with OpenSSL, ensure that the TLS attributes are
> -        available to the access log valve when the connection is closing.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>60461</bug>: Sync SSL session access for the APR connector. (remm)
> -      </fix>
> -      <fix>
> -        <bug>61224</bug>: Make the <code>GlobalRequestProcessor</code> MBean
> -        attributes read-only. Patch provided by Alexis Hassler. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        <bug>49176</bug>: When generating JSP runtime error messages that quote
> -        the relevant JSP source code, switch from using the results of the JSP
> -        page parsing process to using the JSR 045 source map data to identify
> -        the correct part of the JSP source from the stack trace. This
> -        significantly reduces the memory footprint of Jasper in development
> -        mode, provides a small performance improvement for error page generation
> -        and enables source quotes to continue to be provided after a Tomcat
> -        restart. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        Remove references to the Loader attribute
> -        <code>searchExternalFirst</code> from the documentation since the
> -        attribute is no longer supported. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <add>
> -        <bug>51513</bug>: Add support for the <code>compressionMinSize</code>
> -        attribute to the <code>GzipInterceptor</code>, add optional statistics
> -        collection and expose the Interceptor over JMX. Based on a patch by
> -        Christian Stöber. (markt)
> -      </add>
> -      <add>
> -        <bug>61127</bug>: Allow human-readable names for channelSendOptions and
> -        mapSendOptions. Patch provided by Igal Sapir. (schultz)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <scode>
> -        Restore the local definition of the web service annotations since the
> -        JRE provided versions are deprecated and Java 9 does not provide them by
> -        default. (markt)
> -      </scode>
> -      <fix>
> -        Add necessary Java 9 configuration options to the startup scripts to
> -        prevent warnings being generated on web application stop. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M22 (markt)" rtext="2017-06-26">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>48543</bug>: Add the option to specify an alternative file name for
> -        the <code>catalina.config</code> system property. Also document that
> -        relative, as well as absolute, URLs are permitted. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61072</bug>: Respect the documentation statements that allow
> -        using the platform default secure random for session id generation.
> -        (remm)
> -      </fix>
> -      <fix>
> -        Correct the javadoc for
> -        <code>o.a.c.connector.CoyoteAdapter#parseSessionCookiesId</code>.
> -        Patch provided by John Andrew (XUZHOUWANG) via Github. (violetagg)
> -      </fix>
> -      <fix>
> -        <bug>61101</bug>: CORS filter should set Vary header in response.
> -        Submitted by Rick Riemer. (remm)
> -      </fix>
> -      <add>
> -        <bug>61105</bug>: Add a new JULI FileHandler configuration for
> -        specifying the maximum number of days to keep the log files. By default
> -        the log files will be kept 90 days as configured in
> -        <code>logging.properties</code>. (violetagg)
> -      </add>
> -      <update>
> -        Update the Servlet 4.0 implementation to add support for setting
> -        trailer fields for HTTP responses. (markt)
> -      </update>
> -      <fix>
> -        <bug>61125</bug>: Ensure that <code>WarURLConnection</code> returns the
> -        correct value for calls to <code>getLastModified()</code> as this is
> -        required for the correct detection of JSP modifications when the JSP is
> -        packaged in a WAR file. (markt)
> -      </fix>
> -      <fix>
> -        Improve the <code>SSLValve</code> so it is able to handle client
> -        certificate headers from Nginx. Based on a patch by Lucas Ventura Carro.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>61134</bug>: Do not use '[' and ']' symbols around substituted
> -        text fragments when generating the default error pages. Patch provided
> -        by Katya Todorova. (violetagg)
> -      </fix>
> -      <fix>
> -        <bug>61154</bug>: Allow the Manager and Host Manager web applications to
> -        start by default when running under a security manager. This was
> -        accomplished by adding a custom permission,
> -        <code>org.apache.catalina.security.DeployXmlPermission</code>, that
> -        permits an application to use a <code>META-INF/context.xml</code> file
> -        and then granting that permission to the Manager and Host Manager.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>61173</bug>: Polish the javadoc for
> -        <code>o.a.catalina.startup.Tomcat</code>. Patch provided by
> -        peterhansson_se. (violetagg)
> -      </fix>
> -      <add>
> -        A new configuration property <code>crawlerIps</code> is added to the
> -        <code>o.a.catalina.valves.CrawlerSessionManagerValve</code>. Using this
> -        property one can specify a regular expression that will be used to
> -        identify crawlers based on their IP address. Based on a patch provided
> -        by Tetradeus. (violetagg)
> -      </add>
> -      <fix>
> -        <bug>61180</bug>: Log a warning message rather than an information
> -        message if it takes more than 100ms to initialised a
> -        <code>SecureRandom</code> instance for a web application to use to
> -        generate session identifiers. Patch provided by Piotr Chlebda. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61185</bug>: When an asynchronous request is dispatched via
> -        <code>AsyncContext.dispatch()</code> ensure that
> -        <code>getRequestURI()</code> for the dispatched request matches that of
> -        the original request. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61197</bug>: Ensure that the charset name used in the
> -        <code>Content-Type</code> header has exactly the same form as that
> -        provided by the application. This reverts a behavioural change in
> -        9.0.0.M21 that caused problems for some clients. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61201</bug>: Ensure that the <code>SCRIPT_NAME</code> environment
> -        variable for CGI executables is populated in a consistent way regardless
> -        of how the CGI servlet is mapped to a request. (markt)
> -      </fix>
> -      <fix>
> -        Ensure to send a space between trailer field name and field value
> -        for HTTP responses trailer fields. (huxing)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        <bug>61086</bug>: Explicitly signal an empty request body for HTTP 205
> -        responses. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61120</bug>: Do not ignore path parameters when processing HTTP/2
> -        requests. (markt)
> -      </fix>
> -      <fix>
> -        Revert a change introduced in the fix for bug <bug>60718</bug> that
> -        changed the status code recorded in the access log when the client
> -        dropped the connection from 200 to 500. (markt)
> -      </fix>
> -      <fix>
> -        Make asynchronous error handling more robust. In particular ensure that
> -        <code>onError()</code> is called for any registered
> -        <code>AsyncListener</code>s after an I/O error on a non-container
> -        thread. (markt)
> -      </fix>
> -      <fix>
> -        Add additional syncs to the SSL session object provided by the OpenSSL
> -        engine so that a concurrent destruction cannot cause a JVM crash.
> -        (remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        <bug>44787</bug>: Improve error message when JSP compiler configuration
> -        options are not valid. (markt)
> -      </fix>
> -      <add>
> -        <bug>45931</bug>: Extend Jasper's <code>timeSpaces</code> option to add
> -        support for <code>single</code> which replaces template text that
> -        consists entirely of whitespace with a single space character. Based on
> -        a patch by Meetesh Karia. (markt)
> -      </add>
> -      <fix>
> -        <bug>53011</bug>: When pre-compiling with JspC, report all compilation
> -        errors rather than stopping after the first error. A new option
> -        <code>-failFast</code> can be used to restore the previous behaviour of
> -        stopping after the first error. Based on a patch provided by Marc Pompl.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>61137</bug>: <code>j.s.jsp.tagext.TagLibraryInfo#uri</code> and
> -        <code>j.s.jsp.tagext.TagLibraryInfo#prefix</code> fields should not be
> -        final. Patch provided by Katya Todorova. (violetagg)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <fix>
> -        Correct the log message when a <code>MessageHandler</code> for
> -        <code>PongMessage</code> does not implement
> -        <code>MessageHandler.Whole</code>. (rjung)
> -      </fix>
> -      <fix>
> -        Improve thread-safety of <code>Future</code>s used to report the result
> -        of sending WebSocket messages. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61183</bug>: Correct a regression in the previous fix for
> -        <bug>58624</bug> that could trigger a deadlock depending on the locking
> -        strategy employed by the client code. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web applications">
> -    <changelog>
> -      <fix>
> -        Better document the meaning of the trimSpaces option for Jasper. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61150</bug>: Configure the Manager and Host-Manager web
> -        applications to permit serialization and deserialization of
> -        CRSFPreventionFilter related session objects to avoid warning messages
> -        and/or stack traces on web application stop and/or start when running
> -        under a security manager. (markt)
> -      </fix>
> -      <fix>
> -        Correct the TLS configuration documentation to remove SSLv2 and SSLv3
> -        from the list of supported protocols. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <add>
> -        <bug>45832</bug>: Add HTTP DIGEST authentication support to the Catalina
> -        Ant tasks used to communicate with the Manager application. (markt)
> -      </add>
> -      <fix>
> -        <bug>45879</bug>: Add the <code>RELEASE-NOTES</code> file to the root of
> -        the installation created by the Tomcat installer for Windows to make it
> -        easier for users to identify the installed Tomcat version. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61055</bug>: Clarify the code comments in the rewrite valve to make
> -        clear that there are no plans to provide proxy support for this valve
> -        since Tomcat does not have proxy capabilities. (markt)
> -      </fix>
> -      <fix>
> -        <bug>61076</bug>: Document the <code>altDDName</code> attribute for the
> -        <code>Context</code> element. (markt)
> -      </fix>
> -      <fix>
> -        Correct typo in Jar Scan Filter Configuration Reference.
> -        Issue reported via comments.apache.org. (violetagg)
> -      </fix>
> -      <fix>
> -        Correct the requirement for the minimum Java SE version in Application
> -        Developer's Guide. Issue reported via comments.apache.org. (violetagg)
> -      </fix>
> -      <fix>
> -        <bug>61145</bug>: Add missing <code>@Documented</code> annotation to
> -        annotations in the annotations API. Patch provided by Katya Todorova.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>61146</bug>: Add missing <code>lookup()</code> method to
> -        <code>@EJB</code> annotation in the annotations API. Patch provided by
> -        Katya Todorova. (markt)
> -      </fix>
> -      <fix>
> -        Correct typo in Context Container Configuration Reference.
> -        Patch provided by Katya Todorova. (violetagg)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M21 (markt)" rtext="2017-05-10">
> -  <subsection name="General">
> -    <changelog>
> -      <add>
> -        Allow to exclude JUnit test classes using the build property
> -        <code>test.exclude</code> and document the property in
> -        BUILDING.txt. (rjung)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        Review those places where Tomcat re-encodes a URI or URI component and
> -        ensure that the correct encoding (path differs from query string) is
> -        applied and that the encoding is applied consistently. (markt)
> -      </fix>
> -      <fix>
> -        Avoid a <code>NullPointerException</code> when reading attributes for a
> -        initialised HTTP connector where TLS is enabled. (markt)
> -      </fix>
> -      <fix>
> -        Always quote the <code>hostName</code> of an <code>SSLHostConfig</code>
> -        element when using it as part of the JMX object name to avoid errors that
> -        prevent the associated TLS connector from starting if a wild card
> -        <code>hostName</code> is configured (because <code>*</code> is a
> -        reserved character for JMX object names). (markt)
> -      </fix>
> -      <update>
> -        Update the default <code>URIEncoding</code> for a <code>Connector</code>
> -        to <code>UTF-8</code> as required by the Servlet 4.0 specification.
> -        (markt)
> -      </update>
> -      <scode>
> -        Switch to using <code>Charset</code> rather than <code>String</code> to
> -        store encoding settings (including for configuration and for the
> -        <code>Content-Type header</code>) to reduce the number of places the
> -        associated <code>Charset</code> needs to be looked up. (markt)
> -      </scode>
> -      <fix>
> -        Use a more reliable mechanism for the <code>DefaultServlet</code> when
> -        determining if the current request is for custom error page or not.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Ensure that when the Default or WebDAV servlets process an error
> -        dispatch that the error resource is processed via the
> -        <code>doGet()</code> method irrespective of the method used for the
> -        original request that triggered the error. (markt)
> -      </fix>
> -      <fix>
> -        If a static custom error page is specified that does not exist or cannot
> -        be read, ensure that the intended error status is returned rather than a
> -        404 or 403. (markt)
> -      </fix>
> -      <fix>
> -        When the WebDAV servlet is configured and an error dispatch is made to a
> -        custom error page located below <code>WEB-INF</code>, ensure that the
> -        target error page is displayed rather than a 404 response. (markt)
> -      </fix>
> -      <update>
> -        Update the Servlet 4.0 implementation to add support for obtaining
> -        trailer fields from chunked HTTP requests. (markt)
> -      </update>
> -      <add>
> -        <bug>61047</bug>: Add MIME mapping for woff2 fonts in the default
> -        web.xml. Patch provided by Justin Williamson. (violetagg)
> -      </add>
> -      <fix>
> -        Correct the logic that selects the encoding to use to decode the query
> -        string in the <code>SSIServletExternalResolver</code> so that the
> -        <code>useBodyEncodingForURI</code> attribute of the
> -        <code>Connector</code> is correctly taken into account. (markt)
> -      </fix>
> -      <fix>
> -        Within the Expires filter, make the content type value specified with the
> -        <code>ExpiresByType</code> parameter, case insensitive. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        When a <code>TrustManager</code> is configured that does not support
> -        <code>certificateVerificationDepth</code> only log a warning about that
> -        lack of support when <code>certificateVerificationDepth</code> has been
> -        explicitly set. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60970</bug>: Extend the fix for large headers to push requests.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Do not include a <code>Date</code> header in HTTP/2 responses with
> -        status codes less than 200. (markt)
> -      </fix>
> -      <fix>
> -        When sending an HTTP/2 push promise with the NIO2 connector, the pushed
> -        stream ID should only be included with the initial push promise frame
> -        and not any subsequent continuation frames. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        When no BOM is present and an encoding is detected, do not skip the
> -        bytes used to detect the encoding since they are not part of a BOM.
> -        (markt)
> -      </fix>
> -      <update>
> -        <bug>61057</bug>: Update to Eclipse JDT Compiler 4.6.3. (violetagg)
> -      </update>
> -      <fix>
> -        <bug>61065</bug>: Ensure that once the class is resolved by
> -        <code>javax.el.ImportHandler#resolveClass</code> it will be cached with
> -        the proper name. (violetagg)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="WebSocket">
> -    <changelog>
> -      <add>
> -        Introduce new API <code>o.a.tomcat.websocket.WsSession#suspend</code>/
> -        <code>o.a.tomcat.websocket.WsSession#resume</code> that can be used to
> -        suspend/resume reading of the incoming messages. (violetagg)
> -      </add>
> -      <fix>
> -        <bug>61003</bug>: Ensure the flags for reading/writing in
> -        <code>o.a.t.websocket.AsyncChannelWrapperSecure</code> are correctly
> -        reset even if some exceptions occurred during processing. (markt/violetagg)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Web Applications">
> -    <changelog>
> -      <add>
> -        Add documents for <code>maxIdleTime</code> attribute to Channel Receiver
> -        docs. (kfujino)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <add>
> -        Add features to get the statistics of the thread pool of the
> -        <code>Receiver</code> component and
> -        <code>MessageDispatchInterceptor</code>. These statistics information
> -        can be acquired via JMX. (kfujino)
> -      </add>
> -      <add>
> -        Add <code>maxIdleTime</code> attribute to <code>NioReceiverMBean</code>
> -        in order to expose to JMX. (kfujino)
> -      </add>
> -      <add>
> -        Add JMX support for <code>Channel Interceptors</code>. The Interceptors
> -        that implement JMX support are <code>TcpFailureDetector</code>,
> -        <code>ThroughputInterceptor</code>, <code>TcpPingInterceptor</code>,
> -        <code>StaticMembershipInterceptor</code>,
> -        <code>MessageDispatchInterceptor</code> and
> -        <code>DomainFilterInterceptor</code>. (kfujino)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <add>
> -        Modify the Ant build script used to publish to a Maven repository so
> -        that it no longer requires artifacts to be GPG signed. This is make it
> -        possible for the CI system to upload snapshot builds to the ASF Maven
> -        repository. (markt)
> -      </add>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M20 (markt)" rtext="2017-04-18">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <update>
> -        Update the Servlet 4.0 API implementation to reflect the change in
> -        method name from <code>getPushBuilder()</code> to
> -        <code>newPushBuilder()</code>. (markt)
> -      </update>
> -      <fix>
> -        Correct various edge cases in the new HTTP Host header validation
> -        parser. Patch provided by Katya Todorova. (martk)
> -      </fix>
> -      <fix>
> -        Correct a regression in the X to comma refactoring that broke JMX
> -        operations that take parameters. (markt)
> -      </fix>
> -      <fix>
> -        Avoid a <code>NullPointerException</code> when reading attributes for a
> -        running HTTP connector where TLS is not enabled. (markt)
> -      </fix>
> -      <fix>
> -        <bug>47214</bug>: Refactor code so that explicitly referenced inner
> -        classes are given explicit names rather than being anonymous. (markt)
> -      </fix>
> -      <fix>
> -        <bug>59825</bug>: Log a message that lists the components in the
> -        processing chain that do not support async processing when a call to
> -        <code>ServletRequest.startAsync()</code> fails. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60940</bug>: Improve the handling of the <code>META-INF/</code> and
> -        <code>META-INF/MANIFEST.MF</code> entries for Jar files located in
> -        <code>/WEB-INF/lib</code> when running a web application from a packed
> -        WAR file. (markt)
> -      </fix>
> -      <fix>
> -        Pre-load the <code>ExceptionUtils</code> class. Since the class is used
> -        extensively in error handling, it is prudent to pre-load it to avoid any
> -        failure to load this class masking the true problem during error
> -        handling. (markt)
> -      </fix>
> -      <fix>
> -        Avoid potential <code>NullPointerException</code>s related to access
> -        logging during shutdown, some of which have been observed when running
> -        the unit tests. (markt)
> -      </fix>
> -      <fix>
> -        When there is no <code>javax.servlet.WriteListener</code> registered
> -        then a call to <code>javax.servlet.ServletOutputStream#isReady</code>
> -        will return <code>false</code> instead of throwing
> -        <code>IllegalStateException</code>. (violetagg)
> -      </fix>
> -      <fix>
> -        When there is no <code>javax.servlet.ReadListener</code> registered
> -        then a call to <code>javax.servlet.ServletInputStream#isReady</code>
> -        will return <code>false</code> instead of throwing
> -        <code>IllegalStateException</code>. (violetagg)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Align cipher configuration parsing with current OpenSSL master. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60970</bug>: Fix infinite loop if application tries to write a
> -        large header to the response when using HTTP/2. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        <bug>47214</bug>: Refactor code so that explicitly referenced inner
> -        classes are given explicit names rather than being anonymous. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60925</bug>: Improve the handling of access to properties defined
> -        by interfaces when a <code>BeanELResolver</code> is used under a
> -        <code>SecurityManager</code>. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Tribes">
> -    <changelog>
> -      <add>
> -        Add JMX support for Tribes components. (kfujino)
> -      </add>
> -    </changelog>
> -  </subsection>
> -  <subsection name="jdbc-pool">
> -    <changelog>
> -      <scode>
> -        Refactor the creating a constructor for a proxy class to reduce
> -        duplicate code. (kfujino)
> -      </scode>
> -      <fix>
> -        In <code>StatementFacade</code>, the method call on the statements that
> -        have been closed throw <code>SQLException</code> rather than
> -        <code>NullPointerException</code>. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        <bug>60932</bug>: Correctly escape single quotes when used in i18n
> -        messages. Based on a patch by Michael Osipov. (markt)
> -      </fix>
> -      <scode>
> -        Review i18n property files, remove unnecessary escaping and consistently
> -        use <code>[...]</code> to delimit inserted values. (markt)
> -      </scode>
> -      <fix>
> -        Update the custom Ant task that integrates with the Symantec code
> -        signing service to use the now mandatory 2-factor authentication.
> -        (markt)
> -      </fix>
> -      <scode>
> -        Refactoring in preparation for Java 9. Refactor to avoid using some
> -        methods that will be deprecated in Java 9 onwards. (markt)
> -      </scode>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M19 (markt)" rtext="2017-03-30">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <add>
> -        <bug>54618</bug>: Add support to the
> -        <code>HttpHeaderSecurityFilter</code> for the HSTS preload parameter.
> -        (markt)
> -      </add>
> -      <fix>
> -        Correct a bug in the implementation of the Servlet 4.0 feature that
> -        allows specifying a default request and/or response character encoding
> -        per web application. <code>null</code> values passed via the
> -        programmatic interface no longer trigger a
> -        <code>NullPointerException</code>. (markt)
> -      </fix>
> -      <fix>
> -        Correct a potential exception during shutdown when one or more
> -        Containers are configured with a value of 1 for startStopThreads.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>60853</bug>: Expose the <code>SSLHostConfig</code> and
> -        <code>SSLHostConfigCertificate</code> objects via JMX. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60876</bug>: Ensure that <code>Set-Cookie</code> headers generated
> -        by the <code>Rfc6265CookieProcessor</code> are aligned with the
> -        specification. Patch provided by Jim Griswold. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60882</bug>: Fix a <code>NullPointerException</code> when obtaining
> -        a <code>RequestDispatcher</code> for a request that will not have any
> -        pathInfo associated with it. This was a regression in the changes in
> -        9.0.0.M18 for the Servlet 4.0 API changes. (markt)
> -      </fix>
> -      <update>
> -        Align <code>PushBuilder</code> API with changes from the Servlet expert
> -        group. (markt)
> -      </update>
> -      <update>
> -        Align web.xml parsing rules with changes from the Servlet expert group
> -        for <code>&lt;request-character-encoding&gt;</code> and
> -        <code>&lt;response-character-encoding&gt;</code>. (markt)
> -      </update>
> -      <scode>
> -        Refactor the various implementations of X to comma separated list to a
> -        single utility class and update the code to use the new utility class.
> -        (markt)
> -      </scode>
> -      <fix>
> -        <bug>60911</bug>: Ensure NPE will not be thrown when looking for SSL
> -        session ID. Based on a patch by Didier Gutacker. (violetagg)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Add async based IO groundwork for HTTP/2. (remm)
> -      </fix>
> -      <fix>
> -        Fix HTTP/2 incorrect input unblocking on EOF. (remm)
> -      </fix>
> -      <fix>
> -        Close the connection sooner if an event occurs for a current connection
> -        that is not consistent with the current state of that connection.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Speed up shutdown when using multiple acceptor threads by ensuring that
> -        the code that unlocks the acceptor threads correctly handles the case
> -        where there are multiple threads. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60851</bug>: Add <code>application/xml</code> and
> -        <code>application/json</code> to the default list of compressible MIME
> -        types. Patch by Michael Osipov. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60852</bug>: Correctly spell compressible when used in
> -        configuration attributes and internal code. Based on a patch by Michael
> -        Osipov. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60900</bug>: Avoid a <code>NullPointerException</code> in the APR
> -        Poller if a connection is closed at the same time as new data arrives on
> -        that connection. (markt)
> -      </fix>
> -      <fix>
> -        Improve HPACK specification compliance by fixing some test failures
> -        reported by the h2spec tool written by Moto Ishizawa. (markt)
> -      </fix>
> -      <fix>
> -        Improve HTTP/2 specification compliance by fixing some test failures
> -        reported by the h2spec tool written by Moto Ishizawa. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60918</bug>: Fix sendfile processing error that could lead to
> -        subsequent requests experiencing an <code>IllegalStateException</code>.
> -        (markt)
> -      </fix>
> -      <fix>
> -        Improve sendfile handling when requests are pipelined. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Jasper">
> -    <changelog>
> -      <fix>
> -        <bug>60844</bug>: Correctly handle the error when fewer parameter values
> -        than required by the method are used to invoke an EL method expression.
> -        Patch provided by Daniel Gray. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="jdbc-pool">
> -    <changelog>
> -      <fix>
> -        <bug>60764</bug>: Implement <code>equals()</code> and
> -        <code>hashCode()</code> in the <code>StatementFacade</code> in order to
> -        enable these methods to be called on the closed statements if any
> -        statement proxy is set. This behavior can be changed with
> -        <code>useStatementFacade</code> attribute. (kfujino)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Other">
> -    <changelog>
> -      <fix>
> -        Refactor the build script and the NSIS installer script so that either
> -        NSIS 2.x or NSIS 3.x can be used to build the installer. This is
> -        primarily to re-enable building the installer on the Linux based CI
> -        system where the combination of NSIS 3.x and wine leads to failed
> -        installer builds. (markt)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -</section>
> -<section name="Tomcat 9.0.0.M18 (markt)" rtext="2017-03-13">
> -  <subsection name="Catalina">
> -    <changelog>
> -      <fix>
> -        <bug>60469</bug>: Refactor <code>RealmBase</code> for better code re-use
> -        when implementing Realms that use a custom <code>Principal</code>.
> -        (markt)
> -      </fix>
> -      <fix>
> -        <bug>60490</bug>: Various formatting and layout improvements for the
> -        <code>ErrorReportValve</code>. Patch provided by Michael Osipov. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60573</bug>: Remove the reason phrase when sending a
> -        <code>100</code> response status for consistency with other response
> -        status lines. Patch provided by Michael Osipov. (markt)
> -      </fix>
> -      <update>
> -        <bug>60596</bug>: Improve performance of DefaultServlet when sendfile
> -        feature is disabled on connector. (kkolinko)
> -      </update>
> -      <scode>
> -        Make it easier for sub-classes of <code>Tomcat</code> to modify the
> -        default web.xml settings by over-riding
> -        <code>getDefaultWebXmlListener()</code>. Patch provided by Aaron
> -        Anderson. (markt)
> -      </scode>
> -      <fix>
> -        Reduce the contention in the default <code>InstanceManager</code>
> -        implementation when multiple threads are managing objects and need to
> -        reference the annotation cache. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60623</bug>: When startStopThreads is 1 (or a special value that
> -        is equivalent to 1) then rather than using an
> -        <code>ExecutorService</code> to start the children of the current
> -        component, the children will be started on the current thread. (markt)
> -      </fix>
> -      <scode>
> -        <bug>60674</bug>: Remove <code>final</code> marker from
> -        <code>CorsFilter</code> to enable sub-classing. (markt)
> -      </scode>
> -      <fix>
> -        <bug>60683</bug>: Security manager failure causing NPEs when doing IO
> -        on some JVMs. (csutherl)
> -      </fix>
> -      <fix>
> -        <bug>60688</bug>: Update the internal fork of Apache Commons BCEL to
> -        r1782855 to add early access Java 9 support to the annotation scanning
> -        code. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60694</bug>: Prevent NPE during authentication when no JASPIC
> -        <code>AuthConfigFactory</code> is available. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60697</bug>: When HTTP TRACE requests are disabled on the
> -        Connector, ensure that the HTTP OPTIONS response from custom servlets
> -        does not include TRACE in the returned Allow header. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60718</bug>: Improve error handling for asynchronous processing and
> -        correct a number of cases where the <code>requestDestroyed()</code>
> -        event was not being fired and an entry wasn't being made in the access
> -        logs. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60720</bug>: Replace "WWW-Authenticate" literal with static final
> -        AUTH_HEADER_NAME in SpnegoAuthenticator. Patch provided by Michael
> -        Osipov. (violetagg)
> -      </fix>
> -      <fix>
> -        The default JASPIC <code>AuthConfigFactory</code> now correctly notifies
> -        registered <code>RegistrationListener</code>s when a new
> -        <code>AuthConfigProvider</code> is registered. (markt)
> -      </fix>
> -      <scode>
> -        Improve the performance of <code>AuthenticatorBase</code> when there is
> -        no JASPIC configuration available. (violetagg)
> -      </scode>
> -      <fix>
> -        When HTTP TRACE requests are disabled on the Connector, ensure that the
> -        HTTP OPTIONS response from the WebDAV servlet does not include
> -        TRACE in the returned Allow header. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60722</bug>: Take account of the
> -        <strong>dispatchersUseEncodedPaths</strong> setting on the current
> -        <strong>Context</strong> when generating paths for dispatches triggered
> -        by <code>AsyncContext.dispatch()</code>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60728</bug>: Make the separator Tomcat uses in the Tomcat specific
> -        <code>war:file:...</code> URL protocol customizable via a system
> -        property. The separator is equivalent to the use of the <code>!</code>
> -        character in <code>jar:file:...</code> URLs. The default separator of
> -        <code>*</code> remains unchanged. (markt)
> -      </fix>
> -      <update>
> -        Update the Servlet 4.0 API implementation to align with the latest
> -        proposals from the Servlet 4.0 expert group. This includes updates to
> -        the new Servlet mapping API, new methods on the
> -        <code>ServletContext</code> to make the available API more equivalent to
> -        the deployment descriptor, updates to the HTTP push API and the ability
> -        to set default request and response character encoding per web
> -        application. Note that the Servlet 4.0 API is still a work in progress
> -        and further changes are likely. (markt)
> -      </update>
> -      <fix>
> -        <bug>60798</bug>: Correct a bug in the handling of JARs in unpacked WARs
> -        that meant multiple attempts to read the same entry from a JAR in
> -        succession would fail for the second and subsequent attempts. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60808</bug>: Ensure that the <code>Map</code> returned by
> -        <code>ServletRequest.getParameterMap()</code> is fully immutable. Based
> -        on a patch provided by woosan. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60824</bug>: Correctly cache the <code>Subject</code> in the
> -        session - if there is a session - when running under a
> -        <code>SecurityManager</code>. Patch provided by Jan Engehausen. (markt)
> -      </fix>
> -      <fix>
> -        Ensure request and response facades are used when firing application
> -        listeners. (markt/remm)
> -      </fix>
> -    </changelog>
> -  </subsection>
> -  <subsection name="Coyote">
> -    <changelog>
> -      <fix>
> -        Improve handling of case when an HTTP/2 client sends more data that is
> -        subject to flow control than the current window size allows. (markt)
> -      </fix>
> -      <fix>
> -        Improve NIO2 look-ahead parsing of TLS client hello for SNI with large
> -        client hello messages. (markt)
> -      </fix>
> -      <add>
> -        Enable ALPN and also, therefore, HTTP/2 for the NIO and NIO2 HTTP
> -        connectors when using the JSSE implementation for TLS when running on
> -        Java 9. (markt)
> -      </add>
> -      <fix>
> -        Restore Java 9 direct byte buffer compatibility. (remm)
> -      </fix>
> -      <fix>
> -        <bug>59807</bug>: Provide a better error message when there is no
> -        <strong>SSLHostConfig</strong> defined with a <code>hostName</code> that
> -        matches the <code>defaultSSLHostConfigName</code> for the associated
> -        <strong>Connector</strong>. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60627</bug>: Modify the <code>Rfc6265CookieProcessor</code> so that
> -        in addition to cookie headers that start with an explicit RFC 2109
> -        <code>$Version=1</code>, cookies that start with <code>$Version=0</code>
> -        are also parsed as RFC 2109 cookies. (markt)
> -      </fix>
> -      <fix>
> -        Include the value of <code>SslHostConfig.truststoreAlgorithm</code> when
> -        warning that the algorithm does not support the
> -        <code>certificateVerificationDepth</code> configuration option. (markt)
> -      </fix>
> -      <fix>
> -        Ensure that executor thread pools used with connectors pre-start the
> -        configured minimum number of idle threads. (markt)
> -      </fix>
> -      <fix>
> -        <bug>60716</bug>: Add a new JSSE specific attribute,
> -        <code>revocationEnabled</code>, to <code>SSLHostConfig</code> to permit
> -        JSSE provider revocation checks to be enabled when no
> -        <code>certificateRevocationListFile</code> has been configured. The
> -        expectation is that configuration will be performed via a JSSE provider
> -        specific mechanisms. (markt)
> ... 10303 lines suppressed ...
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: dev-help@tomcat.apache.org
> 


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