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'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<URL></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><mapped-name></code> element of a
- <code><env-entry></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><injection-target></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><lookup-name></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><lookup-name></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 <pid></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>""</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>&</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><request-character-encoding></code> and
- <code><response-character-encoding></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'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<URL></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><mapped-name></code> element of a
> - <code><env-entry></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><injection-target></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><lookup-name></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><lookup-name></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 <pid></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>""</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>&</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><request-character-encoding></code> and
> - <code><response-character-encoding></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