Author: markt Date: Mon Nov 14 21:51:23 2011 New Revision: 1201921 URL: http://svn.apache.org/viewvc?rev=1201921&view=rev Log: Improve handling of failed deployments - Allow failed configurations to be fixed via JMX - Ensure once a deployment fails, the context is not deployed - Ensure changes to any context.xml file trigger redeployment rather than reloading - Tweak base lifecycle implementation to allow failed components to be restarted once they have been fixed
Added: tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java Modified: tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties tomcat/trunk/java/org/apache/catalina/util/LifecycleBase.java tomcat/trunk/java/org/apache/catalina/util/LocalStrings.properties tomcat/trunk/webapps/docs/deployer-howto.xml Modified: tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java?rev=1201921&r1=1201920&r2=1201921&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java Mon Nov 14 21:51:23 2011 @@ -840,20 +840,12 @@ public abstract class ContainerBase exte if ((getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState())) && startChildren) { - boolean success = false; try { child.start(); - success = true; } catch (LifecycleException e) { log.error("ContainerBase.addChild: start: ", e); throw new IllegalStateException ("ContainerBase.addChild: start: " + e); - } finally { - if (!success) { - synchronized (children) { - children.remove(child.getName()); - } - } } } 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=1201921&r1=1201920&r2=1201921&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java Mon Nov 14 21:51:23 2011 @@ -569,13 +569,6 @@ public class ContextConfig 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); @@ -1710,7 +1703,6 @@ public class ContextConfig } else { source = new InputSource(file.getAbsoluteFile().toURI().toString()); stream = new FileInputStream(file); - context.addWatchedResource(file.getAbsolutePath()); } if (stream != null && source != null) { Added: tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java?rev=1201921&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java (added) +++ tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java Mon Nov 14 21:51:23 2011 @@ -0,0 +1,643 @@ +/* + * 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.beans.PropertyChangeListener; +import java.io.IOException; +import java.net.URL; +import java.util.Set; + +import javax.naming.directory.DirContext; +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletSecurityElement; +import javax.servlet.descriptor.JspConfigDescriptor; + +import org.apache.catalina.AccessLog; +import org.apache.catalina.Authenticator; +import org.apache.catalina.Cluster; +import org.apache.catalina.Container; +import org.apache.catalina.ContainerListener; +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.Loader; +import org.apache.catalina.Manager; +import org.apache.catalina.Pipeline; +import org.apache.catalina.Realm; +import org.apache.catalina.Valve; +import org.apache.catalina.Wrapper; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.core.ApplicationServletRegistration; +import org.apache.catalina.deploy.ApplicationParameter; +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.NamingResources; +import org.apache.catalina.deploy.SecurityConstraint; +import org.apache.catalina.mbeans.MBeanUtils; +import org.apache.catalina.util.CharsetMapper; +import org.apache.catalina.util.LifecycleMBeanBase; +import org.apache.juli.logging.Log; +import org.apache.tomcat.JarScanner; +import org.apache.tomcat.util.http.mapper.Mapper; +import org.apache.tomcat.util.res.StringManager; + +/** + * An implementation of {@link Context} that is used as a place-holder for + * deployed applications when their deployment fails and a Context instance + * (usually {@link org.apache.catalina.core.StandardContext} but may be any + * Context implementation) cannot be created. + */ +public class FailedContext extends LifecycleMBeanBase implements Context { + + protected static final StringManager sm = + StringManager.getManager(Constants.Package); + + + // --------------------- Methods that need to work even for a failed context + + private URL configFile; + @Override + public URL getConfigFile() { return configFile; } + @Override + public void setConfigFile(URL configFile) { this.configFile = configFile; } + + + private String docBase; + @Override + public String getDocBase() { return docBase; } + @Override + public void setDocBase(String docBase) { this.docBase = docBase; } + + + + private String name = null; + @Override + public String getName() { return name; } + @Override + public void setName(String name) { this.name = name; } + + + private Container parent; + @Override + public Container getParent() { return parent; } + @Override + public void setParent(Container parent) { this.parent = parent; } + + + private String path = null; + @Override + public String getPath() { return path; } + @Override + public void setPath(String path) { this.path = path; } + + + private String webappVersion = null; + @Override + public String getWebappVersion() { return webappVersion; } + @Override + public void setWebappVersion(String webappVersion) { + this.webappVersion = webappVersion; + } + + + @Override + protected String getDomainInternal() { return MBeanUtils.getDomain(this); } + + + @Override + protected String getObjectNameKeyProperties() { + + StringBuilder keyProperties = + new StringBuilder("j2eeType=WebModule,name=//"); + + String hostname = getParent().getName(); + if (hostname == null) { + keyProperties.append("DEFAULT"); + } else { + keyProperties.append(hostname); + } + + String contextName = getName(); + if (!contextName.startsWith("/")) { + keyProperties.append('/'); + } + keyProperties.append(contextName); + + keyProperties.append(",J2EEApplication=none,J2EEServer=none"); + + return keyProperties.toString(); + } + + + @Override + protected void startInternal() throws LifecycleException { + throw new LifecycleException( + sm.getString("failedContext.start", getName())); + } + + + @Override + protected void stopInternal() throws LifecycleException { + // NO-OP + // Allow stop to complete since it is used for clean-up + } + + + // Only need to read these + @Override + public void addWatchedResource(String name) { /* NO-OP */ } + @Override + public String[] findWatchedResources() { return new String[0]; } + @Override + public void removeWatchedResource(String name) { /* NO-OP */ } + + + @Override + public void addChild(Container child) { /* NO-OP */ } + @Override + public Container findChild(String name) { return null; } + @Override + public Container[] findChildren() { return new Container[0]; } + @Override + public void removeChild(Container child) { /* NO-OP */ } + + + // -------------------------------------------- All NO-OPs beyond this point + @Override + public Loader getLoader() { return null; } + @Override + public void setLoader(Loader loader) { /* NO-OP */ } + + @Override + public Log getLogger() { return null; } + + @Override + public Manager getManager() { return null; } + @Override + public void setManager(Manager manager) { /* NO-OP */ } + + @Override + public Pipeline getPipeline() { return null; } + + @Override + public Cluster getCluster() { return null; } + @Override + public void setCluster(Cluster cluster) { /* NO-OP */ } + + @Override + public int getBackgroundProcessorDelay() { return -1; } + @Override + public void setBackgroundProcessorDelay(int delay) { /* NO-OP */ } + + @Override + public ClassLoader getParentClassLoader() { return null; } + @Override + public void setParentClassLoader(ClassLoader parent) { /* NO-OP */ } + + @Override + public Realm getRealm() { return null; } + @Override + public void setRealm(Realm realm) { /* NO-OP */ } + + @Override + public DirContext getResources() { return null; } + @Override + public void setResources(DirContext resources) { /* NO-OP */ } + + @Override + public void backgroundProcess() { /* NO-OP */ } + + @Override + public void addContainerListener(ContainerListener listener) { /* NO-OP */ } + @Override + public ContainerListener[] findContainerListeners() { return null; } + @Override + public void removeContainerListener(ContainerListener listener) { /* NO-OP */ } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { /* NO-OP */ } + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { /* NO-OP */ } + + @Override + public void invoke(Request request, Response response) throws IOException, + ServletException { /* NO-OP */ } + + @Override + public void fireContainerEvent(String type, Object data) { /* NO-OP */ } + + @Override + public void logAccess(Request request, Response response, long time, + boolean useDefault) { /* NO-OP */ } + + @Override + public AccessLog getAccessLog() { return null; } + + @Override + public int getStartStopThreads() { return 0; } + @Override + public void setStartStopThreads(int startStopThreads) { /* NO-OP */ } + + @Override + public boolean getAllowCasualMultipartParsing() { return false; } + @Override + public void setAllowCasualMultipartParsing( + boolean allowCasualMultipartParsing) { /* NO-OP */ } + + @Override + public Object[] getApplicationEventListeners() { return null; } + @Override + public void setApplicationEventListeners(Object[] listeners) { /* NO-OP */ } + + @Override + public Object[] getApplicationLifecycleListeners() { return null; } + @Override + public void setApplicationLifecycleListeners(Object[] listeners) { /* NO-OP */ } + + @Override + public boolean getAvailable() { return false; } + + @Override + public CharsetMapper getCharsetMapper() { return null; } + @Override + public void setCharsetMapper(CharsetMapper mapper) { /* NO-OP */ } + + @Override + public boolean getConfigured() { return false; } + @Override + public void setConfigured(boolean configured) { /* NO-OP */ } + + @Override + public boolean getCookies() { return false; } + @Override + public void setCookies(boolean cookies) { /* NO-OP */ } + + @Override + public String getSessionCookieName() { return null; } + @Override + public void setSessionCookieName(String sessionCookieName) { /* NO-OP */ } + + @Override + public boolean getUseHttpOnly() { return false; } + @Override + public void setUseHttpOnly(boolean useHttpOnly) { /* NO-OP */ } + + @Override + public String getSessionCookieDomain() { return null; } + @Override + public void setSessionCookieDomain(String sessionCookieDomain) { /* NO-OP */ } + + @Override + public String getSessionCookiePath() { return null; } + @Override + public void setSessionCookiePath(String sessionCookiePath) { /* NO-OP */ } + + @Override + public boolean getSessionCookiePathUsesTrailingSlash() { return false; } + @Override + public void setSessionCookiePathUsesTrailingSlash( + boolean sessionCookiePathUsesTrailingSlash) { /* NO-OP */ } + + @Override + public boolean getCrossContext() { return false; } + @Override + public void setCrossContext(boolean crossContext) { /* NO-OP */ } + + @Override + public String getAltDDName() { return null; } + @Override + public void setAltDDName(String altDDName) { /* NO-OP */ } + + @Override + public String getDisplayName() { return null; } + @Override + public void setDisplayName(String displayName) { /* NO-OP */ } + + @Override + public boolean getDistributable() { return false; } + @Override + public void setDistributable(boolean distributable) { /* NO-OP */ } + + @Override + public String getEncodedPath() { return null; } + + @Override + public boolean getIgnoreAnnotations() { return false; } + @Override + public void setIgnoreAnnotations(boolean ignoreAnnotations) { /* NO-OP */ } + + @Override + public LoginConfig getLoginConfig() { return null; } + @Override + public void setLoginConfig(LoginConfig config) { /* NO-OP */ } + + @Override + public Mapper getMapper() { return null; } + + @Override + public NamingResources getNamingResources() { return null; } + @Override + public void setNamingResources(NamingResources namingResources) { /* NO-OP */ } + + @Override + public String getPublicId() { return null; } + @Override + public void setPublicId(String publicId) { /* NO-OP */ } + + @Override + public boolean getReloadable() { return false; } + @Override + public void setReloadable(boolean reloadable) { /* NO-OP */ } + + @Override + public boolean getOverride() { return false; } + @Override + public void setOverride(boolean override) { /* NO-OP */ } + + @Override + public boolean getPrivileged() { return false; } + @Override + public void setPrivileged(boolean privileged) { /* NO-OP */ } + + @Override + public ServletContext getServletContext() { return null; } + + @Override + public int getSessionTimeout() { return 0; } + @Override + public void setSessionTimeout(int timeout) { /* NO-OP */ } + + @Override + public boolean getSwallowAbortedUploads() { return false; } + @Override + public void setSwallowAbortedUploads(boolean swallowAbortedUploads) { /* NO-OP */ } + + @Override + public boolean getSwallowOutput() { return false; } + @Override + public void setSwallowOutput(boolean swallowOutput) { /* NO-OP */ } + + @Override + public String getWrapperClass() { return null; } + @Override + public void setWrapperClass(String wrapperClass) { /* NO-OP */ } + + @Override + public boolean getXmlNamespaceAware() { return false; } + @Override + public void setXmlNamespaceAware(boolean xmlNamespaceAware) { /* NO-OP */ } + + @Override + public boolean getXmlValidation() { return false; } + @Override + public void setXmlValidation(boolean xmlValidation) { /* NO-OP */ } + + @Override + public void setTldValidation(boolean tldValidation) { /* NO-OP */ } + @Override + public boolean getTldValidation() { return false; } + + @Override + public boolean getTldNamespaceAware() { return false; } + @Override + public void setTldNamespaceAware(boolean tldNamespaceAware) { /* NO-OP */ } + + @Override + public JarScanner getJarScanner() { return null; } + @Override + public void setJarScanner(JarScanner jarScanner) { /* NO-OP */ } + + @Override + public Authenticator getAuthenticator() { return null; } + + @Override + public void setLogEffectiveWebXml(boolean logEffectiveWebXml) { /* NO-OP */ } + @Override + public boolean getLogEffectiveWebXml() { return false; } + + @Override + public void addApplicationListener(String listener) { /* NO-OP */ } + @Override + public String[] findApplicationListeners() { return null; } + @Override + public void removeApplicationListener(String listener) { /* NO-OP */ } + + @Override + public void addApplicationParameter(ApplicationParameter parameter) { /* NO-OP */ } + @Override + public ApplicationParameter[] findApplicationParameters() { return null; } + @Override + public void removeApplicationParameter(String name) { /* NO-OP */ } + + @Override + public void addConstraint(SecurityConstraint constraint) { /* NO-OP */ } + @Override + public SecurityConstraint[] findConstraints() { return null; } + @Override + public void removeConstraint(SecurityConstraint constraint) { /* NO-OP */ } + + @Override + public void addErrorPage(ErrorPage errorPage) { /* NO-OP */ } + @Override + public ErrorPage findErrorPage(int errorCode) { return null; } + @Override + public ErrorPage findErrorPage(String exceptionType) { return null; } + @Override + public ErrorPage[] findErrorPages() { return null; } + @Override + public void removeErrorPage(ErrorPage errorPage) { /* NO-OP */ } + + @Override + public void addFilterDef(FilterDef filterDef) { /* NO-OP */ } + @Override + public FilterDef findFilterDef(String filterName) { return null; } + @Override + public FilterDef[] findFilterDefs() { return null; } + @Override + public void removeFilterDef(FilterDef filterDef) { /* NO-OP */ } + + @Override + public void addFilterMap(FilterMap filterMap) { /* NO-OP */ } + @Override + public void addFilterMapBefore(FilterMap filterMap) { /* NO-OP */ } + @Override + public FilterMap[] findFilterMaps() { return null; } + @Override + public void removeFilterMap(FilterMap filterMap) { /* NO-OP */ } + + @Override + public void addInstanceListener(String listener) { /* NO-OP */ } + @Override + public String[] findInstanceListeners() { return null; } + @Override + public void removeInstanceListener(String listener) { /* NO-OP */ } + + @Override + public void addLocaleEncodingMappingParameter(String locale, String encoding) { /* NO-OP */ } + + @Override + public void addMimeMapping(String extension, String mimeType) { /* NO-OP */ } + @Override + public String findMimeMapping(String extension) { return null; } + @Override + public String[] findMimeMappings() { return null; } + @Override + public void removeMimeMapping(String extension) { /* NO-OP */ } + + @Override + public void addParameter(String name, String value) { /* NO-OP */ } + @Override + public String findParameter(String name) { return null; } + @Override + public String[] findParameters() { return null; } + @Override + public void removeParameter(String name) { /* NO-OP */ } + + @Override + public void addRoleMapping(String role, String link) { /* NO-OP */ } + @Override + public String findRoleMapping(String role) { return null; } + @Override + public void removeRoleMapping(String role) { /* NO-OP */ } + + @Override + public void addSecurityRole(String role) { /* NO-OP */ } + @Override + public boolean findSecurityRole(String role) { return false; } + @Override + public String[] findSecurityRoles() { return null; } + @Override + public void removeSecurityRole(String role) { /* NO-OP */ } + + @Override + public void addServletMapping(String pattern, String name) { /* NO-OP */ } + @Override + public void addServletMapping(String pattern, String name, + boolean jspWildcard) { /* NO-OP */ } + @Override + public String findServletMapping(String pattern) { return null; } + @Override + public String[] findServletMappings() { return null; } + @Override + public void removeServletMapping(String pattern) { /* NO-OP */ } + + @Override + public void addWelcomeFile(String name) { /* NO-OP */ } + @Override + public boolean findWelcomeFile(String name) { return false; } + @Override + public String[] findWelcomeFiles() { return null; } + @Override + public void removeWelcomeFile(String name) { /* NO-OP */ } + + @Override + public void addWrapperLifecycle(String listener) { /* NO-OP */ } + @Override + public String[] findWrapperLifecycles() { return null; } + @Override + public void removeWrapperLifecycle(String listener) { /* NO-OP */ } + + @Override + public void addWrapperListener(String listener) { /* NO-OP */ } + @Override + public String[] findWrapperListeners() { return null; } + @Override + public void removeWrapperListener(String listener) { /* NO-OP */ } + + @Override + public Wrapper createWrapper() { return null; } + + @Override + public String findStatusPage(int status) { return null; } + @Override + public int[] findStatusPages() { return null; } + + @Override + public boolean fireRequestInitEvent(ServletRequest request) { return false; } + @Override + public boolean fireRequestDestroyEvent(ServletRequest request) { return false; } + + @Override + public void reload() { /* NO-OP */ } + + @Override + public String getRealPath(String path) { return null; } + + @Override + public int getEffectiveMajorVersion() { return 0; } + @Override + public void setEffectiveMajorVersion(int major) { /* NO-OP */ } + + @Override + public int getEffectiveMinorVersion() { return 0; } + @Override + public void setEffectiveMinorVersion(int minor) { /* NO-OP */ } + + @Override + public JspConfigDescriptor getJspConfigDescriptor() { return null; } + + @Override + public void addResourceJarUrl(URL url) { /* NO-OP */ } + + @Override + public void addServletContainerInitializer(ServletContainerInitializer sci, + Set<Class<?>> classes) { /* NO-OP */ } + + @Override + public boolean getPaused() { return false; } + + @Override + public boolean isServlet22() { return false; } + + @Override + public Set<String> addServletSecurity( + ApplicationServletRegistration registration, + ServletSecurityElement servletSecurityElement) { return null; } + + @Override + public void setResourceOnlyServlets(String resourceOnlyServlets) { /* NO-OP */ } + @Override + public String getResourceOnlyServlets() { return null; } + @Override + public boolean isResourceOnlyServlet(String servletName) { return false; } + + @Override + public String getBaseName() { return null; } + + @Override + public void setFireRequestListenersOnForwards(boolean enable) { /* NO-OP */ } + @Override + public boolean getFireRequestListenersOnForwards() { return false; } + + @Override + public void setPreemptiveAuthentication(boolean enable) { /* NO-OP */ } + @Override + public boolean getPreemptiveAuthentication() { return false; } + + @Override + public void setSendRedirectBody(boolean enable) { /* NO-OP */ } + @Override + public boolean getSendRedirectBody() { return false; } + + @SuppressWarnings("unused") + public synchronized void addValve(Valve valve) { /* NO-OP */ } +} \ No newline at end of file Modified: tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java?rev=1201921&r1=1201920&r2=1201921&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java Mon Nov 14 21:51:23 2011 @@ -572,16 +572,18 @@ public class HostConfig } Context context = null; + boolean isExternalWar = false; + boolean isExternal = false; + File expandedDocBase = null; try { synchronized (digester) { try { context = (Context) digester.parse(contextXml); - if (context == null) { - log.error(sm.getString( - "hostConfig.deployDescriptor.error", - contextXml.getAbsolutePath())); - return; - } + } catch (Exception e) { + log.error(sm.getString( + "hostConfig.deployDescriptor.error", + contextXml.getAbsolutePath())); + context = new FailedContext(); } finally { digester.reset(); } @@ -597,8 +599,6 @@ public class HostConfig context.setPath(cn.getPath()); context.setWebappVersion(cn.getVersion()); // Add the associated docBase to the redeployed list if it's a WAR - boolean isExternalWar = false; - boolean isExternal = false; if (context.getDocBase() != null) { File docBase = new File(context.getDocBase()); if (!docBase.isAbsolute()) { @@ -623,12 +623,18 @@ public class HostConfig context.setDocBase(null); } } + host.addChild(context); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("hostConfig.deployDescriptor.error", + contextXml.getAbsolutePath()), t); + } finally { // Get paths for WAR and expanded WAR in appBase // default to appBase dir + name - File expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName()); - if (context.getDocBase() != null) { + expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName()); + if (context != null && context.getDocBase() != null) { // first assume docBase is absolute expandedDocBase = new File(context.getDocBase()); if (!expandedDocBase.isAbsolute()) { @@ -636,6 +642,7 @@ public class HostConfig expandedDocBase = new File(host.getAppBaseFile(), context.getDocBase()); } } + // Add the eventual unpacked WAR and all the resources which will be // watched inside it if (isExternalWar && unpackWARs) { @@ -668,10 +675,9 @@ public class HostConfig Long.valueOf(contextXml.lastModified())); } } - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("hostConfig.deployDescriptor.error", - contextXml.getAbsolutePath()), t); + // Add the global redeploy resources (which are never deleted) at + // the end so they don't interfere with the deletion process + addGlobalRedeployResources(deployedApp); } if (context != null && host.findChild(context.getName()) != null) { @@ -850,18 +856,17 @@ public class HostConfig log.info(sm.getString("hostConfig.deployWar", war.getAbsolutePath())); + Context context = null; try { - Context context = null; if (deployXML && xml.exists()) { synchronized (digester) { try { context = (Context) digester.parse(xml); - if (context == null) { - log.error(sm.getString( - "hostConfig.deployDescriptor.error", - war.getAbsolutePath())); - return; - } + } catch (Exception e) { + log.error(sm.getString( + "hostConfig.deployDescriptor.error", + war.getAbsolutePath())); + context = new FailedContext(); } finally { digester.reset(); } @@ -875,17 +880,17 @@ public class HostConfig jar.getJarEntry(Constants.ApplicationContextXml); istream = jar.getInputStream(entry); context = (Context) digester.parse(istream); - + } catch (Exception e) { + log.error(sm.getString( + "hostConfig.deployDescriptor.error", + war.getAbsolutePath())); + } finally { if (context == null) { - log.error(sm.getString( - "hostConfig.deployDescriptor.error", - war.getAbsolutePath())); - return; + context = new FailedContext(); } context.setConfigFile(new URL("jar:" + war.toURI().toString() + "!/" + Constants.ApplicationContextXml)); - } finally { if (istream != null) { try { istream.close(); @@ -929,9 +934,14 @@ public class HostConfig context.setWebappVersion(cn.getVersion()); context.setDocBase(cn.getBaseName() + ".war"); host.addChild(context); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("hostConfig.deployWar.error", + war.getAbsolutePath()), t); + } finally { // If we're unpacking WARs, the docBase will be mutated after // starting the context - if (unpackWARs && (context.getDocBase() != null)) { + if (unpackWARs && context != null && context.getDocBase() != null) { File docBase = new File(host.getAppBaseFile(), cn.getBaseName()); deployedApp.redeployResources.put(docBase.getAbsolutePath(), Long.valueOf(docBase.lastModified())); @@ -944,10 +954,9 @@ public class HostConfig } else { addWatchedResources(deployedApp, null, context); } - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("hostConfig.deployWar.error", - war.getAbsolutePath()), t); + // Add the global redeploy resources (which are never deleted) at + // the end so they don't interfere with the deletion process + addGlobalRedeployResources(deployedApp); } deployed.put(cn.getName(), deployedApp); @@ -1005,20 +1014,20 @@ public class HostConfig if( log.isInfoEnabled() ) log.info(sm.getString("hostConfig.deployDir", dir.getAbsolutePath())); + + Context context = null; + File xml = new File(dir, Constants.ApplicationContextXml); + File xmlCopy = null; try { - Context context = null; - File xml = new File(dir, Constants.ApplicationContextXml); - File xmlCopy = null; if (deployXML && xml.exists()) { synchronized (digester) { try { context = (Context) digester.parse(xml); - if (context == null) { - log.error(sm.getString( - "hostConfig.deployDescriptor.error", - xml)); - return; - } + } catch (Exception e) { + log.error(sm.getString( + "hostConfig.deployDescriptor.error", + xml)); + context = new FailedContext(); } finally { digester.reset(); } @@ -1062,6 +1071,11 @@ public class HostConfig context.setWebappVersion(cn.getVersion()); context.setDocBase(cn.getBaseName()); host.addChild(context); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("hostConfig.deployDir.error", + dir.getAbsolutePath()), t); + } finally { deployedApp.redeployResources.put(dir.getAbsolutePath(), Long.valueOf(dir.lastModified())); if (deployXML && xml.exists()) { @@ -1076,10 +1090,9 @@ public class HostConfig } } addWatchedResources(deployedApp, dir.getAbsolutePath(), context); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("hostConfig.deployDir.error", - dir.getAbsolutePath()), t); + // Add the global redeploy resources (which are never deleted) at + // the end so they don't interfere with the deletion process + addGlobalRedeployResources(deployedApp); } deployed.put(cn.getName(), deployedApp); @@ -1137,6 +1150,25 @@ public class HostConfig } + protected void addGlobalRedeployResources(DeployedApplication app) { + // Redeploy resources processing is hard-coded to never delete this file + File hostContextXml = + new File(getConfigBaseName(), Constants.HostContextXml); + if (hostContextXml.isFile()) { + app.redeployResources.put(hostContextXml.getAbsolutePath(), + Long.valueOf(hostContextXml.lastModified())); + } + + // Redeploy resources in CATALINA_BASE/conf are never deleted + File globalContextXml = + returnCanonicalPath(Constants.DefaultContextXml); + if (globalContextXml.isFile()) { + app.redeployResources.put(globalContextXml.getAbsolutePath(), + Long.valueOf(globalContextXml.lastModified())); + } + } + + /** * Check resources for redeployment and reloading. */ @@ -1169,6 +1201,13 @@ public class HostConfig try { File current = new File(resources[j]); current = current.getCanonicalFile(); + // Never delete per host context.xml defaults + if (Constants.HostContextXml.equals( + current.getName())) { + continue; + } + // Only delete resources in the appBase or the + // host's configBase if ((current.getAbsolutePath().startsWith( host.getAppBaseFile().getAbsolutePath() + File.separator)) @@ -1219,6 +1258,13 @@ public class HostConfig try { File current = new File(resources[j]); current = current.getCanonicalFile(); + // Never delete per host context.xml defaults + if (Constants.HostContextXml.equals( + current.getName())) { + continue; + } + // Only delete resources in the appBase or the host's + // configBase if ((current.getAbsolutePath().startsWith( host.getAppBaseFile().getAbsolutePath() + File.separator)) || (current.getAbsolutePath().startsWith( @@ -1240,6 +1286,13 @@ public class HostConfig try { File current = new File(resources2[j]); current = current.getCanonicalFile(); + // Never delete per host context.xml defaults + if (Constants.HostContextXml.equals( + current.getName())) { + continue; + } + // Only delete resources in the appBase or the host's + // configBase if ((current.getAbsolutePath().startsWith( host.getAppBaseFile().getAbsolutePath() + File.separator)) || ((current.getAbsolutePath().startsWith( 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=1201921&r1=1201920&r2=1201921&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties Mon Nov 14 21:51:23 2011 @@ -72,6 +72,7 @@ expandWar.createFailed=Unable to create expandWar.deleteFailed=[{0}] could not be completely deleted. The presence of the remaining files may cause problems expandWar.illegalPath=The archive [{0}] is malformed and will be ignored: an entry contains an illegal path [{1}] which was not expanded to [{2}] since that is outside of the defined docBase [{3}] expandWar.missingJarEntry=Cannot get input stream for JarEntry "{0}" - broken WAR file? +failedContext.start=Failed to process either the global, per-host or context-specific context.xml file therefore the [{0}] Context cannot be started. hostConfig.appBase=Application base [{1}] for host [{0}] does not exist or is not a directory. deployOnStartUp and autoDeploy have been set to false to prevent deployment errors. Other errors may still occur. hostConfig.canonicalizing=Error delete redeploy resources from context [{0}] hostConfig.cce=Lifecycle event data object {0} is not a Host Modified: tomcat/trunk/java/org/apache/catalina/util/LifecycleBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/util/LifecycleBase.java?rev=1201921&r1=1201920&r2=1201921&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/util/LifecycleBase.java (original) +++ tomcat/trunk/java/org/apache/catalina/util/LifecycleBase.java Mon Nov 14 21:51:23 2011 @@ -137,6 +137,8 @@ public abstract class LifecycleBase impl if (state.equals(LifecycleState.NEW)) { init(); + } else if (state.equals(LifecycleState.FAILED)){ + stop(); } else if (!state.equals(LifecycleState.INITIALIZED) && !state.equals(LifecycleState.STOPPED)) { invalidTransition(Lifecycle.BEFORE_START_EVENT); @@ -264,6 +266,16 @@ public abstract class LifecycleBase impl @Override public final synchronized void destroy() throws LifecycleException { + if (LifecycleState.FAILED.equals(state)) { + try { + // Triggers clean-up + stop(); + } catch (LifecycleException e) { + // Just log. Still want to destroy. + log.warn(sm.getString("lifecycleBase.destroyStopFail"), e); + } + } + if (LifecycleState.DESTROYING.equals(state) || LifecycleState.DESTROYED.equals(state)) { Modified: tomcat/trunk/java/org/apache/catalina/util/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/util/LocalStrings.properties?rev=1201921&r1=1201920&r2=1201921&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/util/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/catalina/util/LocalStrings.properties Mon Nov 14 21:51:23 2011 @@ -26,6 +26,7 @@ lifecycleBase.alreadyDestroyed=The destr lifecycleBase.alreadyStarted=The start() method was called on component [{0}] after start() had already been called. The second call will be ignored. lifecycleBase.alreadyStopped=The stop() method was called on component [{0}] after stop() had already been called. The second call will be ignored. lifecycleBase.destroyFail=Failed to destroy component [{0}] +lifecycleBase.destroyStopFail=Calling stop() on failed component [{0}] to trigger clean-up did not complete. lifecycleBase.initFail=Failed to initialize component [{0}] lifecycleBase.invalidTransition=An invalid Lifecycle transition was attempted ([{0}]) for component [{1}] in state [{2}] lifecycleBase.setState=Setting state for [{0}] to [{1}] Modified: tomcat/trunk/webapps/docs/deployer-howto.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/deployer-howto.xml?rev=1201921&r1=1201920&r2=1201921&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/deployer-howto.xml (original) +++ tomcat/trunk/webapps/docs/deployer-howto.xml Mon Nov 14 21:51:23 2011 @@ -195,12 +195,18 @@ will be simply redeployed as a compressed archive. </li> <li> - Re-deployment of a web application if the /WEB-INF/web.xml file (or any - other resource defined as a WatchedResource) is updated. + Re-loading of a web application if the /WEB-INF/web.xml file (or + any other resource defined as a WatchedResource) is updated. </li> <li> - Re-deployment of a web application if the Context Descriptor file from which - the web application has been deployed is updated. + Re-deployment of a web application if the Context Descriptor + file from which the web application has been deployed is + updated. + </li> + <li> + Re-deployment of dependent web applications if the global or + per-host Context Descriptor file used by the web application is + updated. </li> <li> Re-deployment of a web application if a Context Descriptor file (with a --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org