Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding exitOnFirstListenerFailure flag in standardcontext to enable to stop at first listener error #400

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions java/org/apache/catalina/core/StandardContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ public StandardContext() {

// ----------------------------------------------------- Instance Variables

/**
* Allow to fail as soon as the first context listener failed.
* Note that it does not impact stop phase since some listeners can just cleanup some state.
*/
protected boolean exitOnFirstListenerFailure = false;

/**
* Allow multipart/form-data requests to be parsed even when the
* target servlet doesn't specify @MultipartConfig or have a
Expand Down Expand Up @@ -4671,6 +4677,9 @@ public boolean listenerStart() {
getLogger().error(sm.getString("standardContext.listenerStart",
instance.getClass().getName()), t);
ok = false;
if (exitOnFirstListenerFailure) {
break;
}
}
}
return ok;
Expand Down Expand Up @@ -6359,6 +6368,21 @@ public long getStartTime() {
return startTime;
}

/**
* Toggle to enable to stop deployment on first listener and skip next listeners.
* @return true if the first context listener will stop the context deployment, false otherwise.
*/
public boolean isExitOnFirstListenerFailure() {
return exitOnFirstListenerFailure;
}

/**
* Enables to customize if the first context listener failure stops the deployment or not.
* @param value true to stop the deployment at first context listener error.
*/
public void setExitOnFirstListenerFailure(final boolean value) {
this.exitOnFirstListenerFailure = value;
}

private static class NoPluggabilityServletContext
implements ServletContext {
Expand Down
63 changes: 63 additions & 0 deletions test/org/apache/catalina/core/TestStandardContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

Expand All @@ -31,6 +33,8 @@
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletContainerInitializer;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRegistration;
import jakarta.servlet.ServletRequest;
Expand Down Expand Up @@ -72,6 +76,11 @@
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.util.descriptor.web.LoginConfig;

import static java.util.Arrays.asList;
import static java.util.Collections.emptySet;
import static java.util.Collections.singletonList;
import static org.junit.Assert.*;


public class TestStandardContext extends TomcatBaseTest {

Expand All @@ -81,6 +90,60 @@ public class TestStandardContext extends TomcatBaseTest {
"Connection: close\r\n" +
"\r\n";

@Test
public void testExitOnFirstListenerFailure() throws Exception {
final Collection<String> visited = new ArrayList<>();
final ServletContainerInitializer initializer = (c, ctx) -> {
ctx.addListener(new ServletContextListener() {
@Override
public void contextInitialized(final ServletContextEvent sce) {
visited.add("first:init");
throw new IllegalStateException("test failure");
}
});
ctx.addListener(new ServletContextListener() {
@Override
public void contextInitialized(final ServletContextEvent sce) {
visited.add("second:init");
}
});
};
{ // first ensure by default we go through all listeners
final Tomcat tomcat = getTomcatInstance();
final File docBase = new File(tomcat.getHost().getAppBaseFile(), "ROOT");
if (!docBase.mkdirs() && !docBase.isDirectory()) {
Assert.fail("Unable to create docBase");
}

final Context root = tomcat.addContext("", "ROOT");
root.addServletContainerInitializer(initializer, emptySet());
try {
tomcat.start();
assertEquals(LifecycleState.STOPPED, root.getState());
} finally {
tearDown();
}
assertEquals(asList("first:init", "second:init"), visited);
}
{ // now do the same but with the flag to stop ASAP
setUp();
visited.clear();
final Tomcat tomcat = getTomcatInstance();
final File docBase = new File(tomcat.getHost().getAppBaseFile(), "ROOT");
if (!docBase.mkdirs() && !docBase.isDirectory()) {
Assert.fail("Unable to create docBase");
}

final StandardContext root = (StandardContext)
tomcat.addContext("", "ROOT");
root.setExitOnFirstListenerFailure(true);
root.addServletContainerInitializer(initializer, emptySet());
tomcat.start();
assertEquals(LifecycleState.STOPPED, root.getState());
assertEquals(singletonList("first:init"), visited);
}
}

@Test
public void testBug46243() throws Exception {
// This tests that if a Filter init() fails then the web application
Expand Down