Skip to content

Commit 37dc211

Browse files
committed
Support named dispatchers in MockServletContext
Currently the getNamedDispatcher(String) method of MockServletContext always returns null. This poses a problem in certain testing scenarios since one would always expect at least a default Servlet to be present. This is specifically important for web application tests that involve the DefaultServletHttpRequestHandler which attempts to forward to the default Servlet after retrieving it by name. Furthermore, there is no way to register a named RequestDispatcher with the MockServletContext. This commit addresses these issues by introducing the following in MockServletContext. - a new defaultServletName property for configuring the name of the default Servlet, which defaults to "default" - named RequestDispatchers can be registered and unregistered - a MockRequestDispatcher is registered for the "default" Servlet automatically in the constructor - when the defaultServletName property is set to a new value the the current default RequestDispatcher is unregistered and replaced with a MockRequestDispatcher for the new defaultServletName Issue: SPR-9587
1 parent 914557b commit 37dc211

File tree

16 files changed

+1678
-304
lines changed

16 files changed

+1678
-304
lines changed

spring-orm/src/test/java/org/springframework/mock/web/MockRequestDispatcher.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2010 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,22 +34,24 @@
3434
*
3535
* @author Rod Johnson
3636
* @author Juergen Hoeller
37+
* @author Sam Brannen
3738
* @since 1.0.2
3839
*/
3940
public class MockRequestDispatcher implements RequestDispatcher {
4041

4142
private final Log logger = LogFactory.getLog(getClass());
4243

43-
private final String url;
44+
private final String resource;
4445

4546

4647
/**
47-
* Create a new MockRequestDispatcher for the given URL.
48-
* @param url the URL to dispatch to.
48+
* Create a new MockRequestDispatcher for the given resource.
49+
* @param resource the server resource to dispatch to, located at a
50+
* particular path or given by a particular name
4951
*/
50-
public MockRequestDispatcher(String url) {
51-
Assert.notNull(url, "URL must not be null");
52-
this.url = url;
52+
public MockRequestDispatcher(String resource) {
53+
Assert.notNull(resource, "resource must not be null");
54+
this.resource = resource;
5355
}
5456

5557

@@ -59,24 +61,24 @@ public void forward(ServletRequest request, ServletResponse response) {
5961
if (response.isCommitted()) {
6062
throw new IllegalStateException("Cannot perform forward - response is already committed");
6163
}
62-
getMockHttpServletResponse(response).setForwardedUrl(this.url);
64+
getMockHttpServletResponse(response).setForwardedUrl(this.resource);
6365
if (logger.isDebugEnabled()) {
64-
logger.debug("MockRequestDispatcher: forwarding to URL [" + this.url + "]");
66+
logger.debug("MockRequestDispatcher: forwarding to [" + this.resource + "]");
6567
}
6668
}
6769

6870
public void include(ServletRequest request, ServletResponse response) {
6971
Assert.notNull(request, "Request must not be null");
7072
Assert.notNull(response, "Response must not be null");
71-
getMockHttpServletResponse(response).addIncludedUrl(this.url);
73+
getMockHttpServletResponse(response).addIncludedUrl(this.resource);
7274
if (logger.isDebugEnabled()) {
73-
logger.debug("MockRequestDispatcher: including URL [" + this.url + "]");
75+
logger.debug("MockRequestDispatcher: including [" + this.resource + "]");
7476
}
7577
}
7678

7779
/**
78-
* Obtain the underlying MockHttpServletResponse,
79-
* unwrapping {@link javax.servlet.http.HttpServletResponseWrapper} decorators if necessary.
80+
* Obtain the underlying {@link MockHttpServletResponse}, unwrapping
81+
* {@link HttpServletResponseWrapper} decorators if necessary.
8082
*/
8183
protected MockHttpServletResponse getMockHttpServletResponse(ServletResponse response) {
8284
if (response instanceof MockHttpServletResponse) {

spring-orm/src/test/java/org/springframework/mock/web/MockServletContext.java

Lines changed: 95 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2011 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -58,40 +58,57 @@
5858
*
5959
* <p>Used for testing the Spring web framework; only rarely necessary for testing
6060
* application controllers. As long as application components don't explicitly
61-
* access the ServletContext, ClassPathXmlApplicationContext or
62-
* FileSystemXmlApplicationContext can be used to load the context files for testing,
63-
* even for DispatcherServlet context definitions.
61+
* access the {@code ServletContext}, {@code ClassPathXmlApplicationContext} or
62+
* {@code FileSystemXmlApplicationContext} can be used to load the context files
63+
* for testing, even for {@code DispatcherServlet} context definitions.
6464
*
65-
* <p>For setting up a full WebApplicationContext in a test environment, you can
66-
* use XmlWebApplicationContext (or GenericWebApplicationContext), passing in an
67-
* appropriate MockServletContext instance. You might want to configure your
68-
* MockServletContext with a FileSystemResourceLoader in that case, to make your
69-
* resource paths interpreted as relative file system locations.
65+
* <p>For setting up a full {@code WebApplicationContext} in a test environment,
66+
* you can use {@code AnnotationConfigWebApplicationContext},
67+
* {@code XmlWebApplicationContext}, or {@code GenericWebApplicationContext},
68+
* passing in an appropriate {@code MockServletContext} instance. You might want
69+
* to configure your {@code MockServletContext} with a {@code FileSystemResourceLoader}
70+
* in that case to ensure that resource paths are interpreted as relative filesystem
71+
* locations.
7072
*
7173
* <p>A common setup is to point your JVM working directory to the root of your
7274
* web application directory, in combination with filesystem-based resource loading.
7375
* This allows to load the context files as used in the web application, with
7476
* relative paths getting interpreted correctly. Such a setup will work with both
75-
* FileSystemXmlApplicationContext (which will load straight from the file system)
76-
* and XmlWebApplicationContext with an underlying MockServletContext (as long as
77-
* the MockServletContext has been configured with a FileSystemResourceLoader).
77+
* {@code FileSystemXmlApplicationContext} (which will load straight from the
78+
* filesystem) and {@code XmlWebApplicationContext} with an underlying
79+
* {@code MockServletContext} (as long as the {@code MockServletContext} has been
80+
* configured with a {@code FileSystemResourceLoader}).
7881
*
7982
* @author Rod Johnson
8083
* @author Juergen Hoeller
84+
* @author Sam Brannen
8185
* @since 1.0.2
8286
* @see #MockServletContext(org.springframework.core.io.ResourceLoader)
87+
* @see org.springframework.web.context.support.AnnotationConfigWebApplicationContext
8388
* @see org.springframework.web.context.support.XmlWebApplicationContext
8489
* @see org.springframework.web.context.support.GenericWebApplicationContext
8590
* @see org.springframework.context.support.ClassPathXmlApplicationContext
8691
* @see org.springframework.context.support.FileSystemXmlApplicationContext
8792
*/
8893
public class MockServletContext implements ServletContext {
8994

90-
private static final String TEMP_DIR_SYSTEM_PROPERTY = "java.io.tmpdir";
95+
/** Default Servlet name used by Tomcat, Jetty, JBoss, and GlassFish: {@value}. */
96+
private static final String COMMON_DEFAULT_SERVLET_NAME = "default";
9197

98+
private static final String TEMP_DIR_SYSTEM_PROPERTY = "java.io.tmpdir";
9299

93100
private final Log logger = LogFactory.getLog(getClass());
94101

102+
private final Map<String, ServletContext> contexts = new HashMap<String, ServletContext>();
103+
104+
private final Map<String, String> initParameters = new LinkedHashMap<String, String>();
105+
106+
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
107+
108+
private final Set<String> declaredRoles = new HashSet<String>();
109+
110+
private final Map<String, RequestDispatcher> namedRequestDispatchers = new HashMap<String, RequestDispatcher>();
111+
95112
private final ResourceLoader resourceLoader;
96113

97114
private final String resourceBasePath;
@@ -106,15 +123,9 @@ public class MockServletContext implements ServletContext {
106123

107124
private int effectiveMinorVersion = 5;
108125

109-
private final Map<String, ServletContext> contexts = new HashMap<String, ServletContext>();
110-
111-
private final Map<String, String> initParameters = new LinkedHashMap<String, String>();
112-
113-
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
114-
115126
private String servletContextName = "MockServletContext";
116127

117-
private final Set<String> declaredRoles = new HashSet<String>();
128+
private String defaultServletName = COMMON_DEFAULT_SERVLET_NAME;
118129

119130

120131
/**
@@ -128,7 +139,7 @@ public MockServletContext() {
128139

129140
/**
130141
* Create a new MockServletContext, using a DefaultResourceLoader.
131-
* @param resourceBasePath the WAR root directory (should not end with a slash)
142+
* @param resourceBasePath the root directory of the WAR (should not end with a slash)
132143
* @see org.springframework.core.io.DefaultResourceLoader
133144
*/
134145
public MockServletContext(String resourceBasePath) {
@@ -145,9 +156,13 @@ public MockServletContext(ResourceLoader resourceLoader) {
145156
}
146157

147158
/**
148-
* Create a new MockServletContext.
149-
* @param resourceBasePath the WAR root directory (should not end with a slash)
159+
* Create a new MockServletContext using the supplied resource base path and
160+
* resource loader.
161+
* <p>Registers a {@link MockRequestDispatcher} for the Servlet named
162+
* {@value #COMMON_DEFAULT_SERVLET_NAME}.
163+
* @param resourceBasePath the root directory of the WAR (should not end with a slash)
150164
* @param resourceLoader the ResourceLoader to use (or null for the default)
165+
* @see #registerNamedDispatcher
151166
*/
152167
public MockServletContext(String resourceBasePath, ResourceLoader resourceLoader) {
153168
this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
@@ -158,8 +173,9 @@ public MockServletContext(String resourceBasePath, ResourceLoader resourceLoader
158173
if (tempDir != null) {
159174
this.attributes.put(WebUtils.TEMP_DIR_CONTEXT_ATTRIBUTE, new File(tempDir));
160175
}
161-
}
162176

177+
registerNamedDispatcher(this.defaultServletName, new MockRequestDispatcher(this.defaultServletName));
178+
}
163179

164180
/**
165181
* Build a full resource location for the given path,
@@ -174,7 +190,6 @@ protected String getResourceLocation(String path) {
174190
return this.resourceBasePath + path;
175191
}
176192

177-
178193
public void setContextPath(String contextPath) {
179194
this.contextPath = (contextPath != null ? contextPath : "");
180195
}
@@ -295,7 +310,60 @@ public RequestDispatcher getRequestDispatcher(String path) {
295310
}
296311

297312
public RequestDispatcher getNamedDispatcher(String path) {
298-
return null;
313+
return this.namedRequestDispatchers.get(path);
314+
}
315+
316+
/**
317+
* Register a {@link RequestDispatcher} (typically a {@link MockRequestDispatcher})
318+
* that acts as a wrapper for the named Servlet.
319+
*
320+
* @param name the name of the wrapped Servlet
321+
* @param requestDispatcher the dispatcher that wraps the named Servlet
322+
* @see #getNamedDispatcher
323+
* @see #unregisterNamedDispatcher
324+
*/
325+
public void registerNamedDispatcher(String name, RequestDispatcher requestDispatcher) {
326+
Assert.notNull(name, "RequestDispatcher name must not be null");
327+
Assert.notNull(requestDispatcher, "RequestDispatcher must not be null");
328+
this.namedRequestDispatchers.put(name, requestDispatcher);
329+
}
330+
331+
/**
332+
* Unregister the {@link RequestDispatcher} with the given name.
333+
*
334+
* @param name the name of the dispatcher to unregister
335+
* @see #getNamedDispatcher
336+
* @see #registerNamedDispatcher
337+
*/
338+
public void unregisterNamedDispatcher(String name) {
339+
Assert.notNull(name, "RequestDispatcher name must not be null");
340+
this.namedRequestDispatchers.remove(name);
341+
}
342+
343+
/**
344+
* Get the name of the <em>default</em> {@code Servlet}.
345+
* <p>Defaults to {@value #COMMON_DEFAULT_SERVLET_NAME}.
346+
* @see #setDefaultServletName
347+
*/
348+
public String getDefaultServletName() {
349+
return this.defaultServletName;
350+
}
351+
352+
/**
353+
* Set the name of the <em>default</em> {@code Servlet}.
354+
* <p>Also {@link #unregisterNamedDispatcher unregisters} the current default
355+
* {@link RequestDispatcher} and {@link #registerNamedDispatcher replaces}
356+
* it with a {@link MockRequestDispatcher} for the provided
357+
* {@code defaultServletName}.
358+
* @param defaultServletName the name of the <em>default</em> {@code Servlet};
359+
* never {@code null} or empty
360+
* @see #getDefaultServletName
361+
*/
362+
public void setDefaultServletName(String defaultServletName) {
363+
Assert.hasText(defaultServletName, "defaultServletName must not be null or empty");
364+
unregisterNamedDispatcher(this.defaultServletName);
365+
this.defaultServletName = defaultServletName;
366+
registerNamedDispatcher(this.defaultServletName, new MockRequestDispatcher(this.defaultServletName));
299367
}
300368

301369
public Servlet getServlet(String name) {
@@ -410,7 +478,7 @@ public Set<String> getDeclaredRoles() {
410478

411479

412480
/**
413-
* Inner factory class used to just introduce a Java Activation Framework
481+
* Inner factory class used to introduce a Java Activation Framework
414482
* dependency when actually asked to resolve a MIME type.
415483
*/
416484
private static class MimeTypeResolver {

spring-test/src/main/java/org/springframework/mock/web/MockRequestDispatcher.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2010 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,22 +34,24 @@
3434
*
3535
* @author Rod Johnson
3636
* @author Juergen Hoeller
37+
* @author Sam Brannen
3738
* @since 1.0.2
3839
*/
3940
public class MockRequestDispatcher implements RequestDispatcher {
4041

4142
private final Log logger = LogFactory.getLog(getClass());
4243

43-
private final String url;
44+
private final String resource;
4445

4546

4647
/**
47-
* Create a new MockRequestDispatcher for the given URL.
48-
* @param url the URL to dispatch to.
48+
* Create a new MockRequestDispatcher for the given resource.
49+
* @param resource the server resource to dispatch to, located at a
50+
* particular path or given by a particular name
4951
*/
50-
public MockRequestDispatcher(String url) {
51-
Assert.notNull(url, "URL must not be null");
52-
this.url = url;
52+
public MockRequestDispatcher(String resource) {
53+
Assert.notNull(resource, "resource must not be null");
54+
this.resource = resource;
5355
}
5456

5557

@@ -59,24 +61,24 @@ public void forward(ServletRequest request, ServletResponse response) {
5961
if (response.isCommitted()) {
6062
throw new IllegalStateException("Cannot perform forward - response is already committed");
6163
}
62-
getMockHttpServletResponse(response).setForwardedUrl(this.url);
64+
getMockHttpServletResponse(response).setForwardedUrl(this.resource);
6365
if (logger.isDebugEnabled()) {
64-
logger.debug("MockRequestDispatcher: forwarding to URL [" + this.url + "]");
66+
logger.debug("MockRequestDispatcher: forwarding to [" + this.resource + "]");
6567
}
6668
}
6769

6870
public void include(ServletRequest request, ServletResponse response) {
6971
Assert.notNull(request, "Request must not be null");
7072
Assert.notNull(response, "Response must not be null");
71-
getMockHttpServletResponse(response).addIncludedUrl(this.url);
73+
getMockHttpServletResponse(response).addIncludedUrl(this.resource);
7274
if (logger.isDebugEnabled()) {
73-
logger.debug("MockRequestDispatcher: including URL [" + this.url + "]");
75+
logger.debug("MockRequestDispatcher: including [" + this.resource + "]");
7476
}
7577
}
7678

7779
/**
78-
* Obtain the underlying MockHttpServletResponse,
79-
* unwrapping {@link HttpServletResponseWrapper} decorators if necessary.
80+
* Obtain the underlying {@link MockHttpServletResponse}, unwrapping
81+
* {@link HttpServletResponseWrapper} decorators if necessary.
8082
*/
8183
protected MockHttpServletResponse getMockHttpServletResponse(ServletResponse response) {
8284
if (response instanceof MockHttpServletResponse) {

0 commit comments

Comments
 (0)