SPR-7116 work in progress. ResourceHttpRequestHandler is now functionally equivalent to the Spring JS ResourceServlet, with the exception of the resource concatenation feature (which will be deferred for reconsideration in 3.1).

master
Jeremy Grelle 14 years ago
parent 72da237474
commit 60a69bd653
  1. 7
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java
  2. 100
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resources/DefaultServletHttpRequestHandler.java
  3. 324
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resources/ResourceHttpRequestHandler.java
  4. 58
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/resources/ResourceHttpRequestHandlerTests.java
  5. 1
      org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/resources/testalternatepath/bar.css
  6. 1
      org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/resources/testalternatepath/baz.css
  7. 1
      org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/resources/testalternatepath/foo.css
  8. 1
      org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/resources/testalternatepath/js/bar.js
  9. 1
      org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/resources/testalternatepath/js/baz.js
  10. 1
      org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/resources/testalternatepath/js/foo.js

@ -1,9 +1,11 @@
package org.springframework.web.servlet.config;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
@ -34,11 +36,12 @@ public class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
registerHandlerAdapterIfNecessary(parserContext, source);
BeanDefinition handlerMappingDef = registerHandlerMappingIfNecessary(parserContext, source);
String resourceDirectory = "/resources/";
List<String> resourcePaths = new ManagedList<String>();
resourcePaths.add("/");
RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
resourceHandlerDef.setSource(source);
resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
resourceHandlerDef.getConstructorArgumentValues().addIndexedArgumentValue(0, resourceDirectory);
resourceHandlerDef.getConstructorArgumentValues().addIndexedArgumentValue(0, resourcePaths);
Map<String, BeanDefinition> urlMap = getUrlMap(handlerMappingDef);
String resourceRequestPath = "/resources/**";

@ -0,0 +1,100 @@
package org.springframework.web.servlet.resources;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.ServletContextAware;
/**
* An {@link HttpRequestHandler} for serving static files using the Servlet container's "default" Servlet.
*
* <p>This handler is intended to be used with a "/*" mapping when the {@link DispatcherServlet} is mapped to "/", thus
* overriding the Servlet container's default handling of static resources. The mapping to this handler should generally
* be ordered as the last in the chain so that it will only execute when no other more specific mappings (i.e., to controllers)
* can be matched.
*
* <p>Requests are handled by forwarding through the {@link RequestDispatcher} obtained via the name specified through the
* {@code fileServletName} property. In most cases, the {@code fileServletName} does not need to be set explicitly, as the
* handler checks at initialization time for the presence of the default Servlet of one of the known containers. However, if
* running in a container where the default Servlet's name is not known, or where it has been customized via configuration, the
* {@code fileServletName} will need to be set explicitly.
*
* @author Jeremy Grelle
* @since 3.0.4
*/
public class DefaultServletHttpRequestHandler implements InitializingBean, HttpRequestHandler, ServletContextAware {
/**
* Default Servlet name used by Tomcat, Jetty, JBoss, and Glassfish
*/
private static final String COMMON_DEFAULT_SERVLET_NAME = "default";
/**
* Default Servlet name used by Resin
*/
private static final String RESIN_DEFAULT_SERVLET_NAME = "resin-file";
/**
* Default Servlet name used by WebLogic
*/
private static final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet";
/**
* Default Servlet name used by WebSphere
*/
private static final String WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet";
private ServletContext servletContext;
private String fileServletName;
/**
* If the {@code filedServletName} property has not been explicitly set, attempts to locate the default Servlet using the
* known common container-specific names.
*/
public void afterPropertiesSet() throws Exception {
if (!StringUtils.hasText(this.fileServletName)) {
if (this.servletContext.getNamedDispatcher(COMMON_DEFAULT_SERVLET_NAME) != null) {
this.fileServletName = COMMON_DEFAULT_SERVLET_NAME;
} else if (this.servletContext.getNamedDispatcher(RESIN_DEFAULT_SERVLET_NAME) != null) {
this.fileServletName = RESIN_DEFAULT_SERVLET_NAME;
} else if (this.servletContext.getNamedDispatcher(WEBLOGIC_DEFAULT_SERVLET_NAME) != null) {
this.fileServletName = WEBLOGIC_DEFAULT_SERVLET_NAME;
} else if (this.servletContext.getNamedDispatcher(WEBSPHERE_DEFAULT_SERVLET_NAME) != null) {
this.fileServletName = WEBSPHERE_DEFAULT_SERVLET_NAME;
}
Assert.hasText(this.fileServletName, "Unable to locate the default servlet for serving static content. Please set the 'fileServletName' property explicitly.");
}
}
public void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.fileServletName);
Assert.notNull(rd, "A RequestDispatcher could not be located for the servlet name '"+this.fileServletName+"'");
rd.forward(request, response);
}
/**
* Set the name of the default Servlet to be forwarded to for static resource requests.
* @param fileServletName The name of the Servlet to use for static resources.
*/
public void setDefaultServletName(String fileServletName) {
this.fileServletName = fileServletName;
}
/**
* {@inheritDoc}
*/
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}

@ -1,17 +1,23 @@
package org.springframework.web.servlet.resources;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.zip.GZIPOutputStream;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -26,101 +32,112 @@ import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import org.springframework.web.servlet.mvc.LastModified;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
public class ResourceHttpRequestHandler implements HttpRequestHandler {
/**
* {@link HttpRequestHandler} that serves static resources optimized for superior browser performance
* (according to the guidelines of Page Speed, YSlow, etc.) by adding far future cache expiration headers
* and gzip compressing the resources if supported by the client.
*
* <p>TODO - expand the docs further
*
* @author Keith Donald
* @author Jeremy Grelle
* @since 3.0.4
*/
public class ResourceHttpRequestHandler implements HttpRequestHandler, LastModified {
private static final Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class);
private Resource resourceDirectory;
private final List<Resource> resourcePaths;
private int maxAge = 31556926;
private FileMediaTypeMap fileMediaTypeMap = new DefaultFileMediaTypeMap();
private boolean gzipEnabled = true;
private int minGzipSize = 150;
private int maxGzipSize = 500000;
public ResourceHttpRequestHandler(Resource resourceDirectory) {
Assert.notNull(resourceDirectory, "The resource directory may not be null");
this.resourceDirectory = resourceDirectory;
public ResourceHttpRequestHandler(List<Resource> resourcePaths) {
Assert.notNull(resourcePaths, "Resource paths must not be null");
this.resourcePaths = resourcePaths;
}
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (!"GET".equals(request.getMethod())) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(),
new String[] {"GET"}, "ResourceHttpRequestHandler only supports GET requests");
}
List<Resource> resources = getResources(request);
if (resources.size() == 0) {
URLResource resource = getResource(request);
if (resource == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
boolean notModified = checkNotModified(resources, request, response);
if (notModified) {
return;
prepareResponse(resource, response);
writeResponse(resource, request, response);
}
public long getLastModified(HttpServletRequest request) {
try {
Resource resource = getResource(request);
if (resource == null) {
return -1;
}
return resource.lastModified();
} catch (Exception e) {
return -1;
}
prepareResponse(resources, response);
writeResponse(resources, response);
}
public void setGzipEnabled(boolean gzipEnabled) {
this.gzipEnabled = gzipEnabled;
}
private List<Resource> getResources(HttpServletRequest request) throws ServletException, IOException {
public void setMinGzipSize(int minGzipSize) {
this.minGzipSize = minGzipSize;
}
public void setMaxGzipSize(int maxGzipSize) {
this.maxGzipSize = maxGzipSize;
}
private URLResource getResource(HttpServletRequest request) {
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (path == null) {
throw new IllegalStateException("Required request attribute '" + HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + "' is not set");
}
String[] resourceElements = path.split(",");
if (resourceElements.length == 1 && resourceElements[0].length() == 0) {
throw new NoSuchRequestHandlingMethodException(request);
}
List<Resource> resources = new ArrayList<Resource>();
String[] dirAndFilename = splitDirectoryAndFilename(resourceElements[0]);
String dir = dirAndFilename[0];
String filename = dirAndFilename[1];
Resource parent = dir != null ? this.resourceDirectory.createRelative(dir) : this.resourceDirectory;
addResource(parent, filename, resources);
if (resourceElements.length > 1) {
for (int i = 1; i < resourceElements.length; i++) {
addResource(parent, resourceElements[i], resources);
}
}
return resources;
}
private boolean checkNotModified(List<Resource> resources,HttpServletRequest request, HttpServletResponse response) throws IOException {
long lastModifiedTimestamp = -1;
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
for (Resource resource : resources) {
long resourceLastModified = resource.lastModified();
if (resourceLastModified > lastModifiedTimestamp) {
lastModifiedTimestamp = resourceLastModified;
}
if (path.contains("WEB-INF") || path.contains("META-INF")) {
return null;
}
boolean notModified = ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000);
if (notModified) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
} else {
response.setDateHeader("Last-Modified", lastModifiedTimestamp);
}
return notModified;
}
private void prepareResponse(List<Resource> resources, HttpServletResponse response) {
MediaType mediaType = null;
int contentLength = 0;
for (Resource resource : resources) {
for (Resource resourcePath : this.resourcePaths) {
Resource resource;
try {
File file = resource.getFile();
if (mediaType == null) {
mediaType = fileMediaTypeMap.getMediaType(file.getName());
resource = resourcePath.createRelative(path);
if (isValidFile(resource)) {
return new URLResource(resource);
}
contentLength += file.length();
} catch (IOException e) {
//Resource not found
return null;
}
}
return null;
}
private void prepareResponse(URLResource resource, HttpServletResponse response) throws IOException {
MediaType mediaType = null;
if (mediaType == null) {
mediaType = fileMediaTypeMap.getMediaType(resource.getFilename());
}
if (mediaType != null) {
response.setContentType(mediaType.toString());
}
response.setContentLength(contentLength);
response.setContentLength(resource.getContentLength());
response.setDateHeader("Last-Modified", resource.lastModified());
if (this.maxAge > 0) {
// HTTP 1.0 header
response.setDateHeader("Expires", System.currentTimeMillis() + this.maxAge * 1000L);
@ -129,53 +146,43 @@ public class ResourceHttpRequestHandler implements HttpRequestHandler {
}
}
private void writeResponse(List<Resource> resources, HttpServletResponse response) throws IOException {
for (Resource resource : resources) {
InputStream in = null;
private void writeResponse(URLResource resource, HttpServletRequest request, HttpServletResponse response) throws IOException {
OutputStream out = selectOutputStream(resource, request, response);
try {
InputStream in = resource.getInputStream();
try {
in = resource.getInputStream();
byte[] buffer = new byte[1024];
int bytesRead = -1;
byte[] buffer = new byte[4096];
while ((bytesRead = in.read(buffer)) != -1) {
response.getOutputStream().write(buffer, 0, bytesRead);
out.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
in.close();
}
}
} finally {
if (out != null) {
out.close();
}
}
}
private String[] splitDirectoryAndFilename(String firstResourceElement) {
int index = firstResourceElement.lastIndexOf("/");
String dir;
if (index == -1) {
dir = null;
private OutputStream selectOutputStream(URLResource resource, HttpServletRequest request,
HttpServletResponse response) throws IOException {
String acceptEncoding = request.getHeader("Accept-Encoding");
boolean isGzipEligible = resource.getContentLength() >= this.minGzipSize && resource.getContentLength() <= this.maxGzipSize;
if (this.gzipEnabled && isGzipEligible && StringUtils.hasText(acceptEncoding)
&& acceptEncoding.indexOf("gzip") > -1
&& response.getContentType().startsWith("text/")){
return new GZIPResponseStream(response);
} else {
dir = firstResourceElement.substring(0, index + 1);
}
String filename = firstResourceElement.substring(index + 1, firstResourceElement.length());
return new String[] { dir, filename };
}
private void addResource(Resource parent, String name, List<Resource> resources) throws IOException {
if (name.length() > 0) {
Resource resource = parent.createRelative(name);
if (isAllowed(resource)) {
resources.add(resource);
}
return response.getOutputStream();
}
}
private boolean isAllowed(Resource resource) throws IOException {
return resource.exists() && resource.getFile().isFile();
private boolean isValidFile(Resource resource) throws IOException {
return resource.exists() && StringUtils.hasText(resource.getFilename());
}
// TODO promote to top-level and make reusable
@ -259,5 +266,136 @@ public class ResourceHttpRequestHandler implements HttpRequestHandler {
}
}
}
private class GZIPResponseStream extends ServletOutputStream {
private ByteArrayOutputStream byteStream = null;
private GZIPOutputStream gzipStream = null;
private boolean closed = false;
private HttpServletResponse response = null;
private ServletOutputStream servletStream = null;
public GZIPResponseStream(HttpServletResponse response) throws IOException {
super();
closed = false;
this.response = response;
this.servletStream = response.getOutputStream();
byteStream = new ByteArrayOutputStream();
gzipStream = new GZIPOutputStream(byteStream);
}
public void close() throws IOException {
if (closed) {
throw new IOException("This output stream has already been closed");
}
gzipStream.finish();
byte[] bytes = byteStream.toByteArray();
response.setContentLength(bytes.length);
response.addHeader("Content-Encoding", "gzip");
servletStream.write(bytes);
servletStream.flush();
servletStream.close();
closed = true;
}
public void flush() throws IOException {
if (closed) {
throw new IOException("Cannot flush a closed output stream");
}
gzipStream.flush();
}
public void write(int b) throws IOException {
if (closed) {
throw new IOException("Cannot write to a closed output stream");
}
gzipStream.write((byte) b);
}
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
if (closed) {
throw new IOException("Cannot write to a closed output stream");
}
gzipStream.write(b, off, len);
}
}
private static class URLResource implements Resource {
private final Resource wrapped;
private final long lastModified;
private final int contentLength;
public URLResource(Resource wrapped) throws IOException {
this.wrapped = wrapped;
URLConnection connection = null;
try {
connection = wrapped.getURL().openConnection();
this.lastModified = connection.getLastModified();
this.contentLength = connection.getContentLength();
} finally {
if (connection != null) {
connection.getInputStream().close();
}
}
}
public int getContentLength() {
return this.contentLength;
}
public long lastModified() throws IOException {
return this.lastModified;
}
public Resource createRelative(String relativePath) throws IOException {
return wrapped.createRelative(relativePath);
}
public boolean exists() {
return wrapped.exists();
}
public String getDescription() {
return wrapped.getDescription();
}
public File getFile() throws IOException {
return wrapped.getFile();
}
public String getFilename() {
return wrapped.getFilename();
}
public URI getURI() throws IOException {
return wrapped.getURI();
}
public URL getURL() throws IOException {
return wrapped.getURL();
}
public boolean isOpen() {
return wrapped.isOpen();
}
public boolean isReadable() {
return wrapped.isReadable();
}
public InputStream getInputStream() throws IOException {
return wrapped.getInputStream();
}
}
}

@ -3,18 +3,19 @@ package org.springframework.web.servlet.resources;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import org.springframework.web.servlet.resources.ResourceHttpRequestHandler;
public class ResourceHttpRequestHandlerTests {
@ -22,7 +23,10 @@ public class ResourceHttpRequestHandlerTests {
@Before
public void setUp() {
handler = new ResourceHttpRequestHandler(new ClassPathResource("test/", getClass()));
List<Resource> resourcePaths = new ArrayList<Resource>();
resourcePaths.add(new ClassPathResource("test/", getClass()));
resourcePaths.add(new ClassPathResource("testalternatepath/", getClass()));
handler = new ResourceHttpRequestHandler(resourcePaths);
}
@Test
@ -40,32 +44,21 @@ public class ResourceHttpRequestHandlerTests {
assertEquals(response.getHeader("Last-Modified"), new ClassPathResource("test/foo.css", getClass()).getFile().lastModified());
assertEquals("h1 { color:red; }", response.getContentAsString());
}
@Test
public void getResourceBundle() throws Exception {
public void getResourceFromAlternatePath() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/foo.css,bar.css");
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/baz.css");
request.setMethod("GET");
MockHttpServletResponse response = new MockHttpServletResponse();
handler.handleRequest(request, response);
assertEquals("text/css", response.getContentType());
assertEquals(36, response.getContentLength());
assertEquals(17, response.getContentLength());
assertTrue(((Long)response.getHeader("Expires")) > System.currentTimeMillis() + (31556926 * 1000) - 10000);
assertEquals("max-age=31556926", response.getHeader("Cache-Control"));
assertTrue(response.containsHeader("Last-Modified"));
assertEquals(response.getHeader("Last-Modified"), new ClassPathResource("test/bar.css", getClass()).getFile().lastModified());
assertEquals("h1 { color:red; }h2 { color:white; }", response.getContentAsString());
}
@Test
public void getResourceBundleDifferentTypes() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/foo.css,/js/bar.js");
request.setMethod("GET");
MockHttpServletResponse response = new MockHttpServletResponse();
handler.handleRequest(request, response);
assertEquals("text/css", response.getContentType());
assertEquals("h1 { color:red; }function foo() { console.log(\"hello bar\"); }", response.getContentAsString());
assertEquals(response.getHeader("Last-Modified"), new ClassPathResource("testalternatepath/baz.css", getClass()).getFile().lastModified());
assertEquals("h1 { color:red; }", response.getContentAsString());
}
@Test
@ -75,31 +68,28 @@ public class ResourceHttpRequestHandlerTests {
request.setMethod("GET");
MockHttpServletResponse response = new MockHttpServletResponse();
handler.handleRequest(request, response);
System.out.println(response.getContentAsString());
assertEquals("text/javascript", response.getContentType());
assertEquals("function foo() { console.log(\"hello world\"); }", response.getContentAsString());
}
@Test
public void getResourceBundleDifferentTypesIncludingDirectory() throws Exception {
public void getResourceFromSubDirectoryOfAlternatePath() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/foo.css,/js,/js/foo.js");
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/js/baz.js");
request.setMethod("GET");
MockHttpServletResponse response = new MockHttpServletResponse();
handler.handleRequest(request, response);
assertEquals("text/css", response.getContentType());
assertEquals("h1 { color:red; }function foo() { console.log(\"hello world\"); }", response.getContentAsString());
assertEquals("text/javascript", response.getContentType());
assertEquals("function foo() { console.log(\"hello world\"); }", response.getContentAsString());
}
@Test
public void notModified() throws Exception {
public void lastModified() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/foo.css");
request.addHeader("If-Modified-Since", new ClassPathResource("test/foo.css", getClass()).getFile().lastModified());
request.setMethod("GET");
MockHttpServletResponse response = new MockHttpServletResponse();
handler.handleRequest(request, response);
assertEquals(HttpServletResponse.SC_NOT_MODIFIED, response.getStatus());
long expected = new ClassPathResource("test/foo.css", getClass()).lastModified();
long lastModified = handler.getLastModified(request);
assertEquals(expected, lastModified);
}
@Test
@ -124,13 +114,14 @@ public class ResourceHttpRequestHandlerTests {
assertEquals(404, response.getStatus());
}
@Test(expected=NoSuchRequestHandlingMethodException.class)
@Test
public void missingResourcePath() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "");
request.setMethod("GET");
MockHttpServletResponse response = new MockHttpServletResponse();
handler.handleRequest(request, response);
assertEquals(404, response.getStatus());
}
@Test(expected=IllegalStateException.class)
@ -159,5 +150,4 @@ public class ResourceHttpRequestHandlerTests {
handler.handleRequest(request, response);
assertEquals(404, response.getStatus());
}
}

Loading…
Cancel
Save