/* * 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.core;
/** * Standard implementation of the <b>Context</b> interface. Each child container must be a Wrapper implementation to * process the requests directed to a particular servlet. * * @author Craig R. McClanahan * @author Remy Maucherat
*/ publicclass StandardContext extends ContainerBase implements Context, NotificationEmitter {
/** * Create a new StandardContext component with the default basic Valve.
*/ public StandardContext() {
super();
pipeline.setBasic(new StandardContextValve());
broadcaster = new NotificationBroadcasterSupport(); // Set defaults if (!Globals.STRICT_SERVLET_COMPLIANCE) { // Strict servlet compliance requires all extension mapped servlets // to be checked against welcome files
resourceOnlyServlets.add("jsp");
}
}
/** * Allow multipart/form-data requests to be parsed even when the target servlet doesn't specify @MultipartConfig or * have a <multipart-config> element.
*/ protectedboolean allowCasualMultipartParsing = false;
/** * Control whether remaining request data will be read (swallowed) even if the request violates a data size * constraint.
*/ privateboolean swallowAbortedUploads = true;
/** * The antiResourceLocking flag for this Context.
*/ privateboolean antiResourceLocking = false;
/** * The list of unique application listener class names configured for this application, in the order they were * encountered in the resulting merged web.xml file.
*/ private CopyOnWriteArrayList<String> applicationListeners = new CopyOnWriteArrayList<>();
/** * The set of application listeners that are required to have limited access to ServletContext methods. See Servlet * 3.1 section 4.4.
*/ privatefinal Set<Object> noPluggabilityListeners = new HashSet<>();
/** * The list of instantiated application event listener objects. Note that SCIs and other code may use the * pluggability APIs to add listener instances directly to this list before the application starts.
*/ private List<Object> applicationEventListenersList = new CopyOnWriteArrayList<>();
/** * The set of instantiated application lifecycle listener objects. Note that SCIs and other code may use the * pluggability APIs to add listener instances directly to this list before the application starts.
*/ private Object applicationLifecycleListenersObjects[] = new Object[0];
/** * The ordered set of ServletContainerInitializers for this web application.
*/ private Map<ServletContainerInitializer,Set<Class<?>>> initializers = new LinkedHashMap<>();
/** * The set of application parameters defined for this application.
*/ private ApplicationParameter applicationParameters[] = new ApplicationParameter[0];
privatefinal Object applicationParametersLock = new Object();
/** * The broadcaster that sends j2ee notifications.
*/ private NotificationBroadcasterSupport broadcaster = null;
/** * The Locale to character set mapper for this application.
*/ private CharsetMapper charsetMapper = null;
/** * The Java class name of the CharsetMapper class to be created.
*/ private String charsetMapperClass = "org.apache.catalina.util.CharsetMapper";
/** * The URL of the XML descriptor for this context.
*/ private URL configFile = null;
/** * The "correctly configured" flag for this Context.
*/ privateboolean configured = false;
/** * The security constraints for this web application.
*/ privatevolatile SecurityConstraint constraints[] = new SecurityConstraint[0];
privatefinal Object constraintsLock = new Object();
/** * The ServletContext implementation associated with this Context.
*/ protected ApplicationContext context = null;
/** * The wrapped version of the associated ServletContext that is presented to listeners that are required to have * limited access to ServletContext methods. See Servlet 3.1 section 4.4.
*/ private NoPluggabilityServletContext noPluggabilityServletContext = null;
/** * Should we attempt to use cookies for session id communication?
*/ privateboolean cookies = true;
/** * Should we allow the <code>ServletContext.getContext()</code> method to access the context of other web * applications in this server?
*/ privateboolean crossContext = false;
/** * Unencoded path for this web application.
*/ private String path = null;
/** * The "follow standard delegation model" flag that will be used to configure our ClassLoader. Graal cannot actually * load a class from the webapp classloader, so delegate by default.
*/ privateboolean delegate = JreCompat.isGraalAvailable();
privateboolean denyUncoveredHttpMethods;
/** * The display name of this web application.
*/ private String displayName = null;
/** * Override the default context xml location.
*/ private String defaultContextXml;
/** * Override the default web xml location.
*/ private String defaultWebXml;
/** * The distributable flag for this web application.
*/ privateboolean distributable = false;
/** * The document root for this web application.
*/ private String docBase = null;
privatefinal ErrorPageSupport errorPageSupport = new ErrorPageSupport();
/** * The set of filter configurations (and associated filter instances) we have initialized, keyed by filter name.
*/ private Map<String,ApplicationFilterConfig> filterConfigs = new HashMap<>(); // Guarded by filterDefs
/** * The set of filter definitions for this application, keyed by filter name.
*/ private Map<String,FilterDef> filterDefs = new HashMap<>();
/** * The set of filter mappings for this application, in the order they were defined in the deployment descriptor with * additional mappings added via the {@link ServletContext} possibly both before and after those defined in the * deployment descriptor.
*/ privatefinal ContextFilterMaps filterMaps = new ContextFilterMaps();
/** * The Loader implementation with which this Container is associated.
*/ private Loader loader = null; privatefinal ReadWriteLock loaderLock = new ReentrantReadWriteLock();
/** * The login configuration descriptor for this web application.
*/ private LoginConfig loginConfig = null;
/** * The Manager implementation with which this Container is associated.
*/ protected Manager manager = null; privatefinal ReadWriteLock managerLock = new ReentrantReadWriteLock();
/** * The naming context listener for this web application.
*/ private NamingContextListener namingContextListener = null;
/** * The naming resources for this web application.
*/ private NamingResourcesImpl namingResources = null;
/** * The message destinations for this web application.
*/ private HashMap<String,MessageDestination> messageDestinations = new HashMap<>();
/** * The MIME mappings for this web application, keyed by extension.
*/ private Map<String,String> mimeMappings = new HashMap<>();
/** * The context initialization parameters for this web application, keyed by name.
*/ privatefinal Map<String,String> parameters = new ConcurrentHashMap<>();
/** * The request processing pause flag (while reloading occurs)
*/ privatevolatileboolean paused = false;
/** * The public identifier of the DTD for the web application deployment descriptor version we are currently parsing. * This is used to support relaxed validation rules when processing version 2.2 web.xml files.
*/ private String publicId = null;
/** * The reloadable flag for this web application.
*/ privateboolean reloadable = false;
/** * Unpack WAR property.
*/ privateboolean unpackWAR = true;
/** * The default context override flag for this web application.
*/ privateboolean override = false;
/** * The original document root for this web application.
*/ private String originalDocBase = null;
/** * The privileged flag for this web application.
*/ privateboolean privileged = false;
/** * Should the next call to <code>addWelcomeFile()</code> cause replacement of any existing welcome files? This will * be set before processing the web application's deployment descriptor, so that application specified choices * <strong>replace</strong>, rather than append to, those defined in the global descriptor.
*/ privateboolean replaceWelcomeFiles = false;
/** * The security role mappings for this application, keyed by role name (as used within the application).
*/ private Map<String,String> roleMappings = new HashMap<>();
/** * The security roles for this application, keyed by role name.
*/ private String securityRoles[] = new String[0];
privatefinal Object securityRolesLock = new Object();
/** * The servlet mappings for this web application, keyed by matching pattern.
*/ private Map<String,String> servletMappings = new HashMap<>();
privatefinal Object servletMappingsLock = new Object();
/** * The session timeout (in minutes) for this web application.
*/ privateint sessionTimeout = 30;
/** * The notification sequence number.
*/ private AtomicLong sequenceNumber = new AtomicLong(0);
/** * Set flag to true to cause the system.out and system.err to be redirected to the logger when executing a servlet.
*/ privateboolean swallowOutput = false;
/** * Amount of ms that the container will wait for servlets to unload.
*/ privatelong unloadDelay = 2000;
/** * The watched resources for this application.
*/ private String watchedResources[] = new String[0];
privatefinal Object watchedResourcesLock = new Object();
/** * The welcome files for this application.
*/ private String welcomeFiles[] = new String[0];
privatefinal Object welcomeFilesLock = new Object();
/** * The set of classnames of LifecycleListeners that will be added to each newly created Wrapper by * <code>createWrapper()</code>.
*/ private String wrapperLifecycles[] = new String[0];
privatefinal Object wrapperLifecyclesLock = new Object();
/** * The set of classnames of ContainerListeners that will be added to each newly created Wrapper by * <code>createWrapper()</code>.
*/ private String wrapperListeners[] = new String[0];
privatefinal Object wrapperListenersLock = new Object();
/** * The pathname to the work directory for this context (relative to the server's home if not absolute).
*/ private String workDir = null;
/** * Java class name of the Wrapper class implementation we use.
*/ private String wrapperClassName = StandardWrapper.class.getName(); privateClass<?> wrapperClass = null;
/** * JNDI use flag.
*/ privateboolean useNaming = true;
/** * Name of the associated naming context.
*/ private String namingContextName = null;
private WebResourceRoot resources; privatefinal ReadWriteLock resourcesLock = new ReentrantReadWriteLock();
/** * Name of the engine. If null, the domain is used.
*/ private String j2EEApplication = "none"; private String j2EEServer = "none";
/** * Attribute value used to turn on/off XML validation for web.xml and web-fragment.xml files.
*/ privateboolean webXmlValidation = Globals.STRICT_SERVLET_COMPLIANCE;
/** * Attribute value used to turn on/off XML namespace validation
*/ privateboolean webXmlNamespaceAware = Globals.STRICT_SERVLET_COMPLIANCE;
/** * Attribute used to turn on/off the use of external entities.
*/ privateboolean xmlBlockExternal = true;
/** * Attribute value used to turn on/off XML validation
*/ privateboolean tldValidation = Globals.STRICT_SERVLET_COMPLIANCE;
/** * The name to use for session cookies. <code>null</code> indicates that the name is controlled by the application.
*/ private String sessionCookieName;
/** * The flag that indicates that session cookies should use HttpOnly
*/ privateboolean useHttpOnly = true;
/** * The domain to use for session cookies. <code>null</code> indicates that the domain is controlled by the * application.
*/ private String sessionCookieDomain;
/** * The path to use for session cookies. <code>null</code> indicates that the path is controlled by the application.
*/ private String sessionCookiePath;
/** * Is a / added to the end of the session cookie path to ensure browsers, particularly IE, don't send a session * cookie for context /foo with requests intended for context /foobar.
*/ privateboolean sessionCookiePathUsesTrailingSlash = false;
/** * The Jar scanner to use to search for Jars that might contain configuration information such as TLDs or * web-fragment.xml files.
*/ private JarScanner jarScanner = null;
/** * Enables the RMI Target memory leak detection to be controlled. This is necessary since the detection can only * work if some of the modularity checks are disabled.
*/ privateboolean clearReferencesRmiTargets = true;
/** * Should Tomcat attempt to terminate threads that have been started by the web application? Stopping threads is * performed via the deprecated (for good reason) <code>Thread.stop()</code> method and is likely to result in * instability. As such, enabling this should be viewed as an option of last resort in a development environment and * is not recommended in a production environment. If not specified, the default value of <code>false</code> will be * used.
*/ privateboolean clearReferencesStopThreads = false;
/** * Should Tomcat attempt to terminate any {@link java.util.TimerThread}s that have been started by the web * application? If not specified, the default value of <code>false</code> will be used.
*/ privateboolean clearReferencesStopTimerThreads = false;
/** * If an HttpClient keep-alive timer thread has been started by this web application and is still running, should * Tomcat change the context class loader from the current {@link ClassLoader} to {@link ClassLoader#getParent()} to * prevent a memory leak? Note that the keep-alive timer thread will stop on its own once the keep-alives all expire * however, on a busy system that might not happen for some time.
*/ privateboolean clearReferencesHttpClientKeepAliveThread = true;
/** * Should Tomcat renew the threads of the thread pool when the application is stopped to avoid memory leaks because * of uncleaned ThreadLocal variables. This also requires that the threadRenewalDelay property of the * StandardThreadExecutor or ThreadPoolExecutor be set to a positive value.
*/ privateboolean renewThreadsWhenStoppingContext = true;
/** * Should Tomcat attempt to clear references to classes loaded by the web application class loader from the * ObjectStreamClass caches?
*/ privateboolean clearReferencesObjectStreamClassCaches = true;
/** * Should Tomcat attempt to clear references to classes loaded by this class loader from ThreadLocals?
*/ privateboolean clearReferencesThreadLocals = true;
/** * Should Tomcat skip the memory leak checks when the web application is stopped as part of the process of shutting * down the JVM?
*/ privateboolean skipMemoryLeakChecksOnJvmShutdown = false;
/** * Should the effective web.xml be logged when the context starts?
*/ privateboolean logEffectiveWebXml = false;
/** * Servlets created via {@link ApplicationContext#createServlet(Class)} for tracking purposes.
*/ private Set<Servlet> createdServlets = new HashSet<>();
privateboolean preemptiveAuthentication = false;
privateboolean sendRedirectBody = false;
privateboolean jndiExceptionOnFailedWrite = true;
private Map<String,String> postConstructMethods = new HashMap<>(); private Map<String,String> preDestroyMethods = new HashMap<>();
@Override public String getResponseCharacterEncoding() { return responseEncoding;
}
@Override publicvoid setResponseCharacterEncoding(String responseEncoding) { /* * This ensures that the context response encoding is represented by a unique String object. This enables the * Default Servlet to differentiate between a Response using this default encoding and one that has been * explicitly configured.
*/ if (responseEncoding == null) { this.responseEncoding = null;
} else { this.responseEncoding = new String(responseEncoding);
}
}
/** * {@inheritDoc} * <p> * The default value for this implementation is {@code true}.
*/
@Override publicboolean getDispatchersUseEncodedPaths() { return dispatchersUseEncodedPaths;
}
/** * {@inheritDoc} * <p> * The default value for this implementation is {@code true}.
*/
@Override publicboolean getUseRelativeRedirects() { return useRelativeRedirects;
}
/** * {@inheritDoc} * <p> * The default value for this implementation is {@code false}.
*/
@Override publicboolean getMapperContextRootRedirectEnabled() { return mapperContextRootRedirectEnabled;
}
/** * {@inheritDoc} * <p> * The default value for this implementation is {@code false}.
*/
@Override publicboolean getMapperDirectoryRedirectEnabled() { return mapperDirectoryRedirectEnabled;
}
/** * {@inheritDoc} * <p> * The default value for this implementation is {@code true}.
*/
@Override publicboolean getValidateClientProvidedNewSessionId() { return validateClientProvidedNewSessionId;
}
@Override public String getEncodedPath() { return encodedPath;
}
/** * Set to <code>true</code> to allow requests mapped to servlets that do not explicitly declare @MultipartConfig or * have <multipart-config> specified in web.xml to parse multipart/form-data requests. * * @param allowCasualMultipartParsing <code>true</code> to allow such casual parsing, <code>false</code> otherwise.
*/
@Override publicvoid setAllowCasualMultipartParsing(boolean allowCasualMultipartParsing) { this.allowCasualMultipartParsing = allowCasualMultipartParsing;
}
/** * Returns <code>true</code> if requests mapped to servlets without "multipart config" to parse multipart/form-data * requests anyway. * * @return <code>true</code> if requests mapped to servlets without "multipart config" to parse multipart/form-data * requests, <code>false</code> otherwise.
*/
@Override publicboolean getAllowCasualMultipartParsing() { returnthis.allowCasualMultipartParsing;
}
/** * Set to <code>false</code> to disable request data swallowing after an upload was aborted due to size constraints. * * @param swallowAbortedUploads <code>false</code> to disable swallowing, <code>true</code> otherwise (default).
*/
@Override publicvoid setSwallowAbortedUploads(boolean swallowAbortedUploads) { this.swallowAbortedUploads = swallowAbortedUploads;
}
/** * Returns <code>true</code> if remaining request data will be read (swallowed) even the request violates a data * size constraint. * * @return <code>true</code> if data will be swallowed (default), <code>false</code> otherwise.
*/
@Override publicboolean getSwallowAbortedUploads() { returnthis.swallowAbortedUploads;
}
/** * Add a ServletContainerInitializer instance to this web application. * * @param sci The instance to add * @param classes The classes in which the initializer expressed an interest
*/
@Override publicvoid addServletContainerInitializer(ServletContainerInitializer sci, Set<Class<?>> classes) {
initializers.put(sci, classes);
}
/** * Return the "follow standard delegation model" flag used to configure our ClassLoader. * * @return <code>true</code> if classloading delegates to the parent classloader first
*/ publicboolean getDelegate() { returnthis.delegate;
}
/** * Set the "follow standard delegation model" flag used to configure our ClassLoader. * * @param delegate The new flag
*/ publicvoid setDelegate(boolean delegate) {
/** * @return true if the internal naming support is used.
*/ publicboolean isUseNaming() { return useNaming;
}
/** * Enables or disables naming. * * @param useNaming <code>true</code> to enable the naming environment
*/ publicvoid setUseNaming(boolean useNaming) { this.useNaming = useNaming;
}
@Override public Object[] getApplicationEventListeners() { return applicationEventListenersList.toArray();
}
/** * {@inheritDoc} Note that this implementation is not thread safe. If two threads call this method concurrently, the * result may be either set of listeners or a the union of both.
*/
@Override publicvoid setApplicationEventListeners(Object listeners[]) {
applicationEventListenersList.clear(); if (listeners != null && listeners.length > 0) {
applicationEventListenersList.addAll(Arrays.asList(listeners));
}
}
/** * Add a listener to the end of the list of initialized application event listeners. * * @param listener The listener to add
*/ publicvoid addApplicationEventListener(Object listener) {
applicationEventListenersList.add(listener);
}
@Override public Object[] getApplicationLifecycleListeners() { return applicationLifecycleListenersObjects;
}
/** * Store the set of initialized application lifecycle listener objects, in the order they were specified in the web * application deployment descriptor, for this application. * * @param listeners The set of instantiated listener objects.
*/
@Override publicvoid setApplicationLifecycleListeners(Object listeners[]) {
applicationLifecycleListenersObjects = listeners;
}
/** * Add a listener to the end of the list of initialized application lifecycle listeners. * * @param listener The listener to add
*/ publicvoid addApplicationLifecycleListener(Object listener) { int len = applicationLifecycleListenersObjects.length;
Object[] newListeners = Arrays.copyOf(applicationLifecycleListenersObjects, len + 1);
newListeners[len] = listener;
applicationLifecycleListenersObjects = newListeners;
}
/** * @return the antiResourceLocking flag for this Context.
*/ publicboolean getAntiResourceLocking() { returnthis.antiResourceLocking;
}
/** * Set the antiResourceLocking feature for this Context. * * @param antiResourceLocking The new flag value
*/ publicvoid setAntiResourceLocking(boolean antiResourceLocking) {
/** * @return the Locale to character set mapper for this Context.
*/ public CharsetMapper getCharsetMapper() {
// Create a mapper the first time it is requested if (this.charsetMapper == null) { try { Class<?> clazz = Class.forName(charsetMapperClass); this.charsetMapper = (CharsetMapper) clazz.getConstructor().newInstance();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t); this.charsetMapper = new CharsetMapper();
}
}
returnthis.charsetMapper;
}
/** * Set the Locale to character set mapper for this Context. * * @param mapper The new mapper
*/ publicvoid setCharsetMapper(CharsetMapper mapper) {
/** * Set the "correctly configured" flag for this Context. This can be set to false by startup listeners that detect a * fatal configuration error to avoid the application from being made available. * * @param configured The new correctly configured flag
*/
@Override publicvoid setConfigured(boolean configured) {
/** * Gets the name to use for session cookies. Overrides any setting that may be specified by the application. * * @return The value of the default session cookie name or null if not specified
*/
@Override public String getSessionCookieName() { return sessionCookieName;
}
/** * Sets the name to use for session cookies. Overrides any setting that may be specified by the application. * * @param sessionCookieName The name to use
*/
@Override publicvoid setSessionCookieName(String sessionCookieName) {
String oldSessionCookieName = this.sessionCookieName; this.sessionCookieName = sessionCookieName;
support.firePropertyChange("sessionCookieName", oldSessionCookieName, sessionCookieName);
}
/** * Gets the value of the use HttpOnly cookies for session cookies flag. * * @return <code>true</code> if the HttpOnly flag should be set on session cookies
*/
@Override publicboolean getUseHttpOnly() { return useHttpOnly;
}
/** * Sets the use HttpOnly cookies for session cookies flag. * * @param useHttpOnly Set to <code>true</code> to use HttpOnly cookies for session cookies
*/
@Override publicvoid setUseHttpOnly(boolean useHttpOnly) { boolean oldUseHttpOnly = this.useHttpOnly; this.useHttpOnly = useHttpOnly;
support.firePropertyChange("useHttpOnly", oldUseHttpOnly, this.useHttpOnly);
}
/** * Gets the domain to use for session cookies. Overrides any setting that may be specified by the application. * * @return The value of the default session cookie domain or null if not specified
*/
@Override public String getSessionCookieDomain() { return sessionCookieDomain;
}
/** * Sets the domain to use for session cookies. Overrides any setting that may be specified by the application. * * @param sessionCookieDomain The domain to use
*/
@Override publicvoid setSessionCookieDomain(String sessionCookieDomain) {
String oldSessionCookieDomain = this.sessionCookieDomain; this.sessionCookieDomain = sessionCookieDomain;
support.firePropertyChange("sessionCookieDomain", oldSessionCookieDomain, sessionCookieDomain);
}
/** * Gets the path to use for session cookies. Overrides any setting that may be specified by the application. * * @return The value of the default session cookie path or null if not specified
*/
@Override public String getSessionCookiePath() { return sessionCookiePath;
}
/** * Sets the path to use for session cookies. Overrides any setting that may be specified by the application. * * @param sessionCookiePath The path to use
*/
@Override publicvoid setSessionCookiePath(String sessionCookiePath) {
String oldSessionCookiePath = this.sessionCookiePath; this.sessionCookiePath = sessionCookiePath;
support.firePropertyChange("sessionCookiePath", oldSessionCookiePath, sessionCookiePath);
}
/** * Set the "allow crossing servlet contexts" flag. * * @param crossContext The new cross contexts flag
*/
@Override publicvoid setCrossContext(boolean crossContext) {
public String getDefaultContextXml() { return defaultContextXml;
}
/** * Set the location of the default context xml that will be used. If not absolute, it'll be made relative to the * engine's base dir ( which defaults to catalina.base system property ). * * @param defaultContextXml The default web xml
*/ publicvoid setDefaultContextXml(String defaultContextXml) { this.defaultContextXml = defaultContextXml;
}
public String getDefaultWebXml() { return defaultWebXml;
}
/** * Set the location of the default web xml that will be used. If not absolute, it'll be made relative to the * engine's base dir ( which defaults to catalina.base system property ). * * @param defaultWebXml The default web xml
*/ publicvoid setDefaultWebXml(String defaultWebXml) { this.defaultWebXml = defaultWebXml;
}
/** * Gets the time (in milliseconds) it took to start this context. * * @return Time (in milliseconds) it took to start this context.
*/ publiclong getStartupTime() { return startupTime;
}
/** * @return the display name of this web application.
*/
@Override public String getDisplayName() { returnthis.displayName;
}
/** * @return the alternate Deployment Descriptor name.
*/
@Override public String getAltDDName() { return altDDName;
}
/** * Set an alternate Deployment Descriptor name. * * @param altDDName The new name
*/
@Override publicvoid setAltDDName(String altDDName) { this.altDDName = altDDName; if (context != null) {
context.setAttribute(Globals.ALT_DD_ATTR, altDDName);
}
}
/** * Set the display name of this web application. * * @param displayName The new display name
*/
@Override publicvoid setDisplayName(String displayName) {
/** * @return the distributable flag for this web application.
*/
@Override publicboolean getDistributable() { returnthis.distributable;
}
/** * Set the distributable flag for this web application. * * @param distributable The new distributable flag
*/
@Override publicvoid setDistributable(boolean distributable) { boolean oldDistributable = this.distributable; this.distributable = distributable;
support.firePropertyChange("distributable", oldDistributable, this.distributable);
}
@Override public String getDocBase() { returnthis.docBase;
}
// Stop the old component if necessary if (oldManager instanceof Lifecycle) { try {
((Lifecycle) oldManager).stop();
((Lifecycle) oldManager).destroy();
} catch (LifecycleException e) {
log.error(sm.getString("standardContext.setManager.stop"), e);
}
}
// Start the new component if necessary if (manager != null) {
manager.setContext(this);
} if (getState().isAvailable() && manager instanceof Lifecycle) { try {
((Lifecycle) manager).start();
} catch (LifecycleException e) {
log.error(sm.getString("standardContext.setManager.start"), e);
}
}
} finally {
writeLock.unlock();
}
// Report this property change to interested listeners
support.firePropertyChange("manager", oldManager, manager);
}
/** * @return the boolean on the annotations parsing.
*/
@Override publicboolean getIgnoreAnnotations() { returnthis.ignoreAnnotations;
}
/** * Set the boolean on the annotations parsing for this web application. * * @param ignoreAnnotations The boolean on the annotations parsing
*/
@Override publicvoid setIgnoreAnnotations(boolean ignoreAnnotations) { boolean oldIgnoreAnnotations = this.ignoreAnnotations; this.ignoreAnnotations = ignoreAnnotations;
support.firePropertyChange("ignoreAnnotations", oldIgnoreAnnotations, this.ignoreAnnotations);
}
/** * @return the login configuration descriptor for this web application.
*/
@Override public LoginConfig getLoginConfig() { returnthis.loginConfig;
}
/** * Set the login configuration descriptor for this web application. * * @param config The new login configuration
*/
@Override publicvoid setLoginConfig(LoginConfig config) {
// Validate the incoming property value if (config == null) { thrownew IllegalArgumentException(sm.getString("standardContext.loginConfig.required"));
}
String loginPage = config.getLoginPage(); if ((loginPage != null) && !loginPage.startsWith("/")) { if (isServlet22()) { if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.loginConfig.loginWarning", loginPage));
}
config.setLoginPage("/" + loginPage);
} else { thrownew IllegalArgumentException(sm.getString("standardContext.loginConfig.loginPage", loginPage));
}
}
String errorPage = config.getErrorPage(); if ((errorPage != null) && !errorPage.startsWith("/")) { if (isServlet22()) { if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.loginConfig.errorWarning", errorPage));
}
config.setErrorPage("/" + errorPage);
} else { thrownew IllegalArgumentException(sm.getString("standardContext.loginConfig.errorPage", errorPage));
}
}
// Process the property setting change
LoginConfig oldLoginConfig = this.loginConfig; this.loginConfig = config;
support.firePropertyChange("loginConfig", oldLoginConfig, this.loginConfig);
}
/** * @return the naming resources associated with this web application.
*/
@Override public NamingResourcesImpl getNamingResources() { if (namingResources == null) {
setNamingResources(new NamingResourcesImpl());
} return namingResources;
}
/** * Set the naming resources for this web application. * * @param namingResources The new naming resources
*/
@Override publicvoid setNamingResources(NamingResourcesImpl namingResources) {
// Process the property setting change
NamingResourcesImpl oldNamingResources = this.namingResources; this.namingResources = namingResources; if (namingResources != null) {
namingResources.setContainer(this);
}
support.firePropertyChange("namingResources", oldNamingResources, this.namingResources);
if (getState() == LifecycleState.NEW || getState() == LifecycleState.INITIALIZING ||
getState() == LifecycleState.INITIALIZED) { // NEW will occur if Context is defined in server.xml // At this point getObjectKeyPropertiesNameOnly() will trigger an // NPE. // INITIALIZED will occur if the Context is defined in a context.xml // file // If started now, a second start will be attempted when the context // starts
// In both cases, return and let context init the namingResources // when it starts return;
}
/** * @return the context path for this Context.
*/
@Override public String getPath() { return path;
}
/** * Set the context path for this Context. * * @param path The new context path
*/
@Override publicvoid setPath(String path) { boolean invalid = false; if (path == null || path.equals("/")) {
invalid = true; this.path = "";
} elseif (path.isEmpty() || path.startsWith("/")) { this.path = path;
} else {
invalid = true; this.path = "/" + path;
} if (this.path.endsWith("/")) {
invalid = true; this.path = this.path.substring(0, this.path.length() - 1);
} if (invalid) {
log.warn(sm.getString("standardContext.pathInvalid", path, this.path));
}
encodedPath = URLEncoder.DEFAULT.encode(this.path, StandardCharsets.UTF_8); if (getName() == null) {
setName(this.path);
}
}
/** * @return the public identifier of the deployment descriptor DTD that is currently being parsed.
*/
@Override public String getPublicId() { returnthis.publicId;
}
/** * Set the public identifier of the deployment descriptor DTD that is currently being parsed. * * @param publicId The public identifier
*/
@Override publicvoid setPublicId(String publicId) {
if (log.isDebugEnabled()) {
log.debug("Setting deployment descriptor public ID to '" + publicId + "'");
}
/** * @return the reloadable flag for this web application.
*/
@Override publicboolean getReloadable() { returnthis.reloadable;
}
/** * @return the default context override flag for this web application.
*/
@Override publicboolean getOverride() { returnthis.override;
}
/** * @return the original document root for this Context. This can be an absolute pathname, a relative pathname, or a * URL. Is only set as deployment has change docRoot!
*/ public String getOriginalDocBase() { returnthis.originalDocBase;
}
/** * Set the original document root for this Context. This can be an absolute pathname, a relative pathname, or a URL. * * @param docBase The original document root
*/ publicvoid setOriginalDocBase(String docBase) {
this.originalDocBase = docBase;
}
/** * @return the parent class loader (if any) for this web application. This call is meaningful only * <strong>after</strong> a Loader has been configured.
*/
@Override public ClassLoader getParentClassLoader() { if (parentClassLoader != null) { return parentClassLoader;
} if (getPrivileged()) { returnthis.getClass().getClassLoader();
} elseif (parent != null) { return parent.getParentClassLoader();
} return ClassLoader.getSystemClassLoader();
}
/** * @return the privileged flag for this web application.
*/
@Override publicboolean getPrivileged() { returnthis.privileged;
}
/** * Set the privileged flag for this web application. * * @param privileged The new privileged flag
*/
@Override publicvoid setPrivileged(boolean privileged) {
/** * Set the reloadable flag for this web application. * * @param reloadable The new reloadable flag
*/
@Override publicvoid setReloadable(boolean reloadable) {
/** * Set the default context override flag for this web application. * * @param override The new override flag
*/
@Override publicvoid setOverride(boolean override) {
/** * Set the "replace welcome files" property. * * @param replaceWelcomeFiles The new property value
*/ publicvoid setReplaceWelcomeFiles(boolean replaceWelcomeFiles) {
/** * @return the servlet context for which this Context is a facade.
*/
@Override public ServletContext getServletContext() { /* * This method is called (multiple times) during context start which is single threaded so there is concurrency * issue here.
*/ if (context == null) {
context = new ApplicationContext(this); if (altDDName != null) {
context.setAttribute(Globals.ALT_DD_ATTR, altDDName);
}
} return context.getFacade();
}
/** * @return the default session timeout (in minutes) for this web application.
*/
@Override publicint getSessionTimeout() { returnthis.sessionTimeout;
}
/** * Set the default session timeout (in minutes) for this web application. * * @param timeout The new default session timeout
*/
@Override publicvoid setSessionTimeout(int timeout) {
int oldSessionTimeout = this.sessionTimeout; /* * SRV.13.4 ("Deployment Descriptor"): If the timeout is 0 or less, the container ensures the default behaviour * of sessions is never to time out.
*/ this.sessionTimeout = (timeout == 0) ? -1 : timeout;
support.firePropertyChange("sessionTimeout", oldSessionTimeout, this.sessionTimeout);
}
/** * @return the value of the swallowOutput flag.
*/
@Override publicboolean getSwallowOutput() { returnthis.swallowOutput;
}
/** * Set the value of the swallowOutput flag. If set to true, the system.out and system.err will be redirected to the * logger during a servlet execution. * * @param swallowOutput The new value
*/
@Override publicvoid setSwallowOutput(boolean swallowOutput) {
/** * @return the value of the unloadDelay flag.
*/ publiclong getUnloadDelay() { returnthis.unloadDelay;
}
/** * Set the value of the unloadDelay flag, which represents the amount of ms that the container will wait when * unloading servlets. Setting this to a small value may cause more requests to fail to complete when stopping a web * application. * * @param unloadDelay The new value
*/ publicvoid setUnloadDelay(long unloadDelay) {
long oldUnloadDelay = this.unloadDelay; this.unloadDelay = unloadDelay;
support.firePropertyChange("unloadDelay", Long.valueOf(oldUnloadDelay), Long.valueOf(this.unloadDelay));
/** * Unpack WAR flag mutator. * * @param unpackWAR <code>true</code> to unpack WARs on deployment
*/ publicvoid setUnpackWAR(boolean unpackWAR) { this.unpackWAR = unpackWAR;
}
/** * Flag which indicates if bundled context.xml files should be copied to the config folder. The doesn't occur by * default. * * @return <code>true</code> if the <code>META-INF/context.xml</code> file included in a WAR will be copied to the * host configuration base folder on deployment
*/ publicboolean getCopyXML() { return copyXML;
}
/** * Allows copying a bundled context.xml file to the host configuration base folder on deployment. * * @param copyXML the new flag value
*/ publicvoid setCopyXML(boolean copyXML) { this.copyXML = copyXML;
}
/** * @return the Java class name of the Wrapper implementation used for servlets registered in this Context.
*/
@Override public String getWrapperClass() { returnthis.wrapperClassName;
}
/** * Set the Java class name of the Wrapper implementation used for servlets registered in this Context. * * @param wrapperClassName The new wrapper class name * * @throws IllegalArgumentException if the specified wrapper class cannot be found or is not a subclass of * StandardWrapper
*/
@Override publicvoid setWrapperClass(String wrapperClassName) {
// ------------------------------------------------------ Public Properties
/** * @return whether or not an attempt to modify the JNDI context will trigger an exception or if the request will be * ignored.
*/ publicboolean getJndiExceptionOnFailedWrite() { return jndiExceptionOnFailedWrite;
}
/** * Controls whether or not an attempt to modify the JNDI context will trigger an exception or if the request will be * ignored. * * @param jndiExceptionOnFailedWrite <code>false</code> to avoid an exception
*/ publicvoid setJndiExceptionOnFailedWrite(boolean jndiExceptionOnFailedWrite) { this.jndiExceptionOnFailedWrite = jndiExceptionOnFailedWrite;
}
/** * @return the Locale to character set mapper class for this Context.
*/ public String getCharsetMapperClass() { returnthis.charsetMapperClass;
}
/** * Set the Locale to character set mapper class for this Context. * * @param mapper The new mapper class
*/ publicvoid setCharsetMapperClass(String mapper) {
/** * Get the absolute path to the work dir. To avoid duplication. * * @return The work path
*/ public String getWorkPath() { if (getWorkDir() == null) { returnnull;
}
File workDir = new File(getWorkDir()); if (!workDir.isAbsolute()) { try {
workDir = new File(getCatalinaBase().getCanonicalFile(), getWorkDir());
} catch (IOException e) {
log.warn(sm.getString("standardContext.workPath", getName()), e);
}
} return workDir.getAbsolutePath();
}
/** * @return the work directory for this Context.
*/ public String getWorkDir() { returnthis.workDir;
}
/** * Set the work directory for this Context. * * @param workDir The new work directory
*/ publicvoid setWorkDir(String workDir) {
this.workDir = workDir;
if (getState().isAvailable()) {
postWorkDirectory();
}
}
/** * @return the clearReferencesStopThreads flag for this Context.
*/ publicboolean getClearReferencesStopThreads() { returnthis.clearReferencesStopThreads;
}
/** * Set the clearReferencesStopThreads feature for this Context. * * @param clearReferencesStopThreads The new flag value
*/ publicvoid setClearReferencesStopThreads(boolean clearReferencesStopThreads) {
/** * @return the clearReferencesStopTimerThreads flag for this Context.
*/ publicboolean getClearReferencesStopTimerThreads() { returnthis.clearReferencesStopTimerThreads;
}
/** * Set the clearReferencesStopTimerThreads feature for this Context. * * @param clearReferencesStopTimerThreads The new flag value
*/ publicvoid setClearReferencesStopTimerThreads(boolean clearReferencesStopTimerThreads) {
/** * @return the clearReferencesHttpClientKeepAliveThread flag for this Context.
*/ publicboolean getClearReferencesHttpClientKeepAliveThread() { returnthis.clearReferencesHttpClientKeepAliveThread;
}
/** * Set the clearReferencesHttpClientKeepAliveThread feature for this Context. * * @param clearReferencesHttpClientKeepAliveThread The new flag value
*/ publicvoid setClearReferencesHttpClientKeepAliveThread(boolean clearReferencesHttpClientKeepAliveThread) { this.clearReferencesHttpClientKeepAliveThread = clearReferencesHttpClientKeepAliveThread;
}
/** * Add a new Listener class name to the set of Listeners configured for this application. * * @param listener Java class name of a listener class
*/
@Override publicvoid addApplicationListener(String listener) { if (applicationListeners.addIfAbsent(listener)) {
fireContainerEvent("addApplicationListener", listener);
} else {
log.info(sm.getString("standardContext.duplicateListener", listener));
}
}
/** * Add a new application parameter for this application. * * @param parameter The new application parameter
*/
@Override publicvoid addApplicationParameter(ApplicationParameter parameter) {
/** * Add a child Container, only if the proposed child is an implementation of Wrapper. * * @param child Child container to be added * * @exception IllegalArgumentException if the proposed container is not an implementation of Wrapper
*/
@Override publicvoid addChild(Container child) {
// Global JspServlet
Wrapper oldJspServlet = null;
if (!(child instanceof Wrapper)) { thrownew IllegalArgumentException(sm.getString("standardContext.notWrapper"));
}
// Allow webapp to override JspServlet inherited from global web.xml. if (isJspServlet) {
oldJspServlet = (Wrapper) findChild("jsp"); if (oldJspServlet != null) {
removeChild(oldJspServlet);
}
}
super.addChild(child);
if (isJspServlet && oldJspServlet != null) { /* * The webapp-specific JspServlet inherits all the mappings specified in the global web.xml, and may add * additional ones.
*/
String[] jspMappings = oldJspServlet.findMappings(); for (int i = 0; jspMappings != null && i < jspMappings.length; i++) {
addServletMappingDecoded(jspMappings[i], child.getName());
}
}
}
/** * Add a security constraint to the set for this web application. * * @param constraint the new security constraint
*/
@Override publicvoid addConstraint(SecurityConstraint constraint) {
// Add this constraint to the set for our web application synchronized (constraintsLock) {
SecurityConstraint[] results = Arrays.copyOf(constraints, constraints.length + 1);
results[constraints.length] = constraint;
constraints = results;
}
}
/** * Add an error page for the specified error or Java exception. * * @param errorPage The error page definition to be added
*/
@Override publicvoid addErrorPage(ErrorPage errorPage) { // Validate the input parameters if (errorPage == null) { thrownew IllegalArgumentException(sm.getString("standardContext.errorPage.required"));
}
String location = errorPage.getLocation(); if ((location != null) && !location.startsWith("/")) { if (isServlet22()) { if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.errorPage.warning", location));
}
errorPage.setLocation("/" + location);
} else { thrownew IllegalArgumentException(sm.getString("standardContext.errorPage.error", location));
}
}
/** * Add a filter definition to this Context. * * @param filterDef The filter definition to be added
*/
@Override publicvoid addFilterDef(FilterDef filterDef) {
/** * Add a filter mapping to this Context at the end of the current set of filter mappings. * * @param filterMap The filter mapping to be added * * @exception IllegalArgumentException if the specified filter name does not match an existing filter definition, or * the filter mapping is malformed
*/
@Override publicvoid addFilterMap(FilterMap filterMap) {
validateFilterMap(filterMap); // Add this filter mapping to our registered set
filterMaps.add(filterMap);
fireContainerEvent("addFilterMap", filterMap);
}
/** * Add a filter mapping to this Context before the mappings defined in the deployment descriptor but after any other * mappings added via this method. * * @param filterMap The filter mapping to be added * * @exception IllegalArgumentException if the specified filter name does not match an existing filter definition, or * the filter mapping is malformed
*/
@Override publicvoid addFilterMapBefore(FilterMap filterMap) {
validateFilterMap(filterMap); // Add this filter mapping to our registered set
filterMaps.addBefore(filterMap);
fireContainerEvent("addFilterMap", filterMap);
}
/** * Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4) * * @param locale locale to map an encoding for * @param encoding encoding to be used for a give locale
*/
@Override publicvoid addLocaleEncodingMappingParameter(String locale, String encoding) {
getCharsetMapper().addCharsetMappingFromDeploymentDescriptor(locale, encoding);
}
/** * Add a message destination for this web application. * * @param md New message destination
*/ publicvoid addMessageDestination(MessageDestination md) {
/** * Add a new context initialization parameter. * * @param name Name of the new parameter * @param value Value of the new parameter * * @exception IllegalArgumentException if the name or value is missing, or if this context initialization parameter * has already been registered
*/
@Override publicvoid addParameter(String name, String value) { // Validate the proposed context initialization parameter if (name == null || value == null) { thrownew IllegalArgumentException(sm.getString("standardContext.parameter.required"));
}
// Add this parameter to our defined set if not already present
String oldValue = parameters.putIfAbsent(name, value);
if (oldValue != null) { thrownew IllegalArgumentException(sm.getString("standardContext.parameter.duplicate", name));
}
fireContainerEvent("addParameter", name);
}
/** * Add a security role reference for this web application. * * @param role Security role used in the application * @param link Actual security role to check for
*/
@Override publicvoid addRoleMapping(String role, String link) {
/** * Add a new servlet mapping, replacing any existing mapping for the specified pattern. * * @param pattern URL pattern to be mapped * @param name Name of the corresponding servlet to execute * @param jspWildCard true if name identifies the JspServlet and pattern contains a wildcard; false otherwise * * @exception IllegalArgumentException if the specified servlet name is not known to this Context
*/
@Override publicvoid addServletMappingDecoded(String pattern, String name, boolean jspWildCard) { // Validate the proposed mapping if (findChild(name) == null) { thrownew IllegalArgumentException(sm.getString("standardContext.servletMap.name", name));
}
String adjustedPattern = adjustURLPattern(pattern); if (!validateURLPattern(adjustedPattern)) { thrownew IllegalArgumentException(sm.getString("standardContext.servletMap.pattern", adjustedPattern));
}
// Add this mapping to our registered set synchronized (servletMappingsLock) {
String name2 = servletMappings.get(adjustedPattern); if (name2 != null) { // Don't allow more than one servlet on the same pattern
Wrapper wrapper = (Wrapper) findChild(name2);
wrapper.removeMapping(adjustedPattern);
}
servletMappings.put(adjustedPattern, name);
}
Wrapper wrapper = (Wrapper) findChild(name);
wrapper.addMapping(adjustedPattern);
/** * Add a new watched resource to the set recognized by this Context. * * @param name New watched resource file name
*/
@Override publicvoid addWatchedResource(String name) {
/** * Add a new welcome file to the set recognized by this Context. * * @param name New welcome file name
*/
@Override publicvoid addWelcomeFile(String name) {
synchronized (welcomeFilesLock) { // Welcome files from the application deployment descriptor // completely replace those from the default conf/web.xml file if (replaceWelcomeFiles) {
fireContainerEvent(CLEAR_WELCOME_FILES_EVENT, null);
welcomeFiles = new String[0];
setReplaceWelcomeFiles(false);
}
String[] results = Arrays.copyOf(welcomeFiles, welcomeFiles.length + 1);
results[welcomeFiles.length] = name;
welcomeFiles = results;
} if (this.getState().equals(LifecycleState.STARTED)) {
fireContainerEvent(ADD_WELCOME_FILE_EVENT, name);
}
}
/** * Add the classname of a LifecycleListener to be added to each Wrapper appended to this Context. * * @param listener Java class name of a LifecycleListener class
*/
@Override publicvoid addWrapperLifecycle(String listener) {
/** * Add the classname of a ContainerListener to be added to each Wrapper appended to this Context. * * @param listener Java class name of a ContainerListener class
*/
@Override publicvoid addWrapperListener(String listener) {
/** * Factory method to create and return a new Wrapper instance, of the Java implementation class appropriate for this * Context implementation. The constructor of the instantiated Wrapper will have been called, but no properties will * have been set.
*/
@Override public Wrapper createWrapper() {
/** * Return the set of application listener class names configured for this application.
*/
@Override public String[] findApplicationListeners() { return applicationListeners.toArray(new String[0]);
}
/** * Return the set of application parameters for this application.
*/
@Override public ApplicationParameter[] findApplicationParameters() {
/** * Return the security constraints for this web application. If there are none, a zero-length array is returned.
*/
@Override public SecurityConstraint[] findConstraints() { return constraints;
}
/** * Return the error page entry for the specified HTTP error code, if any; otherwise return <code>null</code>. * * @param errorCode Error code to look up
*/
@Override public ErrorPage findErrorPage(int errorCode) { return errorPageSupport.find(errorCode);
}
@Override public ErrorPage findErrorPage(Throwable exceptionType) { return errorPageSupport.find(exceptionType);
}
/** * Return the set of defined error pages for all specified error codes and exception types.
*/
@Override public ErrorPage[] findErrorPages() { return errorPageSupport.findAll();
}
/** * Return the filter definition for the specified filter name, if any; otherwise return <code>null</code>. * * @param filterName Filter name to look up
*/
@Override public FilterDef findFilterDef(String filterName) { synchronized (filterDefs) { return filterDefs.get(filterName);
}
}
/** * @return the set of defined filters for this Context.
*/
@Override public FilterDef[] findFilterDefs() { synchronized (filterDefs) { return filterDefs.values().toArray(new FilterDef[0]);
}
}
/** * @return the set of filter mappings for this Context.
*/
@Override public FilterMap[] findFilterMaps() { return filterMaps.asArray();
}
/** * @return the message destination with the specified name, if any; otherwise, return <code>null</code>. * * @param name Name of the desired message destination
*/ public MessageDestination findMessageDestination(String name) { synchronized (messageDestinations) { return messageDestinations.get(name);
}
}
/** * @return the set of defined message destinations for this web application. If none have been defined, a * zero-length array is returned.
*/ public MessageDestination[] findMessageDestinations() { synchronized (messageDestinations) { return messageDestinations.values().toArray(new MessageDestination[0]);
}
}
/** * @return the MIME type to which the specified extension is mapped, if any; otherwise return <code>null</code>. * * @param extension Extension to map to a MIME type
*/
@Override public String findMimeMapping(String extension) { return mimeMappings.get(extension.toLowerCase(Locale.ENGLISH));
}
/** * @return the extensions for which MIME mappings are defined. If there are none, a zero-length array is returned.
*/
@Override public String[] findMimeMappings() { synchronized (mimeMappings) { return mimeMappings.keySet().toArray(new String[0]);
}
}
/** * @return the value for the specified context initialization parameter name, if any; otherwise return * <code>null</code>. * * @param name Name of the parameter to return
*/
@Override public String findParameter(String name) { return parameters.get(name);
}
/** * @return the names of all defined context initialization parameters for this Context. If no parameters are * defined, a zero-length array is returned.
*/
@Override public String[] findParameters() { return parameters.keySet().toArray(new String[0]);
}
/** * For the given security role (as used by an application), return the corresponding role name (as defined by the * underlying Realm) if there is one. Otherwise, return the specified role unchanged. * * @param role Security role to map * * @return the role name
*/
@Override public String findRoleMapping(String role) {
String realRole = null; synchronized (roleMappings) {
realRole = roleMappings.get(role);
} if (realRole != null) { return realRole;
} else { return role;
}
}
/** * @return <code>true</code> if the specified security role is defined for this application; otherwise return * <code>false</code>. * * @param role Security role to verify
*/
@Override publicboolean findSecurityRole(String role) {
synchronized (securityRolesLock) { for (String securityRole : securityRoles) { if (role.equals(securityRole)) { returntrue;
}
}
} returnfalse;
}
/** * @return the security roles defined for this application. If none have been defined, a zero-length array is * returned.
*/
@Override public String[] findSecurityRoles() { synchronized (securityRolesLock) { return securityRoles;
}
}
/** * @return the servlet name mapped by the specified pattern (if any); otherwise return <code>null</code>. * * @param pattern Pattern for which a mapping is requested
*/
@Override public String findServletMapping(String pattern) { synchronized (servletMappingsLock) { return servletMappings.get(pattern);
}
}
/** * @return the patterns of all defined servlet mappings for this Context. If no mappings are defined, a zero-length * array is returned.
*/
@Override public String[] findServletMappings() { synchronized (servletMappingsLock) { return servletMappings.keySet().toArray(new String[0]);
}
}
/** * @return <code>true</code> if the specified welcome file is defined for this Context; otherwise return * <code>false</code>. * * @param name Welcome file to verify
*/
@Override publicboolean findWelcomeFile(String name) {
synchronized (welcomeFilesLock) { for (String welcomeFile : welcomeFiles) { if (name.equals(welcomeFile)) { returntrue;
}
}
} returnfalse;
}
/** * @return the set of watched resources for this Context. If none are defined, a zero length array will be returned.
*/
@Override public String[] findWatchedResources() { synchronized (watchedResourcesLock) { return watchedResources;
}
}
/** * @return the set of welcome files defined for this Context. If none are defined, a zero-length array is returned.
*/
@Override public String[] findWelcomeFiles() { synchronized (welcomeFilesLock) { return welcomeFiles;
}
}
/** * @return the set of LifecycleListener classes that will be added to newly created Wrappers automatically.
*/
@Override public String[] findWrapperLifecycles() { synchronized (wrapperLifecyclesLock) { return wrapperLifecycles;
}
}
/** * @return the set of ContainerListener classes that will be added to newly created Wrappers automatically.
*/
@Override public String[] findWrapperListeners() { synchronized (wrapperListenersLock) { return wrapperListeners;
}
}
/** * Reload this web application, if reloading is supported. * <p> * <b>IMPLEMENTATION NOTE</b>: This method is designed to deal with reloads required by changes to classes in the * underlying repositories of our class loader and changes to the web.xml file. It does not handle changes to any * context.xml file. If the context.xml has changed, you should stop this Context and create (and start) a new * Context instance instead. Note that there is additional code in <code>CoyoteAdapter#postParseRequest()</code> to * handle mapping requests to paused Contexts. * * @exception IllegalStateException if the <code>reloadable</code> property is set to <code>false</code>.
*/
@Override publicsynchronizedvoid reload() {
// Validate our current component state if (!getState().isAvailable()) { thrownew IllegalStateException(sm.getString("standardContext.notStarted", getName()));
}
if (log.isInfoEnabled()) {
log.info(sm.getString("standardContext.reloadingStarted", getName()));
}
if (log.isInfoEnabled()) {
log.info(sm.getString("standardContext.reloadingCompleted", getName()));
}
}
/** * Remove the specified application listener class from the set of listeners for this application. * * @param listener Java class name of the listener to be removed
*/
@Override publicvoid removeApplicationListener(String listener) { if (applicationListeners.remove(listener)) { // Inform interested listeners if the specified listener was present and has been removed
fireContainerEvent("removeApplicationListener", listener);
}
}
/** * Remove the application parameter with the specified name from the set for this application. * * @param name Name of the application parameter to remove
*/
@Override publicvoid removeApplicationParameter(String name) {
synchronized (applicationParametersLock) {
// Make sure this parameter is currently present int n = -1; for (int i = 0; i < applicationParameters.length; i++) { if (name.equals(applicationParameters[i].getName())) {
n = i; break;
}
} if (n < 0) { return;
}
// Remove the specified parameter int j = 0;
ApplicationParameter results[] = new ApplicationParameter[applicationParameters.length - 1]; for (int i = 0; i < applicationParameters.length; i++) { if (i != n) {
results[j++] = applicationParameters[i];
}
}
applicationParameters = results;
/** * Add a child Container, only if the proposed child is an implementation of Wrapper. * * @param child Child container to be added * * @exception IllegalArgumentException if the proposed container is not an implementation of Wrapper
*/
@Override publicvoid removeChild(Container child) {
if (!(child instanceof Wrapper)) { thrownew IllegalArgumentException(sm.getString("standardContext.notWrapper"));
}
super.removeChild(child);
}
/** * Remove the specified security constraint from this web application. * * @param constraint Constraint to be removed
*/
@Override publicvoid removeConstraint(SecurityConstraint constraint) {
synchronized (constraintsLock) {
// Make sure this constraint is currently present int n = -1; for (int i = 0; i < constraints.length; i++) { if (constraints[i].equals(constraint)) {
n = i; break;
}
} if (n < 0) { return;
}
// Remove the specified constraint int j = 0;
SecurityConstraint results[] = new SecurityConstraint[constraints.length - 1]; for (int i = 0; i < constraints.length; i++) { if (i != n) {
results[j++] = constraints[i];
}
}
constraints = results;
/** * Remove the error page for the specified error code or Java language exception, if it exists; otherwise, no action * is taken. * * @param errorPage The error page definition to be removed
*/
@Override publicvoid removeErrorPage(ErrorPage errorPage) {
errorPageSupport.remove(errorPage);
fireContainerEvent("removeErrorPage", errorPage);
}
/** * Remove the specified filter definition from this Context, if it exists; otherwise, no action is taken. * * @param filterDef Filter definition to be removed
*/
@Override publicvoid removeFilterDef(FilterDef filterDef) {
/** * Remove a filter mapping from this Context. * * @param filterMap The filter mapping to be removed
*/
@Override publicvoid removeFilterMap(FilterMap filterMap) {
filterMaps.remove(filterMap); // Inform interested listeners
fireContainerEvent("removeFilterMap", filterMap);
}
/** * Remove any message destination with the specified name. * * @param name Name of the message destination to remove
*/ publicvoid removeMessageDestination(String name) {
/** * Remove the MIME mapping for the specified extension, if it exists; otherwise, no action is taken. * * @param extension Extension to remove the mapping for
*/
@Override publicvoid removeMimeMapping(String extension) {
/** * Remove the context initialization parameter with the specified name, if it exists; otherwise, no action is taken. * * @param name Name of the parameter to remove
*/
@Override publicvoid removeParameter(String name) {
parameters.remove(name);
fireContainerEvent("removeParameter", name);
}
/** * Remove any security role reference for the specified name * * @param role Security role (as used in the application) to remove
*/
@Override publicvoid removeRoleMapping(String role) {
/** * Remove any security role with the specified name. * * @param role Security role to remove
*/
@Override publicvoid removeSecurityRole(String role) {
synchronized (securityRolesLock) {
// Make sure this security role is currently present int n = -1; for (int i = 0; i < securityRoles.length; i++) { if (role.equals(securityRoles[i])) {
n = i; break;
}
} if (n < 0) { return;
}
// Remove the specified security role int j = 0;
String results[] = new String[securityRoles.length - 1]; for (int i = 0; i < securityRoles.length; i++) { if (i != n) {
results[j++] = securityRoles[i];
}
}
securityRoles = results;
/** * Remove any servlet mapping for the specified pattern, if it exists; otherwise, no action is taken. * * @param pattern URL pattern of the mapping to remove
*/
@Override publicvoid removeServletMapping(String pattern) {
String name = null; synchronized (servletMappingsLock) {
name = servletMappings.remove(pattern);
}
Wrapper wrapper = (Wrapper) findChild(name); if (wrapper != null) {
wrapper.removeMapping(pattern);
}
fireContainerEvent("removeServletMapping", pattern);
}
/** * Remove the specified watched resource name from the list associated with this Context. * * @param name Name of the watched resource to be removed
*/
@Override publicvoid removeWatchedResource(String name) {
synchronized (watchedResourcesLock) {
// Make sure this watched resource is currently present int n = -1; for (int i = 0; i < watchedResources.length; i++) { if (watchedResources[i].equals(name)) {
n = i; break;
}
} if (n < 0) { return;
}
// Remove the specified watched resource int j = 0;
String results[] = new String[watchedResources.length - 1]; for (int i = 0; i < watchedResources.length; i++) { if (i != n) {
results[j++] = watchedResources[i];
}
}
watchedResources = results;
/** * Remove the specified welcome file name from the list recognized by this Context. * * @param name Name of the welcome file to be removed
*/
@Override publicvoid removeWelcomeFile(String name) {
synchronized (welcomeFilesLock) {
// Make sure this welcome file is currently present int n = -1; for (int i = 0; i < welcomeFiles.length; i++) { if (welcomeFiles[i].equals(name)) {
n = i; break;
}
} if (n < 0) { return;
}
// Remove the specified welcome file int j = 0;
String results[] = new String[welcomeFiles.length - 1]; for (int i = 0; i < welcomeFiles.length; i++) { if (i != n) {
results[j++] = welcomeFiles[i];
}
}
welcomeFiles = results;
}
// Inform interested listeners if (this.getState().equals(LifecycleState.STARTED)) {
fireContainerEvent(REMOVE_WELCOME_FILE_EVENT, name);
}
}
/** * Remove a class name from the set of LifecycleListener classes that will be added to newly created Wrappers. * * @param listener Class name of a LifecycleListener class to be removed
*/
@Override publicvoid removeWrapperLifecycle(String listener) {
synchronized (wrapperLifecyclesLock) {
// Make sure this lifecycle listener is currently present int n = -1; for (int i = 0; i < wrapperLifecycles.length; i++) { if (wrapperLifecycles[i].equals(listener)) {
n = i; break;
}
} if (n < 0) { return;
}
// Remove the specified lifecycle listener int j = 0;
String results[] = new String[wrapperLifecycles.length - 1]; for (int i = 0; i < wrapperLifecycles.length; i++) { if (i != n) {
results[j++] = wrapperLifecycles[i];
}
}
wrapperLifecycles = results;
/** * Remove a class name from the set of ContainerListener classes that will be added to newly created Wrappers. * * @param listener Class name of a ContainerListener class to be removed
*/
@Override publicvoid removeWrapperListener(String listener) {
synchronized (wrapperListenersLock) {
// Make sure this listener is currently present int n = -1; for (int i = 0; i < wrapperListeners.length; i++) { if (wrapperListeners[i].equals(listener)) {
n = i; break;
}
} if (n < 0) { return;
}
// Remove the specified listener int j = 0;
String results[] = new String[wrapperListeners.length - 1]; for (int i = 0; i < wrapperListeners.length; i++) { if (i != n) {
results[j++] = wrapperListeners[i];
}
}
wrapperListeners = results;
/** * Gets the cumulative processing times of all servlets in this StandardContext. * * @return Cumulative processing times of all servlets in this StandardContext
*/ publiclong getProcessingTime() {
long result = 0;
Container[] children = findChildren(); if (children != null) { for (Container child : children) {
result += ((StandardWrapper) child).getProcessingTime();
}
}
return result;
}
/** * Gets the maximum processing time of all servlets in this StandardContext. * * @return Maximum processing time of all servlets in this StandardContext
*/ publiclong getMaxTime() {
long result = 0; long time;
Container[] children = findChildren(); if (children != null) { for (Container child : children) {
time = ((StandardWrapper) child).getMaxTime(); if (time > result) {
result = time;
}
}
}
return result;
}
/** * Gets the minimum processing time of all servlets in this StandardContext. * * @return Minimum processing time of all servlets in this StandardContext
*/ publiclong getMinTime() {
long result = -1; long time;
Container[] children = findChildren(); if (children != null) { for (Container child : children) {
time = ((StandardWrapper) child).getMinTime(); if (result < 0 || time < result) {
result = time;
}
}
}
return result;
}
/** * Gets the cumulative request count of all servlets in this StandardContext. * * @return Cumulative request count of all servlets in this StandardContext * * @deprecated The return type will change to long in Tomcat 11 onwards. Callers of this method should switch to * storing the result of calls to this method in a long value rather than an int.
*/
@Deprecated publicint getRequestCount() {
int result = 0;
Container[] children = findChildren(); if (children != null) { for (Container child : children) {
result += ((StandardWrapper) child).getRequestCount();
}
}
return result;
}
/** * Gets the cumulative error count of all servlets in this StandardContext. * * @return Cumulative error count of all servlets in this StandardContext * * @deprecated The return type will change to long in Tomcat 11 onwards. Callers of this method should switch to * storing the result of calls to this method in a long value rather than an int.
*/
@Deprecated publicint getErrorCount() {
int result = 0;
Container[] children = findChildren(); if (children != null) { for (Container child : children) {
result += ((StandardWrapper) child).getErrorCount();
}
}
return result;
}
/** * Return the real path for a given virtual path, if possible; otherwise return <code>null</code>. * * @param path The path to the desired resource
*/
@Override public String getRealPath(String path) { // The WebResources API expects all paths to start with /. This is a // special case for consistency with earlier Tomcat versions. if ("".equals(path)) {
path = "/";
} if (resources != null) { try {
WebResource resource = resources.getResource(path);
String canonicalPath = resource.getCanonicalPath(); if (canonicalPath == null) { returnnull;
} elseif ((resource.isDirectory() && !canonicalPath.endsWith(File.separator) || !resource.exists()) &&
path.endsWith("/")) { return canonicalPath + File.separatorChar;
} else { return canonicalPath;
}
} catch (IllegalArgumentException iae) { // ServletContext.getRealPath() does not allow this to be thrown
}
} returnnull;
}
/** * Hook to track which Servlets were created via {@link ServletContext#createServlet(Class)}. * * @param servlet the created Servlet
*/ publicvoid dynamicServletCreated(Servlet servlet) {
createdServlets.add(servlet);
}
/** * A helper class to manage the filter mappings in a Context.
*/ privatestaticfinalclass ContextFilterMaps { privatefinal Object lock = new Object();
/** * The set of filter mappings for this application, in the order they were defined in the deployment descriptor * with additional mappings added via the {@link ServletContext} possibly both before and after those defined in * the deployment descriptor.
*/ private FilterMap[] array = new FilterMap[0];
/** * Filter mappings added via {@link ServletContext} may have to be inserted before the mappings in the * deployment descriptor but must be inserted in the order the {@link ServletContext} methods are called. This * isn't an issue for the mappings added after the deployment descriptor - they are just added to the end - but * correctly the adding mappings before the deployment descriptor mappings requires knowing where the last * 'before' mapping was added.
*/ privateint insertPoint = 0;
/** * @return The set of filter mappings
*/ public FilterMap[] asArray() { synchronized (lock) { return array;
}
}
/** * Add a filter mapping at the end of the current set of filter mappings. * * @param filterMap The filter mapping to be added
*/ publicvoid add(FilterMap filterMap) { synchronized (lock) {
FilterMap results[] = Arrays.copyOf(array, array.length + 1);
results[array.length] = filterMap;
array = results;
}
}
/** * Add a filter mapping before the mappings defined in the deployment descriptor but after any other mappings * added via this method. * * @param filterMap The filter mapping to be added
*/ publicvoid addBefore(FilterMap filterMap) { synchronized (lock) {
FilterMap results[] = new FilterMap[array.length + 1];
System.arraycopy(array, 0, results, 0, insertPoint);
System.arraycopy(array, insertPoint, results, insertPoint + 1, array.length - insertPoint);
results[insertPoint] = filterMap;
array = results;
insertPoint++;
}
}
/** * Remove a filter mapping. * * @param filterMap The filter mapping to be removed
*/ publicvoid remove(FilterMap filterMap) { synchronized (lock) { // Make sure this filter mapping is currently present int n = -1; for (int i = 0; i < array.length; i++) { if (array[i] == filterMap) {
n = i; break;
}
} if (n < 0) { return;
}
// Remove the specified filter mapping
FilterMap results[] = new FilterMap[array.length - 1];
System.arraycopy(array, 0, results, 0, n);
System.arraycopy(array, n + 1, results, n, (array.length - 1) - n);
array = results; if (n < insertPoint) {
insertPoint--;
}
}
}
}
// --------------------------------------------------------- Public Methods
/** * Configure and initialize the set of filters for this Context. * * @return <code>true</code> if all filter initialization completed successfully, or <code>false</code> otherwise.
*/ publicboolean filterStart() {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Starting filters");
} // Instantiate and record a FilterConfig for each defined filter boolean ok = true; synchronized (filterDefs) {
filterConfigs.clear(); for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
String name = entry.getKey(); if (getLogger().isDebugEnabled()) {
getLogger().debug(" Starting filter '" + name + "'");
} try {
ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue());
filterConfigs.put(name, filterConfig);
} catch (Throwable t) {
t = ExceptionUtils.unwrapInvocationTargetException(t);
ExceptionUtils.handleThrowable(t);
getLogger().error(sm.getString("standardContext.filterStart", name), t);
ok = false;
}
}
}
return ok;
}
/** * Finalize and release the set of filters for this Context. * * @return <code>true</code> if all filter finalization completed successfully, or <code>false</code> otherwise.
*/ publicboolean filterStop() {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Stopping filters");
}
// Release all Filter and FilterConfig instances synchronized (filterDefs) { for (Entry<String,ApplicationFilterConfig> entry : filterConfigs.entrySet()) { if (getLogger().isDebugEnabled()) {
getLogger().debug(" Stopping filter '" + entry.getKey() + "'");
}
ApplicationFilterConfig filterConfig = entry.getValue();
filterConfig.release();
}
filterConfigs.clear();
} returntrue;
}
/** * Find and return the initialized <code>FilterConfig</code> for the specified filter name, if any; otherwise return * <code>null</code>. * * @param name Name of the desired filter * * @return the filter config object
*/ public FilterConfig findFilterConfig(String name) { synchronized (filterDefs) { return filterConfigs.get(name);
}
}
/** * Configure the set of instantiated application event listeners for this Context. * * @return <code>true</code> if all listeners wre initialized successfully, or <code>false</code> otherwise.
*/ publicboolean listenerStart() {
if (log.isDebugEnabled()) {
log.debug("Configuring application event listeners");
}
// Instantiate the required listeners
String listeners[] = findApplicationListeners();
Object results[] = new Object[listeners.length]; boolean ok = true; for (int i = 0; i < results.length; i++) { if (getLogger().isDebugEnabled()) {
getLogger().debug(" Configuring event listener class '" + listeners[i] + "'");
} try {
String listener = listeners[i];
results[i] = getInstanceManager().newInstance(listener);
} catch (Throwable t) {
t = ExceptionUtils.unwrapInvocationTargetException(t);
ExceptionUtils.handleThrowable(t);
getLogger().error(sm.getString("standardContext.applicationListener", listeners[i]), t);
ok = false;
}
} if (!ok) {
getLogger().error(sm.getString("standardContext.applicationSkipped")); returnfalse;
}
// Sort listeners in two arrays
List<Object> eventListeners = new ArrayList<>();
List<Object> lifecycleListeners = new ArrayList<>(); for (Object result : results) { if ((result instanceof ServletContextAttributeListener) ||
(result instanceof ServletRequestAttributeListener) || (result instanceof ServletRequestListener) ||
(result instanceof HttpSessionIdListener) || (result instanceof HttpSessionAttributeListener)) {
eventListeners.add(result);
} if ((result instanceof ServletContextListener) || (result instanceof HttpSessionListener)) {
lifecycleListeners.add(result);
}
}
// Listener instances may have been added directly to this Context by // ServletContextInitializers and other code via the pluggability APIs. // Put them these listeners after the ones defined in web.xml and/or // annotations then overwrite the list of instances with the new, full // list.
eventListeners.addAll(Arrays.asList(getApplicationEventListeners()));
setApplicationEventListeners(eventListeners.toArray()); for (Object lifecycleListener : getApplicationLifecycleListeners()) {
lifecycleListeners.add(lifecycleListener); if (lifecycleListener instanceof ServletContextListener) {
noPluggabilityListeners.add(lifecycleListener);
}
}
setApplicationLifecycleListeners(lifecycleListeners.toArray());
// Send application start events
if (getLogger().isDebugEnabled()) {
getLogger().debug("Sending application start events");
}
// Ensure context is not null
getServletContext();
context.setNewServletContextListenerAllowed(false);
ServletContextEvent event = new ServletContextEvent(getServletContext());
ServletContextEvent tldEvent = null; if (noPluggabilityListeners.size() > 0) {
noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
tldEvent = new ServletContextEvent(noPluggabilityServletContext);
} for (Object instance : instances) { if (!(instance instanceof ServletContextListener)) { continue;
}
ServletContextListener listener = (ServletContextListener) instance; try {
fireContainerEvent("beforeContextInitialized", listener); if (noPluggabilityListeners.contains(listener)) {
listener.contextInitialized(tldEvent);
} else {
listener.contextInitialized(event);
}
fireContainerEvent("afterContextInitialized", listener);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
fireContainerEvent("afterContextInitialized", listener);
getLogger().error(sm.getString("standardContext.listenerStart", instance.getClass().getName()), t);
ok = false;
}
} return ok;
}
/** * Send an application stop event to all interested listeners. * * @return <code>true</code> if all events were sent successfully, or <code>false</code> otherwise.
*/ publicboolean listenerStop() {
if (log.isDebugEnabled()) {
log.debug("Sending application stop events");
}
boolean ok = true;
Object listeners[] = getApplicationLifecycleListeners(); if (listeners != null && listeners.length > 0) {
ServletContextEvent event = new ServletContextEvent(getServletContext());
ServletContextEvent tldEvent = null; if (noPluggabilityServletContext != null) {
tldEvent = new ServletContextEvent(noPluggabilityServletContext);
} for (int i = 0; i < listeners.length; i++) { int j = (listeners.length - 1) - i; if (listeners[j] == null) { continue;
} if (listeners[j] instanceof ServletContextListener) {
ServletContextListener listener = (ServletContextListener) listeners[j]; try {
fireContainerEvent("beforeContextDestroyed", listener); if (noPluggabilityListeners.contains(listener)) {
listener.contextDestroyed(tldEvent);
} else {
listener.contextDestroyed(event);
}
fireContainerEvent("afterContextDestroyed", listener);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
fireContainerEvent("afterContextDestroyed", listener);
getLogger().error(
sm.getString("standardContext.listenerStop", listeners[j].getClass().getName()), t);
ok = false;
}
} try { if (getInstanceManager() != null) {
getInstanceManager().destroyInstance(listeners[j]);
}
} catch (Throwable t) {
t = ExceptionUtils.unwrapInvocationTargetException(t);
ExceptionUtils.handleThrowable(t);
getLogger().error(sm.getString("standardContext.listenerStop", listeners[j].getClass().getName()),
t);
ok = false;
}
}
}
// Annotation processing
listeners = getApplicationEventListeners(); if (listeners != null) { for (int i = 0; i < listeners.length; i++) { int j = (listeners.length - 1) - i; if (listeners[j] == null) { continue;
} try { if (getInstanceManager() != null) {
getInstanceManager().destroyInstance(listeners[j]);
}
} catch (Throwable t) {
t = ExceptionUtils.unwrapInvocationTargetException(t);
ExceptionUtils.handleThrowable(t);
getLogger().error(sm.getString("standardContext.listenerStop", listeners[j].getClass().getName()),
t);
ok = false;
}
}
}
/** * Load and initialize all servlets marked "load on startup" in the web application deployment descriptor. * * @param children Array of wrappers for all currently defined servlets (including those not declared load on * startup) * * @return <code>true</code> if load on startup was considered successful
*/ publicboolean loadOnStartup(Container children[]) {
// Collect "load on startup" servlets that need to be initialized
TreeMap<Integer,ArrayList<Wrapper>> map = new TreeMap<>(); for (Container child : children) {
Wrapper wrapper = (Wrapper) child; int loadOnStartup = wrapper.getLoadOnStartup(); if (loadOnStartup < 0) { continue;
}
Integer key = Integer.valueOf(loadOnStartup);
map.computeIfAbsent(key, k -> new ArrayList<>()).add(wrapper);
}
// Load the collected "load on startup" servlets for (ArrayList<Wrapper> list : map.values()) { for (Wrapper wrapper : list) { try {
wrapper.load();
} catch (ServletException e) {
getLogger().error(
sm.getString("standardContext.loadOnStartup.loadException", getName(), wrapper.getName()),
StandardWrapper.getRootCause(e)); // NOTE: load errors (including a servlet that throws // UnavailableException from the init() method) are NOT // fatal to application startup // unless failCtxIfServletStartFails="true" is specified if (getComputedFailCtxIfServletStartFails()) { returnfalse;
}
}
}
} returntrue;
}
/** * Start this component and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error that prevents this component from being * used
*/
@Override protectedsynchronizedvoid startInternal() throws LifecycleException {
if (log.isDebugEnabled()) {
log.debug("Starting " + getBaseName());
}
// Send j2ee.state.starting notification if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting", this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
setConfigured(false); boolean ok = true;
// Currently this is effectively a NO-OP but needs to be called to // ensure the NamingResources follows the correct lifecycle if (namingResources != null) {
namingResources.start();
}
// Post work directory
postWorkDirectory();
// Add missing components as necessary if (getResources() == null) { // (1) Required by Loader if (log.isDebugEnabled()) {
log.debug("Configuring default Resources");
}
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader();
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
// An explicit cookie processor hasn't been specified; use the default if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
// Initialize character set mapper
getCharsetMapper();
try { if (ok) { // Start our subordinate components, if any
Loader loader = getLoader(); if (loader instanceof Lifecycle) {
((Lifecycle) loader).start();
}
// since the loader just started, the webapp classloader is now // created. if (loader.getClassLoader() instanceof WebappClassLoaderBase) {
WebappClassLoaderBase cl = (WebappClassLoaderBase) loader.getClassLoader();
cl.setClearReferencesRmiTargets(getClearReferencesRmiTargets());
cl.setClearReferencesStopThreads(getClearReferencesStopThreads());
cl.setClearReferencesStopTimerThreads(getClearReferencesStopTimerThreads());
cl.setClearReferencesHttpClientKeepAliveThread(getClearReferencesHttpClientKeepAliveThread());
cl.setClearReferencesObjectStreamClassCaches(getClearReferencesObjectStreamClassCaches());
cl.setClearReferencesThreadLocals(getClearReferencesThreadLocals());
cl.setSkipMemoryLeakChecksOnJvmShutdown(getSkipMemoryLeakChecksOnJvmShutdown());
}
// By calling unbindThread and bindThread in a row, we setup the // current Thread CCL to be the webapp classloader
unbindThread(oldCCL);
oldCCL = bindThread();
// Initialize logger again. Other components might have used it // too early, so it should be reset.
logger = null;
getLogger();
Realm realm = getRealmInternal(); if (null != realm) { if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Place the CredentialHandler into the ServletContext so // applications can have access to it. Wrap it in a "safe" // handler so application's can't modify it.
CredentialHandler safeHandler = new CredentialHandler() {
@Override publicboolean matches(String inputCredentials, String storedCredentials) { return getRealmInternal().getCredentialHandler().matches(inputCredentials,
storedCredentials);
}
// Start our child containers, if not already started for (Container child : findChildren()) { if (!child.getState().isAvailable()) {
child.start();
}
}
// Start the Valves in our pipeline (including the basic), // if any if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// Configure default manager if none was specified if (contextManager != null) { if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.manager", contextManager.getClass().getName()));
}
setManager(contextManager);
}
if (manager != null && (getCluster() != null) && distributable) { // let the cluster know that there is a context that is distributable // and that it has its own manager
getCluster().registerManager(manager);
}
}
if (!getConfigured()) {
log.error(sm.getString("standardContext.configurationFail"));
ok = false;
}
// We put the resources into the servlet context if (ok) {
getServletContext().setAttribute(Globals.RESOURCES_ATTR, getResources());
// Create context attributes that will be required
getServletContext().setAttribute(JarScanner.class.getName(), getJarScanner());
// Make the version info available
getServletContext().setAttribute(Globals.WEBAPP_VERSION, getWebappVersion());
// Make the utility executor available if (!Globals.IS_SECURITY_ENABLED) {
getServletContext().setAttribute(ScheduledThreadPoolExecutor.class.getName(),
Container.getService(this).getServer().getUtilityExecutor());
}
}
// Set up the context init params
mergeParameters();
// Configure and call application event listeners if (ok) { if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
// Check constraints for uncovered HTTP methods // Needs to be after SCIs and listeners as they may programmatically // change constraints if (ok) {
checkConstraintsForUncoveredMethods(findConstraints());
}
// Configure and call application filters if (ok) { if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// Load and initialize all "load on startup" servlets if (ok) { if (!loadOnStartup(findChildren())) {
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
// Set available status depending upon startup success if (ok) { if (log.isDebugEnabled()) {
log.debug("Starting completed");
}
} else {
log.error(sm.getString("standardContext.startFailed", getName()));
}
startTime = System.currentTimeMillis();
// Send j2ee.state.running notification if (ok && (this.getObjectName() != null)) {
Notification notification = new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
// The WebResources implementation caches references to JAR files. On // some platforms these references may lock the JAR files. Since web // application start is likely to have read from lots of JARs, trigger // a clean-up now.
getResources().gc();
// Reinitializing if something went wrong if (!ok) {
setState(LifecycleState.FAILED); // Send j2ee.object.failed notification if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.object.failed", this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
} else {
setState(LifecycleState.STARTING);
}
}
/** * Merge the context initialization parameters specified in the application deployment descriptor with the * application parameters described in the server configuration, respecting the <code>override</code> property of * the application parameters appropriately.
*/ privatevoid mergeParameters() {
Map<String,String> mergedParams = new HashMap<>();
String names[] = findParameters(); for (String s : names) {
mergedParams.put(s, findParameter(s));
}
ApplicationParameter params[] = findApplicationParameters(); for (ApplicationParameter param : params) { if (param.getOverride()) {
mergedParams.computeIfAbsent(param.getName(), k -> param.getValue());
} else {
mergedParams.put(param.getName(), param.getValue());
}
}
/** * Stop this component and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. * * @exception LifecycleException if this component detects a fatal error that prevents this component from being * used
*/
@Override protectedsynchronizedvoid stopInternal() throws LifecycleException {
// Send j2ee.state.stopping notification if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.stopping", this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
// Context has been removed from Mapper at this point (so no new // requests will be mapped) but is still available.
// Give the in progress async requests a chance to complete long limit = System.currentTimeMillis() + unloadDelay; while (inProgressAsyncCount.get() > 0 && System.currentTimeMillis() < limit) { try { Thread.sleep(50);
} catch (InterruptedException e) {
log.info(sm.getString("standardContext.stop.asyncWaitInterrupted"), e); break;
}
}
// Once the state is set to STOPPING, the Context will report itself as // not available and any in progress async requests will timeout
setState(LifecycleState.STOPPING);
// Finalize our character set mapper
setCharsetMapper(null);
// Normal container shutdown processing if (log.isDebugEnabled()) {
log.debug("Processing standard container shutdown");
}
// JNDI resources are unbound in CONFIGURE_STOP_EVENT so stop // naming resources before they are unbound since NamingResources // does a JNDI lookup to retrieve the resource. This needs to be // after the application has finished with the resource if (namingResources != null) {
namingResources.stop();
}
fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
// Stop the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle && ((Lifecycle) pipeline).getState().isAvailable()) {
((Lifecycle) pipeline).stop();
}
// Clear all application-originated servlet context attributes if (context != null) {
context.clearAttributes();
}
// Send j2ee.state.stopped notification if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.stopped", this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
// Reset application context
context = null;
// This object will no longer be visible or used. try {
resetContext();
} catch (Exception ex) {
log.error("Error resetting context " + this + " " + ex, ex);
}
// reset the instance manager
setInstanceManager(null);
if (log.isDebugEnabled()) {
log.debug("Stopping complete");
}
}
/** * Destroy needs to clean up the context completely. The problem is that undoing all the config in start() and * restoring a 'fresh' state is impossible. After stop()/destroy()/init()/start() we should have the same state as * if a fresh start was done - i.e read modified web.xml, etc. This can only be done by completely removing the * context object and remapping a new one, or by cleaning up everything.
*/
@Override protectedvoid destroyInternal() throws LifecycleException {
// If in state NEW when destroy is called, the object name will never // have been set so the notification can't be created if (getObjectName() != null) { // Send j2ee.object.deleted notification
Notification notification = new Notification("j2ee.object.deleted", this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
if (namingResources != null) {
namingResources.destroy();
}
privatevoid resetContext() throws Exception { // Restore the original state ( pre reading web.xml in start ) // If you extend this - override this method and make sure to clean up
// Don't reset anything that is read from a <Context.../> element since // <Context .../> elements are read at initialisation will not be read // again for this object for (Container child : findChildren()) {
removeChild(child);
}
startupTime = 0;
startTime = 0;
tldScanTime = 0;
// Bugzilla 32867
distributable = false;
applicationListeners.clear();
applicationEventListenersList.clear();
applicationLifecycleListenersObjects = new Object[0];
jspConfigDescriptor = null;
/** * Adjust the URL pattern to begin with a leading slash, if appropriate (i.e. we are running a servlet 2.2 * application). Otherwise, return the specified URL pattern unchanged. * * @param urlPattern The URL pattern to be adjusted (if needed) and returned * * @return the URL pattern with a leading slash if needed
*/ protected String adjustURLPattern(String urlPattern) {
if (urlPattern == null) { return urlPattern;
} if (urlPattern.startsWith("/") || urlPattern.startsWith("*.")) { return urlPattern;
} if (!isServlet22()) { return urlPattern;
} if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.urlPattern.patternWarning", urlPattern));
} return"/" + urlPattern;
}
/** * Are we processing a version 2.2 deployment descriptor? * * @return <code>true</code> if running a legacy Servlet 2.2 application
*/
@Override publicboolean isServlet22() { return XmlIdentifiers.WEB_22_PUBLIC.equals(publicId);
}
@Override public Set<String> addServletSecurity(ServletRegistration.Dynamic registration,
ServletSecurityElement servletSecurityElement) {
SecurityConstraint[] securityConstraints = findConstraints(); for (SecurityConstraint securityConstraint : securityConstraints) {
SecurityCollection[] collections = securityConstraint.findCollections(); for (SecurityCollection collection : collections) { if (collection.findPattern(urlPattern)) { // First pattern found will indicate if there is a // conflict since for any given pattern all matching // constraints will be from either the descriptor or // not. It is not permitted to have a mixture if (collection.isFromDescriptor()) { // Skip this pattern
foundConflict = true;
conflicts.add(urlPattern); break;
} else { // Need to overwrite constraint for this pattern
collection.removePattern(urlPattern); // If the collection is now empty, remove it if (collection.findPatterns().length == 0) {
securityConstraint.removeCollection(collection);
}
}
}
}
// If the constraint now has no collections - remove it if (securityConstraint.findCollections().length == 0) {
removeConstraint(securityConstraint);
}
// No need to check other constraints for the current pattern // once a conflict has been found if (foundConflict) { break;
}
}
// Note: For programmatically added Servlets this may not be the // complete set of security constraints since additional // URL patterns can be added after the application has called // setSecurity. For all programmatically added servlets, the // #dynamicServletAdded() method sets a flag that ensures that // the constraints are re-evaluated before the servlet is // first used
// If the pattern did not conflict, add the new constraint(s). if (!foundConflict) {
SecurityConstraint[] newSecurityConstraints =
SecurityConstraint.createConstraints(servletSecurityElement, urlPattern); for (SecurityConstraint securityConstraint : newSecurityConstraints) {
addConstraint(securityConstraint);
}
}
}
return conflicts;
}
/** * Bind current thread, both for CL purposes and for JNDI ENC support during : startup, shutdown and reloading of * the context. * * @return the previous context class loader
*/ protected ClassLoader bindThread() {
if (isUseNaming()) { try {
ContextBindings.bindThread(this, getNamingToken());
} catch (NamingException e) { // Silent catch, as this is a normal case during the early // startup stages
}
}
return oldContextClassLoader;
}
/** * Unbind thread and restore the specified context classloader. * * @param oldContextClassLoader the previous classloader
*/ protectedvoid unbindThread(ClassLoader oldContextClassLoader) {
if (isUseNaming()) {
ContextBindings.unbindThread(this, getNamingToken());
}
Thread currentThread = Thread.currentThread(); if (originalClassLoader == null) { if (usePrivilegedAction) {
PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl(currentThread);
originalClassLoader = AccessController.doPrivileged(pa);
} else {
originalClassLoader = currentThread.getContextClassLoader();
}
}
if (webApplicationClassLoader == null || webApplicationClassLoader == originalClassLoader) { // Not possible or not necessary to switch class loaders. Return // null to indicate this. returnnull;
}
// Create this directory if necessary
File dir = new File(workDir); if (!dir.isAbsolute()) {
String catalinaHomePath = null; try {
catalinaHomePath = getCatalinaBase().getCanonicalPath();
dir = new File(catalinaHomePath, workDir);
} catch (IOException e) {
log.warn(sm.getString("standardContext.workCreateException", workDir, catalinaHomePath, getName()), e);
}
} if (!dir.mkdirs() && !dir.isDirectory()) {
log.warn(sm.getString("standardContext.workCreateFail", dir, getName()));
}
// Set the appropriate servlet context attribute if (context == null) {
getServletContext();
}
context.setAttribute(ServletContext.TEMPDIR, dir);
context.setAttributeReadOnly(ServletContext.TEMPDIR);
}
/** * Set the request processing paused flag for this Context. * * @param paused The new request processing paused flag
*/ privatevoid setPaused(boolean paused) {
this.paused = paused;
}
/** * Validate the syntax of a proposed <code><url-pattern></code> for conformance with specification * requirements. * * @param urlPattern URL pattern to be validated * * @return <code>true</code> if the URL pattern is conformant
*/ privateboolean validateURLPattern(String urlPattern) {
if (urlPattern == null) { returnfalse;
} if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) { returnfalse;
} if (urlPattern.equals("")) { returntrue;
} if (urlPattern.startsWith("*.")) { if (urlPattern.indexOf('/') < 0) {
checkUnusualURLPattern(urlPattern); returntrue;
} else { returnfalse;
}
} if (urlPattern.startsWith("/") && !urlPattern.contains("*.")) {
checkUnusualURLPattern(urlPattern); returntrue;
} else { returnfalse;
}
}
/** * Check for unusual but valid <code><url-pattern></code>s. See Bugzilla 34805, 43079 & 43080
*/ privatevoid checkUnusualURLPattern(String urlPattern) { if (log.isInfoEnabled()) { // First group checks for '*' or '/foo*' style patterns // Second group checks for *.foo.bar style patterns if ((urlPattern.endsWith("*") &&
(urlPattern.length() < 2 || urlPattern.charAt(urlPattern.length() - 2) != '/')) ||
urlPattern.startsWith("*.") && urlPattern.length() > 2 && urlPattern.lastIndexOf('.') > 1) {
log.info(sm.getString("standardContext.suspiciousUrl", urlPattern, getName()));
}
}
}
/** * The J2EE Server ObjectName this module is deployed on.
*/ private String server = null;
public String getServer() { return server;
}
public String setServer(String server) { returnthis.server = server;
}
/** * Gets the time this context was started. * * @return Time (in milliseconds since January 1, 1970, 00:00:00) when this context was started
*/ publiclong getStartTime() { return startTime;
}
@Override public <T extends Servlet> T createServlet(Class<T> c) throws ServletException { thrownew UnsupportedOperationException(sm.getString("noPluggabilityServletContext.notAllowed"));
}
@Override public ServletRegistration getServletRegistration(String servletName) { thrownew UnsupportedOperationException(sm.getString("noPluggabilityServletContext.notAllowed"));
}
@Override public Map<String,? extends ServletRegistration> getServletRegistrations() { thrownew UnsupportedOperationException(sm.getString("noPluggabilityServletContext.notAllowed"));
}
@Override public <T extends Filter> T createFilter(Class<T> c) throws ServletException { thrownew UnsupportedOperationException(sm.getString("noPluggabilityServletContext.notAllowed"));
}
@Override public FilterRegistration getFilterRegistration(String filterName) { thrownew UnsupportedOperationException(sm.getString("noPluggabilityServletContext.notAllowed"));
}
@Override public Map<String,? extends FilterRegistration> getFilterRegistrations() { thrownew UnsupportedOperationException(sm.getString("noPluggabilityServletContext.notAllowed"));
}
@Override public SessionCookieConfig getSessionCookieConfig() { thrownew UnsupportedOperationException(sm.getString("noPluggabilityServletContext.notAllowed"));
}
@Override public <T extends EventListener> T createListener(Class<T> c) throws ServletException { thrownew UnsupportedOperationException(sm.getString("noPluggabilityServletContext.notAllowed"));
}
@Override public JspConfigDescriptor getJspConfigDescriptor() { return sc.getJspConfigDescriptor();
}
@Override public ClassLoader getClassLoader() { return sc.getClassLoader();
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.129Bemerkung:
(vorverarbeitet am 2026-04-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.