From 52a16f8e9e8ef7873eca60d4168acc8e4ad040b9 Mon Sep 17 00:00:00 2001 From: markt Date: Sun, 28 Mar 2010 11:57:46 +0000 Subject: [PATCH] Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=48662 Make extraction of context.xml files configurable and disable it by default for Tomcat 7 git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@928380 13f79535-47bb-0310-9956-ffa450edef68 --- java/org/apache/catalina/Context.java | 12 +- java/org/apache/catalina/core/StandardContext.java | 15 +- java/org/apache/catalina/core/StandardHost.java | 29 ++++ java/org/apache/catalina/mbeans/MBeanFactory.java | 2 +- .../org/apache/catalina/startup/ContextConfig.java | 65 ++++---- java/org/apache/catalina/startup/HostConfig.java | 128 ++++++++++++--- .../catalina/startup/LocalStrings.properties | 1 + webapps/docs/changelog.xml | 5 + webapps/docs/config/host.xml | 179 +++++++++++++-------- 9 files changed, 306 insertions(+), 130 deletions(-) diff --git a/java/org/apache/catalina/Context.java b/java/org/apache/catalina/Context.java index e55b72857..53c49746c 100644 --- a/java/org/apache/catalina/Context.java +++ b/java/org/apache/catalina/Context.java @@ -19,6 +19,8 @@ package org.apache.catalina; +import java.net.URL; + import javax.servlet.ServletContext; import javax.servlet.descriptor.JspConfigDescriptor; @@ -133,17 +135,17 @@ public interface Context extends Container { /** - * Return the path to a file to save this Context information. + * Return the URL of the XML descriptor for this context. */ - public String getConfigFile(); + public URL getConfigFile(); /** - * Set the path to a file to save this Context information. + * Set the URL of the XML descriptor for this context. * - * @param configFile The path to a file to save this Context information. + * @param configFile The URL of the XML descriptor for this context. */ - public void setConfigFile(String configFile); + public void setConfigFile(URL configFile); /** diff --git a/java/org/apache/catalina/core/StandardContext.java b/java/org/apache/catalina/core/StandardContext.java index 03d9d6da4..0d6878f2a 100644 --- a/java/org/apache/catalina/core/StandardContext.java +++ b/java/org/apache/catalina/core/StandardContext.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; @@ -255,9 +256,9 @@ public class StandardContext /** - * The path to a file to save this Context information. + * The URL of the XML descriptor for this context. */ - private String configFile = null; + private URL configFile = null; /** @@ -1196,9 +1197,9 @@ public class StandardContext } /** - * Return the path to a file to save this Context information. + * Return the URL of the XML descriptor for this context. */ - public String getConfigFile() { + public URL getConfigFile() { return (this.configFile); @@ -1206,11 +1207,11 @@ public class StandardContext /** - * Set the path to a file to save this Context information. + * Set the URL of the XML descriptor for this context. * - * @param configFile The path to a file to save this Context information. + * @param configFile The URL of the XML descriptor for this context. */ - public void setConfigFile(String configFile) { + public void setConfigFile(URL configFile) { this.configFile = configFile; } diff --git a/java/org/apache/catalina/core/StandardHost.java b/java/org/apache/catalina/core/StandardHost.java index 5526e2c31..fffe1c178 100644 --- a/java/org/apache/catalina/core/StandardHost.java +++ b/java/org/apache/catalina/core/StandardHost.java @@ -131,6 +131,13 @@ public class StandardHost /** + * Should XML files be copied to $CATALINA_BASE/conf// by + * default when a web application is deployed? + */ + private boolean copyXML = false; + + + /** * The Java class name of the default error reporter implementation class * for deployed web applications. */ @@ -378,6 +385,28 @@ public class StandardHost /** + * Return the copy XML config file flag for this component. + */ + public boolean isCopyXML() { + + return (this.copyXML); + + } + + + /** + * Set the copy XML config file flag for this component. + * + * @param copyXML The new copy XML flag + */ + public void setCopyXML(boolean copyXML) { + + this.copyXML= copyXML; + + } + + + /** * Return the value of the live deploy flag. If true, it indicates that * a background thread should be started that looks for web application * context files, WAR files, or unpacked directories being dropped in to diff --git a/java/org/apache/catalina/mbeans/MBeanFactory.java b/java/org/apache/catalina/mbeans/MBeanFactory.java index 7e62ad56f..e63b7c5f9 100644 --- a/java/org/apache/catalina/mbeans/MBeanFactory.java +++ b/java/org/apache/catalina/mbeans/MBeanFactory.java @@ -611,7 +611,7 @@ public class MBeanFactory extends BaseModelMBean { "configBaseName"); String baseName = getConfigFile(contextPath); File configFile = new File(new File(configPath), baseName+".xml"); - context.setConfigFile(configFile.getAbsolutePath()); + context.setConfigFile(configFile.toURI().toURL()); mserver.invoke(deployer, "manageApp", new Object[] {context}, new String[] {"org.apache.catalina.Context"}); diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java index 843e0ab19..25faae0e9 100644 --- a/java/org/apache/catalina/startup/ContextConfig.java +++ b/java/org/apache/catalina/startup/ContextConfig.java @@ -518,11 +518,31 @@ public class ContextConfig if( defaultContextXml==null ) getDefaultContextXml(); if (!context.getOverride()) { - processContextConfig(new File(getBaseDir()), defaultContextXml); - processContextConfig(getConfigBase(), getHostConfigPath(Constants.HostContextXml)); + File 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(new File(context.getConfigFile()), null); + processContextConfig(context.getConfigFile()); } @@ -530,43 +550,28 @@ public class ContextConfig /** * Process a context.xml. */ - protected void processContextConfig(File baseDir, String resourceName) { + protected void processContextConfig(URL contextXml) { if (log.isDebugEnabled()) log.debug("Processing context [" + context.getName() - + "] configuration file " + baseDir + " " + resourceName); + + "] configuration file [" + contextXml + "]"); 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()); + 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 (contextXml.getProtocol() == "file") { + context.addWatchedResource( + (new File(contextXml.toURI())).getAbsolutePath()); } } catch (Exception e) { log.error(sm.getString("contextConfig.contextMissing", - resourceName + " " + file) , e); + contextXml) , e); } if (source == null) @@ -586,7 +591,7 @@ public class ContextConfig } if (log.isDebugEnabled()) log.debug("Successfully processed context [" + context.getName() - + "] configuration file " + baseDir + " " + resourceName); + + "] configuration file [" + contextXml + "]"); } catch (SAXParseException e) { log.error(sm.getString("contextConfig.contextParse", context.getName()), e); diff --git a/java/org/apache/catalina/startup/HostConfig.java b/java/org/apache/catalina/startup/HostConfig.java index f7b663a11..5535dbe68 100644 --- a/java/org/apache/catalina/startup/HostConfig.java +++ b/java/org/apache/catalina/startup/HostConfig.java @@ -26,6 +26,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -120,6 +121,13 @@ public class HostConfig /** + * Should XML files be copied to $CATALINA_BASE/conf// by + * default when a web application is deployed? + */ + protected boolean copyXML = false; + + + /** * Should we unpack WAR files when auto-deploying applications in the * appBase directory? */ @@ -233,6 +241,28 @@ public class HostConfig /** + * Return the copy XML config file flag for this component. + */ + public boolean isCopyXML() { + + return (this.copyXML); + + } + + + /** + * Set the copy XML config file flag for this component. + * + * @param copyXML The new copy XML flag + */ + public void setCopyXML(boolean copyXML) { + + this.copyXML= copyXML; + + } + + + /** * Return the unpack WARs flag. */ public boolean isUnpackWARs() { @@ -309,6 +339,7 @@ public class HostConfig try { host = (Host) event.getLifecycle(); if (host instanceof StandardHost) { + setCopyXML(((StandardHost) host).isCopyXML()); setDeployXML(((StandardHost) host).isDeployXML()); setUnpackWARs(((StandardHost) host).isUnpackWARs()); setXmlNamespaceAware(((StandardHost) host).getXmlNamespaceAware()); @@ -612,7 +643,7 @@ public class HostConfig (LifecycleListener) clazz.newInstance(); context.addLifecycleListener(listener); - context.setConfigFile(contextXml.getAbsolutePath()); + context.setConfigFile(contextXml.toURI().toURL()); context.setPath(contextPath); // Add the associated docBase to the redeployed list if it's a WAR boolean isExternalWar = false; @@ -806,11 +837,16 @@ public class HostConfig BufferedOutputStream ostream = null; File xml = new File (configBase(), file.substring(0, file.lastIndexOf(".")) + ".xml"); + boolean xmlInWar = false; + if (deployXML && !xml.exists()) { try { jar = new JarFile(war); entry = jar.getJarEntry(Constants.ApplicationContextXml); if (entry != null) { + xmlInWar = true; + } + if (copyXML) { istream = jar.getInputStream(entry); ostream = @@ -882,7 +918,52 @@ public class HostConfig digester.reset(); } } - context.setConfigFile(xml.getAbsolutePath()); + context.setConfigFile(xml.toURI().toURL()); + } else if (deployXML && xmlInWar) { + synchronized (digester) { + try { + jar = new JarFile(war); + entry = + jar.getJarEntry(Constants.ApplicationContextXml); + istream = jar.getInputStream(entry); + context = (Context) digester.parse(istream); + + if (context == null) { + log.error(sm.getString( + "hostConfig.deployDescriptor.error", + file)); + return; + } + context.setConfigFile(new URL("jar:" + + war.toURI().toString() + "!/" + + Constants.ApplicationContextXml)); + } catch (Exception e) { + if (istream != null) { + try { + istream.close(); + } catch (Throwable t) { + // Ignore + } + istream = null; + } + } finally { + entry = null; + if (jar != null) { + try { + jar.close(); + } catch (Throwable t) { + // Ignore + } + jar = null; + } + digester.reset(); + } + } + if (context == null) { + log.error(sm.getString("hostConfig.deployDescriptor.error", + file)); + return; + } } else { context = (Context) Class.forName(contextClass).newInstance(); } @@ -991,9 +1072,6 @@ public class HostConfig File xml = new File(dir, Constants.ApplicationContextXml); File xmlCopy = null; if (deployXML && xml.exists()) { - // Will only do this on initial deployment. On subsequent - // deployments the copied xml file means we'll use - // deployDescriptor() instead synchronized (digester) { try { context = (Context) digester.parse(xml); @@ -1007,27 +1085,31 @@ public class HostConfig digester.reset(); } } - xmlCopy = new File(configBase(), file + ".xml"); - InputStream is = null; - OutputStream os = null; - try { - is = new FileInputStream(xml); - os = new FileOutputStream(xmlCopy); - IOTools.flow(is, os); - // Don't catch IOE - let the outer try/catch handle it - } finally { + if (copyXML) { + xmlCopy = new File(configBase(), file + ".xml"); + InputStream is = null; + OutputStream os = null; try { - if (is != null) is.close(); - } catch (IOException e){ - // Ignore - } - try { - if (os != null) os.close(); - } catch (IOException e){ - // Ignore + is = new FileInputStream(xml); + os = new FileOutputStream(xmlCopy); + IOTools.flow(is, os); + // Don't catch IOE - let the outer try/catch handle it + } finally { + try { + if (is != null) is.close(); + } catch (IOException e){ + // Ignore + } + try { + if (os != null) os.close(); + } catch (IOException e){ + // Ignore + } } + context.setConfigFile(xmlCopy.toURI().toURL()); + } else { + context.setConfigFile(xml.toURI().toURL()); } - context.setConfigFile(xmlCopy.getAbsolutePath()); } else { context = (Context) Class.forName(contextClass).newInstance(); } diff --git a/java/org/apache/catalina/startup/LocalStrings.properties b/java/org/apache/catalina/startup/LocalStrings.properties index 71a6616f1..6f3fe5d47 100644 --- a/java/org/apache/catalina/startup/LocalStrings.properties +++ b/java/org/apache/catalina/startup/LocalStrings.properties @@ -25,6 +25,7 @@ contextConfig.authenticatorConfigured=Configured an authenticator for method {0} contextConfig.authenticatorInstantiate=Cannot instantiate an authenticator of class {0} 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.cce=Lifecycle event data object {0} is not a Context contextConfig.contextClose=Error closing context.xml diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 2f319dd46..efc56e114 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -92,6 +92,11 @@ and stop, and align components that implement Lifecycle with this definition. (markt) + + 48662: Provide a new option to control the copying of context + XML descriptors from web applications to the host's xmlBase. Copying of + XMl descriptors is now disabled by default. (markt) + diff --git a/webapps/docs/config/host.xml b/webapps/docs/config/host.xml index cd20a4b74..094225d26 100644 --- a/webapps/docs/config/host.xml +++ b/webapps/docs/config/host.xml @@ -82,37 +82,42 @@

The Application Base directory for this virtual host. This is the pathname of a directory that may contain web applications to be deployed on this virtual host. You may specify an - absolute pathname for this directory, or a pathname that is relative - to the $CATALINA_BASE directory. See + absolute pathname, or a pathname that is relative to the + $CATALINA_BASE directory. See Automatic Application Deployment for more information on automatic recognition and - deployment of web applications to be deployed automatically. If not - specified, the default of webapps will be used.

+ deployment of web applications. If not specified, the default of + webapps will be used.

The XML Base directory for this virtual host. - This is the pathname of a directory that may contain context XML descriptors - to be deployed on this virtual host. You may specify an + This is the pathname of a directory that may contain context XML + descriptors to be deployed on this virtual host. You may specify an absolute pathname for this directory, or a pathname that is relative to the $CATALINA_BASE directory. See Automatic Application Deployment for more information on automatic recognition and - deployment of web applications to be deployed automatically.

+ deployment of web applications. If not specified the default of + [engine_name]/[host_name] will be used.

-

If set to true, Tomcat will attempt to create the directories defined by the - attributes appBase and xmlBase during the startup phase. - The default value is true. - If set to true, and directory creation fails, an error message will be printed out but will not halt - the startup sequence.

+

If set to true, Tomcat will attempt to create the directories defined + by the attributes appBase and xmlBase during + the startup phase. The default value is true. If set to + true, and directory creation fails, an error message will be printed out + but will not halt the startup sequence.

-

This flag value indicates if new web applications, dropped in to - the appBase directory while Tomcat is running, should - be automatically deployed. The flag's value defaults to true. See +

This flag value indicates if Tomcat should check periodically for new + or updated web applications while Tomcat is running. If true, Tomcat + periodically checks the appBase and xmlBase + directories and deploys any new web applications or context XML + descriptors found. Updated web applications or context XML descriptors + will trigger a reload of the web application. The flag's value defaults + to true. See Automatic Application Deployment for more information.

@@ -141,8 +146,8 @@

This flag value indicates if web applications from this host should - be automatically deployed by the host configurator. - The flag's value defaults to true. See + be automatically deployed when Tomcat starts. The flag's value defaults + to true. See Automatic Application Deployment for more information.

@@ -172,15 +177,28 @@ + +

Set to true if you want a context XML descriptor + embedded inside the application (located at + /META-INF/context.xml) to be copied to xmlBase + when the application is deployed. On subsequent starts, the copied + context XML descriptor will be used in preference to any context XML + descriptor embedded inside the application even if the descriptor + embedded inside the application is more recent. The flag's value + defaults to false. Note if deployXML + is false, this attribute will have no effect.

+
+ -

Set to false if you want to disable parsing the context.xml - file embedded inside the application (located at /META-INF/context.xml). - Security conscious environments should set this to false to prevent - applications from interacting with the container's configuration. The - administrator will then be responsible for providing an external context - configuration file, and put it in - $CATALINA_BASE/conf/[enginename]/[hostname]/ unless the attribute xmlBase is specified. - The flag's value defaults to true.

+

Set to false if you want to disable parsing the context + XML descriptor embedded inside the application (located at + /META-INF/context.xml). Security conscious environments + should set this to false to prevent applications from + interacting with the container's configuration. The administrator will + then be responsible for providing an external context configuration + file, and putting it in the location defined by the + xmlBase attribute. The flag's value defaults to + true.

@@ -199,7 +217,8 @@ placed in the appBase directory as web application archive (WAR) files to be unpacked into a corresponding disk directory structure, false to run such web applications directly - from a WAR file. See + from a WAR file. WAR files located outside of the Host's + appBase will not be expanded. See Automatic Application Deployment for more information.

@@ -251,7 +270,7 @@

A host is associated with the - org.apache.catalina.core.ContainerBase.[enginename].[hostname] + org.apache.catalina.core.ContainerBase.[engine_name].[host_name] log category. Note that the brackets are part of the name, don't omit them.

@@ -296,45 +315,76 @@ started, if the deployOnStartup property is set to true (which is the default value):

    -
  • Any XML file in the - $CATALINA_BASE/conf/[engine_name]/[host_name] directory is - assumed to contain a +
  • Any XML file in the Host's xmlBase directory (by + default $CATALINA_BASE/conf/[engine_name]/[host_name]) is + assumed to be a context XML descriptor containing a Context element (and its associated - subelements) for a single web application. The docBase - attribute of this <Context> element will typically - be the absolute pathname to a web application directory, or the - absolute pathname of a web application archive (WAR) file (which - will not be expanded). The path attribute will be automatically set - as defined in the Context documentation.
  • -
  • Any web application archive file within the application base (appBase) - directory that does not have a corresponding - directory of the same name (without the ".war" extension) will be - automatically expanded, unless the unpackWARs property - is set to false. If you redeploy an updated WAR file, - be sure to delete the expanded directory when restarting Tomcat, so - that the updated WAR file will be re-expanded (note that the auto - deployer, if enabled, will automatically expand the updated WAR file - once the previously expanded directory is removed). Multi-level contexts - may be defined by using #, e.g. use a WAR named foo#bar.war - for a context path of /foo/bar.
  • -
  • Any subdirectory within the application base directory - will receive an automatically generated - Context element, even if this directory is not mentioned in the - conf/server.xml file. The context path for this - deployed Context will be a slash character ("/") followed by the - directory name, unless the directory name is ROOT, in which case - the context path will be an empty string (""). Multi-level contexts - may be defined by using #, e.g. use a directory named foo#bar - for a context path of /foo/bar.
  • + sub-elements) for a single web application. The web applications + associated with each of these context XML descriptor files will be + deployed first.
    + The docBase attribute of this <Context> + element must only be set if the docBase is outside the Host's + appBase. For web applications located inside the Host's + appBase, the docBase will be the name of the + XML file with ".xml" replaced with ".war" for a web application archive + or the name of the XML file with ".xml" removed for a directory.
    + The path attribute must not be set. The context path used + will be a slash character ("/") followed by the name of the XML file + (less the .xml extension). Multi-level context paths may be defined + using #, e.g. foo#bar.xml for a context path of + /foo/bar. The default web application that has a context + path of / may be defined by using a file called + ROOT.xml. +
  • Any web application archive file within the Host's appBase + directory that has not already been deployed as a result of a context + XML descriptor and does not have a corresponding directory of the same + name (without the ".war" extension) will be deployed next. The context + path used will be a slash character ("/") followed by the web + application archive name less the ".war" extension. The one exception to + this rule is that a web application archive named "ROOT.war" will be + deployed with a context path of /. Multi-level contexts may + be defined by using #, e.g. use a WAR named foo#bar.war for + a context path of /foo/bar.
    + If the unpackWARs attribute is true, the web + application archive file will be expanded to a directory of the same + name (without the ".war" extension".
    + Note: If you re-deploy an updated WAR file while Tomcat is stopped, be + sure to delete the associated expanded directory before restarting + Tomcat, so that the updated WAR file will be re-expanded when Tomcat + restarts.
    + If copyXml is true (it is false + by default), any web application archive file within the Hosts's + appBase directory that does not have a corresponding + context XML descriptor (with a ".xml" extension rather than a ".war" + extension) in the Host's xmlBase will be scanned to see + if it contains a context XML descriptor (located at + /META-INF/context.xml) and if one is found the descriptor + will be copied to the xmlBase directory and renamed. +
  • +
  • Finally, any sub-directory within the Host's appBase that + has not already been deployed as a result of a context XML descriptor + will be deployed next. The context path used will be a slash character + ("/") followed by the directory name, unless the directory name is ROOT, + in which case the context path will /. Multi-level contexts + may be defined by using #, e.g. use a directory named + foo#bar for a context path of /foo/bar.
    + If copyXml is true (it is false + by default), any directory within the Hosts's appBase + directory that does not have a corresponding context XML descriptor in + the Host's xmlBase will be scanned to see if it contains + a context XML descriptor (located at /META-INF/context.xml) + and if one is found the descriptor will be copied to the + xmlBase directory and renamed. +

In addition to the automatic deployment that occurs at startup time, you can also request that new XML configuration files, WAR files, or - subdirectories that are dropped in to the appBase (or - $CATALINA_BASE/conf/[engine_name]/[host_name] in the case of - an XML configuration file) directory while Tomcat is running will be - automatically deployed, according to the rules described above. The - auto deployer will also track web applications for the following changes: + sub-directories that are dropped in to the appBase (or + xmlBase in the case of an XML configuration file) directory + while Tomcat is running will be automatically deployed, according to the + rules described above. The auto deployer will also track web applications + for the following changes:

  • An update to the WEB-INF/web.xml file will trigger a reload of the web application
  • @@ -374,9 +424,10 @@ may be experienced deploying the web application or the application may be deployed twice.

    -

    Finally, note that if you are defining contexts explicitly, you should - probably turn off automatic application deployment. Otherwise, your context - will be deployed twice each, and that may cause problems for your app. +

    Finally, note that if you are defining contexts explicitly in server.xml, + you should probably turn off automatic application deployment. Otherwise, + the web applications will each be deployed twice each, and that may cause + problems for the applications.

    -- 2.11.0