Author: markt
Date: Sat Sep 8 21:11:46 2012
New Revision: 1382367
URL: http://svn.apache.org/viewvc?rev=1382367&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=52777
Implement optional automatic removal of old applications where parallel
deployment has been used.
Modified:
tomcat/tc7.0.x/trunk/ (props changed)
tomcat/tc7.0.x/trunk/java/org/apache/catalina/Host.java
tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/StandardHost.java
tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/HostConfig.java
tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/LocalStrings.properties
tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml
tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml
tomcat/tc7.0.x/trunk/webapps/docs/config/context.xml
tomcat/tc7.0.x/trunk/webapps/docs/config/host.xml
Propchange: tomcat/tc7.0.x/trunk/
------------------------------------------------------------------------------
Merged /tomcat/trunk:r1382366
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/Host.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/Host.java?rev=1382367&r1=1382366&r2=1382367&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/Host.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/Host.java Sat Sep 8 21:11:46
2012
@@ -180,6 +180,22 @@ public interface Host extends Container
public ExecutorService getStartStopExecutor();
+ /**
+ * Returns true of the Host is configured to automatically undeploy old
+ * versions of applications deployed using parallel deployment. This only
+ * takes effect is {@link #getAutoDeploy()} also returns true.
+ */
+ public boolean getUndeployOldVersions();
+
+
+ /**
+ * Set to true if the Host should automatically undeploy old versions of
+ * applications deployed using parallel deployment. This only takes effect
+ * if {@link #getAutoDeploy()} returns true.
+ */
+ public void setUndeployOldVersions(boolean undeployOldVersions);
+
+
// --------------------------------------------------------- Public Methods
/**
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/StandardHost.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/StandardHost.java?rev=1382367&r1=1382366&r2=1382367&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/StandardHost.java
(original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/StandardHost.java Sat
Sep 8 21:11:46 2012
@@ -5,9 +5,9 @@
* 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.
@@ -53,7 +53,7 @@ public class StandardHost extends Contai
private static final org.apache.juli.logging.Log log=
org.apache.juli.logging.LogFactory.getLog( StandardHost.class );
-
+
// ----------------------------------------------------------- Constructors
@@ -75,7 +75,7 @@ public class StandardHost extends Contai
* The set of aliases for this Host.
*/
private String[] aliases = new String[0];
-
+
private final Object aliasesLock = new Object();
@@ -132,7 +132,7 @@ public class StandardHost extends Contai
/**
- * The Java class name of the default error reporter implementation class
+ * The Java class name of the default error reporter implementation class
* for deployed web applications.
*/
private String errorReportValveClass =
@@ -162,15 +162,15 @@ public class StandardHost extends Contai
*/
private boolean createDirs = true;
-
+
/**
* Track the class loaders for the child web applications so memory leaks
* can be detected.
*/
private Map<ClassLoader, String> childClassLoaders =
new WeakHashMap<ClassLoader, String>();
-
-
+
+
/**
* Any file or directory in {@link #appBase} that this pattern matches
will
* be ignored by the automatic deployment process (both
@@ -179,12 +179,27 @@ public class StandardHost extends Contai
private Pattern deployIgnore = null;
+ private boolean undeployOldVersions = false;
+
+
// ------------------------------------------------------------- Properties
- @Override
- public ExecutorService getStartStopExecutor() {
- return startStopExecutor;
- }
+ @Override
+ public boolean getUndeployOldVersions() {
+ return undeployOldVersions;
+ }
+
+
+ @Override
+ public void setUndeployOldVersions(boolean undeployOldVersions) {
+ this.undeployOldVersions = undeployOldVersions;
+ }
+
+
+ @Override
+ public ExecutorService getStartStopExecutor() {
+ return startStopExecutor;
+ }
/**
@@ -225,7 +240,7 @@ public class StandardHost extends Contai
return (this.xmlBase);
}
-
+
/**
* Set the Xml root for this Host. This can be an absolute
@@ -264,7 +279,7 @@ public class StandardHost extends Contai
}
/**
- * Return the value of the auto deploy flag. If true, it indicates that
+ * Return the value of the auto deploy flag. If true, it indicates that
* this host's child webapps will be dynamically deployed.
*/
@Override
@@ -277,7 +292,7 @@ public class StandardHost extends Contai
/**
* Set the auto deploy flag value for this host.
- *
+ *
* @param autoDeploy The new auto deploy flag
*/
@Override
@@ -285,7 +300,7 @@ public class StandardHost extends Contai
boolean oldAutoDeploy = this.autoDeploy;
this.autoDeploy = autoDeploy;
- support.firePropertyChange("autoDeploy", oldAutoDeploy,
+ support.firePropertyChange("autoDeploy", oldAutoDeploy,
this.autoDeploy);
}
@@ -348,8 +363,8 @@ public class StandardHost extends Contai
/**
- * Return the value of the deploy on startup flag. If true, it indicates
- * that this host's child webapps should be discovered and automatically
+ * Return the value of the deploy on startup flag. If true, it indicates
+ * that this host's child webapps should be discovered and automatically
* deployed at startup time.
*/
@Override
@@ -362,7 +377,7 @@ public class StandardHost extends Contai
/**
* Set the deploy on startup flag value for this host.
- *
+ *
* @param deployOnStartup The new deploy on startup flag
*/
@Override
@@ -370,7 +385,7 @@ public class StandardHost extends Contai
boolean oldDeployOnStartup = this.deployOnStartup;
this.deployOnStartup = deployOnStartup;
- support.firePropertyChange("deployOnStartup", oldDeployOnStartup,
+ support.firePropertyChange("deployOnStartup", oldDeployOnStartup,
this.deployOnStartup);
}
@@ -416,8 +431,8 @@ public class StandardHost extends Contai
this.copyXML= copyXML;
}
-
-
+
+
/**
* Return the Java class name of the error report valve class
* for new web applications.
@@ -440,12 +455,12 @@ public class StandardHost extends Contai
String oldErrorReportValveClassClass = this.errorReportValveClass;
this.errorReportValveClass = errorReportValveClass;
support.firePropertyChange("errorReportValveClass",
- oldErrorReportValveClassClass,
+ oldErrorReportValveClassClass,
this.errorReportValveClass);
}
-
-
+
+
/**
* Return the canonical, fully qualified, name of the virtual host
* this Container represents.
@@ -529,7 +544,7 @@ public class StandardHost extends Contai
public String getDeployIgnore() {
if (deployIgnore == null) {
return null;
- }
+ }
return this.deployIgnore.toString();
}
@@ -564,7 +579,7 @@ public class StandardHost extends Contai
this.deployIgnore = Pattern.compile(deployIgnore);
}
support.firePropertyChange("deployIgnore",
- oldDeployIgnore,
+ oldDeployIgnore,
deployIgnore);
}
@@ -636,8 +651,8 @@ public class StandardHost extends Contai
}
}
}
-
-
+
+
/**
* Attempt to identify the contexts that have a class loader memory leak.
* This is usually triggered on context reload. Note: This method attempts
@@ -645,11 +660,11 @@ public class StandardHost extends Contai
* caution on a production system.
*/
public String[] findReloadedContextMemoryLeaks() {
-
+
System.gc();
-
+
List<String> result = new ArrayList<String>();
-
+
for (Map.Entry<ClassLoader, String> entry :
childClassLoaders.entrySet()) {
ClassLoader cl = entry.getKey();
@@ -659,7 +674,7 @@ public class StandardHost extends Contai
}
}
}
-
+
return result.toArray(new String[result.size()]);
}
@@ -747,7 +762,7 @@ public class StandardHost extends Contai
return (sb.toString());
}
-
+
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
@@ -757,7 +772,7 @@ public class StandardHost extends Contai
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
-
+
// Set error report valve
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
@@ -821,5 +836,5 @@ public class StandardHost extends Contai
return keyProperties.toString();
}
-
+
}
Modified:
tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml?rev=1382367&r1=1382366&r2=1382367&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
(original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
Sat Sep 8 21:11:46 2012
@@ -1211,6 +1211,10 @@
type="java.lang.String"
writeable="false"/>
+ <attribute name="undeployOldVersions"
+ description="Determines if old versions of applications
deployed using parallel deployment are automatically undeployed when no longer
used. Requires autoDeploy to be enabled."
+ type="boolean"/>
+
<attribute name="unpackWARs"
description="Unpack WARs property"
is="true"
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/HostConfig.java
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/HostConfig.java?rev=1382367&r1=1382366&r2=1382367&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/HostConfig.java
(original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/HostConfig.java Sat
Sep 8 21:11:46 2012
@@ -5,9 +5,9 @@
* 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.
@@ -28,11 +28,14 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -51,6 +54,7 @@ import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.util.ContextName;
import org.apache.catalina.util.IOTools;
@@ -72,7 +76,7 @@ import org.apache.tomcat.util.res.String
*/
public class HostConfig
implements LifecycleListener {
-
+
private static final Log log = LogFactory.getLog( HostConfig.class );
// ----------------------------------------------------- Instance Variables
@@ -107,12 +111,12 @@ public class HostConfig
*/
protected Host host = null;
-
+
/**
* The JMX ObjectName of this component.
*/
protected ObjectName oname = null;
-
+
/**
* The string resources for this package.
@@ -134,8 +138,8 @@ public class HostConfig
* a web application is deployed?
*/
protected boolean copyXML = false;
-
-
+
+
/**
* Should we unpack WAR files when auto-deploying applications in the
* <code>appBase</code> directory?
@@ -149,13 +153,13 @@ public class HostConfig
protected Map<String, DeployedApplication> deployed =
new ConcurrentHashMap<String, DeployedApplication>();
-
+
/**
- * List of applications which are being serviced, and shouldn't be
+ * List of applications which are being serviced, and shouldn't be
* deployed/undeployed/redeployed at the moment.
*/
protected ArrayList<String> serviced = new ArrayList<String>();
-
+
/**
* The <code>Digester</code> instance used to parse context descriptors.
@@ -279,8 +283,8 @@ public class HostConfig
this.unpackWARs = unpackWARs;
}
-
-
+
+
// --------------------------------------------------------- Public Methods
@@ -316,15 +320,15 @@ public class HostConfig
}
-
+
/**
* Add a serviced application to the list.
*/
public synchronized void addServiced(String name) {
serviced.add(name);
}
-
-
+
+
/**
* Is application serviced ?
* @return state of the application
@@ -332,7 +336,7 @@ public class HostConfig
public synchronized boolean isServiced(String name) {
return (serviced.contains(name));
}
-
+
/**
* Removed a serviced application from the list.
@@ -341,7 +345,7 @@ public class HostConfig
serviced.remove(name);
}
-
+
/**
* Get the instant where an application was deployed.
* @return 0L if no application with that name is deployed, or the instant
@@ -352,11 +356,11 @@ public class HostConfig
if (app == null) {
return 0L;
}
-
+
return app.timestamp;
}
-
-
+
+
/**
* Has the specified application been deployed? Note applications defined
* in server.xml will not have been deployed.
@@ -369,14 +373,14 @@ public class HostConfig
if (app == null) {
return false;
}
-
+
return true;
}
-
-
+
+
// ------------------------------------------------------ Protected Methods
-
+
/**
* Create the digester which will be used to parse context config files.
*/
@@ -386,12 +390,12 @@ public class HostConfig
// Add object creation rule
digester.addObjectCreate("Context",
"org.apache.catalina.core.StandardContext",
"className");
- // Set the properties on that object (it doesn't matter if extra
+ // Set the properties on that object (it doesn't matter if extra
// properties are set)
digester.addSetProperties("Context");
return (digester);
}
-
+
protected File returnCanonicalPath(String path) {
File file = new File(path);
File base = new File(System.getProperty(Globals.CATALINA_BASE_PROP));
@@ -403,7 +407,7 @@ public class HostConfig
return file;
}
}
-
+
/**
* Return a File object representing the "application root" directory
@@ -414,7 +418,7 @@ public class HostConfig
if (appBase != null) {
return appBase;
}
-
+
appBase = returnCanonicalPath(host.getAppBase());
return appBase;
@@ -430,7 +434,7 @@ public class HostConfig
if (configBase != null) {
return configBase;
}
-
+
if (host.getXmlBase()!=null) {
configBase = returnCanonicalPath(host.getXmlBase());
} else {
@@ -472,16 +476,16 @@ public class HostConfig
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
-
+
}
/**
* Filter the list of application file paths to remove those that match
* the regular expression defined by {@link Host#getDeployIgnore()}.
- *
+ *
* @param unfilteredAppPaths The list of application paths to filtert
- *
+ *
* @return The filtered list of application paths
*/
protected String[] filterAppPaths(String[] unfilteredAppPaths) {
@@ -520,7 +524,7 @@ public class HostConfig
File configBase = configBase();
ContextName cn = new ContextName(name);
String baseName = cn.getBaseName();
-
+
if (deploymentExists(baseName)) {
return;
}
@@ -551,7 +555,7 @@ public class HostConfig
if (files == null)
return;
-
+
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<Future<?>>();
@@ -563,7 +567,7 @@ public class HostConfig
if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
continue;
-
+
results.add(
es.submit(new DeployDescriptor(this, cn, contextXml)));
}
@@ -585,7 +589,7 @@ public class HostConfig
* @param contextXml
*/
protected void deployDescriptor(ContextName cn, File contextXml) {
-
+
DeployedApplication deployedApp = new
DeployedApplication(cn.getName());
// Assume this is a configuration descriptor and deploy it
@@ -686,7 +690,7 @@ public class HostConfig
if (expandedDocBase.exists()) {
deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
Long.valueOf(expandedDocBase.lastModified()));
- addWatchedResources(deployedApp,
+ addWatchedResources(deployedApp,
expandedDocBase.getAbsolutePath(), context);
} else {
addWatchedResources(deployedApp, null, context);
@@ -713,15 +717,15 @@ public class HostConfig
* Deploy WAR files.
*/
protected void deployWARs(File appBase, String[] files) {
-
+
if (files == null)
return;
-
+
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<Future<?>>();
for (int i = 0; i < files.length; i++) {
-
+
if (files[i].equalsIgnoreCase("META-INF"))
continue;
if (files[i].equalsIgnoreCase("WEB-INF"))
@@ -729,9 +733,9 @@ public class HostConfig
File war = new File(appBase, files[i]);
if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") &&
war.isFile()
&& !invalidWars.contains(files[i]) ) {
-
+
ContextName cn = new ContextName(files[i]);
-
+
// Check for WARs with /../ /./ or similar sequences in the
name
if (!validateContextPath(appBase, cn.getBaseName())) {
log.error(sm.getString(
@@ -742,7 +746,7 @@ public class HostConfig
if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
continue;
-
+
results.add(es.submit(new DeployWar(this, cn, war)));
}
}
@@ -761,10 +765,10 @@ public class HostConfig
private boolean validateContextPath(File appBase, String contextPath) {
// More complicated than the ideal as the canonical path may or may
// not end with File.separator for a directory
-
+
StringBuilder docBase;
String canonicalDocBase = null;
-
+
try {
String canonicalAppBase = appBase.getCanonicalPath();
docBase = new StringBuilder(canonicalAppBase);
@@ -776,10 +780,10 @@ public class HostConfig
}
// At this point docBase should be canonical but will not end
// with File.separator
-
+
canonicalDocBase =
(new File(docBase.toString())).getCanonicalPath();
-
+
// If the canonicalDocBase ends with File.separator, add one to
// docBase before they are compared
if (canonicalDocBase.endsWith(File.separator)) {
@@ -788,9 +792,9 @@ public class HostConfig
} catch (IOException ioe) {
return false;
}
-
+
// Compare the two. If they are not the same, the contextPath must
- // have /../ like sequences in it
+ // have /../ like sequences in it
return canonicalDocBase.equals(docBase.toString());
}
@@ -799,7 +803,7 @@ public class HostConfig
* @param war
*/
protected void deployWAR(ContextName cn, File war) {
-
+
// Checking for a nested /META-INF/context.xml
JarFile jar = null;
JarEntry entry = null;
@@ -814,7 +818,7 @@ public class HostConfig
cn.getBaseName() + "/META-INF/context.xml");
}
boolean xmlInWar = false;
-
+
if (deployXML && !xml.exists()) {
try {
jar = new JarFile(war);
@@ -824,7 +828,7 @@ public class HostConfig
}
if ((copyXML || unpackWARs) && xmlInWar) {
istream = jar.getInputStream(entry);
-
+
fos = new FileOutputStream(xml);
ostream = new BufferedOutputStream(fos, 1024);
byte buffer[] = new byte[1024];
@@ -875,11 +879,11 @@ public class HostConfig
}
}
}
-
+
DeployedApplication deployedApp = new
DeployedApplication(cn.getName());
-
+
// Deploy the application in this WAR file
- if(log.isInfoEnabled())
+ if(log.isInfoEnabled())
log.info(sm.getString("hostConfig.deployWar",
war.getAbsolutePath()));
@@ -985,7 +989,7 @@ public class HostConfig
// the end so they don't interfere with the deletion process
addGlobalRedeployResources(deployedApp);
}
-
+
deployed.put(cn.getName(), deployedApp);
}
@@ -997,7 +1001,7 @@ public class HostConfig
if (files == null)
return;
-
+
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<Future<?>>();
@@ -1028,17 +1032,17 @@ public class HostConfig
}
}
-
+
/**
* @param cn
* @param dir
*/
protected void deployDirectory(ContextName cn, File dir) {
-
+
DeployedApplication deployedApp = new
DeployedApplication(cn.getName());
// Deploy the application in this directory
- if( log.isInfoEnabled() )
+ if( log.isInfoEnabled() )
log.info(sm.getString("hostConfig.deployDir",
dir.getAbsolutePath()));
@@ -1125,17 +1129,17 @@ public class HostConfig
deployed.put(cn.getName(), deployedApp);
}
-
+
/**
* Check if a webapp is already deployed in this host.
- *
+ *
* @param contextName of the context which will be checked
*/
protected boolean deploymentExists(String contextName) {
return (deployed.containsKey(contextName) ||
(host.findChild(contextName) != null));
}
-
+
/**
* Add watched resources to the specified Context.
@@ -1171,11 +1175,11 @@ public class HostConfig
if(log.isDebugEnabled())
log.debug("Watching WatchedResource '" +
resource.getAbsolutePath() + "'");
- app.reloadResources.put(resource.getAbsolutePath(),
+ app.reloadResources.put(resource.getAbsolutePath(),
Long.valueOf(resource.lastModified()));
}
}
-
+
protected void addGlobalRedeployResources(DeployedApplication app) {
// Redeploy resources processing is hard-coded to never delete this
file
@@ -1213,43 +1217,7 @@ public class HostConfig
if ((!resource.isDirectory()) &&
resource.lastModified() > lastModified) {
// Undeploy application
- if (log.isInfoEnabled())
- log.info(sm.getString("hostConfig.undeploy",
app.name));
- Container context = host.findChild(app.name);
- try {
- host.removeChild(context);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.warn(sm.getString
- ("hostConfig.context.remove", app.name), t);
- }
- // Delete other redeploy resources
- for (int j = i + 1; j < resources.length; j++) {
- 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(
- appBase().getAbsolutePath() +
- File.separator))
- || (current.getAbsolutePath().startsWith(
- configBase().getAbsolutePath()))) {
- if (log.isDebugEnabled())
- log.debug("Delete " + current);
- ExpandWar.delete(current);
- }
- } catch (IOException e) {
- log.warn(sm.getString
- ("hostConfig.canonicalizing", app.name),
e);
- }
- }
- deployed.remove(app.name);
+ deleteRedeployResources(app, resources, i, false);
return;
}
} else {
@@ -1270,71 +1238,7 @@ public class HostConfig
continue;
}
// Undeploy application
- if (log.isInfoEnabled())
- log.info(sm.getString("hostConfig.undeploy", app.name));
- Container context = host.findChild(app.name);
- try {
- host.removeChild(context);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.warn(sm.getString
- ("hostConfig.context.remove", app.name), t);
- }
- // Delete all redeploy resources
- for (int j = i + 1; j < resources.length; j++) {
- 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(
- appBase().getAbsolutePath() + File.separator))
- || (current.getAbsolutePath().startsWith(
- configBase().getAbsolutePath()))) {
- if (log.isDebugEnabled())
- log.debug("Delete " + current);
- ExpandWar.delete(current);
- }
- } catch (IOException e) {
- log.warn(sm.getString
- ("hostConfig.canonicalizing", app.name), e);
- }
- }
- // Delete reload resources as well (to remove any remaining
.xml
- // descriptor)
- String[] resources2 =
- app.reloadResources.keySet().toArray(new String[0]);
- for (int j = 0; j < resources2.length; j++) {
- 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(
- appBase().getAbsolutePath() + File.separator))
- || ((current.getAbsolutePath().startsWith(
- configBase().getAbsolutePath())
- &&
(current.getAbsolutePath().endsWith(".xml"))))) {
- if (log.isDebugEnabled())
- log.debug("Delete " + current);
- ExpandWar.delete(current);
- }
- } catch (IOException e) {
- log.warn(sm.getString
- ("hostConfig.canonicalizing", app.name), e);
- }
- }
- deployed.remove(app.name);
+ deleteRedeployResources(app, resources, i, true);
return;
}
}
@@ -1346,7 +1250,7 @@ public class HostConfig
"] reload resource " + resource);
long lastModified =
app.reloadResources.get(resources[i]).longValue();
- if ((!resource.exists() && lastModified != 0L)
+ if ((!resource.exists() && lastModified != 0L)
|| (resource.lastModified() != lastModified)) {
// Reload application
if(log.isInfoEnabled())
@@ -1373,8 +1277,83 @@ public class HostConfig
}
}
}
-
-
+
+
+ private void deleteRedeployResources(DeployedApplication app,
+ String[] resources, int i, boolean deleteReloadResources) {
+
+ // Delete redeploy resources
+ if (log.isInfoEnabled())
+ log.info(sm.getString("hostConfig.undeploy", app.name));
+ Container context = host.findChild(app.name);
+ try {
+ host.removeChild(context);
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ log.warn(sm.getString
+ ("hostConfig.context.remove", app.name), t);
+ }
+ // Delete other redeploy resources
+ for (int j = i + 1; j < resources.length; j++) {
+ 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(
+ appBase().getAbsolutePath() +
+ File.separator))
+ || (current.getAbsolutePath().startsWith(
+ configBase().getAbsolutePath()))) {
+ if (log.isDebugEnabled())
+ log.debug("Delete " + current);
+ ExpandWar.delete(current);
+ }
+ } catch (IOException e) {
+ log.warn(sm.getString
+ ("hostConfig.canonicalizing", app.name), e);
+ }
+ }
+
+ // Delete reload resources (to remove any remaining .xml descriptor)
+ if (deleteReloadResources) {
+ String[] resources2 =
+ app.reloadResources.keySet().toArray(new String[0]);
+ for (int j = 0; j < resources2.length; j++) {
+ 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(
+ appBase().getAbsolutePath() + File.separator))
+ || ((current.getAbsolutePath().startsWith(
+ configBase().getAbsolutePath())
+ &&
(current.getAbsolutePath().endsWith(".xml"))))) {
+ if (log.isDebugEnabled())
+ log.debug("Delete " + current);
+ ExpandWar.delete(current);
+ }
+ } catch (IOException e) {
+ log.warn(sm.getString
+ ("hostConfig.canonicalizing", app.name), e);
+ }
+ }
+
+ }
+ deployed.remove(app.name);
+ }
+
/**
* Process a "start" event for this Host.
*/
@@ -1392,7 +1371,7 @@ public class HostConfig
} catch (Exception e) {
log.error(sm.getString("hostConfig.jmx.register", oname), e);
}
-
+
if (host.getCreateDirs()) {
File[] dirs = new File[] {appBase(),configBase()};
for (int i=0; i<dirs.length; i++) {
@@ -1411,7 +1390,7 @@ public class HostConfig
if (host.getDeployOnStartup())
deployApps();
-
+
}
@@ -1444,19 +1423,24 @@ public class HostConfig
if (host.getAutoDeploy()) {
// Check for resources modification to trigger redeployment
- DeployedApplication[] apps =
+ DeployedApplication[] apps =
deployed.values().toArray(new DeployedApplication[0]);
for (int i = 0; i < apps.length; i++) {
if (!isServiced(apps[i].name))
checkResources(apps[i]);
}
+
+ // Check for old versions of applications that can now be
undeployed
+ if (host.getUndeployOldVersions()) {
+ checkUndeploy();
+ }
+
// Hotdeploy applications
deployApps();
}
-
}
-
+
/**
* Check status of a specific webapp, for use with stuff like management
webapps.
*/
@@ -1470,18 +1454,64 @@ public class HostConfig
}
/**
+ * Check for old versions of applications using parallel deployment that
are
+ * now unused (have no active sessions) and undeploy any that are found.
+ */
+ public void checkUndeploy() {
+ // Need ordered set of names
+ SortedSet<String> sortedAppNames = new TreeSet<String>();
+ sortedAppNames.addAll(deployed.keySet());
+
+ if (sortedAppNames.size() < 2) {
+ return;
+ }
+ Iterator<String> iter = sortedAppNames.iterator();
+
+ ContextName previous = new ContextName(iter.next());
+ do {
+ ContextName current = new ContextName(iter.next());
+
+ if (current.getPath().equals(previous.getPath())) {
+ // Current and previous are same version - current will always
+ // be a later version
+ Context context = (Context) host.findChild(previous.getName());
+ if (context != null) {
+ Manager manager = context.getManager();
+ if (manager != null && manager.getActiveSessions() == 0) {
+ if (log.isInfoEnabled()) {
+ log.info(sm.getString("hostConfig.undeployVersion",
+ previous.getName()));
+ }
+ DeployedApplication app =
+ deployed.get(previous.getName());
+ String[] resources =
+ app.redeployResources.keySet().toArray(
+ new String[0]);
+ // Version is unused - undeploy it completely
+ // The -1 is a 'trick' to ensure all redeploy resources
+ // are removed
+ deleteRedeployResources(app, resources, -1,
+ true);
+ }
+ }
+ }
+ previous = current;
+ } while (iter.hasNext());
+ }
+
+ /**
* Add a new Context to be managed by us.
* Entry point for the admin webapp, and other JMX Context controllers.
*/
- public void manageApp(Context context) {
+ public void manageApp(Context context) {
String contextName = context.getName();
-
+
if (deployed.containsKey(contextName))
return;
DeployedApplication deployedApp = new DeployedApplication(contextName);
-
+
// Add the associated docBase to the redeployed list if it's a WAR
boolean isWar = false;
if (context.getDocBase() != null) {
@@ -1524,22 +1554,22 @@ public class HostConfig
/**
- * This class represents the state of a deployed application, as well as
+ * This class represents the state of a deployed application, as well as
* the monitored resources.
*/
protected static class DeployedApplication {
public DeployedApplication(String name) {
this.name = name;
}
-
+
/**
- * Application context path. The assertion is that
+ * Application context path. The assertion is that
* (host.getChild(name) != null).
*/
public String name;
-
+
/**
- * Any modification of the specified (static) resources will cause a
+ * Any modification of the specified (static) resources will cause a
* redeployment of the application. If any of the specified resources
is
* removed, the application will be undeployed. Typically, this will
* contain resources like the context.xml file, a compressed WAR path.
@@ -1549,7 +1579,7 @@ public class HostConfig
new LinkedHashMap<String, Long>();
/**
- * Any modification of the specified (static) resources will cause a
+ * Any modification of the specified (static) resources will cause a
* reload of the application. This will typically contain resources
* such as the web.xml of a webapp, but can be configured to contain
* additional descriptors.
Modified:
tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/LocalStrings.properties?rev=1382367&r1=1382366&r2=1382367&view=diff
==============================================================================
---
tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/LocalStrings.properties
(original)
+++
tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/LocalStrings.properties
Sat Sep 8 21:11:46 2012
@@ -111,6 +111,7 @@ hostConfig.start=HostConfig: Processing
hostConfig.stop=HostConfig: Processing STOP
hostConfig.undeploy=Undeploying context [{0}]
hostConfig.undeploy.error=Error undeploying web application at context path {0}
+hostConfig.undeployVersion=Undeploying old version of context [{0}] which has
no active session
tldConfig.addListeners=Adding {0} listeners from TLD files
tldConfig.cce=Lifecycle event data object {0} is not a Context
tldConfig.dirFail=Failed to process directory [{0}] for TLD files
Modified:
tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml?rev=1382367&r1=1382366&r2=1382367&view=diff
==============================================================================
---
tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml
(original)
+++
tomcat/tc7.0.x/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml
Sat Sep 8 21:11:46 2012
@@ -109,6 +109,12 @@
type="java.lang.String"/>
</operation>
+ <operation name="checkUndeploy"
+ description="Undeploy any old versions of applications deployed
using parallel deployment that have no active sessions"
+ impact="ACTION"
+ returnType="void">
+ </operation>
+
<operation name="getDeploymentTime"
description="Get the instant where an application was deployed"
impact="ACTION"
Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1382367&r1=1382366&r2=1382367&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Sat Sep 8 21:11:46 2012
@@ -60,6 +60,11 @@
Add one library from JDK 7 to the value of <code>jarsToSkip</code>
property in the <code>catalina.properties</code> file. (kkolinko)
</update>
+ <add>
+ <bug>52777</bug>: Add an option to automatically remove old, unused
+ versions (ones where there are no longer any active sessions) of
+ applications deployed using parallel deployment. (markt)
+ </add>
<fix>
<bug>53828</bug>: Use correct status code when closing a WebSocket
connection normally in response to a close frame from a client. (markt)
Modified: tomcat/tc7.0.x/trunk/webapps/docs/config/context.xml
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/config/context.xml?rev=1382367&r1=1382366&r2=1382367&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/docs/config/context.xml (original)
+++ tomcat/tc7.0.x/trunk/webapps/docs/config/context.xml Sat Sep 8 21:11:46
2012
@@ -82,6 +82,9 @@
<li>If session information is present in the request but no matching session
can be found, use the latest version.</li>
</ul>
+ <p>The <a href="host.html">Host</a> may be configured (via the
+ <code>undeployOldVersions</code>) to remove old versions deployed in this way
+ once they are no longer in use.</p>
</subsection>
<subsection name="Naming">
Modified: tomcat/tc7.0.x/trunk/webapps/docs/config/host.xml
URL:
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/config/host.xml?rev=1382367&r1=1382366&r2=1382367&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/docs/config/host.xml (original)
+++ tomcat/tc7.0.x/trunk/webapps/docs/config/host.xml Sat Sep 8 21:11:46 2012
@@ -201,6 +201,14 @@
not specified, the default value of 1 will be used.</p>
</attribute>
+ <attribute name="undeployOldVersions" required="false">
+ <p>This flag determines if Tomcat, as part of the auto deployment
+ process, will check for old, unused versions of web applications
+ deployed using parallel deployment and, if any are found, remove them.
+ This flag only applies if <code>autoDeploy</code> is true. If not
+ specified the default value of false will be used.</p>
+ </attribute>
+
</attributes>
</subsection>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]