Author: markt
Date: Sat Sep  8 21:00:32 2012
New Revision: 1382366

URL: http://svn.apache.org/viewvc?rev=1382366&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/trunk/java/org/apache/catalina/Host.java
    tomcat/trunk/java/org/apache/catalina/core/StandardHost.java
    tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
    tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java
    tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
    tomcat/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml
    tomcat/trunk/webapps/docs/config/context.xml
    tomcat/trunk/webapps/docs/config/host.xml

Modified: tomcat/trunk/java/org/apache/catalina/Host.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Host.java?rev=1382366&r1=1382365&r2=1382366&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Host.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Host.java Sat Sep  8 21:00:32 2012
@@ -210,6 +210,22 @@ public interface Host extends Container 
     public void setCreateDirs(boolean createDirs);
 
 
+    /**
+     * 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/trunk/java/org/apache/catalina/core/StandardHost.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardHost.java?rev=1382366&r1=1382365&r2=1382366&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/StandardHost.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/StandardHost.java Sat Sep  8 
21:00:32 2012
@@ -181,9 +181,24 @@ public class StandardHost extends Contai
     private Pattern deployIgnore = null;
 
 
+    private boolean undeployOldVersions = false;
+
+
     // ------------------------------------------------------------- Properties
 
     @Override
+    public boolean getUndeployOldVersions() {
+        return undeployOldVersions;
+    }
+
+
+    @Override
+    public void setUndeployOldVersions(boolean undeployOldVersions) {
+        this.undeployOldVersions = undeployOldVersions;
+    }
+
+
+    @Override
     public ExecutorService getStartStopExecutor() {
         return startStopExecutor;
     }

Modified: tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml?rev=1382366&r1=1382365&r2=1382366&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml (original)
+++ tomcat/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml Sat Sep  
8 21:00:32 2012
@@ -1181,6 +1181,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/trunk/java/org/apache/catalina/startup/HostConfig.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java?rev=1382366&r1=1382365&r2=1382366&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/HostConfig.java Sat Sep  8 
21:00:32 2012
@@ -27,11 +27,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;
@@ -48,6 +51,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;
@@ -1163,43 +1167,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(
-                                    host.getAppBaseFile().getAbsolutePath() +
-                                    File.separator))
-                                    || (current.getAbsolutePath().startsWith(
-                                            
host.getConfigBaseFile().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 {
@@ -1220,71 +1188,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(
-                                host.getAppBaseFile().getAbsolutePath() + 
File.separator))
-                            || (current.getAbsolutePath().startsWith(
-                                    
host.getConfigBaseFile().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(
-                                host.getAppBaseFile().getAbsolutePath() + 
File.separator))
-                            || ((current.getAbsolutePath().startsWith(
-                                    host.getConfigBaseFile().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;
             }
         }
@@ -1325,6 +1229,81 @@ 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(
+                        host.getAppBaseFile().getAbsolutePath() +
+                        File.separator))
+                        || (current.getAbsolutePath().startsWith(
+                                host.getConfigBaseFile().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(
+                            host.getAppBaseFile().getAbsolutePath() + 
File.separator))
+                        || ((current.getAbsolutePath().startsWith(
+                                host.getConfigBaseFile().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.
      */
@@ -1397,10 +1376,15 @@ public class HostConfig
                 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();
         }
-
     }
 
 
@@ -1417,6 +1401,52 @@ 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<>();
+        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.
      */

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=1382366&r1=1382365&r2=1382366&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties Sat 
Sep  8 21:00:32 2012
@@ -104,6 +104,7 @@ hostConfig.reload=Reloading context [{0}
 hostConfig.start=HostConfig: Processing START
 hostConfig.stop=HostConfig: Processing STOP
 hostConfig.undeploy=Undeploying context [{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/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml?rev=1382366&r1=1382365&r2=1382366&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml 
(original)
+++ tomcat/trunk/java/org/apache/catalina/startup/mbeans-descriptors.xml Sat 
Sep  8 21:00:32 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/trunk/webapps/docs/config/context.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/context.xml?rev=1382366&r1=1382365&r2=1382366&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/context.xml (original)
+++ tomcat/trunk/webapps/docs/config/context.xml Sat Sep  8 21:00:32 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/trunk/webapps/docs/config/host.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/host.xml?rev=1382366&r1=1382365&r2=1382366&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/host.xml (original)
+++ tomcat/trunk/webapps/docs/config/host.xml Sat Sep  8 21:00:32 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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to