diff --git a/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java b/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java index 725dc9b197..ae75fbe4e8 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java @@ -68,7 +68,7 @@ public abstract class WebApplicationContextUtils { /** - * Find the root {@link WebApplicationContext} for this web app, typically + * Find the root {@code WebApplicationContext} for this web app, typically * loaded via {@link org.springframework.web.context.ContextLoaderListener}. *

Will rethrow an exception that happened on root context startup, * to differentiate between a failed context startup and no context at all. @@ -86,7 +86,7 @@ public abstract class WebApplicationContextUtils { } /** - * Find the root {@link WebApplicationContext} for this web app, typically + * Find the root {@code WebApplicationContext} for this web app, typically * loaded via {@link org.springframework.web.context.ContextLoaderListener}. *

Will rethrow an exception that happened on root context startup, * to differentiate between a failed context startup and no context at all. @@ -99,7 +99,7 @@ public abstract class WebApplicationContextUtils { } /** - * Find a custom {@link WebApplicationContext} for this web app. + * Find a custom {@code WebApplicationContext} for this web app. * @param sc ServletContext to find the web application context for * @param attrName the name of the ServletContext attribute to look for * @return the desired WebApplicationContext for this web app, or {@code null} if none @@ -125,6 +125,40 @@ public abstract class WebApplicationContextUtils { return (WebApplicationContext) attr; } + /** + * Find a unique {@code WebApplicationContext} for this web app: either the + * root web app context (preferred) or a unique {@code WebApplicationContext} + * among the registered {@code ServletContext} attributes (typically coming + * from a single {@code DispatcherServlet} in the current web application). + *

Note that {@code DispatcherServlet}'s exposure of its context can be + * controlled through its {@code publishContext} property, which is {@code true} + * by default but can be selectively switched to only publish a single context + * despite multiple {@code DispatcherServlet} registrations in the web app. + * @param sc ServletContext to find the web application context for + * @return the desired WebApplicationContext for this web app, or {@code null} if none + * @since 4.2 + * @see #getWebApplicationContext(ServletContext) + * @see ServletContext#getAttributeNames() + */ + public static WebApplicationContext findWebApplicationContext(ServletContext sc) { + WebApplicationContext wac = getWebApplicationContext(sc); + if (wac == null) { + Enumeration attrNames = sc.getAttributeNames(); + while (attrNames.hasMoreElements()) { + String attrName = attrNames.nextElement(); + Object attrValue = sc.getAttribute(attrName); + if (attrValue instanceof WebApplicationContext) { + if (wac != null) { + throw new IllegalStateException("No unique WebApplicationContext found: more than one " + + "DispatcherServlet registered with publishContext=true?"); + } + wac = (WebApplicationContext) attrValue; + } + } + } + return wac; + } + /** * Register web-specific scopes ("request", "session", "globalSession") diff --git a/spring-web/src/main/java/org/springframework/web/filter/DelegatingFilterProxy.java b/spring-web/src/main/java/org/springframework/web/filter/DelegatingFilterProxy.java index 47899bef01..ba5bb5813b 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/DelegatingFilterProxy.java +++ b/spring-web/src/main/java/org/springframework/web/filter/DelegatingFilterProxy.java @@ -249,7 +249,8 @@ public class DelegatingFilterProxy extends GenericFilterBean { if (this.delegate == null) { WebApplicationContext wac = findWebApplicationContext(); if (wac == null) { - throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?"); + throw new IllegalStateException("No WebApplicationContext found: " + + "no ContextLoaderListener or DispatcherServlet registered?"); } this.delegate = initDelegate(wac); } @@ -288,11 +289,12 @@ public class DelegatingFilterProxy extends GenericFilterBean { */ protected WebApplicationContext findWebApplicationContext() { if (this.webApplicationContext != null) { - // the user has injected a context at construction time -> use it + // The user has injected a context at construction time -> use it... if (this.webApplicationContext instanceof ConfigurableApplicationContext) { - if (!((ConfigurableApplicationContext)this.webApplicationContext).isActive()) { - // the context has not yet been refreshed -> do so before returning it - ((ConfigurableApplicationContext)this.webApplicationContext).refresh(); + ConfigurableApplicationContext cac = (ConfigurableApplicationContext) this.webApplicationContext; + if (!cac.isActive()) { + // The context has not yet been refreshed -> do so before returning it... + cac.refresh(); } } return this.webApplicationContext; @@ -302,7 +304,7 @@ public class DelegatingFilterProxy extends GenericFilterBean { return WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); } else { - return WebApplicationContextUtils.getWebApplicationContext(getServletContext()); + return WebApplicationContextUtils.findWebApplicationContext(getServletContext()); } } diff --git a/spring-web/src/test/java/org/springframework/web/filter/CompositeFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/CompositeFilterTests.java index 75a2eae17e..fc1d3e5f08 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/CompositeFilterTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/CompositeFilterTests.java @@ -1,6 +1,5 @@ /* - * Copyright 2004, 2005 Acegi Technology Pty Limited - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,9 +43,7 @@ public class CompositeFilterTests { @Test public void testCompositeFilter() throws ServletException, IOException { ServletContext sc = new MockServletContext(); - MockFilter targetFilter = new MockFilter(); - MockFilterConfig proxyConfig = new MockFilterConfig(sc); CompositeFilter filterProxy = new CompositeFilter(); @@ -75,7 +72,7 @@ public class CompositeFilterTests { } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) { request.setAttribute("called", Boolean.TRUE); } diff --git a/spring-web/src/test/java/org/springframework/web/filter/DelegatingFilterProxyTests.java b/spring-web/src/test/java/org/springframework/web/filter/DelegatingFilterProxyTests.java index e68581d07b..8dc6724132 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/DelegatingFilterProxyTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/DelegatingFilterProxyTests.java @@ -1,6 +1,5 @@ /* - * Copyright 2004, 2005 Acegi Technology Pty Limited - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +39,7 @@ import static org.junit.Assert.*; /** * @author Juergen Hoeller * @author Chris Beams + * @author Rob Winch * @since 08.05.2005 */ public class DelegatingFilterProxyTests { @@ -268,6 +268,128 @@ public class DelegatingFilterProxyTests { assertNull(targetFilter.filterConfig); } + @Test + public void testDelegatingFilterProxyWithFrameworkServletContext() throws ServletException, IOException { + ServletContext sc = new MockServletContext(); + StaticWebApplicationContext wac = new StaticWebApplicationContext(); + wac.setServletContext(sc); + wac.registerSingleton("targetFilter", MockFilter.class); + wac.refresh(); + sc.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac); + + MockFilter targetFilter = (MockFilter) wac.getBean("targetFilter"); + + MockFilterConfig proxyConfig = new MockFilterConfig(sc); + proxyConfig.addInitParameter("targetBeanName", "targetFilter"); + DelegatingFilterProxy filterProxy = new DelegatingFilterProxy(); + filterProxy.init(proxyConfig); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + filterProxy.doFilter(request, response, null); + + assertNull(targetFilter.filterConfig); + assertEquals(Boolean.TRUE, request.getAttribute("called")); + + filterProxy.destroy(); + assertNull(targetFilter.filterConfig); + } + + @Test + public void testDelegatingFilterProxyInjectedPreferred() throws ServletException, IOException { + ServletContext sc = new MockServletContext(); + StaticWebApplicationContext wac = new StaticWebApplicationContext(); + wac.setServletContext(sc); + wac.refresh(); + sc.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac); + + StaticWebApplicationContext injectedWac = new StaticWebApplicationContext(); + injectedWac.setServletContext(sc); + String beanName = "targetFilter"; + injectedWac.registerSingleton(beanName, MockFilter.class); + injectedWac.refresh(); + + MockFilter targetFilter = (MockFilter) injectedWac.getBean(beanName); + + DelegatingFilterProxy filterProxy = new DelegatingFilterProxy(beanName, injectedWac); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + filterProxy.doFilter(request, response, null); + + assertNull(targetFilter.filterConfig); + assertEquals(Boolean.TRUE, request.getAttribute("called")); + + filterProxy.destroy(); + assertNull(targetFilter.filterConfig); + } + + @Test + public void testDelegatingFilterProxyNotInjectedWacServletAttrPreferred() throws ServletException, IOException { + ServletContext sc = new MockServletContext(); + StaticWebApplicationContext wac = new StaticWebApplicationContext(); + wac.setServletContext(sc); + wac.refresh(); + sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac); + sc.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac); + + StaticWebApplicationContext wacToUse = new StaticWebApplicationContext(); + wacToUse.setServletContext(sc); + String beanName = "targetFilter"; + String attrName = "customAttrName"; + wacToUse.registerSingleton(beanName, MockFilter.class); + wacToUse.refresh(); + sc.setAttribute(attrName, wacToUse); + + MockFilter targetFilter = (MockFilter) wacToUse.getBean(beanName); + + DelegatingFilterProxy filterProxy = new DelegatingFilterProxy(beanName); + filterProxy.setContextAttribute(attrName); + filterProxy.setServletContext(sc); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + filterProxy.doFilter(request, response, null); + + assertNull(targetFilter.filterConfig); + assertEquals(Boolean.TRUE, request.getAttribute("called")); + + filterProxy.destroy(); + assertNull(targetFilter.filterConfig); + } + + @Test + public void testDelegatingFilterProxyNotInjectedWithRootPreferred() throws ServletException, IOException { + ServletContext sc = new MockServletContext(); + StaticWebApplicationContext wac = new StaticWebApplicationContext(); + wac.setServletContext(sc); + wac.refresh(); + sc.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac); + sc.setAttribute("another", wac); + + StaticWebApplicationContext wacToUse = new StaticWebApplicationContext(); + wacToUse.setServletContext(sc); + String beanName = "targetFilter"; + wacToUse.registerSingleton(beanName, MockFilter.class); + wacToUse.refresh(); + sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wacToUse); + + MockFilter targetFilter = (MockFilter) wacToUse.getBean(beanName); + + DelegatingFilterProxy filterProxy = new DelegatingFilterProxy(beanName); + filterProxy.setServletContext(sc); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + filterProxy.doFilter(request, response, null); + + assertNull(targetFilter.filterConfig); + assertEquals(Boolean.TRUE, request.getAttribute("called")); + + filterProxy.destroy(); + assertNull(targetFilter.filterConfig); + } + public static class MockFilter implements Filter {