https://issues.apache.org/bugzilla/show_bug.cgi?id=50168
authormarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Thu, 4 Nov 2010 23:03:26 +0000 (23:03 +0000)
committermarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Thu, 4 Nov 2010 23:03:26 +0000 (23:03 +0000)
Add a DESTROYING state and associated events and use them to ensure Contexts are only destroyed once.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1031335 13f79535-47bb-0310-9956-ffa450edef68

java/org/apache/catalina/Lifecycle.java
java/org/apache/catalina/LifecycleState.java
java/org/apache/catalina/core/AprLifecycleListener.java
java/org/apache/catalina/core/ContainerBase.java
java/org/apache/catalina/startup/ContextConfig.java
java/org/apache/catalina/util/LifecycleBase.java
webapps/docs/changelog.xml

index fe432e0..5b540d3 100644 (file)
@@ -50,10 +50,11 @@ package org.apache.catalina;
  * |  |          |                    |                         |  |
  * |  |          |                    |auto                     |  |
  * |  |          |    destroy()      \|/              destroy() |  |
- * |  |       FAILED ---->------ DESTROYED ----<-----------------  |
- * |  |                             ^                              |
- * |  |        destroy()            |                              |
- * |  -------------------------------                              |
+ * |  |       FAILED ---->------ DESTROYING ---<-----------------  |
+ * |  |                           ^     |                          |
+ * |  |        destroy()          |     |auto                      |
+ * |  -----------------------------    \|/                         |
+ * |                                 DESTROYED                     |
  * |                                                               |
  * |                            stop()                             |
  * --->------------------------------>------------------------------
@@ -149,9 +150,15 @@ public interface Lifecycle {
 
 
     /**
-     * The LifecycleEvent type for the "component destroy" event.
+     * The LifecycleEvent type for the "component after destroy" event.
      */
-    public static final String DESTROY_EVENT = "destroy";
+    public static final String AFTER_DESTROY_EVENT = "after_destroy";
+
+
+    /**
+     * The LifecycleEvent type for the "component before destroy" event.
+     */
+    public static final String BEFORE_DESTROY_EVENT = "before_destroy";
 
 
     /**
index 5f2e3b7..3ddd48d 100644 (file)
@@ -31,7 +31,8 @@ public enum LifecycleState {
     STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
     STOPPING(false, Lifecycle.STOP_EVENT),
     STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
-    DESTROYED(false, Lifecycle.DESTROY_EVENT),
+    DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
+    DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
     FAILED(false, null),
     MUST_STOP(true, null),
     MUST_DESTROY(true, null);
index 5151ad3..401b411 100644 (file)
@@ -110,7 +110,7 @@ public class AprLifecycleListener
                     }
                 }
             }
-        } else if (Lifecycle.DESTROY_EVENT.equals(event.getType())) {
+        } else if (Lifecycle.AFTER_DESTROY_EVENT.equals(event.getType())) {
             synchronized (lock) {
                 if (!aprAvailable) {
                     return;
index 04206b3..2b6c78b 100644 (file)
@@ -959,7 +959,12 @@ public abstract class ContainerBase extends LifecycleMBeanBase
         // Set child's parent to null to prevent a loop
         child.setParent(null);
         try {
-            child.destroy();
+            // child.destroy() may have already been called which would have
+            // triggered this call. If that is the case, no need to destroy the
+            // child again.
+            if (!LifecycleState.DESTROYING.equals(child.getState())) {
+                child.destroy();
+            }
         } catch (LifecycleException e) {
             log.error("ContainerBase.removeChild: destroy: ", e);
         }
index 32d3751..a8237ea 100644 (file)
@@ -332,7 +332,7 @@ public class ContextConfig
             configureStop();
         } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
             init();
-        } else if (event.getType().equals(Lifecycle.DESTROY_EVENT)) {
+        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
             destroy();
         }
 
index b1850c6..90502cf 100644 (file)
@@ -248,7 +248,8 @@ public abstract class LifecycleBase implements Lifecycle {
 
     @Override
     public synchronized final void destroy() throws LifecycleException {
-        if (LifecycleState.DESTROYED.equals(state)) {
+        if (LifecycleState.DESTROYING.equals(state) ||
+                LifecycleState.DESTROYED.equals(state)) {
 
             if (log.isDebugEnabled()) {
                 Exception e = new LifecycleException();
@@ -265,9 +266,11 @@ public abstract class LifecycleBase implements Lifecycle {
         if (!state.equals(LifecycleState.STOPPED) &&
                 !state.equals(LifecycleState.FAILED) &&
                 !state.equals(LifecycleState.NEW)) {
-            invalidTransition(Lifecycle.DESTROY_EVENT);
+            invalidTransition(Lifecycle.BEFORE_DESTROY_EVENT);
         }
 
+        setState(LifecycleState.DESTROYING);
+        
         destroyInternal();
         
         setState(LifecycleState.DESTROYED);
index a64c7ed..29d52be 100644 (file)
         resource. The default value is <code>true</code>, which will return the
         same instance of the resource in every JNDI lookup. (markt)
       </fix>
+      <fix>
+        <bug>50168</bug>: Separate the <code>Lifecycle.DESTROY_EVENT</code> into
+        <code>Lifecycle.BEFORE_DESTROY_EVENT</code> and
+        <code>Lifecycle.AFTER_DESTROY_EVENT</code>. Use the additional state to
+        ensure that <code>Context</code> objects are only destroyed once.
+        (markt)
+      </fix>
       <add>
         Improve debug logging for MapperListener registration. (markt)
       </add>