1 package com.atlassian.johnson.plugin.servlet.filter;
2
3 import com.atlassian.johnson.Johnson;
4 import com.atlassian.johnson.JohnsonEventContainer;
5 import com.atlassian.plugin.servlet.filter.ServletFilterModuleContainerFilter;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8
9 import javax.servlet.FilterChain;
10 import javax.servlet.ServletException;
11 import javax.servlet.ServletRequest;
12 import javax.servlet.ServletResponse;
13 import java.io.IOException;
14
15 /**
16 * Extends the plugin framework's {@code ServletFilterModuleContainerFilter} to bypass servlet-provided filters when
17 * Johnson {@link com.atlassian.johnson.event.Event events} are present.
18 * <p>
19 * When the system is Johnsoned, the exact set of resources that are available is unknown. In some cases, that can
20 * cause plugin-provided filters which attempt to access those resources to fail in an unexpected ways. That, in turn
21 * can block access to Johnson's error page, or even hang request threads. Replacing the standard filter with this
22 * Johnson-aware version ensures servlet-provided filters are automatically bypassed while Johnson events are present.
23 *
24 * @since 3.0
25 */
26 public class JohnsonServletFilterModuleContainerFilter extends ServletFilterModuleContainerFilter {
27
28 private static final Logger log = LoggerFactory.getLogger(JohnsonServletFilterModuleContainerFilter.class);
29
30 /**
31 * Checks whether plugin-provided filters should be {@link #bypassFilters() bypassed} and, if so, directly
32 * invokes the {@code FilterChain}; otherwise, processes {@code super.doFilter} normally.
33 *
34 * @param request the request
35 * @param response the response
36 * @param chain the remaining filter chain
37 * @throws IOException if thrown by the base class, or the {@code FilterChain}
38 * @throws ServletException if thrown by the base class, or the {@code FilterChain}
39 */
40 @Override
41 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
42 throws IOException, ServletException {
43 if (bypassFilters()) {
44 //If there are Johnson events, bypass plugin filters. This ensures those filters don't attempt
45 //to interact with system resources which are not ready
46 log.debug("{}: Bypassing plugin-provided filters; the system is locked", getFilterLocation());
47 chain.doFilter(request, response);
48 } else {
49 //Otherwise, if there are no events, dispatch through servlet filters if they are available
50 super.doFilter(request, response, chain);
51 }
52 }
53
54 /**
55 * Determines whether plugin-provided filters should be bypassed. The default implementation bypasses plugin-
56 * provided filters whenever there are events in the {@link #getEventContainer() container}.
57 * <p>
58 * Applications can override this method in subclasses to apply a more specific set of restrictions. For example,
59 * they could choose to allow plugin-provided filters to be applied in specific Johnson states, or based on other
60 * knowledge of the application as a whole.
61 *
62 * @return {@code true} if plugin-provided filters should be bypassed; otherwise, {@code false} to apply
63 * plugin-provided filters normally
64 */
65 protected boolean bypassFilters() {
66 return getEventContainer().hasEvents();
67 }
68
69 /**
70 * Retrieves the {@link JohnsonEventContainer} using the {@code FilterConfig}'s {@code ServletContext}.
71 *
72 * @return the {@link JohnsonEventContainer}
73 */
74 protected JohnsonEventContainer getEventContainer() {
75 return Johnson.getEventContainer(getFilterConfig().getServletContext());
76 }
77 }