Author: markt Date: Thu Oct 13 22:28:22 2011 New Revision: 1183142 URL: http://svn.apache.org/viewvc?rev=1183142&view=rev Log: Cache the result of parsing global and host defaults for web.xml to speed up web application start. This change reduces applciation start time by ~15% with trunk and the TCK web apps
Modified: tomcat/trunk/java/org/apache/catalina/startup/Constants.java tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties Modified: tomcat/trunk/java/org/apache/catalina/startup/Constants.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/Constants.java?rev=1183142&r1=1183141&r2=1183142&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/Constants.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/Constants.java Thu Oct 13 22:28:22 2011 @@ -34,7 +34,7 @@ public final class Constants { public static final String ApplicationContextXml = "META-INF/context.xml"; public static final String ApplicationWebXml = "/WEB-INF/web.xml"; public static final String DefaultContextXml = "conf/context.xml"; - public static final String DefaultWebXml = "conf/web.xml"; + public static final String DefaultWebXml = "web.xml"; public static final String HostContextXml = "context.xml.default"; public static final String HostWebXml = "web.xml.default"; Modified: tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java?rev=1183142&r1=1183141&r2=1183142&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java Thu Oct 13 22:28:22 2011 @@ -43,6 +43,7 @@ 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; @@ -172,6 +173,13 @@ public class ContextConfig 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 @@ -1210,7 +1218,6 @@ public class ContextConfig * web.xml file. */ protected void webConfig() { - WebXml webXml = createWebXml(); /* 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 @@ -1218,34 +1225,11 @@ public class ContextConfig * - Mark Servlets as overridable so SCI configuration can replace * configuration from the defaults */ - 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 - InputSource globalWebXml = getGlobalWebXmlSource(); - 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); - InputSource hostWebXml = getHostWebXmlSource(); - parseWebXml(hostWebXml, webXmlDefaultFragment, false); - Set<WebXml> defaults = new HashSet<WebXml>(); - defaults.add(webXmlDefaultFragment); - + defaults.add(getDefaultWebXmlFragment()); + + WebXml webXml = createWebXml(); + // Parse context level web.xml InputSource contextWebXml = getContextWebXmlSource(); parseWebXml(contextWebXml, webXml, false); @@ -1376,6 +1360,98 @@ public class ContextConfig } } + private WebXml getDefaultWebXmlFragment() { + + // Host should never be null + Host host = (Host) context.getParent(); + + DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host); + + File globalWebXml = getGlobalWebXml(); + File hostWebXml = getHostWebXml(); + + long globalTimeStamp = 0; + long hostTimeStamp = 0; + + if (globalWebXml != null) { + globalTimeStamp = globalWebXml.lastModified(); + } + + if (hostWebXml != null) { + hostTimeStamp = hostWebXml.lastModified(); + } + + if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp && + entry.getHostTimeStamp() == hostTimeStamp) { + addWatchedResource(globalWebXml); + addWatchedResource(hostWebXml); + 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) { + addWatchedResource(globalWebXml); + addWatchedResource(hostWebXml); + 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 || !globalWebXml.isFile()) { + // This is unusual enough to log + log.info(sm.getString("contextConfig.defaultMissing")); + globalTimeStamp = 0; + } else { + parseWebXml(getWebXmlSource(globalWebXml.getName(), + globalWebXml.getParent()), + webXmlDefaultFragment, + false); + globalTimeStamp = globalWebXml.lastModified(); + } + + // Parse host level web.xml if present + // Additive apart from welcome pages + webXmlDefaultFragment.setReplaceWelcomeFiles(true); + + if (hostWebXml == null || !hostWebXml.isFile()) { + hostTimeStamp = 0; + } else { + parseWebXml(getWebXmlSource(hostWebXml.getName(), + hostWebXml.getParent()), + webXmlDefaultFragment, + false); + hostTimeStamp = hostWebXml.lastModified(); + } + + entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment, + globalTimeStamp, hostTimeStamp); + + hostWebXmlCache.put(host, entry); + + addWatchedResource(globalWebXml); + addWatchedResource(hostWebXml); + return webXmlDefaultFragment; + } + } + + private void addWatchedResource(File f) { + if (f != null) { + context.addWatchedResource(f.getAbsolutePath()); + } + } private void convertJsps(WebXml webXml) { Map<String,String> jspInitParams; ServletDef jspServlet = webXml.getServlets().get("jsp"); @@ -1574,10 +1650,9 @@ public class ContextConfig /** - * Identify the default web.xml to be used and obtain an input source for - * it. + * Identify the default web.xml to be used. */ - protected InputSource getGlobalWebXmlSource() { + protected File getGlobalWebXml() { // Is a default web.xml specified for the Context? if (defaultWebXml == null && context instanceof StandardContext) { defaultWebXml = ((StandardContext) context).getDefaultWebXml(); @@ -1589,14 +1664,27 @@ public class ContextConfig if (Constants.NoDefaultWebXml.equals(defaultWebXml)) { return null; } - return getWebXmlSource(defaultWebXml, getBaseDir()); + + // 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("contextConfig.baseError"), e); + return null; + } + + return new File(basePath, defaultWebXml); } /** - * Identify the host web.xml to be used and obtain an input source for - * it. + * Identify the host web.xml to be used. */ - protected InputSource getHostWebXmlSource() { + protected File getHostWebXml() { String resourceName = getHostConfigPath(Constants.HostWebXml); // In an embedded environment, configBase might not be set @@ -1608,11 +1696,11 @@ public class ContextConfig try { basePath = configBase.getCanonicalPath(); } catch (IOException e) { - log.error(sm.getString("contectConfig.baseError"), e); + log.error(sm.getString("contextConfig.baseError"), e); return null; } - return getWebXmlSource(resourceName, basePath); + return new File(basePath, resourceName); } /** @@ -1693,7 +1781,6 @@ public class ContextConfig } else { source = new InputSource("file://" + file.getAbsolutePath()); stream = new FileInputStream(file); - context.addWatchedResource(file.getAbsolutePath()); } if (stream != null && source != null) { @@ -2419,4 +2506,29 @@ public class ContextConfig 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; + } + } } Modified: tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties?rev=1183142&r1=1183141&r2=1183142&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties Thu Oct 13 22:28:22 2011 @@ -27,7 +27,7 @@ contextConfig.authenticatorInstantiate=C contextConfig.authenticatorMissing=Cannot configure an authenticator for method {0} contextConfig.authenticatorResources=Cannot load authenticators mapping list contextConfig.badUrl=Unable to process context descriptor [{0}] -contectConfig.baseError=Unable to determine $CATALINA_BASE +contextConfig.baseError=Unable to determine location of global configuration (usually $CATALINA_BASE/conf) contextConfig.cce=Lifecycle event data object {0} is not a Context contextConfig.contextClose=Error closing context.xml contextConfig.contextMissing=Missing context.xml: {0} --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org