From fa2c5b8e8344a1347c603232047343a67d32ce9b Mon Sep 17 00:00:00 2001 From: markt Date: Thu, 13 Oct 2011 22:28:22 +0000 Subject: [PATCH] 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 git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1183142 13f79535-47bb-0310-9956-ffa450edef68 --- java/org/apache/catalina/startup/Constants.java | 2 +- .../org/apache/catalina/startup/ContextConfig.java | 188 ++++++++++++++++----- .../catalina/startup/LocalStrings.properties | 2 +- 3 files changed, 152 insertions(+), 40 deletions(-) diff --git a/java/org/apache/catalina/startup/Constants.java b/java/org/apache/catalina/startup/Constants.java index 5a8067fd8..cad699aa9 100644 --- a/java/org/apache/catalina/startup/Constants.java +++ b/java/org/apache/catalina/startup/Constants.java @@ -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"; diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java index bb1740658..c1d517531 100644 --- a/java/org/apache/catalina/startup/ContextConfig.java +++ b/java/org/apache/catalina/startup/ContextConfig.java @@ -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 hostWebXmlCache = + new ConcurrentHashMap(); + + // ----------------------------------------------------- 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 defaults = new HashSet(); - 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 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; + } + } } diff --git a/java/org/apache/catalina/startup/LocalStrings.properties b/java/org/apache/catalina/startup/LocalStrings.properties index ebfb62092..2d18b4450 100644 --- a/java/org/apache/catalina/startup/LocalStrings.properties +++ b/java/org/apache/catalina/startup/LocalStrings.properties @@ -27,7 +27,7 @@ contextConfig.authenticatorInstantiate=Cannot instantiate an authenticator of cl 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} -- 2.11.0