/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.startup;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.servlet.ServletContext;
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.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.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.JarScannerCallback;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.digester.RuleSet;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
/**
* Startup event listener for a Context that configures the properties
* of that Context, and the associated defined servlets.
*
* @author Craig R. McClanahan
* @author Jean-Francois Arcand
* @version $Revision$ $Date$
*/
public class ContextConfig
implements LifecycleListener {
private static final Log log = LogFactory.getLog( ContextConfig.class );
// ----------------------------------------------------- Instance Variables
/**
* Custom mappings of login methods to authenticators
*/
protected Map customAuthenticators;
/**
* 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 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;
/**
* The string resources for this package.
*/
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* The Digester we will use to process web application
* context files.
*/
protected static Digester contextDigester = null;
/**
* The Digester we will use to process web application
* deployment descriptor files.
*/
protected Digester webDigester = null;
/**
* The Digester we will use to process web fragment
* deployment descriptor files.
*/
protected Digester webFragmentDigester = null;
protected static Digester[] webDigesters = new Digester[4];
/**
* The Digesters available to process web fragment
* deployment descriptor files.
*/
protected static Digester[] webFragmentDigesters = new Digester[4];
/**
* The Rules used to parse the web.xml
*/
protected static WebRuleSet webRuleSet = new WebRuleSet(false);
/**
* The Rules used to parse the web-fragment.xml
*/
protected static WebRuleSet webFragmentRuleSet = new WebRuleSet(true);
/**
* Deployment count.
*/
protected static long deploymentCount = 0L;
protected static final LoginConfig DUMMY_LOGIN_CONFIG =
new LoginConfig("NONE", null, null, null);
// Names of JARs that are known not to contain web-fragment.xml
private static HashSet noFragmentJars;
/*
* Initializes the set of JARs that are known not to contain any web-fragments
*/
static {
// TODO - set this list via configuration (also TLDs in Jasper)
noFragmentJars = new HashSet();
// Bootstrap JARs
noFragmentJars.add("bootstrap.jar");
noFragmentJars.add("commons-daemon.jar");
noFragmentJars.add("tomcat-juli.jar");
// Main JARs
noFragmentJars.add("annotations-api.jar");
noFragmentJars.add("catalina.jar");
noFragmentJars.add("catalina-ant.jar");
noFragmentJars.add("catalina-ha.jar");
noFragmentJars.add("catalina-tribes.jar");
noFragmentJars.add("el-api.jar");
noFragmentJars.add("jasper.jar");
noFragmentJars.add("jasper-el.jar");
noFragmentJars.add("jasper-jdt.jar");
noFragmentJars.add("jsp-api.jar");
noFragmentJars.add("servlet-api.jar");
noFragmentJars.add("tomcat-api.jar");
noFragmentJars.add("tomcat-coyote.jar");
noFragmentJars.add("tomcat-dbcp.jar");
// i18n JARs
noFragmentJars.add("tomcat-i18n-en.jar");
noFragmentJars.add("tomcat-i18n-es.jar");
noFragmentJars.add("tomcat-i18n-fr.jar");
noFragmentJars.add("tomcat-i18n-ja.jar");
// Misc JARs not included with Tomcat
noFragmentJars.add("ant.jar");
noFragmentJars.add("commons-dbcp.jar");
noFragmentJars.add("commons-beanutils.jar");
noFragmentJars.add("commons-fileupload-1.0.jar");
noFragmentJars.add("commons-pool.jar");
noFragmentJars.add("commons-digester.jar");
noFragmentJars.add("commons-logging.jar");
noFragmentJars.add("commons-collections.jar");
noFragmentJars.add("jmx.jar");
noFragmentJars.add("jmx-tools.jar");
noFragmentJars.add("xercesImpl.jar");
noFragmentJars.add("xmlParserAPIs.jar");
noFragmentJars.add("xml-apis.jar");
// JARs from J2SE runtime
noFragmentJars.add("sunjce_provider.jar");
noFragmentJars.add("ldapsec.jar");
noFragmentJars.add("localedata.jar");
noFragmentJars.add("dnsns.jar");
noFragmentJars.add("tools.jar");
noFragmentJars.add("sunpkcs11.jar");
// Apple J2SE runtime
noFragmentJars.add("apple_provider.jar");
noFragmentJars.add("AppleScriptEngine.jar");
noFragmentJars.add("CoreAudio.jar");
noFragmentJars.add("dns_sd.jar");
noFragmentJars.add("j3daudio.jar");
noFragmentJars.add("j3dcore.jar");
noFragmentJars.add("j3dutils.jar");
noFragmentJars.add("jai_core.jar");
noFragmentJars.add("jai_codec.jar");
noFragmentJars.add("mlibwrapper_jai.jar");
noFragmentJars.add("MRJToolkit.jar");
noFragmentJars.add("vecmath.jar");
}
// ------------------------------------------------------------- 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 customAuthenticators) {
this.customAuthenticators = customAuthenticators;
}
// --------------------------------------------------------- Public Methods
/**
* Process events for an associated Context.
*
* @param event The lifecycle event that has occurred
*/
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.START_EVENT)) {
start();
} 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.STOP_EVENT)) {
if (originalDocBase != null) {
String docBase = context.getDocBase();
context.setDocBase(originalDocBase);
originalDocBase = docBase;
}
stop();
} else if (event.getType().equals(Lifecycle.INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.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() {
// Does this Context require an Authenticator?
SecurityConstraint constraints[] = context.findConstraints();
if ((constraints == null) || (constraints.length == 0))
return;
LoginConfig loginConfig = context.getLoginConfig();
if (loginConfig == null) {
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) {
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).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);
}
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);
}
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);
}
webDigester = webDigesters[2];
webFragmentDigester = webFragmentDigesters[2];
} else {
if (webDigesters[3] == null) {
webDigesters[3] = DigesterFactory.newDigester(validation,
namespaceAware, webFragmentRuleSet);
webFragmentDigesters[3] = DigesterFactory.newDigester(validation,
namespaceAware, webRuleSet);
}
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);
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("catalina.base");
}
/**
* Process the default configuration file, if it exists.
*/
protected void contextConfig() {
// Open the default web.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()) {
processContextConfig(new File(getBaseDir()), defaultContextXml);
processContextConfig(getConfigBase(), getHostConfigPath(Constants.HostContextXml));
}
if (context.getConfigFile() != null)
processContextConfig(new File(context.getConfigFile()), null);
}
/**
* Process a context.xml.
*/
protected void processContextConfig(File baseDir, String resourceName) {
if (log.isDebugEnabled())
log.debug("Processing context [" + context.getName()
+ "] configuration file " + baseDir + " " + resourceName);
InputSource source = null;
InputStream stream = null;
File file = baseDir;
if (resourceName != null) {
file = new File(baseDir, resourceName);
}
try {
if ( !file.exists() ) {
if (resourceName != null) {
// Use getResource and getResourceAsStream
stream = getClass().getClassLoader()
.getResourceAsStream(resourceName);
if( stream != null ) {
source = new InputSource
(getClass().getClassLoader()
.getResource(resourceName).toString());
}
}
} else {
source =
new InputSource("file://" + file.getAbsolutePath());
stream = new FileInputStream(file);
// Add as watched resource so that cascade reload occurs if a default
// config file is modified/added/removed
context.addWatchedResource(file.getAbsolutePath());
}
} catch (Exception e) {
log.error(sm.getString("contextConfig.contextMissing",
resourceName + " " + file) , 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);
ContextErrorHandler errorHandler = new ContextErrorHandler();
contextDigester.setErrorHandler(errorHandler);
contextDigester.parse(source);
if (errorHandler.parseException != null) {
ok = false;
}
if (log.isDebugEnabled())
log.debug("Successfully processed context [" + context.getName()
+ "] configuration file " + baseDir + " " + resourceName);
} 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();
String appBase = host.getAppBase();
boolean unpackWARs = true;
if (host instanceof StandardHost) {
unpackWARs = ((StandardHost) host).isUnpackWARs()
&& ((StandardContext) context).getUnpackWAR();
}
File canonicalAppBase = new File(appBase);
if (canonicalAppBase.isAbsolute()) {
canonicalAppBase = canonicalAppBase.getCanonicalFile();
} else {
canonicalAppBase =
new File(System.getProperty("catalina.base"), appBase)
.getCanonicalFile();
}
String docBase = context.getDocBase();
if (docBase == null) {
// Trying to guess the docBase according to the path
String path = context.getPath();
if (path == null) {
return;
}
if (path.equals("")) {
docBase = "ROOT";
} else {
if (path.startsWith("/")) {
docBase = path.substring(1).replace('/', '#');
} else {
docBase = path.replace('/', '#');
}
}
}
File file = new File(docBase);
if (!file.isAbsolute()) {
docBase = (new File(canonicalAppBase, docBase)).getPath();
} else {
docBase = file.getCanonicalPath();
}
file = new File(docBase);
String origDocBase = docBase;
String contextPath = context.getPath();
if (contextPath.equals("")) {
contextPath = "ROOT";
} else {
if (contextPath.lastIndexOf('/') > 0) {
contextPath = "/" + contextPath.substring(1).replace('/','#');
}
}
if (docBase.toLowerCase().endsWith(".war") && !file.isDirectory() && unpackWARs) {
URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/");
docBase = ExpandWar.expand(host, war, contextPath);
file = new File(docBase);
docBase = file.getCanonicalPath();
if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(origDocBase);
}
} else {
File docDir = new File(docBase);
if (!docDir.exists()) {
File warFile = new File(docBase + ".war");
if (warFile.exists()) {
if (unpackWARs) {
URL war =
new URL("jar:" + warFile.toURI().toURL() + "!/");
docBase = ExpandWar.expand(host, war, contextPath);
file = new File(docBase);
docBase = file.getCanonicalPath();
} else {
docBase = warFile.getCanonicalPath();
}
}
if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(origDocBase);
}
}
}
if (docBase.startsWith(canonicalAppBase.getPath() + File.separatorChar)) {
docBase = docBase.substring(canonicalAppBase.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 appBase = host.getAppBase();
String docBase = context.getDocBase();
if (docBase == null)
return;
if (originalDocBase == null) {
originalDocBase = docBase;
} else {
docBase = originalDocBase;
}
File docBaseFile = new File(docBase);
if (!docBaseFile.isAbsolute()) {
File file = new File(appBase);
if (!file.isAbsolute()) {
file = new File(System.getProperty("catalina.base"), appBase);
}
docBaseFile = new File(file, docBase);
}
String path = context.getPath();
if (path == null) {
return;
}
if (path.equals("")) {
docBase = "ROOT";
} else {
if (path.startsWith("/")) {
docBase = path.substring(1);
} else {
docBase = path;
}
}
File file = null;
if (docBase.toLowerCase().endsWith(".war")) {
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.getPath()
+ "] 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();
try {
fixDocBase();
} catch (IOException e) {
log.error(sm.getString(
"contextConfig.fixDocBase", context.getPath()), e);
}
}
/**
* Process a "before start" event for this Context.
*/
protected synchronized void beforeStart() {
antiLocking();
}
/**
* Process a "start" event for this Context.
*/
protected synchronized void start() {
// Called from StandardContext.start()
if (log.isDebugEnabled())
log.debug(sm.getString("contextConfig.start"));
// Process the default and application web.xml files
// Set properties based on default context
boolean useXmlValidation = context.getXmlValidation();
boolean useXmlNamespaceAware = context.getXmlNamespaceAware();
Container container = context.getParent();
// Use the value from the host if:
// - override is false on the context
// - value has been set to false / not set on the context
if( !context.getOverride() ) {
if( container instanceof Host ) {
if (!useXmlValidation) {
useXmlValidation = ((Host)container).getXmlValidation();
}
if (!useXmlNamespaceAware){
useXmlNamespaceAware
= ((Host)container).getXmlNamespaceAware();
}
}
}
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.xmlSettings",
context.getName(), Boolean.valueOf(useXmlValidation),
Boolean.valueOf(useXmlNamespaceAware)));
}
createWebXmlDigester(useXmlNamespaceAware, useXmlValidation);
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 stop() {
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 taglibs
String[] taglibs = context.findTaglibs();
for (i = 0; i < taglibs.length; i++) {
context.removeTaglib(taglibs[i]);
}
// Removing welcome files
String[] welcomeFiles = context.findWelcomeFiles();
for (i = 0; i < welcomeFiles.length; i++) {
context.removeWelcomeFile(welcomeFiles[i]);
}
// Removing wrapper lifecycles
String[] wrapperLifecycles = context.findWrapperLifecycles();
for (i = 0; i < wrapperLifecycles.length; i++) {
context.removeWrapperLifecycle(wrapperLifecycles[i]);
}
// Removing wrapper listeners
String[] wrapperListeners = context.findWrapperListeners();
for (i = 0; i < wrapperListeners.length; i++) {
context.removeWrapperListener(wrapperListeners[i]);
}
// Remove (partially) folders and files created by antiLocking
Host host = (Host) context.getParent();
String appBase = host.getAppBase();
String docBase = context.getDocBase();
if ((docBase != null) && (originalDocBase != null)) {
File docBaseFile = new File(docBase);
if (!docBaseFile.isAbsolute()) {
docBaseFile = new File(appBase, docBase);
}
ExpandWar.delete(docBaseFile);
}
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"));
// Changed to getWorkPath per Bugzilla 35819.
String workDir = ((StandardContext) context).getWorkPath();
if (workDir != null)
ExpandWar.delete(new File(workDir));
}
/**
* 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 ok
* instance variable to false as well).
*/
protected void validateSecurityRoles() {
// Check role names used in 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 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("catalina.base"), "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() {
WebXml webXml = new WebXml();
// Parse global web.xml if present
InputSource globalWebXml = getGlobalWebXmlSource();
if (globalWebXml == null) {
// This is unusual enough to log
log.info(sm.getString("contextConfig.defaultMissing"));
} else {
parseWebXml(globalWebXml, webXml, false);
}
// Parse host level web.xml if present
// Additive apart from welcome pages
webXml.setReplaceWelcomeFiles(true);
InputSource hostWebXml = getHostWebXmlSource();
parseWebXml(hostWebXml, webXml, false);
// Parse context level web.xml
webXml.setReplaceWelcomeFiles(true);
InputSource contextWebXml = getContextWebXmlSource();
parseWebXml(contextWebXml, webXml, false);
if (!webXml.isMetadataComplete()) {
// Process /WEB-INF/classes for annotations
// TODO SERVLET3
// Have to process JARs for fragments
Map fragments = processJarsForWebFragments();
// Merge the fragments into the main web.xml
Set orderedFragments =
WebXml.orderWebFragments(webXml, fragments);
// Process JARs for annotations - only need to process those
// fragments we are going to use
processAnnotationsInJars(orderedFragments);
// Merge fragment into application
if (ok) {
ok = webXml.merge(orderedFragments);
}
// Apply merged web.xml to Context
webXml.configureContext(context);
} else {
// Apply merged web.xml to Context
webXml.configureContext(context);
}
}
/**
* 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();
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
* @return
*/
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).toString());
}
} else {
source = new InputSource("file://" + file.getAbsolutePath());
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;
ContextErrorHandler handler = new ContextErrorHandler();
// 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;
if (fragment) {
digester = webFragmentDigester;
} else {
digester = webDigester;
}
synchronized(digester) {
digester.push(dest);
digester.setErrorHandler(handler);
if(log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.applicationStart",
source.getSystemId()));
}
try {
digester.parse(source);
if (handler.getParseException() != null) {
ok = false;
}
} 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();
if (fragment) {
webFragmentRuleSet.recycle();
} else {
webRuleSet.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
* null 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 processJarsForWebFragments() {
JarScanner jarScanner = context.getJarScanner();
FragmentJarScannerCallback callback = new FragmentJarScannerCallback();
jarScanner.scan(context.getServletContext(),
context.getLoader().getClassLoader(), callback, noFragmentJars);
return callback.getFragments();
}
private class FragmentJarScannerCallback implements JarScannerCallback {
private static final String FRAGMENT_LOCATION =
"META-INF/web-fragment.xml";
private Map fragments = new HashMap();
@Override
public void scan(JarURLConnection urlConn) throws IOException {
JarFile jarFile = null;
InputStream stream = null;
WebXml fragment = new WebXml();
try {
urlConn.setUseCaches(false);
jarFile = urlConn.getJarFile();
JarEntry fragmentEntry =
jarFile.getJarEntry(FRAGMENT_LOCATION);
if (fragmentEntry != null) {
stream = jarFile.getInputStream(fragmentEntry);
InputSource source = new InputSource(
urlConn.getJarFileURL().toString() +
File.separatorChar + FRAGMENT_LOCATION);
source.setByteStream(stream);
parseWebXml(source, fragment, true);
}
} finally {
if (jarFile != null) {
try {
jarFile.close();
} catch (Throwable t) {
// ignore
}
}
if (stream != null) {
try {
stream.close();
} catch (Throwable t) {
// ignore
}
}
fragment.setURL(urlConn.getURL());
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 = null;
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);
fragment = new WebXml();
parseWebXml(source, fragment, true);
}
} finally {
if (stream != null) {
try {
stream.close();
} catch (Throwable t) {
// ignore
}
}
if (fragment == null) {
fragments.put(file.toURI().toURL().toString(), fragment);
} else {
fragment.setURL(file.toURI().toURL());
if (fragment.getName() == null) {
fragment.setName(fragment.getURL().toString());
}
fragments.put(fragment.getName(), fragment);
}
}
}
public Map getFragments() {
return fragments;
}
}
protected void processAnnotationsInJars(Set fragments) {
for(WebXml fragment : fragments) {
if (!fragment.isMetadataComplete()) {
// Scan jar for annotations, add to fragment
// TODO SERVLET3
}
}
}
protected static class ContextErrorHandler
implements ErrorHandler {
private SAXParseException parseException = null;
public void error(SAXParseException exception) {
parseException = exception;
}
public void fatalError(SAXParseException exception) {
parseException = exception;
}
public void warning(SAXParseException exception) {
parseException = exception;
}
public SAXParseException getParseException() {
return parseException;
}
}
}