Skip to content

Commit 1fcd465

Browse files
committed
DelegatingFilterProxy autodetects a unique DispatcherServlet context
Issue: SPR-13191
1 parent e032930 commit 1fcd465

File tree

4 files changed

+171
-16
lines changed

4 files changed

+171
-16
lines changed

spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public abstract class WebApplicationContextUtils {
6868

6969

7070
/**
71-
* Find the root {@link WebApplicationContext} for this web app, typically
71+
* Find the root {@code WebApplicationContext} for this web app, typically
7272
* loaded via {@link org.springframework.web.context.ContextLoaderListener}.
7373
* <p>Will rethrow an exception that happened on root context startup,
7474
* to differentiate between a failed context startup and no context at all.
@@ -86,7 +86,7 @@ public static WebApplicationContext getRequiredWebApplicationContext(ServletCont
8686
}
8787

8888
/**
89-
* Find the root {@link WebApplicationContext} for this web app, typically
89+
* Find the root {@code WebApplicationContext} for this web app, typically
9090
* loaded via {@link org.springframework.web.context.ContextLoaderListener}.
9191
* <p>Will rethrow an exception that happened on root context startup,
9292
* to differentiate between a failed context startup and no context at all.
@@ -99,7 +99,7 @@ public static WebApplicationContext getWebApplicationContext(ServletContext sc)
9999
}
100100

101101
/**
102-
* Find a custom {@link WebApplicationContext} for this web app.
102+
* Find a custom {@code WebApplicationContext} for this web app.
103103
* @param sc ServletContext to find the web application context for
104104
* @param attrName the name of the ServletContext attribute to look for
105105
* @return the desired WebApplicationContext for this web app, or {@code null} if none
@@ -125,6 +125,40 @@ public static WebApplicationContext getWebApplicationContext(ServletContext sc,
125125
return (WebApplicationContext) attr;
126126
}
127127

128+
/**
129+
* Find a unique {@code WebApplicationContext} for this web app: either the
130+
* root web app context (preferred) or a unique {@code WebApplicationContext}
131+
* among the registered {@code ServletContext} attributes (typically coming
132+
* from a single {@code DispatcherServlet} in the current web application).
133+
* <p>Note that {@code DispatcherServlet}'s exposure of its context can be
134+
* controlled through its {@code publishContext} property, which is {@code true}
135+
* by default but can be selectively switched to only publish a single context
136+
* despite multiple {@code DispatcherServlet} registrations in the web app.
137+
* @param sc ServletContext to find the web application context for
138+
* @return the desired WebApplicationContext for this web app, or {@code null} if none
139+
* @since 4.2
140+
* @see #getWebApplicationContext(ServletContext)
141+
* @see ServletContext#getAttributeNames()
142+
*/
143+
public static WebApplicationContext findWebApplicationContext(ServletContext sc) {
144+
WebApplicationContext wac = getWebApplicationContext(sc);
145+
if (wac == null) {
146+
Enumeration<String> attrNames = sc.getAttributeNames();
147+
while (attrNames.hasMoreElements()) {
148+
String attrName = attrNames.nextElement();
149+
Object attrValue = sc.getAttribute(attrName);
150+
if (attrValue instanceof WebApplicationContext) {
151+
if (wac != null) {
152+
throw new IllegalStateException("No unique WebApplicationContext found: more than one " +
153+
"DispatcherServlet registered with publishContext=true?");
154+
}
155+
wac = (WebApplicationContext) attrValue;
156+
}
157+
}
158+
}
159+
return wac;
160+
}
161+
128162

129163
/**
130164
* Register web-specific scopes ("request", "session", "globalSession")

spring-web/src/main/java/org/springframework/web/filter/DelegatingFilterProxy.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,8 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
249249
if (this.delegate == null) {
250250
WebApplicationContext wac = findWebApplicationContext();
251251
if (wac == null) {
252-
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
252+
throw new IllegalStateException("No WebApplicationContext found: " +
253+
"no ContextLoaderListener or DispatcherServlet registered?");
253254
}
254255
this.delegate = initDelegate(wac);
255256
}
@@ -288,11 +289,12 @@ public void destroy() {
288289
*/
289290
protected WebApplicationContext findWebApplicationContext() {
290291
if (this.webApplicationContext != null) {
291-
// the user has injected a context at construction time -> use it
292+
// The user has injected a context at construction time -> use it...
292293
if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
293-
if (!((ConfigurableApplicationContext)this.webApplicationContext).isActive()) {
294-
// the context has not yet been refreshed -> do so before returning it
295-
((ConfigurableApplicationContext)this.webApplicationContext).refresh();
294+
ConfigurableApplicationContext cac = (ConfigurableApplicationContext) this.webApplicationContext;
295+
if (!cac.isActive()) {
296+
// The context has not yet been refreshed -> do so before returning it...
297+
cac.refresh();
296298
}
297299
}
298300
return this.webApplicationContext;
@@ -302,7 +304,7 @@ protected WebApplicationContext findWebApplicationContext() {
302304
return WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
303305
}
304306
else {
305-
return WebApplicationContextUtils.getWebApplicationContext(getServletContext());
307+
return WebApplicationContextUtils.findWebApplicationContext(getServletContext());
306308
}
307309
}
308310

spring-web/src/test/java/org/springframework/web/filter/CompositeFilterTests.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/*
2-
* Copyright 2004, 2005 Acegi Technology Pty Limited
3-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
43
*
54
* Licensed under the Apache License, Version 2.0 (the "License");
65
* you may not use this file except in compliance with the License.
@@ -44,9 +43,7 @@ public class CompositeFilterTests {
4443
@Test
4544
public void testCompositeFilter() throws ServletException, IOException {
4645
ServletContext sc = new MockServletContext();
47-
4846
MockFilter targetFilter = new MockFilter();
49-
5047
MockFilterConfig proxyConfig = new MockFilterConfig(sc);
5148

5249
CompositeFilter filterProxy = new CompositeFilter();
@@ -75,7 +72,7 @@ public void init(FilterConfig filterConfig) throws ServletException {
7572
}
7673

7774
@Override
78-
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
75+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) {
7976
request.setAttribute("called", Boolean.TRUE);
8077
}
8178

spring-web/src/test/java/org/springframework/web/filter/DelegatingFilterProxyTests.java

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/*
2-
* Copyright 2004, 2005 Acegi Technology Pty Limited
3-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
43
*
54
* Licensed under the Apache License, Version 2.0 (the "License");
65
* you may not use this file except in compliance with the License.
@@ -40,6 +39,7 @@
4039
/**
4140
* @author Juergen Hoeller
4241
* @author Chris Beams
42+
* @author Rob Winch
4343
* @since 08.05.2005
4444
*/
4545
public class DelegatingFilterProxyTests {
@@ -268,6 +268,128 @@ public void testDelegatingFilterProxyWithTargetFilterLifecycle() throws ServletE
268268
assertNull(targetFilter.filterConfig);
269269
}
270270

271+
@Test
272+
public void testDelegatingFilterProxyWithFrameworkServletContext() throws ServletException, IOException {
273+
ServletContext sc = new MockServletContext();
274+
StaticWebApplicationContext wac = new StaticWebApplicationContext();
275+
wac.setServletContext(sc);
276+
wac.registerSingleton("targetFilter", MockFilter.class);
277+
wac.refresh();
278+
sc.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac);
279+
280+
MockFilter targetFilter = (MockFilter) wac.getBean("targetFilter");
281+
282+
MockFilterConfig proxyConfig = new MockFilterConfig(sc);
283+
proxyConfig.addInitParameter("targetBeanName", "targetFilter");
284+
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
285+
filterProxy.init(proxyConfig);
286+
287+
MockHttpServletRequest request = new MockHttpServletRequest();
288+
MockHttpServletResponse response = new MockHttpServletResponse();
289+
filterProxy.doFilter(request, response, null);
290+
291+
assertNull(targetFilter.filterConfig);
292+
assertEquals(Boolean.TRUE, request.getAttribute("called"));
293+
294+
filterProxy.destroy();
295+
assertNull(targetFilter.filterConfig);
296+
}
297+
298+
@Test
299+
public void testDelegatingFilterProxyInjectedPreferred() throws ServletException, IOException {
300+
ServletContext sc = new MockServletContext();
301+
StaticWebApplicationContext wac = new StaticWebApplicationContext();
302+
wac.setServletContext(sc);
303+
wac.refresh();
304+
sc.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac);
305+
306+
StaticWebApplicationContext injectedWac = new StaticWebApplicationContext();
307+
injectedWac.setServletContext(sc);
308+
String beanName = "targetFilter";
309+
injectedWac.registerSingleton(beanName, MockFilter.class);
310+
injectedWac.refresh();
311+
312+
MockFilter targetFilter = (MockFilter) injectedWac.getBean(beanName);
313+
314+
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy(beanName, injectedWac);
315+
316+
MockHttpServletRequest request = new MockHttpServletRequest();
317+
MockHttpServletResponse response = new MockHttpServletResponse();
318+
filterProxy.doFilter(request, response, null);
319+
320+
assertNull(targetFilter.filterConfig);
321+
assertEquals(Boolean.TRUE, request.getAttribute("called"));
322+
323+
filterProxy.destroy();
324+
assertNull(targetFilter.filterConfig);
325+
}
326+
327+
@Test
328+
public void testDelegatingFilterProxyNotInjectedWacServletAttrPreferred() throws ServletException, IOException {
329+
ServletContext sc = new MockServletContext();
330+
StaticWebApplicationContext wac = new StaticWebApplicationContext();
331+
wac.setServletContext(sc);
332+
wac.refresh();
333+
sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
334+
sc.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac);
335+
336+
StaticWebApplicationContext wacToUse = new StaticWebApplicationContext();
337+
wacToUse.setServletContext(sc);
338+
String beanName = "targetFilter";
339+
String attrName = "customAttrName";
340+
wacToUse.registerSingleton(beanName, MockFilter.class);
341+
wacToUse.refresh();
342+
sc.setAttribute(attrName, wacToUse);
343+
344+
MockFilter targetFilter = (MockFilter) wacToUse.getBean(beanName);
345+
346+
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy(beanName);
347+
filterProxy.setContextAttribute(attrName);
348+
filterProxy.setServletContext(sc);
349+
350+
MockHttpServletRequest request = new MockHttpServletRequest();
351+
MockHttpServletResponse response = new MockHttpServletResponse();
352+
filterProxy.doFilter(request, response, null);
353+
354+
assertNull(targetFilter.filterConfig);
355+
assertEquals(Boolean.TRUE, request.getAttribute("called"));
356+
357+
filterProxy.destroy();
358+
assertNull(targetFilter.filterConfig);
359+
}
360+
361+
@Test
362+
public void testDelegatingFilterProxyNotInjectedWithRootPreferred() throws ServletException, IOException {
363+
ServletContext sc = new MockServletContext();
364+
StaticWebApplicationContext wac = new StaticWebApplicationContext();
365+
wac.setServletContext(sc);
366+
wac.refresh();
367+
sc.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac);
368+
sc.setAttribute("another", wac);
369+
370+
StaticWebApplicationContext wacToUse = new StaticWebApplicationContext();
371+
wacToUse.setServletContext(sc);
372+
String beanName = "targetFilter";
373+
wacToUse.registerSingleton(beanName, MockFilter.class);
374+
wacToUse.refresh();
375+
sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wacToUse);
376+
377+
MockFilter targetFilter = (MockFilter) wacToUse.getBean(beanName);
378+
379+
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy(beanName);
380+
filterProxy.setServletContext(sc);
381+
382+
MockHttpServletRequest request = new MockHttpServletRequest();
383+
MockHttpServletResponse response = new MockHttpServletResponse();
384+
filterProxy.doFilter(request, response, null);
385+
386+
assertNull(targetFilter.filterConfig);
387+
assertEquals(Boolean.TRUE, request.getAttribute("called"));
388+
389+
filterProxy.destroy();
390+
assertNull(targetFilter.filterConfig);
391+
}
392+
271393

272394
public static class MockFilter implements Filter {
273395

0 commit comments

Comments
 (0)