diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AbstractHttpRequestHandlerBeanDefinitionParser.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AbstractHttpRequestHandlerBeanDefinitionParser.java index 7bb54395068..13f10cf673b 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AbstractHttpRequestHandlerBeanDefinitionParser.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AbstractHttpRequestHandlerBeanDefinitionParser.java @@ -1,20 +1,37 @@ +/* + * Copyright 2002-2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.web.servlet.config; +import org.w3c.dom.Element; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; -import org.w3c.dom.Element; /** - * Abstract base class for {@link BeanDefinitonParser}s that register an {@link HttpRequestHandler}. + * Abstract base class for {@link BeanDefinitonParser}s that register an HttpRequestHandler. * * @author Jeremy Grelle * @since 3.0.4 */ -public abstract class AbstractHttpRequestHandlerBeanDefinitionParser implements BeanDefinitionParser{ +abstract class AbstractHttpRequestHandlerBeanDefinitionParser implements BeanDefinitionParser{ private static final String HANDLER_ADAPTER_BEAN_NAME = "org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"; @@ -36,4 +53,5 @@ public abstract class AbstractHttpRequestHandlerBeanDefinitionParser implements parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME)); } } + } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/DefaultServletHandlerBeanDefinitionParser.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/DefaultServletHandlerBeanDefinitionParser.java index 7ef0d3b6a4a..fe60722234d 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/DefaultServletHandlerBeanDefinitionParser.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/DefaultServletHandlerBeanDefinitionParser.java @@ -1,7 +1,25 @@ +/* + * Copyright 2002-2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.web.servlet.config; import java.util.Map; +import org.w3c.dom.Element; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.ManagedMap; @@ -11,8 +29,7 @@ import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; -import org.springframework.web.servlet.resources.DefaultServletHttpRequestHandler; -import org.w3c.dom.Element; +import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler; /** * {@link BeanDefinitionParser} that parses a {@code default-servlet-handler} element to @@ -23,7 +40,7 @@ import org.w3c.dom.Element; * @author Jeremy Grelle * @since 3.0.4 */ -public class DefaultServletHandlerBeanDefinitionParser extends AbstractHttpRequestHandlerBeanDefinitionParser { +class DefaultServletHandlerBeanDefinitionParser extends AbstractHttpRequestHandlerBeanDefinitionParser { @Override public void doParse(Element element, ParserContext parserContext) { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java index bca860f02e2..c1a5ba1c240 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java @@ -1,8 +1,26 @@ +/* + * Copyright 2002-2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.web.servlet.config; import java.util.List; import java.util.Map; +import org.w3c.dom.Element; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.ManagedList; @@ -14,8 +32,7 @@ import org.springframework.core.Ordered; import org.springframework.util.StringUtils; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; -import org.springframework.web.servlet.resources.ResourceHttpRequestHandler; -import org.w3c.dom.Element; +import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; /** * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a @@ -27,7 +44,7 @@ import org.w3c.dom.Element; * @author Jeremy Grelle * @since 3.0.4 */ -public class ResourcesBeanDefinitionParser extends AbstractHttpRequestHandlerBeanDefinitionParser implements BeanDefinitionParser { +class ResourcesBeanDefinitionParser extends AbstractHttpRequestHandlerBeanDefinitionParser implements BeanDefinitionParser { @Override public void doParse(Element element, ParserContext parserContext) { @@ -76,7 +93,7 @@ public class ResourcesBeanDefinitionParser extends AbstractHttpRequestHandlerBea RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class); resourceHandlerDef.setSource(source); resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - resourceHandlerDef.getConstructorArgumentValues().addIndexedArgumentValue(0, locations); + resourceHandlerDef.getPropertyValues().add("locations", locations); String beanName = parserContext.getReaderContext().generateBeanName(resourceHandlerDef); parserContext.getRegistry().registerBeanDefinition(beanName, resourceHandlerDef); parserContext.registerComponent(new BeanComponentDefinition(resourceHandlerDef, beanName)); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/DefaultServletHttpRequestHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/DefaultServletHttpRequestHandler.java new file mode 100644 index 00000000000..55a81ae5817 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/DefaultServletHttpRequestHandler.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.resource; + +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.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. + * + *
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. + * + *
Requests are handled by forwarding through the {@link RequestDispatcher} obtained via the + * name specified through the {@link #setDefaultServletName "defaultServletName" property}. + * In most cases, the {@code defaultServletName} does not need to be set explicitly, as the + * handler checks at initialization time for the presence of the default Servlet of well-known + * containers such as Tomcat, Jetty, Resin, WebLogic and WebSphere. However, when running in a + * container where the default Servlet's name is not known, or where it has been customized + * via server configuration, the {@code defaultServletName} will need to be set explicitly. + * + * @author Jeremy Grelle + * @author Juergen Hoeller + * @since 3.0.4 + */ +public class DefaultServletHttpRequestHandler implements 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 String defaultServletName; + + private ServletContext servletContext; + + + /** + * Set the name of the default Servlet to be forwarded to for static resource requests. + */ + public void setDefaultServletName(String defaultServletName) { + this.defaultServletName = defaultServletName; + } + + /** + * If the {@code defaultServletName} property has not been explicitly set, + * attempts to locate the default Servlet using the known common + * container-specific names. + */ + public void setServletContext(ServletContext servletContext) { + this.servletContext = servletContext; + if (!StringUtils.hasText(this.defaultServletName)) { + if (this.servletContext.getNamedDispatcher(COMMON_DEFAULT_SERVLET_NAME) != null) { + this.defaultServletName = COMMON_DEFAULT_SERVLET_NAME; + } + else if (this.servletContext.getNamedDispatcher(RESIN_DEFAULT_SERVLET_NAME) != null) { + this.defaultServletName = RESIN_DEFAULT_SERVLET_NAME; + } + else if (this.servletContext.getNamedDispatcher(WEBLOGIC_DEFAULT_SERVLET_NAME) != null) { + this.defaultServletName = WEBLOGIC_DEFAULT_SERVLET_NAME; + } + else if (this.servletContext.getNamedDispatcher(WEBSPHERE_DEFAULT_SERVLET_NAME) != null) { + this.defaultServletName = WEBSPHERE_DEFAULT_SERVLET_NAME; + } + else { + throw new IllegalStateException("Unable to locate the default servlet for serving static content. " + + "Please set the 'defaultServletName' property explicitly."); + } + } + } + + + public void handleRequest(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName); + if (rd == null) { + throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" + + this.defaultServletName +"'"); + } + rd.forward(request, response); + } + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java new file mode 100644 index 00000000000..8b9b9ea2ccc --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -0,0 +1,160 @@ +/* + * Copyright 2002-2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.resource; + +import java.io.IOException; +import java.util.List; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.util.Assert; +import org.springframework.util.FileCopyUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.HttpRequestHandler; +import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.support.WebContentGenerator; + +/** + * {@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. + * + *
The constructor takes a list of Spring {@link Resource} locations from which static resources are allowed + * to be served by this handler. For a given request, the list of locations will be consulted in order for the + * presence of the requested resource, and the first found match will be written to the response, with {@code + * Expires} and {@code Cache-Control} headers set for one year in the future. The handler also properly evaluates + * the {@code Last-Modified} header (if present) so that a {@code 304} status code will be returned as appropriate, + * avoiding unnecessary overhead for resources that are already cached by the client. The use of {@code Resource} + * locations allows resource requests to easily be mapped to locations other than the web application root. For + * example, resources could be served from a classpath location such as "classpath:/META-INF/public-web-resources/", + * allowing convenient packaging and serving of resources such as a JavaScript library from within jar files. + * + *
To ensure that users with a primed browser cache get the latest changes to application-specific resources + * upon deployment of new versions of the application, it is recommended that a version string is used in the URL + * mapping pattern that selects this handler. Such patterns can be easily parameterized using Spring EL. See the + * reference manual for further examples of this approach. + * + *
Rather than being directly configured as a bean, this handler will typically be configured through use of
+ * the Checks for the existence of the requested resource in the configured list of locations.
+ * If the resource does not exist, a {@code 404} response will be returned to the client.
+ * If the resource exists, the request will be checked for the presence of the
+ * {@code Last-Modified} header, and its value will be compared against the last-modified
+ * timestamp of the given resource, returning a {@code 304} status code if the
+ * {@code Last-Modified} value is greater. If the resource is newer than the
+ * {@code Last-Modified} value, or the header is not present, the content resource
+ * of the resource will be written to the response with caching headers
+ * set to expire one year in the future.
+ */
+ public void handleRequest(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+
+ checkAndPrepare(request, response, true);
+ Resource resource = getResource(request);
+ if (resource == null) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ if (checkNotModified(resource, request, response)) {
+ return;
+ }
+ writeResponse(resource, response);
+ }
+
+ private Resource 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");
+ }
+ if (!StringUtils.hasText(path) || path.contains("WEB-INF") || path.contains("META-INF")) {
+ return null;
+ }
+ for (Resource resourcePath : this.locations) {
+ try {
+ Resource resource = resourcePath.createRelative(path);
+ if (resource.exists() && resource.isReadable()) {
+ return resource;
+ }
+ }
+ catch (IOException ex) {
+ // resource not found
+ return null;
+ }
+ }
+ return null;
+ }
+
+ private boolean checkNotModified(Resource resource,HttpServletRequest request, HttpServletResponse response)
+ throws IOException {
+
+ long ifModifiedSince = request.getDateHeader("If-Modified-Since");
+ long lastModified = resource.lastModified();
+ boolean notModified = ifModifiedSince >= (lastModified / 1000 * 1000);
+ if (notModified) {
+ response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ }
+ else {
+ response.setDateHeader("Last-Modified", lastModified);
+ }
+ return notModified;
+ }
+
+ private void writeResponse(Resource resource, HttpServletResponse response) throws IOException {
+ MediaType mediaType = getMediaType(resource);
+ if (mediaType != null) {
+ response.setContentType(mediaType.toString());
+ }
+ response.setContentLength(resource.contentLength());
+ FileCopyUtils.copy(resource.getInputStream(), response.getOutputStream());
+ }
+
+ protected MediaType getMediaType(Resource resource) {
+ String mimeType = getServletContext().getMimeType(resource.getFilename());
+ return (StringUtils.hasText(mimeType) ? MediaType.parseMediaType(mimeType) : null);
+ }
+
+}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/package-info.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/package-info.java
new file mode 100644
index 00000000000..79c06c4f2f9
--- /dev/null
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/package-info.java
@@ -0,0 +1,6 @@
+
+/**
+ * Support classes for serving static resources.
+ */
+package org.springframework.web.servlet.config;
+
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resources/DefaultServletHttpRequestHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resources/DefaultServletHttpRequestHandler.java
deleted file mode 100644
index e6ef69101b0..00000000000
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resources/DefaultServletHttpRequestHandler.java
+++ /dev/null
@@ -1,100 +0,0 @@
-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.
- *
- * 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.
- *
- * Requests are handled by forwarding through the {@link RequestDispatcher} obtained via the name specified through the
- * {@code defaultServletName} property. In most cases, the {@code defaultServletName} 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 defaultServletName} 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 defaultServletName;
-
- /**
- * 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.defaultServletName)) {
- if (this.servletContext.getNamedDispatcher(COMMON_DEFAULT_SERVLET_NAME) != null) {
- this.defaultServletName = COMMON_DEFAULT_SERVLET_NAME;
- } else if (this.servletContext.getNamedDispatcher(RESIN_DEFAULT_SERVLET_NAME) != null) {
- this.defaultServletName = RESIN_DEFAULT_SERVLET_NAME;
- } else if (this.servletContext.getNamedDispatcher(WEBLOGIC_DEFAULT_SERVLET_NAME) != null) {
- this.defaultServletName = WEBLOGIC_DEFAULT_SERVLET_NAME;
- } else if (this.servletContext.getNamedDispatcher(WEBSPHERE_DEFAULT_SERVLET_NAME) != null) {
- this.defaultServletName = WEBSPHERE_DEFAULT_SERVLET_NAME;
- }
- Assert.hasText(this.defaultServletName, "Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.");
- }
- }
-
- public void handleRequest(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
- Assert.notNull(rd, "A RequestDispatcher could not be located for the servlet name '"+this.defaultServletName+"'");
- rd.forward(request, response);
- }
-
- /**
- * Set the name of the default Servlet to be forwarded to for static resource requests.
- * @param defaultServletName The name of the Servlet to use for static resources.
- */
- public void setDefaultServletName(String defaultServletName) {
- this.defaultServletName = defaultServletName;
- }
-
- /**
- * {@inheritDoc}
- */
- public void setServletContext(ServletContext servletContext) {
- this.servletContext = servletContext;
- }
-}
\ No newline at end of file
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resources/ResourceHttpRequestHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resources/ResourceHttpRequestHandler.java
deleted file mode 100644
index 65c32b19aa3..00000000000
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resources/ResourceHttpRequestHandler.java
+++ /dev/null
@@ -1,360 +0,0 @@
-package org.springframework.web.servlet.resources;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-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 javax.activation.FileTypeMap;
-import javax.activation.MimetypesFileTypeMap;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-import org.springframework.http.MediaType;
-import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.StringUtils;
-import org.springframework.web.HttpRequestHandler;
-import org.springframework.web.HttpRequestMethodNotSupportedException;
-import org.springframework.web.context.ServletContextAware;
-import org.springframework.web.servlet.HandlerMapping;
-import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
-
-/**
- * {@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.
- *
- * The constructor takes a list of Spring {@link Resource} locations from which static resources are allowed
- * to be served by this handler. For a given request, the list of locations will be consulted in order for the
- * presence of the requested resource, and the first found match will be written to the response, with {@code
- * Expires} and {@code Cache-Control} headers set for one year in the future. The handler also properly evaluates
- * the {@code Last-Modified} header (if present) so that a {@code 304} status code will be returned as appropriate,
- * avoiding unnecessary overhead for resources that are already cached by the client. The use of {@code Resource}
- * locations allows resource requests to easily be mapped to locations other than the web application root. For
- * example, resources could be served from a classpath location such as "classpath:/META-INF/public-web-resources/",
- * allowing convenient packaging and serving of resources such as a JavaScript library from within jar files.
- *
- * To ensure that users with a primed browser cache get the latest changes to application-specific resources
- * upon deployment of new versions of the application, it is recommended that a version string is used in the URL
- * mapping pattern that selects this handler. Such patterns can be easily parameterized using Spring EL. See the
- * reference manual for further examples of this approach.
- *
- * Rather than being directly configured as a bean, this handler will typically be configured through use of
- * the Checks for the existence of the requested resource in the configured list of locations. If the resource
- * does not exist, a {@code 404} response will be returned to the client. If the resource exists, the request will
- * be checked for the presence of the {@code Last-Modified} header, and its value will be compared against the last
- * modified timestamp of the given resource, returning a {@code 304} status code if the {@code Last-Modified} value
- * is greater. If the resource is newer than the {@code Last-Modified} value, or the header is not present, the
- * content resource of the resource will be written to the response with caching headers set to expire one year in
- * the future.
- */
- 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");
- }
- URLResource resource = getResource(request);
- if (resource == null) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- if (checkNotModified(resource, request, response)) {
- return;
- }
- prepareResponse(resource, response);
- writeResponse(resource, request, response);
- }
-
- public void setServletContext(ServletContext servletContext) {
- this.fileMediaTypeMap = new DefaultFileMediaTypeMap(servletContext);
- }
-
- private boolean checkNotModified(Resource resource,HttpServletRequest request, HttpServletResponse response) throws IOException {
- long ifModifiedSince = request.getDateHeader("If-Modified-Since");
- boolean notModified = ifModifiedSince >= (resource.lastModified() / 1000 * 1000);
- if (notModified) {
- response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
- } else {
- response.setDateHeader("Last-Modified", resource.lastModified());
- }
- return notModified;
- }
-
- 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");
- }
- if (path.contains("WEB-INF") || path.contains("META-INF")) {
- return null;
- }
- for (Resource resourcePath : this.resourcePaths) {
- Resource resource;
- try {
- resource = resourcePath.createRelative(path);
- if (isValidFile(resource)) {
- return new URLResource(resource, fileMediaTypeMap.getMediaType(resource.getFilename()));
- }
- } catch (IOException e) {
- //Resource not found
- return null;
- }
- }
- return null;
- }
-
- private void prepareResponse(URLResource resource, HttpServletResponse response) throws IOException {
- response.setContentType(resource.getMediaType().toString());
- 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);
- // HTTP 1.1 header
- response.setHeader("Cache-Control", "max-age=" + this.maxAge);
- }
- }
-
- private void writeResponse(URLResource resource, HttpServletRequest request, HttpServletResponse response) throws IOException {
- OutputStream out = response.getOutputStream();
- try {
- InputStream in = resource.getInputStream();
- try {
- byte[] buffer = new byte[1024];
- int bytesRead = -1;
- while ((bytesRead = in.read(buffer)) != -1) {
- out.write(buffer, 0, bytesRead);
- }
- } finally {
- if (in != null) {
- in.close();
- }
- }
- } finally {
- if (out != null) {
- out.close();
- }
- }
- }
-
- private boolean isValidFile(Resource resource) throws IOException {
- return resource.exists() && StringUtils.hasText(resource.getFilename());
- }
-
- private void validateResourcePaths(List<mvc:resources/> Spring configuration tag.
+ *
+ * @author Keith Donald
+ * @author Jeremy Grelle
+ * @author Juergen Hoeller
+ * @since 3.0.4
+ */
+public class ResourceHttpRequestHandler extends WebContentGenerator implements HttpRequestHandler {
+
+ private List<mvc:resources/> Spring configuration tag.
- *
- * @author Keith Donald
- * @author Jeremy Grelle
- * @since 3.0.4
- */
-public class ResourceHttpRequestHandler implements HttpRequestHandler, ServletContextAware {
-
- private static final Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class);
-
- private final List