Author: kkolinko
Date: Tue Jan  8 08:22:58 2013
New Revision: 1430165

URL: http://svn.apache.org/viewvc?rev=1430165&view=rev
Log:
Fix NPE in WebappLoader that happens when stop() is called after a failed start.
Add tests that a context can be started again after a failure.

Modified:
    tomcat/trunk/java/org/apache/catalina/loader/WebappLoader.java
    tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java

Modified: tomcat/trunk/java/org/apache/catalina/loader/WebappLoader.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/WebappLoader.java?rev=1430165&r1=1430164&r2=1430165&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/loader/WebappLoader.java (original)
+++ tomcat/trunk/java/org/apache/catalina/loader/WebappLoader.java Tue Jan  8 
08:22:58 2013
@@ -487,7 +487,9 @@ public class WebappLoader extends Lifecy
         servletContext.removeAttribute(Globals.CLASS_PATH_ATTR);
 
         // Throw away our current class loader
-        ((Lifecycle) classLoader).stop();
+        if (classLoader != null) {
+            ((Lifecycle) classLoader).stop();
+        }
 
         try {
             String contextName = context.getName();

Modified: tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java?rev=1430165&r1=1430164&r2=1430165&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java 
(original)
+++ tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java Tue Jan 
 8 08:22:58 2013
@@ -49,12 +49,17 @@ import static org.junit.Assert.fail;
 import org.junit.Test;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
 import org.apache.catalina.LifecycleState;
 import org.apache.catalina.Wrapper;
 import org.apache.catalina.authenticator.BasicAuthenticator;
 import org.apache.catalina.deploy.FilterDef;
 import org.apache.catalina.deploy.FilterMap;
 import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.loader.WebappLoader;
 import org.apache.catalina.startup.SimpleHttpClient;
 import org.apache.catalina.startup.TestTomcat.MapRealm;
 import org.apache.catalina.startup.Tomcat;
@@ -166,7 +171,7 @@ public class TestStandardContext extends
         public void init(FilterConfig filterConfig) throws ServletException {
             boolean fail = 
filterConfig.getInitParameter("fail").equals("true");
             if (fail) {
-                throw new ServletException("Init fail",
+                throw new ServletException("Init fail (test)",
                         new ClassNotFoundException());
             }
         }
@@ -174,6 +179,106 @@ public class TestStandardContext extends
     }
 
     @Test
+    public void testWebappLoaderStartFail() throws Exception {
+        // Test that if WebappLoader start() fails and if the cause of
+        // the failure is gone, the context can be started without
+        // a need to redeploy it.
+
+        // Set up a container
+        Tomcat tomcat = getTomcatInstance();
+        tomcat.start();
+        // To not start Context automatically, as we have to configure it first
+        ((ContainerBase) tomcat.getHost()).setStartChildren(false);
+
+        FailingWebappLoader loader = new FailingWebappLoader();
+        File root = new File("test/webapp-3.0");
+        Context context = tomcat.addWebapp("", root.getAbsolutePath());
+        context.setLoader(loader);
+
+        try {
+            context.start();
+            fail();
+        } catch (LifecycleException ex) {
+            // As expected
+        }
+        assertEquals(LifecycleState.FAILED, context.getState());
+
+        // The second attempt
+        loader.setFail(false);
+        context.start();
+        assertEquals(LifecycleState.STARTED, context.getState());
+
+        // Using a test from testBug49922() to check that the webapp is running
+        ByteChunk result = getUrl("http://localhost:"; + getPort() +
+                "/bug49922/target");
+        assertEquals("Target", result.toString());
+    }
+
+    @Test
+    public void testWebappListenerConfigureFail() throws Exception {
+        // Test that if LifecycleListener on webapp fails during
+        // configure_start event and if the cause of the failure is gone,
+        // the context can be started without a need to redeploy it.
+
+        // Set up a container
+        Tomcat tomcat = getTomcatInstance();
+        tomcat.start();
+        // To not start Context automatically, as we have to configure it first
+        ((ContainerBase) tomcat.getHost()).setStartChildren(false);
+
+        FailingLifecycleListener listener = new FailingLifecycleListener();
+        File root = new File("test/webapp-3.0");
+        Context context = tomcat.addWebapp("", root.getAbsolutePath());
+        context.addLifecycleListener(listener);
+
+        try {
+            context.start();
+            fail();
+        } catch (LifecycleException ex) {
+            // As expected
+        }
+        assertEquals(LifecycleState.FAILED, context.getState());
+
+        // The second attempt
+        listener.setFail(false);
+        context.start();
+        assertEquals(LifecycleState.STARTED, context.getState());
+
+        // Using a test from testBug49922() to check that the webapp is running
+        ByteChunk result = getUrl("http://localhost:"; + getPort() +
+                "/bug49922/target");
+        assertEquals("Target", result.toString());
+    }
+
+    private static class FailingWebappLoader extends WebappLoader {
+        private boolean fail = true;
+        protected void setFail(boolean fail) {
+            this.fail = fail;
+        }
+        @Override
+        protected void startInternal() throws LifecycleException {
+            if (fail) {
+                throw new RuntimeException("Start fail (test)");
+            }
+            super.startInternal();
+        }
+    }
+
+    private static class FailingLifecycleListener implements LifecycleListener 
{
+        private final String failEvent = Lifecycle.CONFIGURE_START_EVENT;
+        private boolean fail = true;
+        protected void setFail(boolean fail) {
+            this.fail = fail;
+        }
+        @Override
+        public void lifecycleEvent(LifecycleEvent event) {
+            if (fail && event.getType().equals(failEvent)) {
+                throw new RuntimeException(failEvent + " fail (test)");
+            }
+        }
+    }
+
+    @Test
     public void testBug49922() throws Exception {
         // Test that filter mapping works. Test that the same filter is
         // called only once, even if is selected by several mapping



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to