/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/ package org.apache.catalina.startup;
/** * Startup event listener for a <b>Context</b> that configures the properties * of that Context, and the associated defined servlets. * * @author Craig R. McClanahan
*/ publicclass ContextConfig implements LifecycleListener {
/** * The string resources for this package.
*/ protectedstaticfinal StringManager sm =
StringManager.getManager(Constants.Package);
protectedstaticfinal LoginConfig DUMMY_LOGIN_CONFIG = new LoginConfig("NONE", null, null, null);
/** * The set of Authenticators that we know how to configure. The key is * the name of the implemented authentication method, and the value is * the fully qualified Java class name of the corresponding Valve.
*/ protectedstaticfinal Properties authenticators;
static { // Load our mapping properties for the standard authenticators
Properties props = new Properties(); try (InputStream is = ContextConfig.class.getClassLoader().getResourceAsStream( "org/apache/catalina/startup/Authenticators.properties")) { if (is != null) {
props.load(is);
}
} catch (IOException ioe) {
props = null;
}
authenticators = props;
}
/** * Cache of default web.xml fragments per Host
*/ protectedstaticfinal Map<Host,DefaultWebXmlCacheEntry> hostWebXmlCache = new ConcurrentHashMap<>();
/** * Set used as the value for {@code JavaClassCacheEntry.sciSet} when there * are no SCIs associated with a class.
*/ privatestaticfinal Set<ServletContainerInitializer> EMPTY_SCI_SET = Collections.emptySet();
// ----------------------------------------------------- Instance Variables /** * Custom mappings of login methods to authenticators
*/ protected Map<String,Authenticator> customAuthenticators;
/** * The Context we are associated with.
*/ protectedvolatile Context context = null;
/** * The default web application's deployment descriptor location.
*/ protected String defaultWebXml = null;
/** * Track any fatal errors during startup configuration processing.
*/ protectedboolean ok = false;
/** * Original docBase.
*/ protected String originalDocBase = null;
/** * Anti-locking docBase. It is a path to a copy of the web application * in the java.io.tmpdir directory. This path is always an absolute one.
*/ private File antiLockingDocBase = null;
/** * Map of ServletContainerInitializer to classes they expressed interest in.
*/ protectedfinal Map<ServletContainerInitializer, Set<Class<?>>> initializerClassMap = new LinkedHashMap<>();
/** * Map of Types to ServletContainerInitializer that are interested in those * types.
*/ protectedfinal Map<Class<?>, Set<ServletContainerInitializer>> typeInitializerMap = new HashMap<>();
/** * Flag that indicates if at least one {@link HandlesTypes} entry is present * that represents an annotation.
*/ protectedboolean handlesTypesAnnotations = false;
/** * Flag that indicates if at least one {@link HandlesTypes} entry is present * that represents a non-annotation.
*/ protectedboolean handlesTypesNonAnnotations = false;
/** * Obtain the location of the default deployment descriptor. * * @return The path to the default web.xml. If not absolute, it is relative * to CATALINA_BASE.
*/ public String getDefaultWebXml() { if (defaultWebXml == null) {
defaultWebXml = Constants.DefaultWebXml;
} return defaultWebXml;
}
/** * Set the location of the default deployment descriptor. * * @param path The path to the default web.xml. If not absolute, it is * relative to CATALINA_BASE.
*/ publicvoid setDefaultWebXml(String path) { this.defaultWebXml = path;
}
/** * Sets custom mappings of login methods to authenticators. * * @param customAuthenticators Custom mappings of login methods to * authenticators
*/ publicvoid setCustomAuthenticators(
Map<String,Authenticator> customAuthenticators) { this.customAuthenticators = customAuthenticators;
}
// --------------------------------------------------------- Public Methods
/** * Process events for an associated Context. * * @param event The lifecycle event that has occurred
*/
@Override publicvoid lifecycleEvent(LifecycleEvent event) {
// Identify the context we are associated with try {
context = (Context) event.getLifecycle();
} catch (ClassCastException e) {
log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e); return;
}
// Process the event that has occurred if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart();
} elseif (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} elseif (event.getType().equals(Lifecycle.AFTER_START_EVENT)) { // Restore docBase for management tools if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
} elseif (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
configureStop();
} elseif (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
init();
} elseif (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
destroy();
}
long t2=System.currentTimeMillis(); if (context instanceof StandardContext) {
((StandardContext) context).setStartupTime(t2-t1+
((StandardContext) context).getStartupTime());
}
}
/** * Set up an Authenticator automatically if required, and one has not * already been configured.
*/ protectedvoid authenticatorConfig() {
LoginConfig loginConfig = context.getLoginConfig(); if (loginConfig == null) { // Need an authenticator to support HttpServletRequest.login()
loginConfig = DUMMY_LOGIN_CONFIG;
context.setLoginConfig(loginConfig);
}
// Has an authenticator been configured already? if (context.getAuthenticator() != null) { return;
}
// Has a Realm been configured for us to authenticate against? if (context.getRealm() == null) {
log.error(sm.getString("contextConfig.missingRealm"));
ok = false; return;
}
/* * First check to see if there is a custom mapping for the login * method. If so, use it. Otherwise, check if there is a mapping in * org/apache/catalina/startup/Authenticators.properties.
*/
Valve authenticator = null; if (customAuthenticators != null) {
authenticator = (Valve) customAuthenticators.get(loginConfig.getAuthMethod());
}
if (authenticator == null) { if (authenticators == null) {
log.error(sm.getString("contextConfig.authenticatorResources"));
ok = false; return;
}
// Identify the class name of the Valve we should configure
String authenticatorName = authenticators.getProperty(loginConfig.getAuthMethod()); if (authenticatorName == null) {
log.error(sm.getString("contextConfig.authenticatorMissing",
loginConfig.getAuthMethod()));
ok = false; return;
}
// Instantiate and install an Authenticator of the requested class try { Class<?> authenticatorClass = Class.forName(authenticatorName);
authenticator = (Valve) authenticatorClass.getConstructor().newInstance();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString( "contextConfig.authenticatorInstantiate",
authenticatorName),
t);
ok = false;
}
}
if (authenticator != null) {
Pipeline pipeline = context.getPipeline(); if (pipeline != null) {
pipeline.addValve(authenticator); if (log.isDebugEnabled()) {
log.debug(sm.getString( "contextConfig.authenticatorConfigured",
loginConfig.getAuthMethod()));
}
}
}
}
/** * Create (if necessary) and return a Digester configured to process the * context configuration descriptor for an application. * @return the digester for context.xml files
*/ protected Digester createContextDigester() {
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
List<String> objectAttrs = new ArrayList<>();
objectAttrs.add("className");
fakeAttributes.put(Object.class, objectAttrs); // Ignore attribute added by Eclipse for its internal tracking
List<String> contextAttrs = new ArrayList<>();
contextAttrs.add("source");
fakeAttributes.put(StandardContext.class, contextAttrs);
digester.setFakeAttributes(fakeAttributes);
RuleSet contextRuleSet = new ContextRuleSet("", false);
digester.addRuleSet(contextRuleSet);
RuleSet namingRuleSet = new NamingRuleSet("Context/");
digester.addRuleSet(namingRuleSet); return digester;
}
/** * Process the default configuration file, if it exists. * @param digester The digester that will be used for XML parsing
*/ protectedvoid contextConfig(Digester digester) {
// Open the default context.xml file, if it exists if (context instanceof StandardContext) {
defaultContextXml = ((StandardContext)context).getDefaultContextXml();
} // set the default if we don't have any overrides if (defaultContextXml == null) {
defaultContextXml = Constants.DefaultContextXml;
}
/** * Process a context.xml. * @param digester The digester that will be used for XML parsing * @param contextXml The URL to the context.xml configuration * @param stream The XML resource stream
*/ protectedvoid processContextConfig(Digester digester, URL contextXml, InputStream stream) {
// This could be blank, relative, absolute or canonical
String docBaseConfigured = context.getDocBase(); // If there is no explicit docBase, derive it from the path and version if (docBaseConfigured == null) { // Trying to guess the docBase according to the path
String path = context.getPath(); if (path == null) { return;
}
ContextName cn = new ContextName(path, context.getWebappVersion());
docBaseConfigured = cn.getBaseName();
}
// Obtain the absolute docBase in String and File form
String docBaseAbsolute;
File docBaseConfiguredFile = new File(docBaseConfigured); if (!docBaseConfiguredFile.isAbsolute()) {
docBaseAbsolute = (new File(appBase, docBaseConfigured)).getAbsolutePath();
} else {
docBaseAbsolute = docBaseConfiguredFile.getAbsolutePath();
}
File docBaseAbsoluteFile = new File(docBaseAbsolute);
String originalDocBase = docBaseAbsolute;
ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
String pathName = cn.getBaseName();
// At this point we need to determine if we have a WAR file in the // appBase that needs to be expanded. Therefore we consider the absolute // docBase NOT the canonical docBase. This is because some users symlink // WAR files into the appBase and we want this to work correctly. boolean docBaseAbsoluteInAppBase = docBaseAbsolute.startsWith(appBase.getPath() + File.separatorChar); if (docBaseAbsolute.toLowerCase(Locale.ENGLISH).endsWith(".war") && !docBaseAbsoluteFile.isDirectory()) {
URL war = UriUtil.buildJarUrl(docBaseAbsoluteFile); if (unpackWARs) {
docBaseAbsolute = ExpandWar.expand(host, war, pathName);
docBaseAbsoluteFile = new File(docBaseAbsolute); if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(originalDocBase);
}
} else {
ExpandWar.validate(host, war, pathName);
}
} else {
File docBaseAbsoluteFileWar = new File(docBaseAbsolute + ".war");
URL war = null; if (docBaseAbsoluteFileWar.exists() && docBaseAbsoluteInAppBase) {
war = UriUtil.buildJarUrl(docBaseAbsoluteFileWar);
} if (docBaseAbsoluteFile.exists()) { if (war != null && unpackWARs) { // Check if WAR needs to be re-expanded (e.g. if it has // changed). Note: HostConfig.deployWar() takes care of // ensuring that the correct XML file is used. // This will be a NO-OP if the WAR is unchanged.
ExpandWar.expand(host, war, pathName);
}
} else { if (war != null) { if (unpackWARs) {
docBaseAbsolute = ExpandWar.expand(host, war, pathName);
docBaseAbsoluteFile = new File(docBaseAbsolute);
} else {
docBaseAbsoluteFile = docBaseAbsoluteFileWar;
ExpandWar.validate(host, war, pathName);
}
} if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(originalDocBase);
}
}
}
File docBaseFile = new File(docBase); if (!docBaseFile.isAbsolute()) {
docBaseFile = new File(host.getAppBaseFile(), docBase);
}
String path = context.getPath(); if (path == null) { return;
}
ContextName cn = new ContextName(path, context.getWebappVersion());
docBase = cn.getBaseName();
String tmp = System.getProperty("java.io.tmpdir");
File tmpFile = new File(tmp); if (!tmpFile.isDirectory()) {
log.error(sm.getString("contextConfig.noAntiLocking", tmp, context.getName())); return;
}
if (originalDocBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
antiLockingDocBase = new File(tmpFile, deploymentCount++ + "-" + docBase + ".war");
} else {
antiLockingDocBase = new File(tmpFile, deploymentCount++ + "-" + docBase);
}
antiLockingDocBase = antiLockingDocBase.getAbsoluteFile();
if (log.isDebugEnabled()) {
log.debug("Anti locking context[" + context.getName()
+ "] setting docBase to " +
antiLockingDocBase.getPath());
}
// Cleanup just in case an old deployment is lying around
ExpandWar.delete(antiLockingDocBase); if (ExpandWar.copy(docBaseFile, antiLockingDocBase)) {
context.setDocBase(antiLockingDocBase.getPath());
}
}
}
/** * Process a "init" event for this Context.
*/ protectedsynchronizedvoid init() { // Called from StandardContext.init()
/** * Process a "contextConfig" event for this Context.
*/ protectedsynchronizedvoid configureStart() { // Called from StandardContext.start()
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.start"));
}
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.xmlSettings",
context.getName(), Boolean.valueOf(context.getXmlValidation()), Boolean.valueOf(context.getXmlNamespaceAware())));
}
webConfig();
if (!context.getIgnoreAnnotations()) {
applicationAnnotationsConfig();
} if (ok) {
validateSecurityRoles();
}
// Configure an authenticator if we need one if (ok) {
authenticatorConfig();
}
// Dump the contents of this pipeline if requested if (log.isDebugEnabled()) {
log.debug("Pipeline Configuration:");
Pipeline pipeline = context.getPipeline();
Valve valves[] = null; if (pipeline != null) {
valves = pipeline.getValves();
} if (valves != null) { for (Valve valve : valves) {
log.debug(" " + valve.getClass().getName());
}
}
log.debug("======================");
}
// Make our application available if no problems were encountered if (ok) {
context.setConfigured(true);
} else {
log.error(sm.getString("contextConfig.unavailable"));
context.setConfigured(false);
}
}
/** * Process a "stop" event for this Context.
*/ protectedsynchronizedvoid configureStop() {
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.stop"));
}
int i;
// Removing children
Container[] children = context.findChildren(); for (i = 0; i < children.length; i++) {
context.removeChild(children[i]);
}
// Removing application parameters /* ApplicationParameter[] applicationParameters = context.findApplicationParameters(); for (i = 0; i < applicationParameters.length; i++) { context.removeApplicationParameter (applicationParameters[i].getName()); }
*/
// Removing security constraints
SecurityConstraint[] securityConstraints = context.findConstraints(); for (i = 0; i < securityConstraints.length; i++) {
context.removeConstraint(securityConstraints[i]);
}
// Removing errors pages
ErrorPage[] errorPages = context.findErrorPages(); for (i = 0; i < errorPages.length; i++) {
context.removeErrorPage(errorPages[i]);
}
// Removing filter defs
FilterDef[] filterDefs = context.findFilterDefs(); for (i = 0; i < filterDefs.length; i++) {
context.removeFilterDef(filterDefs[i]);
}
// Removing filter maps
FilterMap[] filterMaps = context.findFilterMaps(); for (i = 0; i < filterMaps.length; i++) {
context.removeFilterMap(filterMaps[i]);
}
// Removing Mime mappings
String[] mimeMappings = context.findMimeMappings(); for (i = 0; i < mimeMappings.length; i++) {
context.removeMimeMapping(mimeMappings[i]);
}
// Removing parameters
String[] parameters = context.findParameters(); for (i = 0; i < parameters.length; i++) {
context.removeParameter(parameters[i]);
}
// Removing security role
String[] securityRoles = context.findSecurityRoles(); for (i = 0; i < securityRoles.length; i++) {
context.removeSecurityRole(securityRoles[i]);
}
// Removing servlet mappings
String[] servletMappings = context.findServletMappings(); for (i = 0; i < servletMappings.length; i++) {
context.removeServletMapping(servletMappings[i]);
}
// Removing welcome files
String[] welcomeFiles = context.findWelcomeFiles(); for (i = 0; i < welcomeFiles.length; i++) {
context.removeWelcomeFile(welcomeFiles[i]);
}
// Removing wrapper lifecycles
String[] wrapperLifecycles = context.findWrapperLifecycles(); for (i = 0; i < wrapperLifecycles.length; i++) {
context.removeWrapperLifecycle(wrapperLifecycles[i]);
}
// Removing wrapper listeners
String[] wrapperListeners = context.findWrapperListeners(); for (i = 0; i < wrapperListeners.length; i++) {
context.removeWrapperListener(wrapperListeners[i]);
}
// Remove (partially) folders and files created by antiLocking if (antiLockingDocBase != null) { // No need to log failure - it is expected in this case
ExpandWar.delete(antiLockingDocBase, false);
}
/** * Process a "destroy" event for this Context.
*/ protectedsynchronizedvoid destroy() { // Called from StandardContext.destroy() if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.destroy"));
}
// Skip clearing the work directory if Tomcat is being shutdown
Server s = getServer(); if (s != null && !s.getState().isAvailable()) { return;
}
// Changed to getWorkPath per Bugzilla 35819. if (context instanceof StandardContext) {
String workDir = ((StandardContext) context).getWorkPath(); if (workDir != null) {
ExpandWar.delete(new File(workDir));
}
}
}
private Server getServer() {
Container c = context; while (c != null && !(c instanceof Engine)) {
c = c.getParent();
}
if (c == null) { returnnull;
}
Service s = ((Engine)c).getService();
if (s == null) { returnnull;
}
return s.getServer();
}
/** * Validate the usage of security role names in the web application * deployment descriptor. If any problems are found, issue warning * messages (for backwards compatibility) and add the missing roles. * (To make these problems fatal instead, simply set the <code>ok</code> * instance variable to <code>false</code> as well).
*/ protectedvoid validateSecurityRoles() {
// Check role names used in <security-constraint> elements
SecurityConstraint constraints[] = context.findConstraints(); for (SecurityConstraint constraint : constraints) {
String roles[] = constraint.findAuthRoles(); for (String role : roles) { if (!"*".equals(role) &&
!context.findSecurityRole(role)) {
log.warn(sm.getString("contextConfig.role.auth", role));
context.addSecurityRole(role);
}
}
}
// Check role names used in <servlet> elements
Container wrappers[] = context.findChildren(); for (Container container : wrappers) {
Wrapper wrapper = (Wrapper) container;
String runAs = wrapper.getRunAs(); if ((runAs != null) && !context.findSecurityRole(runAs)) {
log.warn(sm.getString("contextConfig.role.runas", runAs));
context.addSecurityRole(runAs);
}
String names[] = wrapper.findSecurityReferences(); for (String name : names) {
String link = wrapper.findSecurityReference(name); if ((link != null) && !context.findSecurityRole(link)) {
log.warn(sm.getString("contextConfig.role.link", link));
context.addSecurityRole(link);
}
}
}
/** * Scan the web.xml files that apply to the web application and merge them * using the rules defined in the spec. For the global web.xml files, * where there is duplicate configuration, the most specific level wins. ie * an application's web.xml takes precedence over the host level or global * web.xml file.
*/ protectedvoid webConfig() { /* * Anything and everything can override the global and host defaults. * This is implemented in two parts * - Handle as a web fragment that gets added after everything else so * everything else takes priority * - Mark Servlets as overridable so SCI configuration can replace * configuration from the defaults
*/
/* * The rules for annotation scanning are not as clear-cut as one might * think. Tomcat implements the following process: * - As per SRV.1.6.2, Tomcat will scan for annotations regardless of * which Servlet spec version is declared in web.xml. The EG has * confirmed this is the expected behaviour. * - As per http://java.net/jira/browse/SERVLET_SPEC-36, if the main * web.xml is marked as metadata-complete, JARs are still processed * for SCIs. * - If metadata-complete=true and an absolute ordering is specified, * JARs excluded from the ordering are also excluded from the SCI * processing. * - If an SCI has a @HandlesType annotation then all classes (except * those in JARs excluded from an absolute ordering) need to be * scanned to check if they match.
*/
WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
context.getXmlValidation(), context.getXmlBlockExternal());
Set<WebXml> defaults = new HashSet<>();
defaults.add(getDefaultWebXmlFragment(webXmlParser));
Set<WebXml> tomcatWebXml = new HashSet<>();
tomcatWebXml.add(getTomcatWebXmlFragment(webXmlParser));
WebXml webXml = createWebXml();
// Parse context level web.xml
InputSource contextWebXml = getContextWebXmlSource(); if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
ok = false;
}
// Step 1. Identify all the JARs packaged with the application and those // provided by the container. If any of the application JARs have a // web-fragment.xml it will be parsed at this point. web-fragment.xml // files are ignored for container provided JARs.
Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
// Step 2. Order the fragments.
Set<WebXml> orderedFragments = null;
orderedFragments =
WebXml.orderWebFragments(webXml, fragments, sContext);
// Step 3. Look for ServletContainerInitializer implementations if (ok) {
processServletContainerInitializers();
}
if (!webXml.isMetadataComplete()) { // Step 6. Merge web-fragment.xml files into the main web.xml // file. if (ok) {
ok = webXml.merge(orderedFragments);
}
// Step 7b. Apply global defaults // Have to merge defaults before JSP conversion since defaults // provide JSP servlet definition.
webXml.merge(defaults);
// Step 8. Convert explicitly mentioned jsps to servlets if (ok) {
convertJsps(webXml);
}
if (context.getLogEffectiveWebXml()) {
log.info(sm.getString("contextConfig.effectiveWebXml", webXml.toXml()));
}
// Always need to look for static resources // Step 10. Look for static resources packaged in JARs if (ok) { // Spec does not define an order. // Use ordered JARs followed by remaining JARs
Set<WebXml> resourceJars = new LinkedHashSet<>(orderedFragments); for (WebXml fragment : fragments.values()) { if (!resourceJars.contains(fragment)) {
resourceJars.add(fragment);
}
}
processResourceJARs(resourceJars); // See also StandardContext.resourcesStart() for // WEB-INF/classes/META-INF/resources configuration
}
// Step 11. Apply the ServletContainerInitializer config to the // context if (ok) { for (Map.Entry<ServletContainerInitializer,
Set<Class<?>>> entry :
initializerClassMap.entrySet()) { if (entry.getValue().isEmpty()) {
context.addServletContainerInitializer(
entry.getKey(), null);
} else {
context.addServletContainerInitializer(
entry.getKey(), entry.getValue());
}
}
}
}
protectedvoid processClasses(WebXml webXml, Set<WebXml> orderedFragments) { // Step 4. Process /WEB-INF/classes for annotations and // @HandlesTypes matches
Map<String, JavaClassCacheEntry> javaClassCache;
if (context.getParallelAnnotationScanning()) {
javaClassCache = new ConcurrentHashMap<>();
} else {
javaClassCache = new HashMap<>();
}
if (ok) {
WebResource[] webResources =
context.getResources().listResources("/WEB-INF/classes");
for (WebResource webResource : webResources) { // Skip the META-INF directory from any JARs that have been // expanded in to WEB-INF/classes (sometimes IDEs do this). if ("META-INF".equals(webResource.getName())) { continue;
}
processAnnotationsWebResource(webResource, webXml,
webXml.isMetadataComplete(), javaClassCache);
}
}
// Step 5. Process JARs for annotations and // @HandlesTypes matches - only need to process those fragments we // are going to use (remember orderedFragments includes any // container fragments) if (ok) {
processAnnotations(
orderedFragments, webXml.isMetadataComplete(), javaClassCache);
}
// Cache, if used, is no longer required so clear it
javaClassCache.clear();
}
privatevoid configureContext(WebXml webxml) { // As far as possible, process in alphabetical order so it is easy to // check everything is present // Some validation depends on correct public ID
context.setPublicId(webxml.getPublicId());
// Everything else in order
context.setEffectiveMajorVersion(webxml.getMajorVersion());
context.setEffectiveMinorVersion(webxml.getMinorVersion());
for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
context.addParameter(entry.getKey(), entry.getValue());
}
context.setDenyUncoveredHttpMethods(
webxml.getDenyUncoveredHttpMethods());
context.setDisplayName(webxml.getDisplayName());
context.setDistributable(webxml.isDistributable()); for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
context.getNamingResources().addLocalEjb(ejbLocalRef);
} for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
context.getNamingResources().addEjb(ejbRef);
} for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
context.getNamingResources().addEnvironment(environment);
} for (ErrorPage errorPage : webxml.getErrorPages().values()) {
context.addErrorPage(errorPage);
} for (FilterDef filter : webxml.getFilters().values()) { if (filter.getAsyncSupported() == null) {
filter.setAsyncSupported("false");
}
context.addFilterDef(filter);
} for (FilterMap filterMap : webxml.getFilterMappings()) {
context.addFilterMap(filterMap);
}
context.setJspConfigDescriptor(webxml.getJspConfigDescriptor()); for (String listener : webxml.getListeners()) {
context.addApplicationListener(listener);
} for (Entry<String, String> entry :
webxml.getLocaleEncodingMappings().entrySet()) {
context.addLocaleEncodingMappingParameter(entry.getKey(),
entry.getValue());
} // Prevents IAE if (webxml.getLoginConfig() != null) {
context.setLoginConfig(webxml.getLoginConfig());
} for (MessageDestinationRef mdr :
webxml.getMessageDestinationRefs().values()) {
context.getNamingResources().addMessageDestinationRef(mdr);
}
// messageDestinations were ignored in Tomcat 6, so ignore here
context.setIgnoreAnnotations(webxml.isMetadataComplete()); for (Entry<String, String> entry :
webxml.getMimeMappings().entrySet()) {
context.addMimeMapping(entry.getKey(), entry.getValue());
}
context.setRequestCharacterEncoding(webxml.getRequestCharacterEncoding()); // Name is just used for ordering for (ContextResourceEnvRef resource :
webxml.getResourceEnvRefs().values()) {
context.getNamingResources().addResourceEnvRef(resource);
} for (ContextResource resource : webxml.getResourceRefs().values()) {
context.getNamingResources().addResource(resource);
}
context.setResponseCharacterEncoding(webxml.getResponseCharacterEncoding()); boolean allAuthenticatedUsersIsAppRole =
webxml.getSecurityRoles().contains(
SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS); for (SecurityConstraint constraint : webxml.getSecurityConstraints()) { if (allAuthenticatedUsersIsAppRole) {
constraint.treatAllAuthenticatedUsersAsApplicationRole();
}
context.addConstraint(constraint);
} for (String role : webxml.getSecurityRoles()) {
context.addSecurityRole(role);
} for (ContextService service : webxml.getServiceRefs().values()) {
context.getNamingResources().addService(service);
} for (ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = context.createWrapper(); // Description is ignored // Display name is ignored // Icons are ignored
// jsp-file gets passed to the JSP Servlet as an init-param
if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
} if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
wrapper.setName(servlet.getServletName());
Map<String,String> params = servlet.getParameterMap(); for (Entry<String, String> entry : params.entrySet()) {
wrapper.addInitParameter(entry.getKey(), entry.getValue());
}
wrapper.setRunAs(servlet.getRunAs());
Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs(); for (SecurityRoleRef roleRef : roleRefs) {
wrapper.addSecurityReference(
roleRef.getName(), roleRef.getLink());
}
wrapper.setServletClass(servlet.getServletClass());
MultipartDef multipartdef = servlet.getMultipartDef(); if (multipartdef != null) { long maxFileSize = -1; long maxRequestSize = -1; int fileSizeThreshold = 0;
for (String welcomeFile : webxml.getWelcomeFiles()) { /* * The following will result in a welcome file of "" so don't add * that to the context * <welcome-file-list> * <welcome-file/> * </welcome-file-list>
*/ if (welcomeFile != null && welcomeFile.length() > 0) {
context.addWelcomeFile(welcomeFile);
}
}
// Do this last as it depends on servlets for (JspPropertyGroup jspPropertyGroup :
webxml.getJspPropertyGroups()) {
String jspServletName = context.findServletMapping("*.jsp"); if (jspServletName == null) {
jspServletName = "jsp";
} if (context.findChild(jspServletName) != null) { for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
context.addServletMappingDecoded(urlPattern, jspServletName, true);
}
} else { if(log.isDebugEnabled()) { for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
log.debug("Skipping " + urlPattern + " , no servlet " +
jspServletName);
}
}
}
}
for (Entry<String, String> entry :
webxml.getPostConstructMethods().entrySet()) {
context.addPostConstructMethod(entry.getKey(), entry.getValue());
}
// Set to distributable else every app will be prevented from being // distributable when the Tomcat fragment is merged with the main // web.xml
webXmlTomcatFragment.setDistributable(true); // When merging, the default welcome files are only used if the app has // not defined any welcomes files.
webXmlTomcatFragment.setAlwaysAddWelcomeFiles(false);
WebResource resource = context.getResources().getResource(Constants.TomcatWebXml); if (resource.isFile()) { try {
InputSource source = new InputSource(resource.getURL().toURI().toString());
source.setByteStream(resource.getInputStream()); if (!webXmlParser.parseWebXml(source, webXmlTomcatFragment, false)) {
ok = false;
}
} catch (URISyntaxException e) {
log.error(sm.getString("contextConfig.tomcatWebXmlError"), e);
}
} return webXmlTomcatFragment;
}
// Parsing global web.xml is relatively expensive. Use a sync block to // make sure it only happens once. Use the pipeline since a lock will // already be held on the host by another thread synchronized (host.getPipeline()) {
entry = hostWebXmlCache.get(host); if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
entry.getHostTimeStamp() == hostTimeStamp) { return entry.getWebXml();
}
WebXml webXmlDefaultFragment = createWebXml();
webXmlDefaultFragment.setOverridable(true); // Set to distributable else every app will be prevented from being // distributable when the default fragment is merged with the main // web.xml
webXmlDefaultFragment.setDistributable(true); // When merging, the default welcome files are only used if the app has // not defined any welcomes files.
webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
// Parse global web.xml if present if (globalWebXml == null) { // This is unusual enough to log
log.info(sm.getString("contextConfig.defaultMissing"));
} else { if (!webXmlParser.parseWebXml(
globalWebXml, webXmlDefaultFragment, false)) {
ok = false;
}
}
// Parse host level web.xml if present // Additive apart from welcome pages
webXmlDefaultFragment.setReplaceWelcomeFiles(true);
if (!webXmlParser.parseWebXml(
hostWebXml, webXmlDefaultFragment, false)) {
ok = false;
}
// Don't update the cache if an error occurs if (globalTimeStamp != -1 && hostTimeStamp != -1) {
entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
globalTimeStamp, hostTimeStamp);
hostWebXmlCache.put(host, entry); // Add a Lifecycle listener to the Host that will remove it from // the hostWebXmlCache once the Host is destroyed
host.addLifecycleListener(new HostWebXmlCacheCleaner());
}
return webXmlDefaultFragment;
}
}
privatevoid convertJsps(WebXml webXml) {
Map<String,String> jspInitParams;
ServletDef jspServlet = webXml.getServlets().get("jsp"); if (jspServlet == null) {
jspInitParams = new HashMap<>();
Wrapper w = (Wrapper) context.findChild("jsp"); if (w != null) {
String[] params = w.findInitParameters(); for (String param : params) {
jspInitParams.put(param, w.findInitParameter(param));
}
}
} else {
jspInitParams = jspServlet.getParameterMap();
} for (ServletDef servletDef: webXml.getServlets().values()) { if (servletDef.getJspFile() != null) {
convertJsp(servletDef, jspInitParams);
}
}
}
for (Class<?> type : types) { if (type.isAnnotation()) {
handlesTypesAnnotations = true;
} else {
handlesTypesNonAnnotations = true;
}
typeInitializerMap.computeIfAbsent(type, k -> new HashSet<>()).add(sci);
}
}
}
/** * Scan JARs that contain web-fragment.xml files that will be used to * configure this application to see if they also contain static resources. * If static resources are found, add them to the context. Resources are * added in web-fragment.xml priority order. * @param fragments The set of fragments that will be scanned for * static resources
*/ protectedvoid processResourceJARs(Set<WebXml> fragments) { for (WebXml fragment : fragments) {
URL url = fragment.getURL(); try { if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) { try (Jar jar = JarFactory.newInstance(url)) {
jar.nextEntry();
String entryName = jar.getEntryName(); while (entryName != null) { if (entryName.startsWith("META-INF/resources/")) {
context.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.RESOURCE_JAR, "/", url, "/META-INF/resources"); break;
}
jar.nextEntry();
entryName = jar.getEntryName();
}
}
} elseif ("file".equals(url.getProtocol())) {
File file = new File(url.toURI());
File resources = new File(file, "META-INF/resources/"); if (resources.isDirectory()) {
context.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.RESOURCE_JAR, "/", resources.getAbsolutePath(), null, "/");
}
}
} catch (IOException | URISyntaxException e) {
log.error(sm.getString("contextConfig.resourceJarFail", url,
context.getName()));
}
}
}
/** * Identify the default web.xml to be used and obtain an input source for * it. * @return an input source to the default web.xml
*/ protected InputSource getGlobalWebXmlSource() { // Is a default web.xml specified for the Context? if (defaultWebXml == null && context instanceof StandardContext) {
defaultWebXml = ((StandardContext) context).getDefaultWebXml();
} // Set the default if we don't have any overrides if (defaultWebXml == null) {
getDefaultWebXml();
}
// Is it explicitly suppressed, e.g. in embedded environment? if (Constants.NoDefaultWebXml.equals(defaultWebXml)) { returnnull;
} return getWebXmlSource(defaultWebXml, true);
}
/** * Identify the host web.xml to be used and obtain an input source for * it. * @return an input source to the default per host web.xml
*/ protected InputSource getHostWebXmlSource() {
File hostConfigBase = getHostConfigBase(); if (hostConfigBase == null) { returnnull;
}
/** * Identify the application web.xml to be used and obtain an input source * for it. * @return an input source to the context web.xml
*/ protected InputSource getContextWebXmlSource() {
InputStream stream = null;
InputSource source = null;
URL url = null;
/** * Utility method to create an input source from the specified XML file. * @param filename Name of the file (possibly with one or more leading path * segments) to read * @param global true if processing a shared resource, false if processing * a host based resource * @return the input source
*/ protected InputSource getWebXmlSource(String filename, boolean global) {
ConfigurationSource.Resource webXmlResource = null; try { if (global) { if (Constants.DefaultWebXml.equals(filename)) {
webXmlResource = ConfigFileLoader.getSource().getSharedWebXml();
} else {
webXmlResource = ConfigFileLoader.getSource().getResource(filename);
}
} else {
String hostWebXml = Container.getConfigPath(context, Constants.HostWebXml);
webXmlResource = ConfigFileLoader.getSource().getResource(hostWebXml);
}
} catch (IOException e) { // Ignore if not found returnnull;
}
/** * Scan /WEB-INF/lib for JARs and for each one found add it and any * /META-INF/web-fragment.xml to the resulting Map. web-fragment.xml files * will be parsed before being added to the map. Every JAR will be added and * <code>null</code> will be used if no web-fragment.xml was found. Any JARs * known not contain fragments will be skipped. * * @param application The main web.xml metadata * @param webXmlParser The parser to use to process the web.xml file * @return A map of JAR name to processed web fragment (if any)
*/ protected Map<String,WebXml> processJarsForWebFragments(WebXml application,
WebXmlParser webXmlParser) {
JarScanner jarScanner = context.getJarScanner(); boolean delegate = false; if (context instanceof StandardContext) {
delegate = ((StandardContext) context).getDelegate();
} boolean parseRequired = true;
Set<String> absoluteOrder = application.getAbsoluteOrdering(); if (absoluteOrder != null && absoluteOrder.isEmpty() &&
!context.getXmlValidation()) { // Skip parsing when there is an empty absolute ordering and // validation is not enabled
parseRequired = false;
}
FragmentJarScannerCallback callback = new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);
// Only need to scan for @HandlesTypes matches if any of the // following are true: // - it has already been determined only @HandlesTypes is required // (e.g. main web.xml has metadata-complete="true" // - this fragment is for a container JAR (Servlet 3.1 section 8.1) // - this fragment has metadata-complete="true" boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() ||
fragment.isMetadataComplete();
WebXml annotations = new WebXml(); // no impact on distributable
annotations.setDistributable(true);
URL url = fragment.getURL();
processAnnotationsUrl(url, annotations, htOnly, javaClassCache);
Set<WebXml> set = new HashSet<>();
set.add(annotations); // Merge annotations into fragment - fragment takes priority
fragment.merge(set);
}
/** * Executable task to scan a segment for annotations. Each task does the * same work as the for loop inside processAnnotations();
*/ privateclass AnnotationScanTask implements Runnable { privatefinal WebXml fragment; privatefinalboolean handlesTypesOnly; private Map<String, JavaClassCacheEntry> javaClassCache;
/** * Parallelized version of processAnnotationsInParallel(). Constructs tasks, * submits them as they're created, then waits for completion. * * @param fragments Set of parallelizable scans * @param handlesTypesOnly Important parameter for the underlying scan * @param javaClassCache The class cache
*/ protectedvoid processAnnotationsInParallel(Set<WebXml> fragments, boolean handlesTypesOnly,
Map<String, JavaClassCacheEntry> javaClassCache) {
Server s = getServer();
ExecutorService pool = (s == null) ? null : s.getUtilityExecutor(); if (pool != null) {
List<Future<?>> futures = new ArrayList<>(fragments.size()); for (WebXml fragment : fragments) {
Runnable task = new AnnotationScanTask(fragment, handlesTypesOnly, javaClassCache);
futures.add(pool.submit(task));
} try { for (Future<?> future : futures) {
future.get();
}
} catch (Exception e) { thrownew RuntimeException(sm.getString("contextConfig.processAnnotationsInParallelFailure"), e);
}
} else { // Fallback to regular processing for (WebXml fragment : fragments) {
scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache);
}
}
}
/** * For classes packaged with the web application, the class and each * super class needs to be checked for a match with {@link HandlesTypes} or * for an annotation that matches {@link HandlesTypes}. * @param javaClass the class to check * @param javaClassCache a class cache
*/ protectedvoid checkHandlesTypes(JavaClass javaClass,
Map<String,JavaClassCacheEntry> javaClassCache) {
// Skip this if we can if (typeInitializerMap.size() == 0) { return;
}
Class<?> clazz = null; if (handlesTypesNonAnnotations) { // This *might* be match for a HandlesType.
populateJavaClassCache(className, javaClass, javaClassCache);
JavaClassCacheEntry entry = javaClassCache.get(className); if (entry.getSciSet() == null) { try {
populateSCIsForCacheEntry(entry, javaClassCache);
} catch (StackOverflowError soe) { thrownew IllegalStateException(sm.getString( "contextConfig.annotationsStackOverflow",
context.getName(),
classHierarchyToString(className, entry, javaClassCache)));
}
} if (!entry.getSciSet().isEmpty()) { // Need to try and load the class
clazz = Introspection.loadClass(context, className); if (clazz == null) { // Can't load the class so no point continuing return;
}
for (ServletContainerInitializer sci : entry.getSciSet()) {
Set<Class<?>> classes = initializerClassMap.computeIfAbsent(sci, k -> new HashSet<>());
classes.add(clazz);
}
}
}
if (handlesTypesAnnotations) {
AnnotationEntry[] annotationEntries = javaClass.getAllAnnotationEntries(); if (annotationEntries != null) { for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
typeInitializerMap.entrySet()) { if (entry.getKey().isAnnotation()) {
String entryClassName = entry.getKey().getName(); for (AnnotationEntry annotationEntry : annotationEntries) { if (entryClassName.equals(
getClassName(annotationEntry.getAnnotationType()))) { if (clazz == null) {
clazz = Introspection.loadClass(
context, className); if (clazz == null) { // Can't load the class so no point // continuing return;
}
} for (ServletContainerInitializer sci : entry.getValue()) {
initializerClassMap.get(sci).add(clazz);
} break;
}
}
}
}
}
}
}
privatevoid populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry,
Map<String,JavaClassCacheEntry> javaClassCache) {
Set<ServletContainerInitializer> result = new HashSet<>();
// Super class
String superClassName = cacheEntry.getSuperclassName();
JavaClassCacheEntry superClassCacheEntry =
javaClassCache.get(superClassName);
// Avoid an infinite loop with java.lang.Object if (cacheEntry.equals(superClassCacheEntry)) {
cacheEntry.setSciSet(EMPTY_SCI_SET); return;
}
// May be null of the class is not present or could not be loaded. if (superClassCacheEntry != null) { if (superClassCacheEntry.getSciSet() == null) {
populateSCIsForCacheEntry(superClassCacheEntry, javaClassCache);
}
result.addAll(superClassCacheEntry.getSciSet());
}
result.addAll(getSCIsForClass(superClassName));
// Interfaces for (String interfaceName : cacheEntry.getInterfaceNames()) {
JavaClassCacheEntry interfaceEntry =
javaClassCache.get(interfaceName); // A null could mean that the class not present in application or // that there is nothing of interest. Either way, nothing to do here // so move along if (interfaceEntry != null) { if (interfaceEntry.getSciSet() == null) {
populateSCIsForCacheEntry(interfaceEntry, javaClassCache);
}
result.addAll(interfaceEntry.getSciSet());
}
result.addAll(getSCIsForClass(interfaceName));
}
/** * process filter annotation and merge with existing one! * FIXME: refactoring method too long and has redundant subroutines with * processAnnotationWebServlet! * @param className The filter class name * @param ae The filter annotation * @param fragment The corresponding fragment
*/ protectedvoid processAnnotationWebFilter(String className,
AnnotationEntry ae, WebXml fragment) {
String filterName = null; // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
List<ElementValuePair> evps = ae.getElementValuePairs(); for (ElementValuePair evp : evps) {
String name = evp.getNameString(); if ("filterName".equals(name)) {
filterName = evp.getValue().stringifyValue(); break;
}
} if (filterName == null) { // classname is default filterName as annotation has no name!
filterName = className;
}
FilterDef filterDef = fragment.getFilters().get(filterName);
FilterMap filterMap = new FilterMap();
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.