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).
This commit is contained in:
parent
72da237474
commit
60a69bd653
|
|
@ -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;
|
||||
|
||||
public ResourceHttpRequestHandler(Resource resourceDirectory) {
|
||||
Assert.notNull(resourceDirectory, "The resource directory may not be null");
|
||||
this.resourceDirectory = resourceDirectory;
|
||||
private int minGzipSize = 150;
|
||||
|
||||
private int maxGzipSize = 500000;
|
||||
|
||||
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);
|
||||
if (path.contains("WEB-INF") || path.contains("META-INF")) {
|
||||
return null;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String[] splitDirectoryAndFilename(String firstResourceElement) {
|
||||
int index = firstResourceElement.lastIndexOf("/");
|
||||
String dir;
|
||||
if (index == -1) {
|
||||
dir = null;
|
||||
} 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);
|
||||
} finally {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAllowed(Resource resource) throws IOException {
|
||||
return resource.exists() && resource.getFile().isFile();
|
||||
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 {
|
||||
return response.getOutputStream();
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
fail
|
||||
|
|
@ -0,0 +1 @@
|
|||
h1 { color:red; }
|
||||
|
|
@ -0,0 +1 @@
|
|||
fail
|
||||
|
|
@ -0,0 +1 @@
|
|||
fail
|
||||
|
|
@ -0,0 +1 @@
|
|||
function foo() { console.log("hello world"); }
|
||||
|
|
@ -0,0 +1 @@
|
|||
fail
|
||||
Loading…
Reference in New Issue