-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package org.apache.catalina.startup;
-
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
-import java.net.JarURLConnection;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.servlet.ServletContainerInitializer;
-import javax.servlet.ServletContext;
-import javax.servlet.annotation.HandlesTypes;
-
-import org.apache.catalina.Authenticator;
-import org.apache.catalina.Container;
-import org.apache.catalina.Context;
-import org.apache.catalina.Engine;
-import org.apache.catalina.Globals;
-import org.apache.catalina.Host;
-import org.apache.catalina.Lifecycle;
-import org.apache.catalina.LifecycleEvent;
-import org.apache.catalina.LifecycleListener;
-import org.apache.catalina.Pipeline;
-import org.apache.catalina.Server;
-import org.apache.catalina.Service;
-import org.apache.catalina.Valve;
-import org.apache.catalina.Wrapper;
-import org.apache.catalina.core.ContainerBase;
-import org.apache.catalina.core.StandardContext;
-import org.apache.catalina.core.StandardEngine;
-import org.apache.catalina.core.StandardHost;
-import org.apache.catalina.deploy.ErrorPage;
-import org.apache.catalina.deploy.FilterDef;
-import org.apache.catalina.deploy.FilterMap;
-import org.apache.catalina.deploy.LoginConfig;
-import org.apache.catalina.deploy.SecurityConstraint;
-import org.apache.catalina.deploy.ServletDef;
-import org.apache.catalina.deploy.WebXml;
-import org.apache.catalina.util.ContextName;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.naming.resources.DirContextURLConnection;
-import org.apache.naming.resources.ResourceAttributes;
-import org.apache.tomcat.JarScanner;
-import org.apache.tomcat.JarScannerCallback;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.bcel.classfile.AnnotationElementValue;
-import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;
-import org.apache.tomcat.util.bcel.classfile.ArrayElementValue;
-import org.apache.tomcat.util.bcel.classfile.ClassFormatException;
-import org.apache.tomcat.util.bcel.classfile.ClassParser;
-import org.apache.tomcat.util.bcel.classfile.ElementValue;
-import org.apache.tomcat.util.bcel.classfile.ElementValuePair;
-import org.apache.tomcat.util.bcel.classfile.JavaClass;
-import org.apache.tomcat.util.digester.Digester;
-import org.apache.tomcat.util.digester.RuleSet;
-import org.apache.tomcat.util.res.StringManager;
-import org.apache.tomcat.util.scan.Jar;
-import org.apache.tomcat.util.scan.JarFactory;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXParseException;
-
-/**
- * Startup event listener for a <b>Context</b> that configures the properties
- * of that Context, and the associated defined servlets.
- *
- * @author Craig R. McClanahan
- * @author Jean-Francois Arcand
- * @version $Id$
- */
-
-public class ContextConfig
- implements LifecycleListener {
-
- private static final Log log = LogFactory.getLog( ContextConfig.class );
-
- private static final String SCI_LOCATION =
- "META-INF/services/javax.servlet.ServletContainerInitializer";
-
-
- /**
- * The string resources for this package.
- */
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- protected static final LoginConfig DUMMY_LOGIN_CONFIG =
- new LoginConfig("NONE", null, null, null);
-
- /**
- * The <code>Digester</code> we will use to process web application
- * context files.
- */
- protected static Digester contextDigester = null;
-
-
- /**
- * The set of Authenticators that we know how to configure. The key is
- * the name of the implemented authentication method, and the value is
- * the fully qualified Java class name of the corresponding Valve.
- */
- protected static Properties authenticators = null;
-
-
- /**
- * The <code>Digester</code>s available to process web deployment descriptor
- * files.
- */
- protected static Digester[] webDigesters = new Digester[4];
-
-
- /**
- * The <code>Digester</code>s available to process web fragment deployment
- * descriptor files.
- */
- protected static Digester[] webFragmentDigesters = new Digester[4];
-
-
- /**
- * The <code>Rule</code>s used to parse the web.xml
- */
- protected static WebRuleSet webRuleSet = new WebRuleSet(false);
-
-
- /**
- * The <code>Rule</code>s used to parse the web-fragment.xml
- */
- protected static WebRuleSet webFragmentRuleSet = new WebRuleSet(true);
-
-
- /**
- * Deployment count.
- */
- protected static long deploymentCount = 0L;
-
-
- /**
- * Cache of default web.xml fragments per Host
- */
- protected static final Map<Host,DefaultWebXmlCacheEntry> hostWebXmlCache =
- new ConcurrentHashMap<Host,DefaultWebXmlCacheEntry>();
-
-
- // ----------------------------------------------------- Instance Variables
- /**
- * Custom mappings of login methods to authenticators
- */
- protected Map<String,Authenticator> customAuthenticators;
-
-
- /**
- * The Context we are associated with.
- */
- protected Context context = null;
-
-
- /**
- * The default web application's context file location.
- */
- protected String defaultContextXml = null;
-
-
- /**
- * The default web application's deployment descriptor location.
- */
- protected String defaultWebXml = null;
-
-
- /**
- * Track any fatal errors during startup configuration processing.
- */
- protected boolean ok = false;
-
-
- /**
- * Original docBase.
- */
- protected String originalDocBase = null;
-
-
- /**
- * Map of ServletContainerInitializer to classes they expressed interest in.
- */
- protected Map<ServletContainerInitializer, Set<Class<?>>> initializerClassMap =
- new LinkedHashMap<ServletContainerInitializer, Set<Class<?>>>();
-
- /**
- * Map of Types to ServletContainerInitializer that are interested in those
- * types.
- */
- protected Map<Class<?>, Set<ServletContainerInitializer>> typeInitializerMap =
- new HashMap<Class<?>, Set<ServletContainerInitializer>>();
-
- /**
- * The <code>Digester</code> we will use to process web application
- * deployment descriptor files.
- */
- protected Digester webDigester = null;
-
- /**
- * The <code>Digester</code> we will use to process web fragment
- * deployment descriptor files.
- */
- protected Digester webFragmentDigester = null;
-
-
- // ------------------------------------------------------------- Properties
- /**
- * Return the location of the default deployment descriptor
- */
- public String getDefaultWebXml() {
- if( defaultWebXml == null ) {
- defaultWebXml=Constants.DefaultWebXml;
- }
-
- return (this.defaultWebXml);
-
- }
-
-
- /**
- * Set the location of the default deployment descriptor
- *
- * @param path Absolute/relative path to the default web.xml
- */
- public void setDefaultWebXml(String path) {
-
- this.defaultWebXml = path;
-
- }
-
-
- /**
- * Return the location of the default context file
- */
- public String getDefaultContextXml() {
- if( defaultContextXml == null ) {
- defaultContextXml=Constants.DefaultContextXml;
- }
-
- return (this.defaultContextXml);
-
- }
-
-
- /**
- * Set the location of the default context file
- *
- * @param path Absolute/relative path to the default context.xml
- */
- public void setDefaultContextXml(String path) {
-
- this.defaultContextXml = path;
-
- }
-
-
- /**
- * Sets custom mappings of login methods to authenticators.
- *
- * @param customAuthenticators Custom mappings of login methods to
- * authenticators
- */
- public void setCustomAuthenticators(
- Map<String,Authenticator> customAuthenticators) {
- this.customAuthenticators = customAuthenticators;
- }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Process events for an associated Context.
- *
- * @param event The lifecycle event that has occurred
- */
- @Override
- public void lifecycleEvent(LifecycleEvent event) {
-
- // Identify the context we are associated with
- try {
- context = (Context) event.getLifecycle();
- } catch (ClassCastException e) {
- log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
- return;
- }
-
- // Process the event that has occurred
- if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
- configureStart();
- } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
- beforeStart();
- } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
- // Restore docBase for management tools
- if (originalDocBase != null) {
- String docBase = context.getDocBase();
- context.setDocBase(originalDocBase);
- originalDocBase = docBase;
- }
- } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
- if (originalDocBase != null) {
- String docBase = context.getDocBase();
- context.setDocBase(originalDocBase);
- originalDocBase = docBase;
- }
- configureStop();
- } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
- init();
- } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
- destroy();
- }
-
- }
-
-
- // -------------------------------------------------------- protected Methods
-
-
- /**
- * Process the application classes annotations, if it exists.
- */
- protected void applicationAnnotationsConfig() {
-
- long t1=System.currentTimeMillis();
-
- WebAnnotationSet.loadApplicationAnnotations(context);
-
- long t2=System.currentTimeMillis();
- if (context instanceof StandardContext) {
- ((StandardContext) context).setStartupTime(t2-t1+
- ((StandardContext) context).getStartupTime());
- }
- }
-
-
- /**
- * Set up an Authenticator automatically if required, and one has not
- * already been configured.
- */
- protected synchronized void authenticatorConfig() {
-
- LoginConfig loginConfig = context.getLoginConfig();
-
- SecurityConstraint constraints[] = context.findConstraints();
- if (context.getIgnoreAnnotations() &&
- (constraints == null || constraints.length ==0) &&
- !context.getPreemptiveAuthentication()) {
- return;
- } else {
- if (loginConfig == null) {
- // Not metadata-complete or security constraints present, need
- // an authenticator to support @ServletSecurity annotations
- // and/or constraints
- loginConfig = DUMMY_LOGIN_CONFIG;
- context.setLoginConfig(loginConfig);
- }
- }
-
- // Has an authenticator been configured already?
- if (context.getAuthenticator() != null)
- return;
-
- if (!(context instanceof ContainerBase)) {
- return; // Cannot install a Valve even if it would be needed
- }
-
- // 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) {
- // Load our mapping properties if necessary
- if (authenticators == null) {
- try {
- InputStream is=this.getClass().getClassLoader().getResourceAsStream("org/apache/catalina/startup/Authenticators.properties");
- if( is!=null ) {
- authenticators = new Properties();
- authenticators.load(is);
- } else {
- log.error(sm.getString(
- "contextConfig.authenticatorResources"));
- ok=false;
- return;
- }
- } catch (IOException e) {
- log.error(sm.getString(
- "contextConfig.authenticatorResources"), e);
- ok = false;
- return;
- }
- }
-
- // Identify the class name of the Valve we should configure
- String authenticatorName = null;
- 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.newInstance();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString(
- "contextConfig.authenticatorInstantiate",
- authenticatorName),
- t);
- ok = false;
- }
- }
-
- if (authenticator != null && context instanceof ContainerBase) {
- Pipeline pipeline = ((ContainerBase) context).getPipeline();
- if (pipeline != null) {
- ((ContainerBase) context).getPipeline().addValve(authenticator);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString(
- "contextConfig.authenticatorConfigured",
- loginConfig.getAuthMethod()));
- }
- }
- }
-
- }
-
-
- /**
- * Create (if necessary) and return a Digester configured to process the
- * web application deployment descriptor (web.xml).
- */
- public void createWebXmlDigester(boolean namespaceAware,
- boolean validation) {
-
- if (!namespaceAware && !validation) {
- if (webDigesters[0] == null) {
- webDigesters[0] = DigesterFactory.newDigester(validation,
- namespaceAware, webRuleSet);
- webFragmentDigesters[0] = DigesterFactory.newDigester(validation,
- namespaceAware, webFragmentRuleSet);
- webDigesters[0].getParser();
- webFragmentDigesters[0].getParser();
- }
- webDigester = webDigesters[0];
- webFragmentDigester = webFragmentDigesters[0];
-
- } else if (!namespaceAware && validation) {
- if (webDigesters[1] == null) {
- webDigesters[1] = DigesterFactory.newDigester(validation,
- namespaceAware, webRuleSet);
- webFragmentDigesters[1] = DigesterFactory.newDigester(validation,
- namespaceAware, webFragmentRuleSet);
- webDigesters[1].getParser();
- webFragmentDigesters[1].getParser();
- }
- webDigester = webDigesters[1];
- webFragmentDigester = webFragmentDigesters[1];
-
- } else if (namespaceAware && !validation) {
- if (webDigesters[2] == null) {
- webDigesters[2] = DigesterFactory.newDigester(validation,
- namespaceAware, webRuleSet);
- webFragmentDigesters[2] = DigesterFactory.newDigester(validation,
- namespaceAware, webFragmentRuleSet);
- webDigesters[2].getParser();
- webFragmentDigesters[2].getParser();
- }
- webDigester = webDigesters[2];
- webFragmentDigester = webFragmentDigesters[2];
-
- } else {
- if (webDigesters[3] == null) {
- webDigesters[3] = DigesterFactory.newDigester(validation,
- namespaceAware, webRuleSet);
- webFragmentDigesters[3] = DigesterFactory.newDigester(validation,
- namespaceAware, webFragmentRuleSet);
- webDigesters[3].getParser();
- webFragmentDigesters[3].getParser();
- }
- webDigester = webDigesters[3];
- webFragmentDigester = webFragmentDigesters[3];
- }
- }
-
-
- /**
- * Create (if necessary) and return a Digester configured to process the
- * context configuration descriptor for an application.
- */
- protected Digester createContextDigester() {
- Digester digester = new Digester();
- digester.setValidating(false);
- digester.setRulesValidation(true);
- HashMap<Class<?>, List<String>> fakeAttributes =
- new HashMap<Class<?>, List<String>>();
- ArrayList<String> attrs = new ArrayList<String>();
- attrs.add("className");
- fakeAttributes.put(Object.class, attrs);
- digester.setFakeAttributes(fakeAttributes);
- RuleSet contextRuleSet = new ContextRuleSet("", false);
- digester.addRuleSet(contextRuleSet);
- RuleSet namingRuleSet = new NamingRuleSet("Context/");
- digester.addRuleSet(namingRuleSet);
- return digester;
- }
-
-
- protected String getBaseDir() {
- Container engineC=context.getParent().getParent();
- if( engineC instanceof StandardEngine ) {
- return ((StandardEngine)engineC).getBaseDir();
- }
- return System.getProperty(Globals.CATALINA_BASE_PROP);
- }
-
-
- /**
- * Process the default configuration file, if it exists.
- */
- protected void contextConfig() {
-
- // Open the default context.xml file, if it exists
- if( defaultContextXml==null && context instanceof StandardContext ) {
- defaultContextXml = ((StandardContext)context).getDefaultContextXml();
- }
- // set the default if we don't have any overrides
- if( defaultContextXml==null ) getDefaultContextXml();
-
- if (!context.getOverride()) {
- File defaultContextFile = new File(defaultContextXml);
- if (!defaultContextFile.isAbsolute()) {
- defaultContextFile =new File(getBaseDir(), defaultContextXml);
- }
- if (defaultContextFile.exists()) {
- try {
- URL defaultContextUrl = defaultContextFile.toURI().toURL();
- processContextConfig(defaultContextUrl);
- } catch (MalformedURLException e) {
- log.error(sm.getString(
- "contextConfig.badUrl", defaultContextFile), e);
- }
- }
-
- File hostContextFile = new File(getConfigBase(),
- getHostConfigPath(Constants.HostContextXml));
- if (hostContextFile.exists()) {
- try {
- URL hostContextUrl = hostContextFile.toURI().toURL();
- processContextConfig(hostContextUrl);
- } catch (MalformedURLException e) {
- log.error(sm.getString(
- "contextConfig.badUrl", hostContextFile), e);
- }
- }
- }
- if (context.getConfigFile() != null)
- processContextConfig(context.getConfigFile());
-
- }
-
-
- /**
- * Process a context.xml.
- */
- protected void processContextConfig(URL contextXml) {
-
- if (log.isDebugEnabled())
- log.debug("Processing context [" + context.getName()
- + "] configuration file [" + contextXml + "]");
-
- InputSource source = null;
- InputStream stream = null;
-
- try {
- source = new InputSource(contextXml.toString());
- stream = contextXml.openStream();
-
- // Add as watched resource so that cascade reload occurs if a default
- // config file is modified/added/removed
- if ("file".equals(contextXml.getProtocol())) {
- context.addWatchedResource(
- (new File(contextXml.toURI())).getAbsolutePath());
- }
- } catch (Exception e) {
- log.error(sm.getString("contextConfig.contextMissing",
- contextXml) , e);
- }
-
- if (source == null)
- return;
- synchronized (contextDigester) {
- try {
- source.setByteStream(stream);
- contextDigester.setClassLoader(this.getClass().getClassLoader());
- contextDigester.setUseContextClassLoader(false);
- contextDigester.push(context.getParent());
- contextDigester.push(context);
- XmlErrorHandler errorHandler = new XmlErrorHandler();
- contextDigester.setErrorHandler(errorHandler);
- contextDigester.parse(source);
- if (errorHandler.getWarnings().size() > 0 ||
- errorHandler.getErrors().size() > 0) {
- errorHandler.logFindings(log, contextXml.toString());
- ok = false;
- }
- if (log.isDebugEnabled())
- log.debug("Successfully processed context [" + context.getName()
- + "] configuration file [" + contextXml + "]");
- } catch (SAXParseException e) {
- log.error(sm.getString("contextConfig.contextParse",
- context.getName()), e);
- log.error(sm.getString("contextConfig.defaultPosition",
- "" + e.getLineNumber(),
- "" + e.getColumnNumber()));
- ok = false;
- } catch (Exception e) {
- log.error(sm.getString("contextConfig.contextParse",
- context.getName()), e);
- ok = false;
- } finally {
- contextDigester.reset();
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException e) {
- log.error(sm.getString("contextConfig.contextClose"), e);
- }
- }
- }
- }
-
-
- /**
- * Adjust docBase.
- */
- protected void fixDocBase()
- throws IOException {
-
- Host host = (Host) context.getParent();
- File appBase = host.getAppBaseFile();
-
- String docBase = context.getDocBase();
- if (docBase == null) {
- // Trying to guess the docBase according to the path
- String path = context.getPath();
- if (path == null) {
- return;
- }
- ContextName cn = new ContextName(path, context.getWebappVersion());
- docBase = cn.getBaseName();
- }
-
- File file = new File(docBase);
- if (!file.isAbsolute()) {
- docBase = (new File(appBase, docBase)).getPath();
- } else {
- docBase = file.getCanonicalPath();
- }
- file = new File(docBase);
- String origDocBase = docBase;
-
- ContextName cn = new ContextName(context.getPath(),
- context.getWebappVersion());
- String pathName = cn.getBaseName();
-
- boolean unpackWARs = true;
- if (host instanceof StandardHost) {
- unpackWARs = ((StandardHost) host).isUnpackWARs() &&
- ((StandardContext) context).getUnpackWAR() &&
- (docBase.startsWith(host.getAppBaseFile().getPath()));
- }
-
- if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory() && unpackWARs) {
- URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/");
- docBase = ExpandWar.expand(host, war, pathName);
- file = new File(docBase);
- docBase = file.getCanonicalPath();
- if (context instanceof StandardContext) {
- ((StandardContext) context).setOriginalDocBase(origDocBase);
- }
- } else if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") &&
- !file.isDirectory() && !unpackWARs) {
- URL war =
- new URL("jar:" + (new File (docBase)).toURI().toURL() + "!/");
- ExpandWar.validate(host, war, pathName);
- } else {
- File docDir = new File(docBase);
- if (!docDir.exists()) {
- File warFile = new File(docBase + ".war");
- if (warFile.exists()) {
- URL war =
- new URL("jar:" + warFile.toURI().toURL() + "!/");
- if (unpackWARs) {
- docBase = ExpandWar.expand(host, war, pathName);
- file = new File(docBase);
- docBase = file.getCanonicalPath();
- } else {
- docBase = warFile.getCanonicalPath();
- ExpandWar.validate(host, war, pathName);
- }
- }
- if (context instanceof StandardContext) {
- ((StandardContext) context).setOriginalDocBase(origDocBase);
- }
- }
- }
-
- if (docBase.startsWith(appBase.getPath() + File.separatorChar)) {
- docBase = docBase.substring(appBase.getPath().length());
- docBase = docBase.replace(File.separatorChar, '/');
- if (docBase.startsWith("/")) {
- docBase = docBase.substring(1);
- }
- } else {
- docBase = docBase.replace(File.separatorChar, '/');
- }
-
- context.setDocBase(docBase);
-
- }
-
-
- protected void antiLocking() {
-
- if ((context instanceof StandardContext)
- && ((StandardContext) context).getAntiResourceLocking()) {
-
- Host host = (Host) context.getParent();
- String docBase = context.getDocBase();
- if (docBase == null)
- return;
- if (originalDocBase == null) {
- originalDocBase = docBase;
- } else {
- docBase = 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();
-
- File file = null;
- if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
- // TODO - This is never executed. Bug or code to delete?
- file = new File(System.getProperty("java.io.tmpdir"),
- deploymentCount++ + "-" + docBase + ".war");
- } else {
- file = new File(System.getProperty("java.io.tmpdir"),
- deploymentCount++ + "-" + docBase);
- }
-
- if (log.isDebugEnabled())
- log.debug("Anti locking context[" + context.getName()
- + "] setting docBase to " + file);
-
- // Cleanup just in case an old deployment is lying around
- ExpandWar.delete(file);
- if (ExpandWar.copy(docBaseFile, file)) {
- context.setDocBase(file.getAbsolutePath());
- }
-
- }
-
- }
-
-
- /**
- * Process a "init" event for this Context.
- */
- protected void init() {
- // Called from StandardContext.init()
-
- if (contextDigester == null){
- contextDigester = createContextDigester();
- contextDigester.getParser();
- }
-
- if (log.isDebugEnabled())
- log.debug(sm.getString("contextConfig.init"));
- context.setConfigured(false);
- ok = true;
-
- contextConfig();
-
- createWebXmlDigester(context.getXmlNamespaceAware(),
- context.getXmlValidation());
-
- try {
- fixDocBase();
- } catch (IOException e) {
- log.error(sm.getString(
- "contextConfig.fixDocBase", context.getName()), e);
- }
-
- }
-
-
- /**
- * Process a "before start" event for this Context.
- */
- protected synchronized void beforeStart() {
-
- antiLocking();
-
- }
-
-
- /**
- * Process a "contextConfig" event for this Context.
- */
- protected synchronized void configureStart() {
- // Called from StandardContext.start()
-
- if (log.isDebugEnabled())
- log.debug(sm.getString("contextConfig.start"));
-
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("contextConfig.xmlSettings",
- context.getName(),
- Boolean.valueOf(context.getXmlValidation()),
- Boolean.valueOf(context.getXmlNamespaceAware())));
- }
-
- webConfig();
-
- if (!context.getIgnoreAnnotations()) {
- applicationAnnotationsConfig();
- }
- if (ok) {
- validateSecurityRoles();
- }
-
- // Configure an authenticator if we need one
- if (ok)
- authenticatorConfig();
-
- // Dump the contents of this pipeline if requested
- if ((log.isDebugEnabled()) && (context instanceof ContainerBase)) {
- log.debug("Pipeline Configuration:");
- Pipeline pipeline = ((ContainerBase) context).getPipeline();
- Valve valves[] = null;
- if (pipeline != null)
- valves = pipeline.getValves();
- if (valves != null) {
- for (int i = 0; i < valves.length; i++) {
- log.debug(" " + valves[i].getInfo());
- }
- }
- log.debug("======================");
- }
-
- // Make our application available if no problems were encountered
- if (ok)
- context.setConfigured(true);
- else {
- log.error(sm.getString("contextConfig.unavailable"));
- context.setConfigured(false);
- }
-
- }
-
-
- /**
- * Process a "stop" event for this Context.
- */
- protected synchronized void configureStop() {
-
- if (log.isDebugEnabled())
- log.debug(sm.getString("contextConfig.stop"));
-
- int i;
-
- // Removing children
- Container[] children = context.findChildren();
- for (i = 0; i < children.length; i++) {
- context.removeChild(children[i]);
- }
-
- // Removing application parameters
- /*
- ApplicationParameter[] applicationParameters =
- context.findApplicationParameters();
- for (i = 0; i < applicationParameters.length; i++) {
- context.removeApplicationParameter
- (applicationParameters[i].getName());
- }
- */
-
- // Removing security constraints
- SecurityConstraint[] securityConstraints = context.findConstraints();
- for (i = 0; i < securityConstraints.length; i++) {
- context.removeConstraint(securityConstraints[i]);
- }
-
- // Removing Ejbs
- /*
- ContextEjb[] contextEjbs = context.findEjbs();
- for (i = 0; i < contextEjbs.length; i++) {
- context.removeEjb(contextEjbs[i].getName());
- }
- */
-
- // Removing environments
- /*
- ContextEnvironment[] contextEnvironments = context.findEnvironments();
- for (i = 0; i < contextEnvironments.length; i++) {
- context.removeEnvironment(contextEnvironments[i].getName());
- }
- */
-
- // Removing errors pages
- ErrorPage[] errorPages = context.findErrorPages();
- for (i = 0; i < errorPages.length; i++) {
- context.removeErrorPage(errorPages[i]);
- }
-
- // Removing filter defs
- FilterDef[] filterDefs = context.findFilterDefs();
- for (i = 0; i < filterDefs.length; i++) {
- context.removeFilterDef(filterDefs[i]);
- }
-
- // Removing filter maps
- FilterMap[] filterMaps = context.findFilterMaps();
- for (i = 0; i < filterMaps.length; i++) {
- context.removeFilterMap(filterMaps[i]);
- }
-
- // Removing local ejbs
- /*
- ContextLocalEjb[] contextLocalEjbs = context.findLocalEjbs();
- for (i = 0; i < contextLocalEjbs.length; i++) {
- context.removeLocalEjb(contextLocalEjbs[i].getName());
- }
- */
-
- // Removing Mime mappings
- String[] mimeMappings = context.findMimeMappings();
- for (i = 0; i < mimeMappings.length; i++) {
- context.removeMimeMapping(mimeMappings[i]);
- }
-
- // Removing parameters
- String[] parameters = context.findParameters();
- for (i = 0; i < parameters.length; i++) {
- context.removeParameter(parameters[i]);
- }
-
- // Removing resource env refs
- /*
- String[] resourceEnvRefs = context.findResourceEnvRefs();
- for (i = 0; i < resourceEnvRefs.length; i++) {
- context.removeResourceEnvRef(resourceEnvRefs[i]);
- }
- */
-
- // Removing resource links
- /*
- ContextResourceLink[] contextResourceLinks =
- context.findResourceLinks();
- for (i = 0; i < contextResourceLinks.length; i++) {
- context.removeResourceLink(contextResourceLinks[i].getName());
- }
- */
-
- // Removing resources
- /*
- ContextResource[] contextResources = context.findResources();
- for (i = 0; i < contextResources.length; i++) {
- context.removeResource(contextResources[i].getName());
- }
- */
-
- // Removing security role
- String[] securityRoles = context.findSecurityRoles();
- for (i = 0; i < securityRoles.length; i++) {
- context.removeSecurityRole(securityRoles[i]);
- }
-
- // Removing servlet mappings
- String[] servletMappings = context.findServletMappings();
- for (i = 0; i < servletMappings.length; i++) {
- context.removeServletMapping(servletMappings[i]);
- }
-
- // FIXME : Removing status pages
-
- // Removing welcome files
- String[] welcomeFiles = context.findWelcomeFiles();
- for (i = 0; i < welcomeFiles.length; i++) {
- context.removeWelcomeFile(welcomeFiles[i]);
- }
-
- // Removing wrapper lifecycles
- String[] wrapperLifecycles = context.findWrapperLifecycles();
- for (i = 0; i < wrapperLifecycles.length; i++) {
- context.removeWrapperLifecycle(wrapperLifecycles[i]);
- }
-
- // Removing wrapper listeners
- String[] wrapperListeners = context.findWrapperListeners();
- for (i = 0; i < wrapperListeners.length; i++) {
- context.removeWrapperListener(wrapperListeners[i]);
- }
-
- // Remove (partially) folders and files created by antiLocking
- Host host = (Host) context.getParent();
- String docBase = context.getDocBase();
- if ((docBase != null) && (originalDocBase != null)) {
- File docBaseFile = new File(docBase);
- if (!docBaseFile.isAbsolute()) {
- docBaseFile = new File(host.getAppBaseFile(), docBase);
- }
- // No need to log failure - it is expected in this case
- ExpandWar.delete(docBaseFile, false);
- }
-
- // Reset ServletContextInitializer scanning
- initializerClassMap.clear();
- typeInitializerMap.clear();
-
- ok = true;
-
- }
-
-
- /**
- * Process a "destroy" event for this Context.
- */
- protected synchronized void destroy() {
- // Called from StandardContext.destroy()
- if (log.isDebugEnabled())
- log.debug(sm.getString("contextConfig.destroy"));
-
- // Skip clearing the work directory if Tomcat is being shutdown
- Server s = getServer();
- if (s != null && !s.getState().isAvailable()) {
- return;
- }
-
- // Changed to getWorkPath per Bugzilla 35819.
- String workDir = ((StandardContext) context).getWorkPath();
- if (workDir != null)
- ExpandWar.delete(new File(workDir));
- }
-
-
- private Server getServer() {
- Container c = context;
- while (c != null && !(c instanceof Engine)) {
- c = c.getParent();
- }
-
- if (c == null) {
- return null;
- }
-
- Service s = ((Engine)c).getService();
-
- if (s == null) {
- return null;
- }
-
- return s.getServer();
- }
-
- /**
- * Validate the usage of security role names in the web application
- * deployment descriptor. If any problems are found, issue warning
- * messages (for backwards compatibility) and add the missing roles.
- * (To make these problems fatal instead, simply set the <code>ok</code>
- * instance variable to <code>false</code> as well).
- */
- protected void validateSecurityRoles() {
-
- // Check role names used in <security-constraint> elements
- SecurityConstraint constraints[] = context.findConstraints();
- for (int i = 0; i < constraints.length; i++) {
- String roles[] = constraints[i].findAuthRoles();
- for (int j = 0; j < roles.length; j++) {
- if (!"*".equals(roles[j]) &&
- !context.findSecurityRole(roles[j])) {
- log.info(sm.getString("contextConfig.role.auth", roles[j]));
- context.addSecurityRole(roles[j]);
- }
- }
- }
-
- // Check role names used in <servlet> elements
- Container wrappers[] = context.findChildren();
- for (int i = 0; i < wrappers.length; i++) {
- Wrapper wrapper = (Wrapper) wrappers[i];
- String runAs = wrapper.getRunAs();
- if ((runAs != null) && !context.findSecurityRole(runAs)) {
- log.info(sm.getString("contextConfig.role.runas", runAs));
- context.addSecurityRole(runAs);
- }
- String names[] = wrapper.findSecurityReferences();
- for (int j = 0; j < names.length; j++) {
- String link = wrapper.findSecurityReference(names[j]);
- if ((link != null) && !context.findSecurityRole(link)) {
- log.info(sm.getString("contextConfig.role.link", link));
- context.addSecurityRole(link);
- }
- }
- }
-
- }
-
-
- /**
- * Get config base.
- */
- protected File getConfigBase() {
- File configBase =
- new File(System.getProperty(Globals.CATALINA_BASE_PROP), "conf");
- if (!configBase.exists()) {
- return null;
- }
- return configBase;
- }
-
-
- protected String getHostConfigPath(String resourceName) {
- StringBuilder result = new StringBuilder();
- Container container = context;
- Container host = null;
- Container engine = null;
- while (container != null) {
- if (container instanceof Host)
- host = container;
- if (container instanceof Engine)
- engine = container;
- container = container.getParent();
- }
- if (engine != null) {
- result.append(engine.getName()).append('/');
- }
- if (host != null) {
- result.append(host.getName()).append('/');
- }
- result.append(resourceName);
- return result.toString();
- }
-
-
- /**
- * Scan the web.xml files that apply to the web application and merge them
- * using the rules defined in the spec. For the global web.xml files,
- * where there is duplicate configuration, the most specific level wins. ie
- * an application's web.xml takes precedence over the host level or global
- * web.xml file.
- */
- protected void webConfig() {
- /* Anything and everything can override the global and host defaults.
- * This is implemented in two parts
- * - Handle as a web fragment that gets added after everything else so
- * everything else takes priority
- * - Mark Servlets as overridable so SCI configuration can replace
- * configuration from the defaults
- */
- Set<WebXml> defaults = new HashSet<WebXml>();
- defaults.add(getDefaultWebXmlFragment());
-
- WebXml webXml = createWebXml();
-
- // Parse context level web.xml
- InputSource contextWebXml = getContextWebXmlSource();
- parseWebXml(contextWebXml, webXml, false);
-
- if (webXml.getMajorVersion() >= 3) {
- // Ordering is important here
-
- // Step 1. Identify all the JARs packaged with the application
- // If the JARs have a web-fragment.xml it will be parsed at this
- // point.
- Map<String,WebXml> fragments = processJarsForWebFragments();
-
- // Only need to process fragments and annotations if metadata is
- // not complete
- Set<WebXml> orderedFragments = null;
- if (!webXml.isMetadataComplete()) {
- // Step 2. Order the fragments.
- orderedFragments = WebXml.orderWebFragments(webXml, fragments);
-
- // Step 3. Look for ServletContainerInitializer implementations
- if (ok) {
- processServletContainerInitializers(orderedFragments);
- }
-
- // Step 4. Process /WEB-INF/classes for annotations
- // This will add any matching classes to the typeInitializerMap
- if (ok) {
- URL webinfClasses;
- try {
- webinfClasses = context.getServletContext().getResource(
- "/WEB-INF/classes");
- processAnnotationsUrl(webinfClasses, webXml);
- } catch (MalformedURLException e) {
- log.error(sm.getString(
- "contextConfig.webinfClassesUrl"), e);
- }
- }
-
- // Step 5. Process JARs for annotations - only need to process
- // those fragments we are going to use
- // This will add any matching classes to the typeInitializerMap
- if (ok) {
- processAnnotations(orderedFragments);
- }
-
- // Step 6. Merge web-fragment.xml files into the main web.xml
- // file.
- if (ok) {
- ok = webXml.merge(orderedFragments);
- }
-
- // Step 7. Apply global defaults
- // Have to merge defaults before JSP conversion since defaults
- // provide JSP servlet definition.
- webXml.merge(defaults);
-
- // Step 8. Convert explicitly mentioned jsps to servlets
- if (ok) {
- convertJsps(webXml);
- }
-
- // Step 9. Apply merged web.xml to Context
- if (ok) {
- webXml.configureContext(context);
-
- // Step 9a. Make the merged web.xml available to other
- // components, specifically Jasper, to save those components
- // from having to re-generate it.
- // TODO Use a ServletContainerInitializer for Jasper
- String mergedWebXml = webXml.toXml();
- context.getServletContext().setAttribute(
- org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,
- mergedWebXml);
- if (context.getLogEffectiveWebXml()) {
- log.info("web.xml:\n" + mergedWebXml);
- }
- }
- } else {
- webXml.merge(defaults);
- webXml.configureContext(context);
- }
-
- // 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<WebXml>();
- if (orderedFragments != null) {
- for (WebXml fragment : orderedFragments) {
- resourceJars.add(fragment);
- }
- }
- for (WebXml fragment : fragments.values()) {
- if (!resourceJars.contains(fragment)) {
- resourceJars.add(fragment);
- }
- }
- processResourceJARs(resourceJars);
- // See also StandardContext.resourcesStart() for
- // WEB-INF/classes/META-INF/resources configuration
- }
-
- // Only look for ServletContainerInitializer if metadata is not
- // complete
- if (!webXml.isMetadataComplete()) {
- // 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());
- }
- }
- }
- }
- } else {
- // Apply unmerged web.xml to Context
- webXml.merge(defaults);
- convertJsps(webXml);
- webXml.configureContext(context);
- }
- }
-
- private WebXml getDefaultWebXmlFragment() {
-
- // Host should never be null
- Host host = (Host) context.getParent();
-
- DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
-
- InputSource globalWebXml = getGlobalWebXmlSource();
- InputSource hostWebXml = getHostWebXmlSource();
-
- long globalTimeStamp = 0;
- long hostTimeStamp = 0;
-
- if (globalWebXml != null) {
- try {
- File f = new File(new URI(globalWebXml.getSystemId()));
- globalTimeStamp = f.lastModified();
- } catch (URISyntaxException e) {
- globalTimeStamp = -1;
- }
- }
-
- if (hostWebXml != null) {
- try {
- File f = new File(new URI(hostWebXml.getSystemId()));
- hostTimeStamp = f.lastModified();
- } catch (URISyntaxException e) {
- hostTimeStamp = -1;
- }
- }
-
- if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
- entry.getHostTimeStamp() == hostTimeStamp) {
- return entry.getWebXml();
- }
-
- // Parsing global web.xml is relatively expensive. Use a sync block to
- // make sure it only happens once
- synchronized (host) {
- 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 {
- parseWebXml(globalWebXml, webXmlDefaultFragment, false);
- }
-
- // Parse host level web.xml if present
- // Additive apart from welcome pages
- webXmlDefaultFragment.setReplaceWelcomeFiles(true);
-
- parseWebXml(hostWebXml, webXmlDefaultFragment, false);
-
- // Don't update the cache if an error occurs
- if (globalTimeStamp != -1 && hostTimeStamp != -1) {
- entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
- globalTimeStamp, hostTimeStamp);
- hostWebXmlCache.put(host, entry);
- }
-
- return webXmlDefaultFragment;
- }
- }
-
-
- private void convertJsps(WebXml webXml) {
- Map<String,String> jspInitParams;
- ServletDef jspServlet = webXml.getServlets().get("jsp");
- if (jspServlet == null) {
- jspInitParams = new HashMap<String,String>();
- Wrapper w = (Wrapper) context.findChild("jsp");
- if (w != null) {
- String[] params = w.findInitParameters();
- for (String param : params) {
- jspInitParams.put(param, w.findInitParameter(param));
- }
- }
- } else {
- jspInitParams = jspServlet.getParameterMap();
- }
- for (ServletDef servletDef: webXml.getServlets().values()) {
- if (servletDef.getJspFile() != null) {
- convertJsp(servletDef, jspInitParams);
- }
- }
- }
-
- private void convertJsp(ServletDef servletDef,
- Map<String,String> jspInitParams) {
- servletDef.setServletClass(org.apache.catalina.core.Constants.JSP_SERVLET_CLASS);
- String jspFile = servletDef.getJspFile();
- if ((jspFile != null) && !jspFile.startsWith("/")) {
- if (context.isServlet22()) {
- if(log.isDebugEnabled())
- log.debug(sm.getString("contextConfig.jspFile.warning",
- jspFile));
- jspFile = "/" + jspFile;
- } else {
- throw new IllegalArgumentException
- (sm.getString("contextConfig.jspFile.error", jspFile));
- }
- }
- servletDef.getParameterMap().put("jspFile", jspFile);
- servletDef.setJspFile(null);
- for (Map.Entry<String, String> initParam: jspInitParams.entrySet()) {
- servletDef.addInitParameter(initParam.getKey(), initParam.getValue());
- }
- }
-
- protected WebXml createWebXml() {
- return new WebXml();
- }
-
- /**
- * Scan JARs for ServletContainerInitializer implementations.
- * Implementations will be added in web-fragment.xml priority order.
- */
- protected void processServletContainerInitializers(
- Set<WebXml> fragments) {
-
- for (WebXml fragment : fragments) {
- URL url = fragment.getURL();
- Jar jar = null;
- InputStream is = null;
- ServletContainerInitializer sci = null;
- try {
- if ("jar".equals(url.getProtocol())) {
- jar = JarFactory.newInstance(url);
- is = jar.getInputStream(SCI_LOCATION);
- } else if ("file".equals(url.getProtocol())) {
- String path = url.getPath();
- File file = new File(path, SCI_LOCATION);
- if (file.exists()) {
- is = new FileInputStream(file);
- }
- }
- if (is != null) {
- sci = getServletContainerInitializer(is);
- }
- } catch (IOException ioe) {
- log.error(sm.getString(
- "contextConfig.servletContainerInitializerFail", url,
- context.getName()));
- ok = false;
- return;
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- if (jar != null) {
- jar.close();
- }
- }
-
- if (sci == null) {
- continue;
- }
-
- initializerClassMap.put(sci, new HashSet<Class<?>>());
-
- HandlesTypes ht =
- sci.getClass().getAnnotation(HandlesTypes.class);
- if (ht != null) {
- Class<?>[] types = ht.value();
- if (types != null) {
- for (Class<?> type : types) {
- Set<ServletContainerInitializer> scis =
- typeInitializerMap.get(type);
- if (scis == null) {
- scis = new HashSet<ServletContainerInitializer>();
- typeInitializerMap.put(type, scis);
- }
- scis.add(sci);
- }
- }
- }
-
- }
- }
-
-
- /**
- * Extract the name of the ServletContainerInitializer.
- *
- * @param is The resource where the name is defined
- * @return The class name
- * @throws IOException
- */
- protected ServletContainerInitializer getServletContainerInitializer(
- InputStream is) throws IOException {
-
- String className = null;
-
- if (is != null) {
- String line = null;
- try {
- BufferedReader br =
- new BufferedReader(new InputStreamReader(is, "UTF-8"));
- line = br.readLine();
- if (line != null && line.trim().length() > 0) {
- className = line.trim();
- }
- } catch (UnsupportedEncodingException e) {
- // Should never happen with UTF-8
- // If it does - ignore & return null
- }
- }
-
- ServletContainerInitializer sci = null;
- try {
- Class<?> clazz = Class.forName(className,true,
- context.getLoader().getClassLoader());
- sci = (ServletContainerInitializer) clazz.newInstance();
- } catch (ClassNotFoundException e) {
- log.error(sm.getString("contextConfig.invalidSci", className), e);
- throw new IOException(e);
- } catch (InstantiationException e) {
- log.error(sm.getString("contextConfig.invalidSci", className), e);
- throw new IOException(e);
- } catch (IllegalAccessException e) {
- log.error(sm.getString("contextConfig.invalidSci", className), e);
- throw new IOException(e);
- }
-
- return 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.
- */
- protected void processResourceJARs(Set<WebXml> fragments) {
- for (WebXml fragment : fragments) {
- URL url = fragment.getURL();
- Jar jar = null;
- try {
- // Note: Ignore file URLs for now since only jar URLs will be accepted
- if ("jar".equals(url.getProtocol())) {
- jar = JarFactory.newInstance(url);
- if (jar.entryExists("META-INF/resources/")) {
- context.addResourceJarUrl(url);
- }
- }
- } catch (IOException ioe) {
- log.error(sm.getString("contextConfig.resourceJarFail", url,
- context.getName()));
- } finally {
- if (jar != null) {
- jar.close();
- }
- }
- }
- }
-
-
- /**
- * Identify the default web.xml to be used and obtain an input source for
- * it.
- */
- protected InputSource getGlobalWebXmlSource() {
- // Is a default web.xml specified for the Context?
- if (defaultWebXml == null && context instanceof StandardContext) {
- defaultWebXml = ((StandardContext) context).getDefaultWebXml();
- }
- // Set the default if we don't have any overrides
- if (defaultWebXml == null) getDefaultWebXml();
-
- // Is it explicitly suppressed, e.g. in embedded environment?
- if (Constants.NoDefaultWebXml.equals(defaultWebXml)) {
- return null;
- }
- return getWebXmlSource(defaultWebXml, getBaseDir());
- }
-
-
- /**
- * Identify the host web.xml to be used and obtain an input source for
- * it.
- */
- protected InputSource getHostWebXmlSource() {
- String resourceName = getHostConfigPath(Constants.HostWebXml);
-
- // In an embedded environment, configBase might not be set
- File configBase = getConfigBase();
- if (configBase == null)
- return null;
-
- String basePath = null;
- try {
- basePath = configBase.getCanonicalPath();
- } catch (IOException e) {
- log.error(sm.getString("contectConfig.baseError"), e);
- return null;
- }
-
- return getWebXmlSource(resourceName, basePath);
- }
-
- /**
- * Identify the application web.xml to be used and obtain an input source
- * for it.
- */
- protected InputSource getContextWebXmlSource() {
- InputStream stream = null;
- InputSource source = null;
- URL url = null;
-
- String altDDName = null;
-
- // Open the application web.xml file, if it exists
- ServletContext servletContext = context.getServletContext();
- if (servletContext != null) {
- altDDName = (String)servletContext.getAttribute(
- Globals.ALT_DD_ATTR);
- if (altDDName != null) {
- try {
- stream = new FileInputStream(altDDName);
- url = new File(altDDName).toURI().toURL();
- } catch (FileNotFoundException e) {
- log.error(sm.getString("contextConfig.altDDNotFound",
- altDDName));
- } catch (MalformedURLException e) {
- log.error(sm.getString("contextConfig.applicationUrl"));
- }
- }
- else {
- stream = servletContext.getResourceAsStream
- (Constants.ApplicationWebXml);
- try {
- url = servletContext.getResource(
- Constants.ApplicationWebXml);
- } catch (MalformedURLException e) {
- log.error(sm.getString("contextConfig.applicationUrl"));
- }
- }
- }
- if (stream == null || url == null) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("contextConfig.applicationMissing") + " " + context);
- }
- } else {
- source = new InputSource(url.toExternalForm());
- source.setByteStream(stream);
- }
-
- return source;
- }
-
- /**
- *
- * @param filename Name of the file (possibly with one or more leading path
- * segments) to read
- * @param path Location that filename is relative to
- */
- protected InputSource getWebXmlSource(String filename, String path) {
- File file = new File(filename);
- if (!file.isAbsolute()) {
- file = new File(path, filename);
- }
-
- InputStream stream = null;
- InputSource source = null;
-
- try {
- if (!file.exists()) {
- // Use getResource and getResourceAsStream
- stream =
- getClass().getClassLoader().getResourceAsStream(filename);
- if(stream != null) {
- source =
- new InputSource(getClass().getClassLoader().getResource(
- filename).toURI().toString());
- }
- } else {
- source = new InputSource(file.getAbsoluteFile().toURI().toString());
- stream = new FileInputStream(file);
- context.addWatchedResource(file.getAbsolutePath());
- }
-
- if (stream != null && source != null) {
- source.setByteStream(stream);
- }
- } catch (Exception e) {
- log.error(sm.getString(
- "contextConfig.defaultError", filename, file), e);
- }
-
- return source;
- }
-
-
- protected void parseWebXml(InputSource source, WebXml dest,
- boolean fragment) {
-
- if (source == null) return;
-
- XmlErrorHandler handler = new XmlErrorHandler();
-
- // Web digesters and rulesets are shared between contexts but are not
- // thread safe. Whilst there should only be one thread at a time
- // processing a config, play safe and sync.
- Digester digester;
- WebRuleSet ruleSet;
- if (fragment) {
- digester = webFragmentDigester;
- ruleSet = webFragmentRuleSet;
- } else {
- digester = webDigester;
- ruleSet = webRuleSet;
- }
-
- // Sync on the ruleSet since the same ruleSet is shared across all four
- // digesters
- synchronized(ruleSet) {
-
- digester.push(dest);
- digester.setErrorHandler(handler);
-
- if(log.isDebugEnabled()) {
- log.debug(sm.getString("contextConfig.applicationStart",
- source.getSystemId()));
- }
-
- try {
- digester.parse(source);
-
- if (handler.getWarnings().size() > 0 ||
- handler.getErrors().size() > 0) {
- ok = false;
- handler.logFindings(log, source.getSystemId());
- }
- } catch (SAXParseException e) {
- log.error(sm.getString("contextConfig.applicationParse",
- source.getSystemId()), e);
- log.error(sm.getString("contextConfig.applicationPosition",
- "" + e.getLineNumber(),
- "" + e.getColumnNumber()));
- ok = false;
- } catch (Exception e) {
- log.error(sm.getString("contextConfig.applicationParse",
- source.getSystemId()), e);
- ok = false;
- } finally {
- digester.reset();
- ruleSet.recycle();
- }
- }
- }
-
-
- /**
- * Scan /META-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.
- *
- * @return A map of JAR name to processed web fragment (if any)
- */
- protected Map<String,WebXml> processJarsForWebFragments() {
-
- JarScanner jarScanner = context.getJarScanner();
- FragmentJarScannerCallback callback = new FragmentJarScannerCallback();
-
- jarScanner.scan(context.getServletContext(),
- context.getLoader().getClassLoader(), callback, null);
-
- return callback.getFragments();
- }
-
- protected void processAnnotations(Set<WebXml> fragments) {
- for(WebXml fragment : fragments) {
- if (!fragment.isMetadataComplete()) {
- WebXml annotations = new WebXml();
- // no impact on distributable
- annotations.setDistributable(true);
- URL url = fragment.getURL();
- processAnnotationsUrl(url, annotations);
- Set<WebXml> set = new HashSet<WebXml>();
- set.add(annotations);
- // Merge annotations into fragment - fragment takes priority
- fragment.merge(set);
- }
- }
- }
-
- protected void processAnnotationsUrl(URL url, WebXml fragment) {
- if (url == null) {
- // Nothing to do.
- return;
- } else if ("jar".equals(url.getProtocol())) {
- processAnnotationsJar(url, fragment);
- } else if ("jndi".equals(url.getProtocol())) {
- processAnnotationsJndi(url, fragment);
- } else if ("file".equals(url.getProtocol())) {
- try {
- processAnnotationsFile(new File(url.toURI()), fragment);
- } catch (URISyntaxException e) {
- log.error(sm.getString("contextConfig.fileUrl", url), e);
- }
- } else {
- log.error(sm.getString("contextConfig.unknownUrlProtocol",
- url.getProtocol(), url));
- }
-
- }
-
-
- protected void processAnnotationsJar(URL url, WebXml fragment) {
-
- Jar jar = null;
- InputStream is;
-
- try {
- jar = JarFactory.newInstance(url);
-
- jar.nextEntry();
- String entryName = jar.getEntryName();
- while (entryName != null) {
- if (entryName.endsWith(".class")) {
- is = null;
- try {
- is = jar.getEntryInputStream();
- processAnnotationsStream(is, fragment);
- } catch (IOException e) {
- log.error(sm.getString("contextConfig.inputStreamJar",
- entryName, url),e);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException ioe) {
- // Ignore
- }
- }
- }
- }
- jar.nextEntry();
- entryName = jar.getEntryName();
- }
- } catch (IOException e) {
- log.error(sm.getString("contextConfig.jarFile", url), e);
- } finally {
- if (jar != null) {
- jar.close();
- }
- }
- }
-
-
- protected void processAnnotationsJndi(URL url, WebXml fragment) {
- try {
- URLConnection urlConn = url.openConnection();
- DirContextURLConnection dcUrlConn;
- if (!(urlConn instanceof DirContextURLConnection)) {
- // This should never happen
- sm.getString("contextConfig.jndiUrlNotDirContextConn", url);
- return;
- }
-
- dcUrlConn = (DirContextURLConnection) urlConn;
- dcUrlConn.setUseCaches(false);
-
- String type = dcUrlConn.getHeaderField(ResourceAttributes.TYPE);
- if (ResourceAttributes.COLLECTION_TYPE.equals(type)) {
- // Collection
- Enumeration<String> dirs = dcUrlConn.list();
- while (dirs.hasMoreElements()) {
- String dir = dirs.nextElement();
- URL dirUrl = new URL(url.toString() + '/' + dir);
- processAnnotationsJndi(dirUrl, fragment);
- }
-
- } else {
- // Single file
- if (url.getPath().endsWith(".class")) {
- InputStream is = null;
- try {
- is = dcUrlConn.getInputStream();
- processAnnotationsStream(is, fragment);
- } catch (IOException e) {
- log.error(sm.getString("contextConfig.inputStreamJndi",
- url),e);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
- }
- }
- }
- }
- } catch (IOException e) {
- log.error(sm.getString("contextConfig.jndiUrl", url), e);
- }
- }
-
-
- protected void processAnnotationsFile(File file, WebXml fragment) {
-
- if (file.isDirectory()) {
- String[] dirs = file.list();
- for (String dir : dirs) {
- processAnnotationsFile(new File(file,dir), fragment);
- }
- } else if (file.canRead() && file.getName().endsWith(".class")) {
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(file);
- processAnnotationsStream(fis, fragment);
- } catch (IOException e) {
- log.error(sm.getString("contextConfig.inputStreamFile",
- file.getAbsolutePath()),e);
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
- }
- }
- }
- }
-
-
- protected void processAnnotationsStream(InputStream is, WebXml fragment)
- throws ClassFormatException, IOException {
-
- ClassParser parser = new ClassParser(is, null);
- JavaClass clazz = parser.parse();
-
- checkHandlesTypes(clazz);
-
- String className = clazz.getClassName();
-
- AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
-
- for (AnnotationEntry ae : annotationsEntries) {
- String type = ae.getAnnotationType();
- if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
- processAnnotationWebServlet(className, ae, fragment);
- }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
- processAnnotationWebFilter(className, ae, fragment);
- }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
- fragment.addListener(className);
- } else {
- // Unknown annotation - ignore
- }
- }
- }
-
- /**
- * For classes packaged with the web application, the class and each
- * super class needs to be checked for a match with {@link HandlesTypes} or
- * for an annotation that matches {@link HandlesTypes}.
- * @param javaClass
- */
- protected void checkHandlesTypes(JavaClass javaClass) {
-
- // Skip this if we can
- if (typeInitializerMap.size() == 0)
- return;
-
- // No choice but to load the class
- String className = javaClass.getClassName();
-
- Class<?> clazz = null;
- try {
- clazz = context.getLoader().getClassLoader().loadClass(className);
- } catch (NoClassDefFoundError e) {
- log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
- className), e);
- return;
- } catch (ClassNotFoundException e) {
- log.warn(sm.getString("contextConfig.invalidSciHandlesTypes",
- className), e);
- return;
- } catch (ClassFormatError e) {
- log.warn(sm.getString("contextConfig.invalidSciHandlesTypes",
- className), e);
- return;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.warn(sm.getString("contextConfig.invalidSciHandlesTypes",
- className), t);
- return;
- }
-
- if (clazz.isAnnotation()) {
- // Skip
- return;
- }
-
- boolean match = false;
-
- for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
- typeInitializerMap.entrySet()) {
- if (entry.getKey().isAnnotation()) {
- AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries();
- for (AnnotationEntry annotationEntry : annotationEntries) {
- if (entry.getKey().getName().equals(
- getClassName(annotationEntry.getAnnotationType()))) {
- match = true;
- break;
- }
- }
- } else if (entry.getKey().isAssignableFrom(clazz)) {
- match = true;
- }
- if (match) {
- for (ServletContainerInitializer sci : entry.getValue()) {
- initializerClassMap.get(sci).add(clazz);
- }
- match = false;
- }
- }
- }
-
- private static final String getClassName(String internalForm) {
- if (!internalForm.startsWith("L")) {
- return internalForm;
- }
-
- // Assume starts with L, ends with ; and uses / rather than .
- return internalForm.substring(1,
- internalForm.length() - 1).replace('/', '.');
- }
-
- protected void processAnnotationWebServlet(String className,
- AnnotationEntry ae, WebXml fragment) {
- String servletName = null;
- // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
- ElementValuePair[] evps = ae.getElementValuePairs();
- for (ElementValuePair evp : evps) {
- String name = evp.getNameString();
- if ("name".equals(name)) {
- servletName = evp.getValue().stringifyValue();
- break;
- }
- }
- if (servletName == null) {
- // classname is default servletName as annotation has no name!
- servletName = className;
- }
- ServletDef servletDef = fragment.getServlets().get(servletName);
-
- boolean isWebXMLservletDef;
- if (servletDef == null) {
- servletDef = new ServletDef();
- servletDef.setServletName(servletName);
- servletDef.setServletClass(className);
- isWebXMLservletDef = false;
- } else {
- isWebXMLservletDef = true;
- }
-
- boolean urlPatternsSet = false;
- String[] urlPatterns = null;
-
- // ElementValuePair[] evps = ae.getElementValuePairs();
- for (ElementValuePair evp : evps) {
- String name = evp.getNameString();
- if ("value".equals(name) || "urlPatterns".equals(name)) {
- if (urlPatternsSet) {
- throw new IllegalArgumentException(sm.getString(
- "contextConfig.urlPatternValue", className));
- }
- urlPatternsSet = true;
- urlPatterns = processAnnotationsStringArray(evp.getValue());
- } else if ("description".equals(name)) {
- if (servletDef.getDescription() == null) {
- servletDef.setDescription(evp.getValue().stringifyValue());
- }
- } else if ("displayName".equals(name)) {
- if (servletDef.getDisplayName() == null) {
- servletDef.setDisplayName(evp.getValue().stringifyValue());
- }
- } else if ("largeIcon".equals(name)) {
- if (servletDef.getLargeIcon() == null) {
- servletDef.setLargeIcon(evp.getValue().stringifyValue());
- }
- } else if ("smallIcon".equals(name)) {
- if (servletDef.getSmallIcon() == null) {
- servletDef.setSmallIcon(evp.getValue().stringifyValue());
- }
- } else if ("asyncSupported".equals(name)) {
- if (servletDef.getAsyncSupported() == null) {
- servletDef.setAsyncSupported(evp.getValue()
- .stringifyValue());
- }
- } else if ("loadOnStartup".equals(name)) {
- if (servletDef.getLoadOnStartup() == null) {
- servletDef
- .setLoadOnStartup(evp.getValue().stringifyValue());
- }
- } else if ("initParams".equals(name)) {
- Map<String, String> initParams = processAnnotationWebInitParams(evp
- .getValue());
- if (isWebXMLservletDef) {
- Map<String, String> webXMLInitParams = servletDef
- .getParameterMap();
- for (Map.Entry<String, String> entry : initParams
- .entrySet()) {
- if (webXMLInitParams.get(entry.getKey()) == null) {
- servletDef.addInitParameter(entry.getKey(), entry
- .getValue());
- }
- }
- } else {
- for (Map.Entry<String, String> entry : initParams
- .entrySet()) {
- servletDef.addInitParameter(entry.getKey(), entry
- .getValue());
- }
- }
- }
- }
- if (!isWebXMLservletDef && urlPatterns != null) {
- fragment.addServlet(servletDef);
- }
- if (urlPatterns != null) {
- if (!fragment.getServletMappings().containsValue(servletName)) {
- for (String urlPattern : urlPatterns) {
- fragment.addServletMapping(urlPattern, servletName);
- }
- }
- }
-
- }
-
- /**
- * process filter annotation and merge with existing one!
- * FIXME: refactoring method to long and has redundant subroutines with processAnnotationWebServlet!
- * @param className
- * @param ae
- * @param fragment
- */
- protected void processAnnotationWebFilter(String className,
- AnnotationEntry ae, WebXml fragment) {
- String filterName = null;
- // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
- ElementValuePair[] evps = ae.getElementValuePairs();
- for (ElementValuePair evp : evps) {
- String name = evp.getNameString();
- if ("filterName".equals(name)) {
- filterName = evp.getValue().stringifyValue();
- break;
- }
- }
- if (filterName == null) {
- // classname is default filterName as annotation has no name!
- filterName = className;
- }
- FilterDef filterDef = fragment.getFilters().get(filterName);
- FilterMap filterMap = new FilterMap();
-
- boolean isWebXMLfilterDef;
- if (filterDef == null) {
- filterDef = new FilterDef();
- filterDef.setFilterName(filterName);
- filterDef.setFilterClass(className);
- isWebXMLfilterDef = false;
- } else {
- isWebXMLfilterDef = true;
- }
-
- boolean urlPatternsSet = false;
- boolean dispatchTypesSet = false;
- String[] urlPatterns = null;
-
- for (ElementValuePair evp : evps) {
- String name = evp.getNameString();
- if ("value".equals(name) || "urlPatterns".equals(name)) {
- if (urlPatternsSet) {
- throw new IllegalArgumentException(sm.getString(
- "contextConfig.urlPatternValue", className));
- }
- urlPatterns = processAnnotationsStringArray(evp.getValue());
- urlPatternsSet = urlPatterns.length > 0;
- for (String urlPattern : urlPatterns) {
- filterMap.addURLPattern(urlPattern);
- }
- } else if ("servletNames".equals(name)) {
- String[] servletNames = processAnnotationsStringArray(evp
- .getValue());
- for (String servletName : servletNames) {
- filterMap.addServletName(servletName);
- }
- } else if ("dispatcherTypes".equals(name)) {
- String[] dispatcherTypes = processAnnotationsStringArray(evp
- .getValue());
- dispatchTypesSet = dispatcherTypes.length > 0;
- for (String dispatcherType : dispatcherTypes) {
- filterMap.setDispatcher(dispatcherType);
- }
- } else if ("description".equals(name)) {
- if (filterDef.getDescription() == null) {
- filterDef.setDescription(evp.getValue().stringifyValue());
- }
- } else if ("displayName".equals(name)) {
- if (filterDef.getDisplayName() == null) {
- filterDef.setDisplayName(evp.getValue().stringifyValue());
- }
- } else if ("largeIcon".equals(name)) {
- if (filterDef.getLargeIcon() == null) {
- filterDef.setLargeIcon(evp.getValue().stringifyValue());
- }
- } else if ("smallIcon".equals(name)) {
- if (filterDef.getSmallIcon() == null) {
- filterDef.setSmallIcon(evp.getValue().stringifyValue());
- }
- } else if ("asyncSupported".equals(name)) {
- if (filterDef.getAsyncSupported() == null) {
- filterDef
- .setAsyncSupported(evp.getValue().stringifyValue());
- }
- } else if ("initParams".equals(name)) {
- Map<String, String> initParams = processAnnotationWebInitParams(evp
- .getValue());
- if (isWebXMLfilterDef) {
- Map<String, String> webXMLInitParams = filterDef
- .getParameterMap();
- for (Map.Entry<String, String> entry : initParams
- .entrySet()) {
- if (webXMLInitParams.get(entry.getKey()) == null) {
- filterDef.addInitParameter(entry.getKey(), entry
- .getValue());
- }
- }
- } else {
- for (Map.Entry<String, String> entry : initParams
- .entrySet()) {
- filterDef.addInitParameter(entry.getKey(), entry
- .getValue());
- }
- }
-
- }
- }
- if (!isWebXMLfilterDef) {
- fragment.addFilter(filterDef);
- filterMap.setFilterName(filterName);
- fragment.addFilterMapping(filterMap);
- }
- if (urlPatternsSet || dispatchTypesSet) {
- Set<FilterMap> fmap = fragment.getFilterMappings();
- FilterMap descMap = null;
- for (FilterMap map : fmap) {
- if (filterName.equals(map.getFilterName())) {
- descMap = map;
- break;
- }
- }
- if (descMap != null) {
- String[] urlsPatterns = descMap.getURLPatterns();
- if (urlPatternsSet
- && (urlsPatterns == null || urlsPatterns.length == 0)) {
- for (String urlPattern : filterMap.getURLPatterns()) {
- descMap.addURLPattern(urlPattern);
- }
- }
- String[] dispatcherNames = descMap.getDispatcherNames();
- if (dispatchTypesSet
- && (dispatcherNames == null || dispatcherNames.length == 0)) {
- for (String dis : filterMap.getDispatcherNames()) {
- descMap.setDispatcher(dis);
- }
- }
- }
- }
-
- }
-
- protected String[] processAnnotationsStringArray(ElementValue ev) {
- ArrayList<String> values = new ArrayList<String>();
- if (ev instanceof ArrayElementValue) {
- ElementValue[] arrayValues =
- ((ArrayElementValue) ev).getElementValuesArray();
- for (ElementValue value : arrayValues) {
- values.add(value.stringifyValue());
- }
- } else {
- values.add(ev.stringifyValue());
- }
- String[] result = new String[values.size()];
- return values.toArray(result);
- }
-
- protected Map<String,String> processAnnotationWebInitParams(
- ElementValue ev) {
- Map<String, String> result = new HashMap<String,String>();
- if (ev instanceof ArrayElementValue) {
- ElementValue[] arrayValues =
- ((ArrayElementValue) ev).getElementValuesArray();
- for (ElementValue value : arrayValues) {
- if (value instanceof AnnotationElementValue) {
- ElementValuePair[] evps = ((AnnotationElementValue)
- value).getAnnotationEntry().getElementValuePairs();
- String initParamName = null;
- String initParamValue = null;
- for (ElementValuePair evp : evps) {
- if ("name".equals(evp.getNameString())) {
- initParamName = evp.getValue().stringifyValue();
- } else if ("value".equals(evp.getNameString())) {
- initParamValue = evp.getValue().stringifyValue();
- } else {
- // Ignore
- }
- }
- result.put(initParamName, initParamValue);
- }
- }
- }
- return result;
- }
-
- private class FragmentJarScannerCallback implements JarScannerCallback {
-
- private static final String FRAGMENT_LOCATION =
- "META-INF/web-fragment.xml";
- private Map<String,WebXml> fragments = new HashMap<String,WebXml>();
-
- @Override
- public void scan(JarURLConnection jarConn) throws IOException {
-
- URL url = jarConn.getURL();
- URL resourceURL = jarConn.getJarFileURL();
- Jar jar = null;
- InputStream is = null;
- WebXml fragment = new WebXml();
-
- try {
- jar = JarFactory.newInstance(url);
- is = jar.getInputStream(FRAGMENT_LOCATION);
-
- if (is == null) {
- // If there is no web.xml, normal JAR no impact on
- // distributable
- fragment.setDistributable(true);
- } else {
- InputSource source = new InputSource(
- resourceURL.toString() + "!/" + FRAGMENT_LOCATION);
- source.setByteStream(is);
- parseWebXml(source, fragment, true);
- }
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException ioe) {
- // Ignore
- }
- }
- if (jar != null) {
- jar.close();
- }
- fragment.setURL(url);
- if (fragment.getName() == null) {
- fragment.setName(fragment.getURL().toString());
- }
- fragments.put(fragment.getName(), fragment);
- }
- }
-
- @Override
- public void scan(File file) throws IOException {
-
- InputStream stream = null;
- WebXml fragment = new WebXml();
-
- try {
- File fragmentFile = new File(file, FRAGMENT_LOCATION);
- if (fragmentFile.isFile()) {
- stream = new FileInputStream(fragmentFile);
- InputSource source =
- new InputSource(fragmentFile.toURI().toURL().toString());
- source.setByteStream(stream);
- parseWebXml(source, fragment, true);
- }
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
- }
- fragment.setURL(file.toURI().toURL());
- if (fragment.getName() == null) {
- fragment.setName(fragment.getURL().toString());
- }
- fragments.put(fragment.getName(), fragment);
- }
- }
-
- public Map<String,WebXml> getFragments() {
- return fragments;
- }
- }
-
- private static class DefaultWebXmlCacheEntry {
- private final WebXml webXml;
- private final long globalTimeStamp;
- private final long hostTimeStamp;
-
- public DefaultWebXmlCacheEntry(WebXml webXml, long globalTimeStamp,
- long hostTimeStamp) {
- this.webXml = webXml;
- this.globalTimeStamp = globalTimeStamp;
- this.hostTimeStamp = hostTimeStamp;
- }
-
- public WebXml getWebXml() {
- return webXml;
- }
-
- public long getGlobalTimeStamp() {
- return globalTimeStamp;
- }
-
- public long getHostTimeStamp() {
- return hostTimeStamp;
- }
- }
-}
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+\r
+package org.apache.catalina.startup;\r
+\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.InputStreamReader;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.JarURLConnection;\r
+import java.net.MalformedURLException;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.net.URL;\r
+import java.net.URLConnection;\r
+import java.util.ArrayList;\r
+import java.util.Enumeration;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.LinkedHashMap;\r
+import java.util.LinkedHashSet;\r
+import java.util.List;\r
+import java.util.Locale;\r
+import java.util.Map;\r
+import java.util.Properties;\r
+import java.util.Set;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+\r
+import javax.servlet.ServletContainerInitializer;\r
+import javax.servlet.ServletContext;\r
+import javax.servlet.annotation.HandlesTypes;\r
+\r
+import org.apache.catalina.Authenticator;\r
+import org.apache.catalina.Container;\r
+import org.apache.catalina.Context;\r
+import org.apache.catalina.Engine;\r
+import org.apache.catalina.Globals;\r
+import org.apache.catalina.Host;\r
+import org.apache.catalina.Lifecycle;\r
+import org.apache.catalina.LifecycleEvent;\r
+import org.apache.catalina.LifecycleListener;\r
+import org.apache.catalina.Pipeline;\r
+import org.apache.catalina.Server;\r
+import org.apache.catalina.Service;\r
+import org.apache.catalina.Valve;\r
+import org.apache.catalina.Wrapper;\r
+import org.apache.catalina.core.ContainerBase;\r
+import org.apache.catalina.core.StandardContext;\r
+import org.apache.catalina.core.StandardEngine;\r
+import org.apache.catalina.core.StandardHost;\r
+import org.apache.catalina.deploy.ErrorPage;\r
+import org.apache.catalina.deploy.FilterDef;\r
+import org.apache.catalina.deploy.FilterMap;\r
+import org.apache.catalina.deploy.LoginConfig;\r
+import org.apache.catalina.deploy.SecurityConstraint;\r
+import org.apache.catalina.deploy.ServletDef;\r
+import org.apache.catalina.deploy.WebXml;\r
+import org.apache.catalina.util.ContextName;\r
+import org.apache.juli.logging.Log;\r
+import org.apache.juli.logging.LogFactory;\r
+import org.apache.naming.resources.DirContextURLConnection;\r
+import org.apache.naming.resources.ResourceAttributes;\r
+import org.apache.tomcat.JarScanner;\r
+import org.apache.tomcat.JarScannerCallback;\r
+import org.apache.tomcat.util.ExceptionUtils;\r
+import org.apache.tomcat.util.bcel.classfile.AnnotationElementValue;\r
+import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;\r
+import org.apache.tomcat.util.bcel.classfile.ArrayElementValue;\r
+import org.apache.tomcat.util.bcel.classfile.ClassFormatException;\r
+import org.apache.tomcat.util.bcel.classfile.ClassParser;\r
+import org.apache.tomcat.util.bcel.classfile.ElementValue;\r
+import org.apache.tomcat.util.bcel.classfile.ElementValuePair;\r
+import org.apache.tomcat.util.bcel.classfile.JavaClass;\r
+import org.apache.tomcat.util.digester.Digester;\r
+import org.apache.tomcat.util.digester.RuleSet;\r
+import org.apache.tomcat.util.res.StringManager;\r
+import org.apache.tomcat.util.scan.Jar;\r
+import org.apache.tomcat.util.scan.JarFactory;\r
+import org.xml.sax.InputSource;\r
+import org.xml.sax.SAXParseException;\r
+\r
+/**\r
+ * Startup event listener for a <b>Context</b> that configures the properties\r
+ * of that Context, and the associated defined servlets.\r
+ *\r
+ * @author Craig R. McClanahan\r
+ * @author Jean-Francois Arcand\r
+ * @version $Id$\r
+ */\r
+\r
+public class ContextConfig\r
+ implements LifecycleListener {\r
+\r
+ private static final Log log = LogFactory.getLog( ContextConfig.class );\r
+ \r
+ private static final String SCI_LOCATION =\r
+ "META-INF/services/javax.servlet.ServletContainerInitializer";\r
+\r
+\r
+ /**\r
+ * The string resources for this package.\r
+ */\r
+ protected static final StringManager sm =\r
+ StringManager.getManager(Constants.Package);\r
+\r
+\r
+ protected static final LoginConfig DUMMY_LOGIN_CONFIG =\r
+ new LoginConfig("NONE", null, null, null);\r
+\r
+ /**\r
+ * The <code>Digester</code> we will use to process web application\r
+ * context files.\r
+ */\r
+ protected static Digester contextDigester = null;\r
+ \r
+\r
+ /**\r
+ * The set of Authenticators that we know how to configure. The key is\r
+ * the name of the implemented authentication method, and the value is\r
+ * the fully qualified Java class name of the corresponding Valve.\r
+ */\r
+ protected static Properties authenticators = null;\r
+\r
+\r
+ /**\r
+ * The <code>Digester</code>s available to process web deployment descriptor\r
+ * files.\r
+ */\r
+ protected static Digester[] webDigesters = new Digester[4];\r
+\r
+\r
+ /**\r
+ * The <code>Digester</code>s available to process web fragment deployment\r
+ * descriptor files.\r
+ */\r
+ protected static Digester[] webFragmentDigesters = new Digester[4];\r
+\r
+\r
+ /**\r
+ * The <code>Rule</code>s used to parse the web.xml\r
+ */\r
+ protected static WebRuleSet webRuleSet = new WebRuleSet(false);\r
+\r
+\r
+ /**\r
+ * The <code>Rule</code>s used to parse the web-fragment.xml\r
+ */\r
+ protected static WebRuleSet webFragmentRuleSet = new WebRuleSet(true);\r
+\r
+\r
+ /**\r
+ * Deployment count.\r
+ */\r
+ protected static long deploymentCount = 0L;\r
+\r
+\r
+ /**\r
+ * Cache of default web.xml fragments per Host\r
+ */\r
+ protected static final Map<Host,DefaultWebXmlCacheEntry> hostWebXmlCache =\r
+ new ConcurrentHashMap<Host,DefaultWebXmlCacheEntry>();\r
+\r
+\r
+ // ----------------------------------------------------- Instance Variables\r
+ /**\r
+ * Custom mappings of login methods to authenticators\r
+ */\r
+ protected Map<String,Authenticator> customAuthenticators;\r
+\r
+\r
+ /**\r
+ * The Context we are associated with.\r
+ */\r
+ protected Context context = null;\r
+\r
+\r
+ /**\r
+ * The default web application's context file location.\r
+ */\r
+ protected String defaultContextXml = null;\r
+ \r
+ \r
+ /**\r
+ * The default web application's deployment descriptor location.\r
+ */\r
+ protected String defaultWebXml = null;\r
+ \r
+ \r
+ /**\r
+ * Track any fatal errors during startup configuration processing.\r
+ */\r
+ protected boolean ok = false;\r
+\r
+\r
+ /**\r
+ * Original docBase.\r
+ */\r
+ protected String originalDocBase = null;\r
+ \r
+\r
+ /**\r
+ * Map of ServletContainerInitializer to classes they expressed interest in.\r
+ */\r
+ protected Map<ServletContainerInitializer, Set<Class<?>>> initializerClassMap =\r
+ new LinkedHashMap<ServletContainerInitializer, Set<Class<?>>>();\r
+ \r
+ /**\r
+ * Map of Types to ServletContainerInitializer that are interested in those\r
+ * types.\r
+ */\r
+ protected Map<Class<?>, Set<ServletContainerInitializer>> typeInitializerMap =\r
+ new HashMap<Class<?>, Set<ServletContainerInitializer>>();\r
+\r
+ /**\r
+ * The <code>Digester</code> we will use to process web application\r
+ * deployment descriptor files.\r
+ */\r
+ protected Digester webDigester = null;\r
+\r
+ /**\r
+ * The <code>Digester</code> we will use to process web fragment\r
+ * deployment descriptor files.\r
+ */\r
+ protected Digester webFragmentDigester = null;\r
+\r
+ \r
+ // ------------------------------------------------------------- Properties\r
+ /**\r
+ * Return the location of the default deployment descriptor\r
+ */\r
+ public String getDefaultWebXml() {\r
+ if( defaultWebXml == null ) {\r
+ defaultWebXml=Constants.DefaultWebXml;\r
+ }\r
+\r
+ return (this.defaultWebXml);\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Set the location of the default deployment descriptor\r
+ *\r
+ * @param path Absolute/relative path to the default web.xml\r
+ */\r
+ public void setDefaultWebXml(String path) {\r
+\r
+ this.defaultWebXml = path;\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Return the location of the default context file\r
+ */\r
+ public String getDefaultContextXml() {\r
+ if( defaultContextXml == null ) {\r
+ defaultContextXml=Constants.DefaultContextXml;\r
+ }\r
+\r
+ return (this.defaultContextXml);\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Set the location of the default context file\r
+ *\r
+ * @param path Absolute/relative path to the default context.xml\r
+ */\r
+ public void setDefaultContextXml(String path) {\r
+\r
+ this.defaultContextXml = path;\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Sets custom mappings of login methods to authenticators.\r
+ *\r
+ * @param customAuthenticators Custom mappings of login methods to\r
+ * authenticators\r
+ */\r
+ public void setCustomAuthenticators(\r
+ Map<String,Authenticator> customAuthenticators) {\r
+ this.customAuthenticators = customAuthenticators;\r
+ }\r
+\r
+\r
+ // --------------------------------------------------------- Public Methods\r
+\r
+\r
+ /**\r
+ * Process events for an associated Context.\r
+ *\r
+ * @param event The lifecycle event that has occurred\r
+ */\r
+ @Override\r
+ public void lifecycleEvent(LifecycleEvent event) {\r
+\r
+ // Identify the context we are associated with\r
+ try {\r
+ context = (Context) event.getLifecycle();\r
+ } catch (ClassCastException e) {\r
+ log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);\r
+ return;\r
+ }\r
+\r
+ // Process the event that has occurred\r
+ if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {\r
+ configureStart();\r
+ } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {\r
+ beforeStart();\r
+ } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {\r
+ // Restore docBase for management tools\r
+ if (originalDocBase != null) {\r
+ String docBase = context.getDocBase();\r
+ context.setDocBase(originalDocBase);\r
+ originalDocBase = docBase;\r
+ }\r
+ } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {\r
+ if (originalDocBase != null) {\r
+ String docBase = context.getDocBase();\r
+ context.setDocBase(originalDocBase);\r
+ originalDocBase = docBase;\r
+ }\r
+ configureStop();\r
+ } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {\r
+ init();\r
+ } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {\r
+ destroy();\r
+ }\r
+\r
+ }\r
+\r
+\r
+ // -------------------------------------------------------- protected Methods\r
+\r
+\r
+ /**\r
+ * Process the application classes annotations, if it exists.\r
+ */\r
+ protected void applicationAnnotationsConfig() {\r
+ \r
+ long t1=System.currentTimeMillis();\r
+ \r
+ WebAnnotationSet.loadApplicationAnnotations(context);\r
+ \r
+ long t2=System.currentTimeMillis();\r
+ if (context instanceof StandardContext) {\r
+ ((StandardContext) context).setStartupTime(t2-t1+\r
+ ((StandardContext) context).getStartupTime());\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Set up an Authenticator automatically if required, and one has not\r
+ * already been configured.\r
+ */\r
+ protected synchronized void authenticatorConfig() {\r
+\r
+ LoginConfig loginConfig = context.getLoginConfig();\r
+\r
+ SecurityConstraint constraints[] = context.findConstraints();\r
+ if (context.getIgnoreAnnotations() &&\r
+ (constraints == null || constraints.length ==0) &&\r
+ !context.getPreemptiveAuthentication()) {\r
+ return;\r
+ } else {\r
+ if (loginConfig == null) {\r
+ // Not metadata-complete or security constraints present, need\r
+ // an authenticator to support @ServletSecurity annotations\r
+ // and/or constraints\r
+ loginConfig = DUMMY_LOGIN_CONFIG;\r
+ context.setLoginConfig(loginConfig);\r
+ }\r
+ }\r
+\r
+ // Has an authenticator been configured already?\r
+ if (context.getAuthenticator() != null)\r
+ return;\r
+ \r
+ if (!(context instanceof ContainerBase)) {\r
+ return; // Cannot install a Valve even if it would be needed\r
+ }\r
+\r
+ // Has a Realm been configured for us to authenticate against?\r
+ if (context.getRealm() == null) {\r
+ log.error(sm.getString("contextConfig.missingRealm"));\r
+ ok = false;\r
+ return;\r
+ }\r
+\r
+ /*\r
+ * First check to see if there is a custom mapping for the login\r
+ * method. If so, use it. Otherwise, check if there is a mapping in\r
+ * org/apache/catalina/startup/Authenticators.properties.\r
+ */\r
+ Valve authenticator = null;\r
+ if (customAuthenticators != null) {\r
+ authenticator = (Valve)\r
+ customAuthenticators.get(loginConfig.getAuthMethod());\r
+ }\r
+ if (authenticator == null) {\r
+ // Load our mapping properties if necessary\r
+ if (authenticators == null) {\r
+ try {\r
+ InputStream is=this.getClass().getClassLoader().getResourceAsStream("org/apache/catalina/startup/Authenticators.properties");\r
+ if( is!=null ) {\r
+ authenticators = new Properties();\r
+ authenticators.load(is);\r
+ } else {\r
+ log.error(sm.getString(\r
+ "contextConfig.authenticatorResources"));\r
+ ok=false;\r
+ return;\r
+ }\r
+ } catch (IOException e) {\r
+ log.error(sm.getString(\r
+ "contextConfig.authenticatorResources"), e);\r
+ ok = false;\r
+ return;\r
+ }\r
+ }\r
+\r
+ // Identify the class name of the Valve we should configure\r
+ String authenticatorName = null;\r
+ authenticatorName =\r
+ authenticators.getProperty(loginConfig.getAuthMethod());\r
+ if (authenticatorName == null) {\r
+ log.error(sm.getString("contextConfig.authenticatorMissing",\r
+ loginConfig.getAuthMethod()));\r
+ ok = false;\r
+ return;\r
+ }\r
+\r
+ // Instantiate and install an Authenticator of the requested class\r
+ try {\r
+ Class<?> authenticatorClass = Class.forName(authenticatorName);\r
+ authenticator = (Valve) authenticatorClass.newInstance();\r
+ } catch (Throwable t) {\r
+ ExceptionUtils.handleThrowable(t);\r
+ log.error(sm.getString(\r
+ "contextConfig.authenticatorInstantiate",\r
+ authenticatorName),\r
+ t);\r
+ ok = false;\r
+ }\r
+ }\r
+\r
+ if (authenticator != null && context instanceof ContainerBase) {\r
+ Pipeline pipeline = ((ContainerBase) context).getPipeline();\r
+ if (pipeline != null) {\r
+ ((ContainerBase) context).getPipeline().addValve(authenticator);\r
+ if (log.isDebugEnabled()) {\r
+ log.debug(sm.getString(\r
+ "contextConfig.authenticatorConfigured",\r
+ loginConfig.getAuthMethod()));\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Create (if necessary) and return a Digester configured to process the\r
+ * web application deployment descriptor (web.xml).\r
+ */\r
+ public void createWebXmlDigester(boolean namespaceAware,\r
+ boolean validation) {\r
+ \r
+ if (!namespaceAware && !validation) {\r
+ if (webDigesters[0] == null) {\r
+ webDigesters[0] = DigesterFactory.newDigester(validation,\r
+ namespaceAware, webRuleSet);\r
+ webFragmentDigesters[0] = DigesterFactory.newDigester(validation,\r
+ namespaceAware, webFragmentRuleSet);\r
+ webDigesters[0].getParser();\r
+ webFragmentDigesters[0].getParser();\r
+ }\r
+ webDigester = webDigesters[0];\r
+ webFragmentDigester = webFragmentDigesters[0];\r
+ \r
+ } else if (!namespaceAware && validation) {\r
+ if (webDigesters[1] == null) {\r
+ webDigesters[1] = DigesterFactory.newDigester(validation,\r
+ namespaceAware, webRuleSet);\r
+ webFragmentDigesters[1] = DigesterFactory.newDigester(validation,\r
+ namespaceAware, webFragmentRuleSet);\r
+ webDigesters[1].getParser();\r
+ webFragmentDigesters[1].getParser();\r
+ }\r
+ webDigester = webDigesters[1];\r
+ webFragmentDigester = webFragmentDigesters[1];\r
+ \r
+ } else if (namespaceAware && !validation) {\r
+ if (webDigesters[2] == null) {\r
+ webDigesters[2] = DigesterFactory.newDigester(validation,\r
+ namespaceAware, webRuleSet);\r
+ webFragmentDigesters[2] = DigesterFactory.newDigester(validation,\r
+ namespaceAware, webFragmentRuleSet);\r
+ webDigesters[2].getParser();\r
+ webFragmentDigesters[2].getParser();\r
+ }\r
+ webDigester = webDigesters[2];\r
+ webFragmentDigester = webFragmentDigesters[2];\r
+ \r
+ } else {\r
+ if (webDigesters[3] == null) {\r
+ webDigesters[3] = DigesterFactory.newDigester(validation,\r
+ namespaceAware, webRuleSet);\r
+ webFragmentDigesters[3] = DigesterFactory.newDigester(validation,\r
+ namespaceAware, webFragmentRuleSet);\r
+ webDigesters[3].getParser();\r
+ webFragmentDigesters[3].getParser();\r
+ }\r
+ webDigester = webDigesters[3];\r
+ webFragmentDigester = webFragmentDigesters[3];\r
+ }\r
+ }\r
+\r
+ \r
+ /**\r
+ * Create (if necessary) and return a Digester configured to process the\r
+ * context configuration descriptor for an application.\r
+ */\r
+ protected Digester createContextDigester() {\r
+ Digester digester = new Digester();\r
+ digester.setValidating(false);\r
+ digester.setRulesValidation(true);\r
+ HashMap<Class<?>, List<String>> fakeAttributes =\r
+ new HashMap<Class<?>, List<String>>();\r
+ ArrayList<String> attrs = new ArrayList<String>();\r
+ attrs.add("className");\r
+ fakeAttributes.put(Object.class, attrs);\r
+ digester.setFakeAttributes(fakeAttributes);\r
+ RuleSet contextRuleSet = new ContextRuleSet("", false);\r
+ digester.addRuleSet(contextRuleSet);\r
+ RuleSet namingRuleSet = new NamingRuleSet("Context/");\r
+ digester.addRuleSet(namingRuleSet);\r
+ return digester;\r
+ }\r
+\r
+\r
+ protected String getBaseDir() {\r
+ Container engineC=context.getParent().getParent();\r
+ if( engineC instanceof StandardEngine ) {\r
+ return ((StandardEngine)engineC).getBaseDir();\r
+ }\r
+ return System.getProperty(Globals.CATALINA_BASE_PROP);\r
+ }\r
+\r
+ \r
+ /**\r
+ * Process the default configuration file, if it exists.\r
+ */\r
+ protected void contextConfig() {\r
+ \r
+ // Open the default context.xml file, if it exists\r
+ if( defaultContextXml==null && context instanceof StandardContext ) {\r
+ defaultContextXml = ((StandardContext)context).getDefaultContextXml();\r
+ }\r
+ // set the default if we don't have any overrides\r
+ if( defaultContextXml==null ) getDefaultContextXml();\r
+\r
+ if (!context.getOverride()) {\r
+ File defaultContextFile = new File(defaultContextXml);\r
+ if (!defaultContextFile.isAbsolute()) {\r
+ defaultContextFile =new File(getBaseDir(), defaultContextXml);\r
+ }\r
+ if (defaultContextFile.exists()) {\r
+ try {\r
+ URL defaultContextUrl = defaultContextFile.toURI().toURL();\r
+ processContextConfig(defaultContextUrl);\r
+ } catch (MalformedURLException e) {\r
+ log.error(sm.getString(\r
+ "contextConfig.badUrl", defaultContextFile), e);\r
+ }\r
+ }\r
+ \r
+ File hostContextFile = new File(getConfigBase(),\r
+ getHostConfigPath(Constants.HostContextXml));\r
+ if (hostContextFile.exists()) {\r
+ try {\r
+ URL hostContextUrl = hostContextFile.toURI().toURL();\r
+ processContextConfig(hostContextUrl);\r
+ } catch (MalformedURLException e) {\r
+ log.error(sm.getString(\r
+ "contextConfig.badUrl", hostContextFile), e);\r
+ }\r
+ }\r
+ }\r
+ if (context.getConfigFile() != null)\r
+ processContextConfig(context.getConfigFile());\r
+ \r
+ }\r
+\r
+ \r
+ /**\r
+ * Process a context.xml.\r
+ */\r
+ protected void processContextConfig(URL contextXml) {\r
+ \r
+ if (log.isDebugEnabled())\r
+ log.debug("Processing context [" + context.getName() \r
+ + "] configuration file [" + contextXml + "]");\r
+\r
+ InputSource source = null;\r
+ InputStream stream = null;\r
+\r
+ try {\r
+ source = new InputSource(contextXml.toString());\r
+ stream = contextXml.openStream();\r
+ \r
+ // Add as watched resource so that cascade reload occurs if a default\r
+ // config file is modified/added/removed\r
+ if ("file".equals(contextXml.getProtocol())) {\r
+ context.addWatchedResource(\r
+ (new File(contextXml.toURI())).getAbsolutePath());\r
+ }\r
+ } catch (Exception e) {\r
+ log.error(sm.getString("contextConfig.contextMissing", \r
+ contextXml) , e);\r
+ }\r
+ \r
+ if (source == null)\r
+ return;\r
+ synchronized (contextDigester) {\r
+ try {\r
+ source.setByteStream(stream);\r
+ contextDigester.setClassLoader(this.getClass().getClassLoader());\r
+ contextDigester.setUseContextClassLoader(false);\r
+ contextDigester.push(context.getParent());\r
+ contextDigester.push(context);\r
+ XmlErrorHandler errorHandler = new XmlErrorHandler();\r
+ contextDigester.setErrorHandler(errorHandler);\r
+ contextDigester.parse(source);\r
+ if (errorHandler.getWarnings().size() > 0 ||\r
+ errorHandler.getErrors().size() > 0) {\r
+ errorHandler.logFindings(log, contextXml.toString());\r
+ ok = false;\r
+ }\r
+ if (log.isDebugEnabled())\r
+ log.debug("Successfully processed context [" + context.getName() \r
+ + "] configuration file [" + contextXml + "]");\r
+ } catch (SAXParseException e) {\r
+ log.error(sm.getString("contextConfig.contextParse",\r
+ context.getName()), e);\r
+ log.error(sm.getString("contextConfig.defaultPosition",\r
+ "" + e.getLineNumber(),\r
+ "" + e.getColumnNumber()));\r
+ ok = false;\r
+ } catch (Exception e) {\r
+ log.error(sm.getString("contextConfig.contextParse",\r
+ context.getName()), e);\r
+ ok = false;\r
+ } finally {\r
+ contextDigester.reset();\r
+ try {\r
+ if (stream != null) {\r
+ stream.close();\r
+ }\r
+ } catch (IOException e) {\r
+ log.error(sm.getString("contextConfig.contextClose"), e);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ \r
+ /**\r
+ * Adjust docBase.\r
+ */\r
+ protected void fixDocBase()\r
+ throws IOException {\r
+ \r
+ Host host = (Host) context.getParent();\r
+ File appBase = host.getAppBaseFile();\r
+\r
+ String docBase = context.getDocBase();\r
+ if (docBase == null) {\r
+ // Trying to guess the docBase according to the path\r
+ String path = context.getPath();\r
+ if (path == null) {\r
+ return;\r
+ }\r
+ ContextName cn = new ContextName(path, context.getWebappVersion());\r
+ docBase = cn.getBaseName();\r
+ }\r
+\r
+ File file = new File(docBase);\r
+ if (!file.isAbsolute()) {\r
+ docBase = (new File(appBase, docBase)).getPath();\r
+ } else {\r
+ docBase = file.getCanonicalPath();\r
+ }\r
+ file = new File(docBase);\r
+ String origDocBase = docBase;\r
+ \r
+ ContextName cn = new ContextName(context.getPath(),\r
+ context.getWebappVersion());\r
+ String pathName = cn.getBaseName();\r
+\r
+ boolean unpackWARs = true;\r
+ if (host instanceof StandardHost) {\r
+ unpackWARs = ((StandardHost) host).isUnpackWARs() &&\r
+ ((StandardContext) context).getUnpackWAR() &&\r
+ (docBase.startsWith(host.getAppBaseFile().getPath()));\r
+ }\r
+\r
+ if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory() && unpackWARs) {\r
+ URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/");\r
+ docBase = ExpandWar.expand(host, war, pathName);\r
+ file = new File(docBase);\r
+ docBase = file.getCanonicalPath();\r
+ if (context instanceof StandardContext) {\r
+ ((StandardContext) context).setOriginalDocBase(origDocBase);\r
+ }\r
+ } else if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") &&\r
+ !file.isDirectory() && !unpackWARs) {\r
+ URL war =\r
+ new URL("jar:" + (new File (docBase)).toURI().toURL() + "!/");\r
+ ExpandWar.validate(host, war, pathName);\r
+ } else {\r
+ File docDir = new File(docBase);\r
+ if (!docDir.exists()) {\r
+ File warFile = new File(docBase + ".war");\r
+ if (warFile.exists()) {\r
+ URL war =\r
+ new URL("jar:" + warFile.toURI().toURL() + "!/");\r
+ if (unpackWARs) {\r
+ docBase = ExpandWar.expand(host, war, pathName);\r
+ file = new File(docBase);\r
+ docBase = file.getCanonicalPath();\r
+ } else {\r
+ docBase = warFile.getCanonicalPath();\r
+ ExpandWar.validate(host, war, pathName);\r
+ }\r
+ }\r
+ if (context instanceof StandardContext) {\r
+ ((StandardContext) context).setOriginalDocBase(origDocBase);\r
+ }\r
+ }\r
+ }\r
+\r
+ if (docBase.startsWith(appBase.getPath() + File.separatorChar)) {\r
+ docBase = docBase.substring(appBase.getPath().length());\r
+ docBase = docBase.replace(File.separatorChar, '/');\r
+ if (docBase.startsWith("/")) {\r
+ docBase = docBase.substring(1);\r
+ }\r
+ } else {\r
+ docBase = docBase.replace(File.separatorChar, '/');\r
+ }\r
+\r
+ context.setDocBase(docBase);\r
+\r
+ }\r
+ \r
+ \r
+ protected void antiLocking() {\r
+\r
+ if ((context instanceof StandardContext) \r
+ && ((StandardContext) context).getAntiResourceLocking()) {\r
+ \r
+ Host host = (Host) context.getParent();\r
+ String docBase = context.getDocBase();\r
+ if (docBase == null)\r
+ return;\r
+ if (originalDocBase == null) {\r
+ originalDocBase = docBase;\r
+ } else {\r
+ docBase = originalDocBase;\r
+ }\r
+ File docBaseFile = new File(docBase);\r
+ if (!docBaseFile.isAbsolute()) {\r
+ docBaseFile = new File(host.getAppBaseFile(), docBase);\r
+ }\r
+ \r
+ String path = context.getPath();\r
+ if (path == null) {\r
+ return;\r
+ }\r
+ ContextName cn = new ContextName(path, context.getWebappVersion());\r
+ docBase = cn.getBaseName();\r
+\r
+ File file = null;\r
+ if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {\r
+ // TODO - This is never executed. Bug or code to delete?\r
+ file = new File(System.getProperty("java.io.tmpdir"),\r
+ deploymentCount++ + "-" + docBase + ".war");\r
+ } else {\r
+ file = new File(System.getProperty("java.io.tmpdir"), \r
+ deploymentCount++ + "-" + docBase);\r
+ }\r
+ \r
+ if (log.isDebugEnabled())\r
+ log.debug("Anti locking context[" + context.getName() \r
+ + "] setting docBase to " + file);\r
+ \r
+ // Cleanup just in case an old deployment is lying around\r
+ ExpandWar.delete(file);\r
+ if (ExpandWar.copy(docBaseFile, file)) {\r
+ context.setDocBase(file.getAbsolutePath());\r
+ }\r
+ \r
+ }\r
+ \r
+ }\r
+ \r
+\r
+ /**\r
+ * Process a "init" event for this Context.\r
+ */\r
+ protected void init() {\r
+ // Called from StandardContext.init()\r
+\r
+ if (contextDigester == null){\r
+ contextDigester = createContextDigester();\r
+ contextDigester.getParser();\r
+ }\r
+\r
+ if (log.isDebugEnabled())\r
+ log.debug(sm.getString("contextConfig.init"));\r
+ context.setConfigured(false);\r
+ ok = true;\r
+ \r
+ contextConfig();\r
+ \r
+ createWebXmlDigester(context.getXmlNamespaceAware(),\r
+ context.getXmlValidation());\r
+\r
+ try {\r
+ fixDocBase();\r
+ } catch (IOException e) {\r
+ log.error(sm.getString(\r
+ "contextConfig.fixDocBase", context.getName()), e);\r
+ }\r
+ \r
+ }\r
+ \r
+ \r
+ /**\r
+ * Process a "before start" event for this Context.\r
+ */\r
+ protected synchronized void beforeStart() {\r
+ \r
+ antiLocking();\r
+\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Process a "contextConfig" event for this Context.\r
+ */\r
+ protected synchronized void configureStart() {\r
+ // Called from StandardContext.start()\r
+\r
+ if (log.isDebugEnabled())\r
+ log.debug(sm.getString("contextConfig.start"));\r
+\r
+ if (log.isDebugEnabled()) {\r
+ log.debug(sm.getString("contextConfig.xmlSettings",\r
+ context.getName(),\r
+ Boolean.valueOf(context.getXmlValidation()),\r
+ Boolean.valueOf(context.getXmlNamespaceAware())));\r
+ }\r
+ \r
+ webConfig();\r
+\r
+ if (!context.getIgnoreAnnotations()) {\r
+ applicationAnnotationsConfig();\r
+ }\r
+ if (ok) {\r
+ validateSecurityRoles();\r
+ }\r
+\r
+ // Configure an authenticator if we need one\r
+ if (ok)\r
+ authenticatorConfig();\r
+\r
+ // Dump the contents of this pipeline if requested\r
+ if ((log.isDebugEnabled()) && (context instanceof ContainerBase)) {\r
+ log.debug("Pipeline Configuration:");\r
+ Pipeline pipeline = ((ContainerBase) context).getPipeline();\r
+ Valve valves[] = null;\r
+ if (pipeline != null)\r
+ valves = pipeline.getValves();\r
+ if (valves != null) {\r
+ for (int i = 0; i < valves.length; i++) {\r
+ log.debug(" " + valves[i].getInfo());\r
+ }\r
+ }\r
+ log.debug("======================");\r
+ }\r
+\r
+ // Make our application available if no problems were encountered\r
+ if (ok)\r
+ context.setConfigured(true);\r
+ else {\r
+ log.error(sm.getString("contextConfig.unavailable"));\r
+ context.setConfigured(false);\r
+ }\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Process a "stop" event for this Context.\r
+ */\r
+ protected synchronized void configureStop() {\r
+\r
+ if (log.isDebugEnabled())\r
+ log.debug(sm.getString("contextConfig.stop"));\r
+\r
+ int i;\r
+\r
+ // Removing children\r
+ Container[] children = context.findChildren();\r
+ for (i = 0; i < children.length; i++) {\r
+ context.removeChild(children[i]);\r
+ }\r
+\r
+ // Removing application parameters\r
+ /*\r
+ ApplicationParameter[] applicationParameters =\r
+ context.findApplicationParameters();\r
+ for (i = 0; i < applicationParameters.length; i++) {\r
+ context.removeApplicationParameter\r
+ (applicationParameters[i].getName());\r
+ }\r
+ */\r
+\r
+ // Removing security constraints\r
+ SecurityConstraint[] securityConstraints = context.findConstraints();\r
+ for (i = 0; i < securityConstraints.length; i++) {\r
+ context.removeConstraint(securityConstraints[i]);\r
+ }\r
+\r
+ // Removing Ejbs\r
+ /*\r
+ ContextEjb[] contextEjbs = context.findEjbs();\r
+ for (i = 0; i < contextEjbs.length; i++) {\r
+ context.removeEjb(contextEjbs[i].getName());\r
+ }\r
+ */\r
+\r
+ // Removing environments\r
+ /*\r
+ ContextEnvironment[] contextEnvironments = context.findEnvironments();\r
+ for (i = 0; i < contextEnvironments.length; i++) {\r
+ context.removeEnvironment(contextEnvironments[i].getName());\r
+ }\r
+ */\r
+\r
+ // Removing errors pages\r
+ ErrorPage[] errorPages = context.findErrorPages();\r
+ for (i = 0; i < errorPages.length; i++) {\r
+ context.removeErrorPage(errorPages[i]);\r
+ }\r
+\r
+ // Removing filter defs\r
+ FilterDef[] filterDefs = context.findFilterDefs();\r
+ for (i = 0; i < filterDefs.length; i++) {\r
+ context.removeFilterDef(filterDefs[i]);\r
+ }\r
+\r
+ // Removing filter maps\r
+ FilterMap[] filterMaps = context.findFilterMaps();\r
+ for (i = 0; i < filterMaps.length; i++) {\r
+ context.removeFilterMap(filterMaps[i]);\r
+ }\r
+\r
+ // Removing local ejbs\r
+ /*\r
+ ContextLocalEjb[] contextLocalEjbs = context.findLocalEjbs();\r
+ for (i = 0; i < contextLocalEjbs.length; i++) {\r
+ context.removeLocalEjb(contextLocalEjbs[i].getName());\r
+ }\r
+ */\r
+\r
+ // Removing Mime mappings\r
+ String[] mimeMappings = context.findMimeMappings();\r
+ for (i = 0; i < mimeMappings.length; i++) {\r
+ context.removeMimeMapping(mimeMappings[i]);\r
+ }\r
+\r
+ // Removing parameters\r
+ String[] parameters = context.findParameters();\r
+ for (i = 0; i < parameters.length; i++) {\r
+ context.removeParameter(parameters[i]);\r
+ }\r
+\r
+ // Removing resource env refs\r
+ /*\r
+ String[] resourceEnvRefs = context.findResourceEnvRefs();\r
+ for (i = 0; i < resourceEnvRefs.length; i++) {\r
+ context.removeResourceEnvRef(resourceEnvRefs[i]);\r
+ }\r
+ */\r
+\r
+ // Removing resource links\r
+ /*\r
+ ContextResourceLink[] contextResourceLinks =\r
+ context.findResourceLinks();\r
+ for (i = 0; i < contextResourceLinks.length; i++) {\r
+ context.removeResourceLink(contextResourceLinks[i].getName());\r
+ }\r
+ */\r
+\r
+ // Removing resources\r
+ /*\r
+ ContextResource[] contextResources = context.findResources();\r
+ for (i = 0; i < contextResources.length; i++) {\r
+ context.removeResource(contextResources[i].getName());\r
+ }\r
+ */\r
+\r
+ // Removing security role\r
+ String[] securityRoles = context.findSecurityRoles();\r
+ for (i = 0; i < securityRoles.length; i++) {\r
+ context.removeSecurityRole(securityRoles[i]);\r
+ }\r
+\r
+ // Removing servlet mappings\r
+ String[] servletMappings = context.findServletMappings();\r
+ for (i = 0; i < servletMappings.length; i++) {\r
+ context.removeServletMapping(servletMappings[i]);\r
+ }\r
+\r
+ // FIXME : Removing status pages\r
+\r
+ // Removing welcome files\r
+ String[] welcomeFiles = context.findWelcomeFiles();\r
+ for (i = 0; i < welcomeFiles.length; i++) {\r
+ context.removeWelcomeFile(welcomeFiles[i]);\r
+ }\r
+\r
+ // Removing wrapper lifecycles\r
+ String[] wrapperLifecycles = context.findWrapperLifecycles();\r
+ for (i = 0; i < wrapperLifecycles.length; i++) {\r
+ context.removeWrapperLifecycle(wrapperLifecycles[i]);\r
+ }\r
+\r
+ // Removing wrapper listeners\r
+ String[] wrapperListeners = context.findWrapperListeners();\r
+ for (i = 0; i < wrapperListeners.length; i++) {\r
+ context.removeWrapperListener(wrapperListeners[i]);\r
+ }\r
+\r
+ // Remove (partially) folders and files created by antiLocking\r
+ Host host = (Host) context.getParent();\r
+ String docBase = context.getDocBase();\r
+ if ((docBase != null) && (originalDocBase != null)) {\r
+ File docBaseFile = new File(docBase);\r
+ if (!docBaseFile.isAbsolute()) {\r
+ docBaseFile = new File(host.getAppBaseFile(), docBase);\r
+ }\r
+ // No need to log failure - it is expected in this case\r
+ ExpandWar.delete(docBaseFile, false);\r
+ }\r
+ \r
+ // Reset ServletContextInitializer scanning\r
+ initializerClassMap.clear();\r
+ typeInitializerMap.clear();\r
+ \r
+ ok = true;\r
+\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Process a "destroy" event for this Context.\r
+ */\r
+ protected synchronized void destroy() {\r
+ // Called from StandardContext.destroy()\r
+ if (log.isDebugEnabled())\r
+ log.debug(sm.getString("contextConfig.destroy"));\r
+\r
+ // Skip clearing the work directory if Tomcat is being shutdown\r
+ Server s = getServer();\r
+ if (s != null && !s.getState().isAvailable()) {\r
+ return;\r
+ }\r
+ \r
+ // Changed to getWorkPath per Bugzilla 35819.\r
+ String workDir = ((StandardContext) context).getWorkPath();\r
+ if (workDir != null)\r
+ ExpandWar.delete(new File(workDir));\r
+ }\r
+ \r
+ \r
+ private Server getServer() {\r
+ Container c = context;\r
+ while (c != null && !(c instanceof Engine)) {\r
+ c = c.getParent();\r
+ }\r
+ \r
+ if (c == null) {\r
+ return null;\r
+ }\r
+ \r
+ Service s = ((Engine)c).getService();\r
+ \r
+ if (s == null) {\r
+ return null;\r
+ }\r
+ \r
+ return s.getServer();\r
+ }\r
+\r
+ /**\r
+ * Validate the usage of security role names in the web application\r
+ * deployment descriptor. If any problems are found, issue warning\r
+ * messages (for backwards compatibility) and add the missing roles.\r
+ * (To make these problems fatal instead, simply set the <code>ok</code>\r
+ * instance variable to <code>false</code> as well).\r
+ */\r
+ protected void validateSecurityRoles() {\r
+\r
+ // Check role names used in <security-constraint> elements\r
+ SecurityConstraint constraints[] = context.findConstraints();\r
+ for (int i = 0; i < constraints.length; i++) {\r
+ String roles[] = constraints[i].findAuthRoles();\r
+ for (int j = 0; j < roles.length; j++) {\r
+ if (!"*".equals(roles[j]) &&\r
+ !context.findSecurityRole(roles[j])) {\r
+ log.info(sm.getString("contextConfig.role.auth", roles[j]));\r
+ context.addSecurityRole(roles[j]);\r
+ }\r
+ }\r
+ }\r
+\r
+ // Check role names used in <servlet> elements\r
+ Container wrappers[] = context.findChildren();\r
+ for (int i = 0; i < wrappers.length; i++) {\r
+ Wrapper wrapper = (Wrapper) wrappers[i];\r
+ String runAs = wrapper.getRunAs();\r
+ if ((runAs != null) && !context.findSecurityRole(runAs)) {\r
+ log.info(sm.getString("contextConfig.role.runas", runAs));\r
+ context.addSecurityRole(runAs);\r
+ }\r
+ String names[] = wrapper.findSecurityReferences();\r
+ for (int j = 0; j < names.length; j++) {\r
+ String link = wrapper.findSecurityReference(names[j]);\r
+ if ((link != null) && !context.findSecurityRole(link)) {\r
+ log.info(sm.getString("contextConfig.role.link", link));\r
+ context.addSecurityRole(link);\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Get config base.\r
+ */\r
+ protected File getConfigBase() {\r
+ File configBase = \r
+ new File(System.getProperty(Globals.CATALINA_BASE_PROP), "conf");\r
+ if (!configBase.exists()) {\r
+ return null;\r
+ }\r
+ return configBase;\r
+ } \r
+\r
+ \r
+ protected String getHostConfigPath(String resourceName) {\r
+ StringBuilder result = new StringBuilder();\r
+ Container container = context;\r
+ Container host = null;\r
+ Container engine = null;\r
+ while (container != null) {\r
+ if (container instanceof Host)\r
+ host = container;\r
+ if (container instanceof Engine)\r
+ engine = container;\r
+ container = container.getParent();\r
+ }\r
+ if (engine != null) {\r
+ result.append(engine.getName()).append('/');\r
+ }\r
+ if (host != null) {\r
+ result.append(host.getName()).append('/');\r
+ }\r
+ result.append(resourceName);\r
+ return result.toString();\r
+ }\r
+\r
+\r
+ /**\r
+ * Scan the web.xml files that apply to the web application and merge them\r
+ * using the rules defined in the spec. For the global web.xml files,\r
+ * where there is duplicate configuration, the most specific level wins. ie\r
+ * an application's web.xml takes precedence over the host level or global\r
+ * web.xml file.\r
+ */\r
+ protected void webConfig() {\r
+ /* Anything and everything can override the global and host defaults.\r
+ * This is implemented in two parts\r
+ * - Handle as a web fragment that gets added after everything else so\r
+ * everything else takes priority\r
+ * - Mark Servlets as overridable so SCI configuration can replace\r
+ * configuration from the defaults\r
+ */ \r
+ Set<WebXml> defaults = new HashSet<WebXml>();\r
+ defaults.add(getDefaultWebXmlFragment());\r
+\r
+ WebXml webXml = createWebXml();\r
+\r
+ // Parse context level web.xml\r
+ InputSource contextWebXml = getContextWebXmlSource();\r
+ parseWebXml(contextWebXml, webXml, false);\r
+ \r
+ if (webXml.getMajorVersion() >= 3) {\r
+ // Ordering is important here\r
+\r
+ // Step 1. Identify all the JARs packaged with the application\r
+ // If the JARs have a web-fragment.xml it will be parsed at this\r
+ // point.\r
+ Map<String,WebXml> fragments = processJarsForWebFragments();\r
+\r
+ // Only need to process fragments and annotations if metadata is\r
+ // not complete\r
+ Set<WebXml> orderedFragments = null;\r
+ if (!webXml.isMetadataComplete()) {\r
+ // Step 2. Order the fragments.\r
+ orderedFragments = WebXml.orderWebFragments(webXml, fragments);\r
+ \r
+ // Step 3. Look for ServletContainerInitializer implementations\r
+ if (ok) {\r
+ processServletContainerInitializers(orderedFragments);\r
+ }\r
+ \r
+ // Step 4. Process /WEB-INF/classes for annotations\r
+ // This will add any matching classes to the typeInitializerMap\r
+ if (ok) {\r
+ URL webinfClasses;\r
+ try {\r
+ webinfClasses = context.getServletContext().getResource(\r
+ "/WEB-INF/classes");\r
+ processAnnotationsUrl(webinfClasses, webXml);\r
+ } catch (MalformedURLException e) {\r
+ log.error(sm.getString(\r
+ "contextConfig.webinfClassesUrl"), e);\r
+ }\r
+ }\r
+ \r
+ // Step 5. Process JARs for annotations - only need to process\r
+ // those fragments we are going to use\r
+ // This will add any matching classes to the typeInitializerMap\r
+ if (ok) {\r
+ processAnnotations(orderedFragments);\r
+ }\r
+ \r
+ // Step 6. Merge web-fragment.xml files into the main web.xml\r
+ // file.\r
+ if (ok) {\r
+ ok = webXml.merge(orderedFragments);\r
+ }\r
+ \r
+ // Step 7. Apply global defaults\r
+ // Have to merge defaults before JSP conversion since defaults\r
+ // provide JSP servlet definition.\r
+ webXml.merge(defaults);\r
+\r
+ // Step 8. Convert explicitly mentioned jsps to servlets\r
+ if (ok) {\r
+ convertJsps(webXml);\r
+ }\r
+ \r
+ // Step 9. Apply merged web.xml to Context\r
+ if (ok) {\r
+ webXml.configureContext(context);\r
+ \r
+ // Step 9a. Make the merged web.xml available to other\r
+ // components, specifically Jasper, to save those components\r
+ // from having to re-generate it.\r
+ // TODO Use a ServletContainerInitializer for Jasper\r
+ String mergedWebXml = webXml.toXml();\r
+ context.getServletContext().setAttribute(\r
+ org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,\r
+ mergedWebXml);\r
+ if (context.getLogEffectiveWebXml()) {\r
+ log.info("web.xml:\n" + mergedWebXml);\r
+ }\r
+ }\r
+ } else {\r
+ webXml.merge(defaults);\r
+ webXml.configureContext(context);\r
+ }\r
+ \r
+ // Always need to look for static resources\r
+ // Step 10. Look for static resources packaged in JARs\r
+ if (ok) {\r
+ // Spec does not define an order.\r
+ // Use ordered JARs followed by remaining JARs\r
+ Set<WebXml> resourceJars = new LinkedHashSet<WebXml>();\r
+ if (orderedFragments != null) {\r
+ for (WebXml fragment : orderedFragments) {\r
+ resourceJars.add(fragment);\r
+ }\r
+ }\r
+ for (WebXml fragment : fragments.values()) {\r
+ if (!resourceJars.contains(fragment)) {\r
+ resourceJars.add(fragment);\r
+ }\r
+ }\r
+ processResourceJARs(resourceJars);\r
+ // See also StandardContext.resourcesStart() for\r
+ // WEB-INF/classes/META-INF/resources configuration\r
+ }\r
+ \r
+ // Only look for ServletContainerInitializer if metadata is not\r
+ // complete\r
+ if (!webXml.isMetadataComplete()) {\r
+ // Step 11. Apply the ServletContainerInitializer config to the\r
+ // context\r
+ if (ok) {\r
+ for (Map.Entry<ServletContainerInitializer,\r
+ Set<Class<?>>> entry : \r
+ initializerClassMap.entrySet()) {\r
+ if (entry.getValue().isEmpty()) {\r
+ context.addServletContainerInitializer(\r
+ entry.getKey(), null);\r
+ } else {\r
+ context.addServletContainerInitializer(\r
+ entry.getKey(), entry.getValue());\r
+ }\r
+ }\r
+ }\r
+ }\r
+ } else {\r
+ // Apply unmerged web.xml to Context\r
+ webXml.merge(defaults);\r
+ convertJsps(webXml);\r
+ webXml.configureContext(context);\r
+ }\r
+ }\r
+\r
+ private WebXml getDefaultWebXmlFragment() {\r
+\r
+ // Host should never be null\r
+ Host host = (Host) context.getParent();\r
+\r
+ DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);\r
+ \r
+ InputSource globalWebXml = getGlobalWebXmlSource();\r
+ InputSource hostWebXml = getHostWebXmlSource();\r
+ \r
+ long globalTimeStamp = 0;\r
+ long hostTimeStamp = 0;\r
+ \r
+ if (globalWebXml != null) {\r
+ try {\r
+ File f = new File(new URI(globalWebXml.getSystemId()));\r
+ globalTimeStamp = f.lastModified();\r
+ } catch (URISyntaxException e) {\r
+ globalTimeStamp = -1;\r
+ }\r
+ }\r
+ \r
+ if (hostWebXml != null) {\r
+ try {\r
+ File f = new File(new URI(hostWebXml.getSystemId()));\r
+ hostTimeStamp = f.lastModified();\r
+ } catch (URISyntaxException e) {\r
+ hostTimeStamp = -1;\r
+ }\r
+ }\r
+ \r
+ if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&\r
+ entry.getHostTimeStamp() == hostTimeStamp) {\r
+ return entry.getWebXml();\r
+ }\r
+ \r
+ // Parsing global web.xml is relatively expensive. Use a sync block to\r
+ // make sure it only happens once\r
+ synchronized (host) {\r
+ entry = hostWebXmlCache.get(host);\r
+ if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&\r
+ entry.getHostTimeStamp() == hostTimeStamp) {\r
+ return entry.getWebXml();\r
+ }\r
+\r
+ WebXml webXmlDefaultFragment = createWebXml();\r
+ webXmlDefaultFragment.setOverridable(true);\r
+ // Set to distributable else every app will be prevented from being\r
+ // distributable when the default fragment is merged with the main\r
+ // web.xml\r
+ webXmlDefaultFragment.setDistributable(true);\r
+ // When merging, the default welcome files are only used if the app has\r
+ // not defined any welcomes files.\r
+ webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);\r
+\r
+ // Parse global web.xml if present\r
+ if (globalWebXml == null) {\r
+ // This is unusual enough to log\r
+ log.info(sm.getString("contextConfig.defaultMissing"));\r
+ } else {\r
+ parseWebXml(globalWebXml, webXmlDefaultFragment, false);\r
+ }\r
+ \r
+ // Parse host level web.xml if present\r
+ // Additive apart from welcome pages\r
+ webXmlDefaultFragment.setReplaceWelcomeFiles(true);\r
+ \r
+ parseWebXml(hostWebXml, webXmlDefaultFragment, false);\r
+ \r
+ // Don't update the cache if an error occurs\r
+ if (globalTimeStamp != -1 && hostTimeStamp != -1) {\r
+ entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,\r
+ globalTimeStamp, hostTimeStamp);\r
+ hostWebXmlCache.put(host, entry);\r
+ }\r
+\r
+ return webXmlDefaultFragment;\r
+ }\r
+ }\r
+\r
+\r
+ private void convertJsps(WebXml webXml) {\r
+ Map<String,String> jspInitParams;\r
+ ServletDef jspServlet = webXml.getServlets().get("jsp");\r
+ if (jspServlet == null) {\r
+ jspInitParams = new HashMap<String,String>();\r
+ Wrapper w = (Wrapper) context.findChild("jsp");\r
+ if (w != null) {\r
+ String[] params = w.findInitParameters();\r
+ for (String param : params) {\r
+ jspInitParams.put(param, w.findInitParameter(param));\r
+ }\r
+ }\r
+ } else {\r
+ jspInitParams = jspServlet.getParameterMap();\r
+ }\r
+ for (ServletDef servletDef: webXml.getServlets().values()) {\r
+ if (servletDef.getJspFile() != null) {\r
+ convertJsp(servletDef, jspInitParams);\r
+ }\r
+ }\r
+ }\r
+\r
+ private void convertJsp(ServletDef servletDef,\r
+ Map<String,String> jspInitParams) {\r
+ servletDef.setServletClass(org.apache.catalina.core.Constants.JSP_SERVLET_CLASS);\r
+ String jspFile = servletDef.getJspFile();\r
+ if ((jspFile != null) && !jspFile.startsWith("/")) {\r
+ if (context.isServlet22()) {\r
+ if(log.isDebugEnabled())\r
+ log.debug(sm.getString("contextConfig.jspFile.warning",\r
+ jspFile));\r
+ jspFile = "/" + jspFile;\r
+ } else {\r
+ throw new IllegalArgumentException\r
+ (sm.getString("contextConfig.jspFile.error", jspFile));\r
+ }\r
+ }\r
+ servletDef.getParameterMap().put("jspFile", jspFile);\r
+ servletDef.setJspFile(null);\r
+ for (Map.Entry<String, String> initParam: jspInitParams.entrySet()) {\r
+ servletDef.addInitParameter(initParam.getKey(), initParam.getValue());\r
+ }\r
+ }\r
+\r
+ protected WebXml createWebXml() {\r
+ return new WebXml();\r
+ }\r
+\r
+ /**\r
+ * Scan JARs for ServletContainerInitializer implementations.\r
+ * Implementations will be added in web-fragment.xml priority order.\r
+ */\r
+ protected void processServletContainerInitializers(\r
+ Set<WebXml> fragments) {\r
+ \r
+ for (WebXml fragment : fragments) {\r
+ URL url = fragment.getURL();\r
+ Jar jar = null;\r
+ InputStream is = null;\r
+ ServletContainerInitializer sci = null;\r
+ try {\r
+ if ("jar".equals(url.getProtocol())) {\r
+ jar = JarFactory.newInstance(url);\r
+ is = jar.getInputStream(SCI_LOCATION);\r
+ } else if ("file".equals(url.getProtocol())) {\r
+ String path = url.getPath();\r
+ File file = new File(path, SCI_LOCATION);\r
+ if (file.exists()) {\r
+ is = new FileInputStream(file);\r
+ }\r
+ }\r
+ if (is != null) {\r
+ sci = getServletContainerInitializer(is);\r
+ }\r
+ } catch (IOException ioe) {\r
+ log.error(sm.getString(\r
+ "contextConfig.servletContainerInitializerFail", url,\r
+ context.getName()));\r
+ ok = false;\r
+ return;\r
+ } finally {\r
+ if (is != null) {\r
+ try {\r
+ is.close();\r
+ } catch (IOException e) {\r
+ // Ignore\r
+ }\r
+ }\r
+ if (jar != null) {\r
+ jar.close();\r
+ }\r
+ }\r
+ \r
+ if (sci == null) {\r
+ continue;\r
+ }\r
+\r
+ initializerClassMap.put(sci, new HashSet<Class<?>>());\r
+ \r
+ HandlesTypes ht =\r
+ sci.getClass().getAnnotation(HandlesTypes.class);\r
+ if (ht != null) {\r
+ Class<?>[] types = ht.value();\r
+ if (types != null) {\r
+ for (Class<?> type : types) {\r
+ Set<ServletContainerInitializer> scis =\r
+ typeInitializerMap.get(type);\r
+ if (scis == null) {\r
+ scis = new HashSet<ServletContainerInitializer>();\r
+ typeInitializerMap.put(type, scis);\r
+ }\r
+ scis.add(sci);\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Extract the name of the ServletContainerInitializer.\r
+ * \r
+ * @param is The resource where the name is defined \r
+ * @return The class name\r
+ * @throws IOException\r
+ */\r
+ protected ServletContainerInitializer getServletContainerInitializer(\r
+ InputStream is) throws IOException {\r
+\r
+ String className = null;\r
+ \r
+ if (is != null) {\r
+ String line = null;\r
+ try {\r
+ BufferedReader br =\r
+ new BufferedReader(new InputStreamReader(is, "UTF-8"));\r
+ line = br.readLine();\r
+ if (line != null && line.trim().length() > 0) {\r
+ className = line.trim();\r
+ }\r
+ } catch (UnsupportedEncodingException e) {\r
+ // Should never happen with UTF-8\r
+ // If it does - ignore & return null\r
+ }\r
+ }\r
+ \r
+ ServletContainerInitializer sci = null;\r
+ try {\r
+ Class<?> clazz = Class.forName(className,true,\r
+ context.getLoader().getClassLoader());\r
+ sci = (ServletContainerInitializer) clazz.newInstance();\r
+ } catch (ClassNotFoundException e) {\r
+ log.error(sm.getString("contextConfig.invalidSci", className), e);\r
+ throw new IOException(e);\r
+ } catch (InstantiationException e) {\r
+ log.error(sm.getString("contextConfig.invalidSci", className), e);\r
+ throw new IOException(e);\r
+ } catch (IllegalAccessException e) {\r
+ log.error(sm.getString("contextConfig.invalidSci", className), e);\r
+ throw new IOException(e);\r
+ }\r
+ \r
+ return sci;\r
+ }\r
+\r
+ \r
+ /**\r
+ * Scan JARs that contain web-fragment.xml files that will be used to\r
+ * configure this application to see if they also contain static resources.\r
+ * If static resources are found, add them to the context. Resources are\r
+ * added in web-fragment.xml priority order.\r
+ */\r
+ protected void processResourceJARs(Set<WebXml> fragments) {\r
+ for (WebXml fragment : fragments) {\r
+ URL url = fragment.getURL();\r
+ Jar jar = null;\r
+ try {\r
+ // Note: Ignore file URLs for now since only jar URLs will be accepted\r
+ if ("jar".equals(url.getProtocol())) {\r
+ jar = JarFactory.newInstance(url);\r
+ if (jar.entryExists("META-INF/resources/")) {\r
+ context.addResourceJarUrl(url);\r
+ }\r
+ }\r
+ } catch (IOException ioe) {\r
+ log.error(sm.getString("contextConfig.resourceJarFail", url,\r
+ context.getName()));\r
+ } finally {\r
+ if (jar != null) {\r
+ jar.close();\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Identify the default web.xml to be used and obtain an input source for\r
+ * it.\r
+ */\r
+ protected InputSource getGlobalWebXmlSource() {\r
+ // Is a default web.xml specified for the Context?\r
+ if (defaultWebXml == null && context instanceof StandardContext) {\r
+ defaultWebXml = ((StandardContext) context).getDefaultWebXml();\r
+ }\r
+ // Set the default if we don't have any overrides\r
+ if (defaultWebXml == null) getDefaultWebXml();\r
+\r
+ // Is it explicitly suppressed, e.g. in embedded environment?\r
+ if (Constants.NoDefaultWebXml.equals(defaultWebXml)) {\r
+ return null;\r
+ }\r
+ return getWebXmlSource(defaultWebXml, getBaseDir());\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Identify the host web.xml to be used and obtain an input source for\r
+ * it.\r
+ */\r
+ protected InputSource getHostWebXmlSource() {\r
+ String resourceName = getHostConfigPath(Constants.HostWebXml);\r
+ \r
+ // In an embedded environment, configBase might not be set\r
+ File configBase = getConfigBase();\r
+ if (configBase == null)\r
+ return null;\r
+ \r
+ String basePath = null;\r
+ try {\r
+ basePath = configBase.getCanonicalPath();\r
+ } catch (IOException e) {\r
+ log.error(sm.getString("contextConfig.baseError"), e);\r
+ return null;\r
+ }\r
+\r
+ return getWebXmlSource(resourceName, basePath);\r
+ }\r
+ \r
+ /**\r
+ * Identify the application web.xml to be used and obtain an input source\r
+ * for it.\r
+ */\r
+ protected InputSource getContextWebXmlSource() {\r
+ InputStream stream = null;\r
+ InputSource source = null;\r
+ URL url = null;\r
+ \r
+ String altDDName = null;\r
+\r
+ // Open the application web.xml file, if it exists\r
+ ServletContext servletContext = context.getServletContext();\r
+ if (servletContext != null) {\r
+ altDDName = (String)servletContext.getAttribute(\r
+ Globals.ALT_DD_ATTR);\r
+ if (altDDName != null) {\r
+ try {\r
+ stream = new FileInputStream(altDDName);\r
+ url = new File(altDDName).toURI().toURL();\r
+ } catch (FileNotFoundException e) {\r
+ log.error(sm.getString("contextConfig.altDDNotFound",\r
+ altDDName));\r
+ } catch (MalformedURLException e) {\r
+ log.error(sm.getString("contextConfig.applicationUrl"));\r
+ }\r
+ }\r
+ else {\r
+ stream = servletContext.getResourceAsStream\r
+ (Constants.ApplicationWebXml);\r
+ try {\r
+ url = servletContext.getResource(\r
+ Constants.ApplicationWebXml);\r
+ } catch (MalformedURLException e) {\r
+ log.error(sm.getString("contextConfig.applicationUrl"));\r
+ }\r
+ }\r
+ }\r
+ if (stream == null || url == null) {\r
+ if (log.isDebugEnabled()) {\r
+ log.debug(sm.getString("contextConfig.applicationMissing") + " " + context);\r
+ }\r
+ } else {\r
+ source = new InputSource(url.toExternalForm());\r
+ source.setByteStream(stream);\r
+ }\r
+ \r
+ return source;\r
+ }\r
+ \r
+ /**\r
+ * \r
+ * @param filename Name of the file (possibly with one or more leading path\r
+ * segments) to read\r
+ * @param path Location that filename is relative to \r
+ */\r
+ protected InputSource getWebXmlSource(String filename, String path) {\r
+ File file = new File(filename);\r
+ if (!file.isAbsolute()) {\r
+ file = new File(path, filename);\r
+ }\r
+\r
+ InputStream stream = null;\r
+ InputSource source = null;\r
+\r
+ try {\r
+ if (!file.exists()) {\r
+ // Use getResource and getResourceAsStream\r
+ stream =\r
+ getClass().getClassLoader().getResourceAsStream(filename);\r
+ if(stream != null) {\r
+ source =\r
+ new InputSource(getClass().getClassLoader().getResource(\r
+ filename).toURI().toString());\r
+ } \r
+ } else {\r
+ source = new InputSource(file.getAbsoluteFile().toURI().toString());\r
+ stream = new FileInputStream(file);\r
+ context.addWatchedResource(file.getAbsolutePath());\r
+ }\r
+\r
+ if (stream != null && source != null) {\r
+ source.setByteStream(stream);\r
+ }\r
+ } catch (Exception e) {\r
+ log.error(sm.getString(\r
+ "contextConfig.defaultError", filename, file), e);\r
+ }\r
+\r
+ return source;\r
+ }\r
+\r
+\r
+ protected void parseWebXml(InputSource source, WebXml dest,\r
+ boolean fragment) {\r
+ \r
+ if (source == null) return;\r
+\r
+ XmlErrorHandler handler = new XmlErrorHandler();\r
+\r
+ // Web digesters and rulesets are shared between contexts but are not\r
+ // thread safe. Whilst there should only be one thread at a time\r
+ // processing a config, play safe and sync.\r
+ Digester digester;\r
+ WebRuleSet ruleSet;\r
+ if (fragment) {\r
+ digester = webFragmentDigester;\r
+ ruleSet = webFragmentRuleSet;\r
+ } else {\r
+ digester = webDigester;\r
+ ruleSet = webRuleSet;\r
+ }\r
+ \r
+ // Sync on the ruleSet since the same ruleSet is shared across all four\r
+ // digesters\r
+ synchronized(ruleSet) {\r
+ \r
+ digester.push(dest);\r
+ digester.setErrorHandler(handler);\r
+ \r
+ if(log.isDebugEnabled()) {\r
+ log.debug(sm.getString("contextConfig.applicationStart",\r
+ source.getSystemId()));\r
+ }\r
+\r
+ try {\r
+ digester.parse(source);\r
+\r
+ if (handler.getWarnings().size() > 0 ||\r
+ handler.getErrors().size() > 0) {\r
+ ok = false;\r
+ handler.logFindings(log, source.getSystemId());\r
+ }\r
+ } catch (SAXParseException e) {\r
+ log.error(sm.getString("contextConfig.applicationParse",\r
+ source.getSystemId()), e);\r
+ log.error(sm.getString("contextConfig.applicationPosition",\r
+ "" + e.getLineNumber(),\r
+ "" + e.getColumnNumber()));\r
+ ok = false;\r
+ } catch (Exception e) {\r
+ log.error(sm.getString("contextConfig.applicationParse",\r
+ source.getSystemId()), e);\r
+ ok = false;\r
+ } finally {\r
+ digester.reset();\r
+ ruleSet.recycle();\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Scan /META-INF/lib for JARs and for each one found add it and any\r
+ * /META-INF/web-fragment.xml to the resulting Map. web-fragment.xml files\r
+ * will be parsed before being added to the map. Every JAR will be added and\r
+ * <code>null</code> will be used if no web-fragment.xml was found. Any JARs\r
+ * known not contain fragments will be skipped.\r
+ * \r
+ * @return A map of JAR name to processed web fragment (if any)\r
+ */\r
+ protected Map<String,WebXml> processJarsForWebFragments() {\r
+ \r
+ JarScanner jarScanner = context.getJarScanner();\r
+ FragmentJarScannerCallback callback = new FragmentJarScannerCallback();\r
+ \r
+ jarScanner.scan(context.getServletContext(),\r
+ context.getLoader().getClassLoader(), callback, null);\r
+ \r
+ return callback.getFragments();\r
+ }\r
+\r
+ protected void processAnnotations(Set<WebXml> fragments) {\r
+ for(WebXml fragment : fragments) {\r
+ if (!fragment.isMetadataComplete()) {\r
+ WebXml annotations = new WebXml();\r
+ // no impact on distributable\r
+ annotations.setDistributable(true);\r
+ URL url = fragment.getURL();\r
+ processAnnotationsUrl(url, annotations);\r
+ Set<WebXml> set = new HashSet<WebXml>();\r
+ set.add(annotations);\r
+ // Merge annotations into fragment - fragment takes priority\r
+ fragment.merge(set);\r
+ }\r
+ }\r
+ }\r
+\r
+ protected void processAnnotationsUrl(URL url, WebXml fragment) {\r
+ if (url == null) {\r
+ // Nothing to do.\r
+ return;\r
+ } else if ("jar".equals(url.getProtocol())) {\r
+ processAnnotationsJar(url, fragment);\r
+ } else if ("jndi".equals(url.getProtocol())) {\r
+ processAnnotationsJndi(url, fragment);\r
+ } else if ("file".equals(url.getProtocol())) {\r
+ try {\r
+ processAnnotationsFile(new File(url.toURI()), fragment);\r
+ } catch (URISyntaxException e) {\r
+ log.error(sm.getString("contextConfig.fileUrl", url), e);\r
+ }\r
+ } else {\r
+ log.error(sm.getString("contextConfig.unknownUrlProtocol",\r
+ url.getProtocol(), url));\r
+ }\r
+ \r
+ }\r
+\r
+\r
+ protected void processAnnotationsJar(URL url, WebXml fragment) {\r
+\r
+ Jar jar = null;\r
+ InputStream is;\r
+ \r
+ try {\r
+ jar = JarFactory.newInstance(url);\r
+ \r
+ jar.nextEntry();\r
+ String entryName = jar.getEntryName();\r
+ while (entryName != null) {\r
+ if (entryName.endsWith(".class")) {\r
+ is = null;\r
+ try {\r
+ is = jar.getEntryInputStream();\r
+ processAnnotationsStream(is, fragment);\r
+ } catch (IOException e) {\r
+ log.error(sm.getString("contextConfig.inputStreamJar",\r
+ entryName, url),e);\r
+ } finally {\r
+ if (is != null) {\r
+ try {\r
+ is.close();\r
+ } catch (IOException ioe) {\r
+ // Ignore\r
+ }\r
+ }\r
+ }\r
+ }\r
+ jar.nextEntry();\r
+ entryName = jar.getEntryName();\r
+ }\r
+ } catch (IOException e) {\r
+ log.error(sm.getString("contextConfig.jarFile", url), e);\r
+ } finally {\r
+ if (jar != null) {\r
+ jar.close();\r
+ }\r
+ }\r
+ }\r
+\r
+ \r
+ protected void processAnnotationsJndi(URL url, WebXml fragment) {\r
+ try {\r
+ URLConnection urlConn = url.openConnection();\r
+ DirContextURLConnection dcUrlConn;\r
+ if (!(urlConn instanceof DirContextURLConnection)) {\r
+ // This should never happen\r
+ sm.getString("contextConfig.jndiUrlNotDirContextConn", url);\r
+ return;\r
+ }\r
+ \r
+ dcUrlConn = (DirContextURLConnection) urlConn;\r
+ dcUrlConn.setUseCaches(false);\r
+ \r
+ String type = dcUrlConn.getHeaderField(ResourceAttributes.TYPE);\r
+ if (ResourceAttributes.COLLECTION_TYPE.equals(type)) {\r
+ // Collection\r
+ Enumeration<String> dirs = dcUrlConn.list();\r
+ while (dirs.hasMoreElements()) {\r
+ String dir = dirs.nextElement();\r
+ URL dirUrl = new URL(url.toString() + '/' + dir);\r
+ processAnnotationsJndi(dirUrl, fragment);\r
+ }\r
+ \r
+ } else {\r
+ // Single file\r
+ if (url.getPath().endsWith(".class")) {\r
+ InputStream is = null;\r
+ try {\r
+ is = dcUrlConn.getInputStream();\r
+ processAnnotationsStream(is, fragment);\r
+ } catch (IOException e) {\r
+ log.error(sm.getString("contextConfig.inputStreamJndi",\r
+ url),e);\r
+ } finally {\r
+ if (is != null) {\r
+ try {\r
+ is.close();\r
+ } catch (Throwable t) {\r
+ ExceptionUtils.handleThrowable(t);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ } catch (IOException e) {\r
+ log.error(sm.getString("contextConfig.jndiUrl", url), e);\r
+ }\r
+ }\r
+ \r
+ \r
+ protected void processAnnotationsFile(File file, WebXml fragment) {\r
+ \r
+ if (file.isDirectory()) {\r
+ String[] dirs = file.list();\r
+ for (String dir : dirs) {\r
+ processAnnotationsFile(new File(file,dir), fragment);\r
+ }\r
+ } else if (file.canRead() && file.getName().endsWith(".class")) {\r
+ FileInputStream fis = null;\r
+ try {\r
+ fis = new FileInputStream(file);\r
+ processAnnotationsStream(fis, fragment);\r
+ } catch (IOException e) {\r
+ log.error(sm.getString("contextConfig.inputStreamFile",\r
+ file.getAbsolutePath()),e);\r
+ } finally {\r
+ if (fis != null) {\r
+ try {\r
+ fis.close();\r
+ } catch (Throwable t) {\r
+ ExceptionUtils.handleThrowable(t);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ protected void processAnnotationsStream(InputStream is, WebXml fragment)\r
+ throws ClassFormatException, IOException {\r
+ \r
+ ClassParser parser = new ClassParser(is, null);\r
+ JavaClass clazz = parser.parse();\r
+ \r
+ checkHandlesTypes(clazz);\r
+ \r
+ String className = clazz.getClassName();\r
+ \r
+ AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();\r
+\r
+ for (AnnotationEntry ae : annotationsEntries) {\r
+ String type = ae.getAnnotationType();\r
+ if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {\r
+ processAnnotationWebServlet(className, ae, fragment);\r
+ }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {\r
+ processAnnotationWebFilter(className, ae, fragment);\r
+ }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {\r
+ fragment.addListener(className);\r
+ } else {\r
+ // Unknown annotation - ignore\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * For classes packaged with the web application, the class and each\r
+ * super class needs to be checked for a match with {@link HandlesTypes} or\r
+ * for an annotation that matches {@link HandlesTypes}.\r
+ * @param javaClass\r
+ */\r
+ protected void checkHandlesTypes(JavaClass javaClass) {\r
+ \r
+ // Skip this if we can\r
+ if (typeInitializerMap.size() == 0)\r
+ return;\r
+ \r
+ // No choice but to load the class\r
+ String className = javaClass.getClassName();\r
+ \r
+ Class<?> clazz = null;\r
+ try {\r
+ clazz = context.getLoader().getClassLoader().loadClass(className);\r
+ } catch (NoClassDefFoundError e) {\r
+ log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",\r
+ className), e);\r
+ return;\r
+ } catch (ClassNotFoundException e) {\r
+ log.warn(sm.getString("contextConfig.invalidSciHandlesTypes",\r
+ className), e);\r
+ return;\r
+ } catch (ClassFormatError e) {\r
+ log.warn(sm.getString("contextConfig.invalidSciHandlesTypes",\r
+ className), e);\r
+ return;\r
+ } catch (Throwable t) {\r
+ ExceptionUtils.handleThrowable(t);\r
+ log.warn(sm.getString("contextConfig.invalidSciHandlesTypes",\r
+ className), t);\r
+ return;\r
+ }\r
+\r
+ if (clazz.isAnnotation()) {\r
+ // Skip\r
+ return;\r
+ }\r
+ \r
+ boolean match = false;\r
+ \r
+ for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :\r
+ typeInitializerMap.entrySet()) {\r
+ if (entry.getKey().isAnnotation()) {\r
+ AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries();\r
+ for (AnnotationEntry annotationEntry : annotationEntries) {\r
+ if (entry.getKey().getName().equals(\r
+ getClassName(annotationEntry.getAnnotationType()))) {\r
+ match = true;\r
+ break;\r
+ }\r
+ }\r
+ } else if (entry.getKey().isAssignableFrom(clazz)) {\r
+ match = true;\r
+ }\r
+ if (match) {\r
+ for (ServletContainerInitializer sci : entry.getValue()) {\r
+ initializerClassMap.get(sci).add(clazz);\r
+ }\r
+ match = false;\r
+ }\r
+ }\r
+ }\r
+\r
+ private static final String getClassName(String internalForm) {\r
+ if (!internalForm.startsWith("L")) {\r
+ return internalForm;\r
+ }\r
+ \r
+ // Assume starts with L, ends with ; and uses / rather than .\r
+ return internalForm.substring(1,\r
+ internalForm.length() - 1).replace('/', '.');\r
+ }\r
+\r
+ protected void processAnnotationWebServlet(String className,\r
+ AnnotationEntry ae, WebXml fragment) {\r
+ String servletName = null;\r
+ // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81\r
+ ElementValuePair[] evps = ae.getElementValuePairs();\r
+ for (ElementValuePair evp : evps) {\r
+ String name = evp.getNameString();\r
+ if ("name".equals(name)) {\r
+ servletName = evp.getValue().stringifyValue();\r
+ break;\r
+ }\r
+ }\r
+ if (servletName == null) {\r
+ // classname is default servletName as annotation has no name!\r
+ servletName = className;\r
+ }\r
+ ServletDef servletDef = fragment.getServlets().get(servletName);\r
+ \r
+ boolean isWebXMLservletDef;\r
+ if (servletDef == null) {\r
+ servletDef = new ServletDef();\r
+ servletDef.setServletName(servletName);\r
+ servletDef.setServletClass(className);\r
+ isWebXMLservletDef = false;\r
+ } else {\r
+ isWebXMLservletDef = true;\r
+ }\r
+\r
+ boolean urlPatternsSet = false;\r
+ String[] urlPatterns = null;\r
+\r
+ // ElementValuePair[] evps = ae.getElementValuePairs();\r
+ for (ElementValuePair evp : evps) {\r
+ String name = evp.getNameString();\r
+ if ("value".equals(name) || "urlPatterns".equals(name)) {\r
+ if (urlPatternsSet) {\r
+ throw new IllegalArgumentException(sm.getString(\r
+ "contextConfig.urlPatternValue", className));\r
+ }\r
+ urlPatternsSet = true;\r
+ urlPatterns = processAnnotationsStringArray(evp.getValue());\r
+ } else if ("description".equals(name)) {\r
+ if (servletDef.getDescription() == null) {\r
+ servletDef.setDescription(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("displayName".equals(name)) {\r
+ if (servletDef.getDisplayName() == null) {\r
+ servletDef.setDisplayName(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("largeIcon".equals(name)) {\r
+ if (servletDef.getLargeIcon() == null) {\r
+ servletDef.setLargeIcon(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("smallIcon".equals(name)) {\r
+ if (servletDef.getSmallIcon() == null) {\r
+ servletDef.setSmallIcon(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("asyncSupported".equals(name)) {\r
+ if (servletDef.getAsyncSupported() == null) {\r
+ servletDef.setAsyncSupported(evp.getValue()\r
+ .stringifyValue());\r
+ }\r
+ } else if ("loadOnStartup".equals(name)) {\r
+ if (servletDef.getLoadOnStartup() == null) {\r
+ servletDef\r
+ .setLoadOnStartup(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("initParams".equals(name)) {\r
+ Map<String, String> initParams = processAnnotationWebInitParams(evp\r
+ .getValue());\r
+ if (isWebXMLservletDef) {\r
+ Map<String, String> webXMLInitParams = servletDef\r
+ .getParameterMap();\r
+ for (Map.Entry<String, String> entry : initParams\r
+ .entrySet()) {\r
+ if (webXMLInitParams.get(entry.getKey()) == null) {\r
+ servletDef.addInitParameter(entry.getKey(), entry\r
+ .getValue());\r
+ }\r
+ }\r
+ } else {\r
+ for (Map.Entry<String, String> entry : initParams\r
+ .entrySet()) {\r
+ servletDef.addInitParameter(entry.getKey(), entry\r
+ .getValue());\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if (!isWebXMLservletDef && urlPatterns != null) {\r
+ fragment.addServlet(servletDef);\r
+ }\r
+ if (urlPatterns != null) {\r
+ if (!fragment.getServletMappings().containsValue(servletName)) {\r
+ for (String urlPattern : urlPatterns) {\r
+ fragment.addServletMapping(urlPattern, servletName);\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * process filter annotation and merge with existing one!\r
+ * FIXME: refactoring method to long and has redundant subroutines with processAnnotationWebServlet!\r
+ * @param className\r
+ * @param ae\r
+ * @param fragment\r
+ */\r
+ protected void processAnnotationWebFilter(String className,\r
+ AnnotationEntry ae, WebXml fragment) {\r
+ String filterName = null;\r
+ // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81\r
+ ElementValuePair[] evps = ae.getElementValuePairs();\r
+ for (ElementValuePair evp : evps) {\r
+ String name = evp.getNameString();\r
+ if ("filterName".equals(name)) {\r
+ filterName = evp.getValue().stringifyValue();\r
+ break;\r
+ }\r
+ }\r
+ if (filterName == null) {\r
+ // classname is default filterName as annotation has no name!\r
+ filterName = className;\r
+ }\r
+ FilterDef filterDef = fragment.getFilters().get(filterName);\r
+ FilterMap filterMap = new FilterMap();\r
+\r
+ boolean isWebXMLfilterDef;\r
+ if (filterDef == null) {\r
+ filterDef = new FilterDef();\r
+ filterDef.setFilterName(filterName);\r
+ filterDef.setFilterClass(className);\r
+ isWebXMLfilterDef = false;\r
+ } else {\r
+ isWebXMLfilterDef = true;\r
+ }\r
+\r
+ boolean urlPatternsSet = false;\r
+ boolean dispatchTypesSet = false;\r
+ String[] urlPatterns = null;\r
+\r
+ for (ElementValuePair evp : evps) {\r
+ String name = evp.getNameString();\r
+ if ("value".equals(name) || "urlPatterns".equals(name)) {\r
+ if (urlPatternsSet) {\r
+ throw new IllegalArgumentException(sm.getString(\r
+ "contextConfig.urlPatternValue", className));\r
+ }\r
+ urlPatterns = processAnnotationsStringArray(evp.getValue());\r
+ urlPatternsSet = urlPatterns.length > 0;\r
+ for (String urlPattern : urlPatterns) {\r
+ filterMap.addURLPattern(urlPattern);\r
+ }\r
+ } else if ("servletNames".equals(name)) {\r
+ String[] servletNames = processAnnotationsStringArray(evp\r
+ .getValue());\r
+ for (String servletName : servletNames) {\r
+ filterMap.addServletName(servletName);\r
+ }\r
+ } else if ("dispatcherTypes".equals(name)) {\r
+ String[] dispatcherTypes = processAnnotationsStringArray(evp\r
+ .getValue());\r
+ dispatchTypesSet = dispatcherTypes.length > 0;\r
+ for (String dispatcherType : dispatcherTypes) {\r
+ filterMap.setDispatcher(dispatcherType);\r
+ }\r
+ } else if ("description".equals(name)) {\r
+ if (filterDef.getDescription() == null) {\r
+ filterDef.setDescription(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("displayName".equals(name)) {\r
+ if (filterDef.getDisplayName() == null) {\r
+ filterDef.setDisplayName(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("largeIcon".equals(name)) {\r
+ if (filterDef.getLargeIcon() == null) {\r
+ filterDef.setLargeIcon(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("smallIcon".equals(name)) {\r
+ if (filterDef.getSmallIcon() == null) {\r
+ filterDef.setSmallIcon(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("asyncSupported".equals(name)) {\r
+ if (filterDef.getAsyncSupported() == null) {\r
+ filterDef\r
+ .setAsyncSupported(evp.getValue().stringifyValue());\r
+ }\r
+ } else if ("initParams".equals(name)) {\r
+ Map<String, String> initParams = processAnnotationWebInitParams(evp\r
+ .getValue());\r
+ if (isWebXMLfilterDef) {\r
+ Map<String, String> webXMLInitParams = filterDef\r
+ .getParameterMap();\r
+ for (Map.Entry<String, String> entry : initParams\r
+ .entrySet()) {\r
+ if (webXMLInitParams.get(entry.getKey()) == null) {\r
+ filterDef.addInitParameter(entry.getKey(), entry\r
+ .getValue());\r
+ }\r
+ }\r
+ } else {\r
+ for (Map.Entry<String, String> entry : initParams\r
+ .entrySet()) {\r
+ filterDef.addInitParameter(entry.getKey(), entry\r
+ .getValue());\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+ if (!isWebXMLfilterDef) {\r
+ fragment.addFilter(filterDef);\r
+ filterMap.setFilterName(filterName);\r
+ fragment.addFilterMapping(filterMap);\r
+ }\r
+ if (urlPatternsSet || dispatchTypesSet) {\r
+ Set<FilterMap> fmap = fragment.getFilterMappings();\r
+ FilterMap descMap = null;\r
+ for (FilterMap map : fmap) {\r
+ if (filterName.equals(map.getFilterName())) {\r
+ descMap = map;\r
+ break;\r
+ }\r
+ }\r
+ if (descMap != null) {\r
+ String[] urlsPatterns = descMap.getURLPatterns();\r
+ if (urlPatternsSet\r
+ && (urlsPatterns == null || urlsPatterns.length == 0)) {\r
+ for (String urlPattern : filterMap.getURLPatterns()) {\r
+ descMap.addURLPattern(urlPattern);\r
+ }\r
+ }\r
+ String[] dispatcherNames = descMap.getDispatcherNames();\r
+ if (dispatchTypesSet\r
+ && (dispatcherNames == null || dispatcherNames.length == 0)) {\r
+ for (String dis : filterMap.getDispatcherNames()) {\r
+ descMap.setDispatcher(dis);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ protected String[] processAnnotationsStringArray(ElementValue ev) {\r
+ ArrayList<String> values = new ArrayList<String>();\r
+ if (ev instanceof ArrayElementValue) {\r
+ ElementValue[] arrayValues =\r
+ ((ArrayElementValue) ev).getElementValuesArray();\r
+ for (ElementValue value : arrayValues) {\r
+ values.add(value.stringifyValue());\r
+ }\r
+ } else {\r
+ values.add(ev.stringifyValue());\r
+ }\r
+ String[] result = new String[values.size()];\r
+ return values.toArray(result);\r
+ }\r
+ \r
+ protected Map<String,String> processAnnotationWebInitParams(\r
+ ElementValue ev) {\r
+ Map<String, String> result = new HashMap<String,String>();\r
+ if (ev instanceof ArrayElementValue) {\r
+ ElementValue[] arrayValues =\r
+ ((ArrayElementValue) ev).getElementValuesArray();\r
+ for (ElementValue value : arrayValues) {\r
+ if (value instanceof AnnotationElementValue) {\r
+ ElementValuePair[] evps = ((AnnotationElementValue)\r
+ value).getAnnotationEntry().getElementValuePairs();\r
+ String initParamName = null;\r
+ String initParamValue = null;\r
+ for (ElementValuePair evp : evps) {\r
+ if ("name".equals(evp.getNameString())) {\r
+ initParamName = evp.getValue().stringifyValue();\r
+ } else if ("value".equals(evp.getNameString())) {\r
+ initParamValue = evp.getValue().stringifyValue();\r
+ } else {\r
+ // Ignore\r
+ }\r
+ }\r
+ result.put(initParamName, initParamValue);\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ private class FragmentJarScannerCallback implements JarScannerCallback {\r
+\r
+ private static final String FRAGMENT_LOCATION =\r
+ "META-INF/web-fragment.xml";\r
+ private Map<String,WebXml> fragments = new HashMap<String,WebXml>();\r
+ \r
+ @Override\r
+ public void scan(JarURLConnection jarConn) throws IOException {\r
+ \r
+ URL url = jarConn.getURL();\r
+ URL resourceURL = jarConn.getJarFileURL();\r
+ Jar jar = null;\r
+ InputStream is = null;\r
+ WebXml fragment = new WebXml();\r
+\r
+ try {\r
+ jar = JarFactory.newInstance(url);\r
+ is = jar.getInputStream(FRAGMENT_LOCATION);\r
+\r
+ if (is == null) {\r
+ // If there is no web.xml, normal JAR no impact on\r
+ // distributable\r
+ fragment.setDistributable(true);\r
+ } else {\r
+ InputSource source = new InputSource(\r
+ resourceURL.toString() + "!/" + FRAGMENT_LOCATION);\r
+ source.setByteStream(is);\r
+ parseWebXml(source, fragment, true);\r
+ }\r
+ } finally {\r
+ if (is != null) {\r
+ try {\r
+ is.close();\r
+ } catch (IOException ioe) {\r
+ // Ignore\r
+ }\r
+ }\r
+ if (jar != null) {\r
+ jar.close();\r
+ }\r
+ fragment.setURL(url);\r
+ if (fragment.getName() == null) {\r
+ fragment.setName(fragment.getURL().toString());\r
+ }\r
+ fragments.put(fragment.getName(), fragment);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void scan(File file) throws IOException {\r
+\r
+ InputStream stream = null;\r
+ WebXml fragment = new WebXml();\r
+ \r
+ try {\r
+ File fragmentFile = new File(file, FRAGMENT_LOCATION);\r
+ if (fragmentFile.isFile()) {\r
+ stream = new FileInputStream(fragmentFile);\r
+ InputSource source =\r
+ new InputSource(fragmentFile.toURI().toURL().toString());\r
+ source.setByteStream(stream);\r
+ parseWebXml(source, fragment, true);\r
+ }\r
+ } finally {\r
+ if (stream != null) {\r
+ try {\r
+ stream.close();\r
+ } catch (Throwable t) {\r
+ ExceptionUtils.handleThrowable(t);\r
+ }\r
+ }\r
+ fragment.setURL(file.toURI().toURL());\r
+ if (fragment.getName() == null) {\r
+ fragment.setName(fragment.getURL().toString());\r
+ }\r
+ fragments.put(fragment.getName(), fragment);\r
+ }\r
+ }\r
+ \r
+ public Map<String,WebXml> getFragments() {\r
+ return fragments;\r
+ }\r
+ }\r
+\r
+ private static class DefaultWebXmlCacheEntry {\r
+ private final WebXml webXml;\r
+ private final long globalTimeStamp;\r
+ private final long hostTimeStamp;\r
+\r
+ public DefaultWebXmlCacheEntry(WebXml webXml, long globalTimeStamp,\r
+ long hostTimeStamp) {\r
+ this.webXml = webXml;\r
+ this.globalTimeStamp = globalTimeStamp;\r
+ this.hostTimeStamp = hostTimeStamp;\r
+ }\r
+\r
+ public WebXml getWebXml() {\r
+ return webXml;\r
+ }\r
+\r
+ public long getGlobalTimeStamp() {\r
+ return globalTimeStamp;\r
+ }\r
+\r
+ public long getHostTimeStamp() {\r
+ return hostTimeStamp;\r
+ }\r
+ }\r
+}\r