1 package com.atlassian.johnson.spring.web.filter; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.web.context.WebApplicationContext; 6 import org.springframework.web.filter.DelegatingFilterProxy; 7 8 import javax.servlet.*; 9 import java.io.IOException; 10 11 /** 12 * A more permissive subclass of Spring's {@code DelegatingFilterProxy} which bypasses the filter if there is no 13 * Spring {@code WebApplicationContext} available. 14 * <p/> 15 * The default behaviour of the base class is to throw an {@code IllegalStateException} if, by the time the filter 16 * is invoked for the first time, no context is available. This subclass bypasses that behaviour and invokes the 17 * filter chain instead. This is necessary to allow redirects to the configured Johnson error page to make it through 18 * a filter chain which includes delegating filters. 19 * <p/> 20 * Note: This class preserves the behaviour from the base class where the proxy will fail if it cannot find a bean 21 * with the correct name. It only suppresses failing if Spring is completely unavailable. 22 */ 23 public class BypassableDelegatingFilterProxy extends DelegatingFilterProxy 24 { 25 private static final Logger LOG = LoggerFactory.getLogger(BypassableDelegatingFilterProxy.class); 26 27 private volatile boolean bypassable = true; 28 29 /** 30 * Bypasses execution of the filter if no {@code WebApplicationContext} is available, delegating directly to the 31 * filter chain, or performs normal filtering if a context is available. 32 * <p/> 33 * Once a {@code WebApplicationContext} is available once, this filter will never bypass again (even if, for some 34 * reason, the context later disappears). 35 * 36 * @param request the servlet request to filter 37 * @param response the servlet response 38 * @param filterChain the filter chain 39 * @throws ServletException May be thrown by the base class or the chain; never thrown locally. 40 * @throws IOException May be thrown by the base class or the chain; never thrown locally. 41 */ 42 @Override 43 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 44 throws ServletException, IOException 45 { 46 if (bypassable && findWebApplicationContext() == null) 47 { 48 LOG.warn("Bypassing [{}]; no Spring WebApplicationContext is available", getFilterName()); 49 filterChain.doFilter(request, response); 50 } 51 else 52 { 53 LOG.trace("Found Spring WebApplicationContext; attempting to invoke delegate"); 54 super.doFilter(request, response, filterChain); 55 } 56 } 57 58 /** 59 * As an optimisation to this approach, once the delegate is initialised we set a flag indicating the filter is 60 * no longer bypassable. That way we don't continue to resolve the {@code WebApplicationContext} unnecessarily 61 * on every request. 62 * 63 * @param wac the resolved {@code WebApplicationContext} 64 * @return the targeted filter 65 * @throws ServletException See documentation for the base class. 66 * @see DelegatingFilterProxy#initDelegate(org.springframework.web.context.WebApplicationContext) 67 */ 68 @Override 69 protected Filter initDelegate(WebApplicationContext wac) throws ServletException 70 { 71 LOG.debug("Filter [{}] is no longer bypassable; the WebApplicationContext is available", getFilterName()); 72 bypassable = false; 73 74 return super.initDelegate(wac); 75 } 76 }