All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.web.servlet.ResourceServlet Maven / Gradle / Ivy

There is a newer version: 5.3.34
Show newest version
/*
 * Copyright 2002-2005 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;

import java.io.File;
import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.ServletContextResource;

/**
 * Simple servlet that can expose an internal resource, including a 
 * default URL if the specified resource is not found. An alternative,
 * for example, to trying and catching exceptions when using JSP include.
 *
 * 

A further usage of this servlet is the ability to apply last-modified * timestamps to quasi-static resources (typically JSPs). This can happen * as bridge to parameter-specified resources, or as proxy for a specific * target resource (or a list of specific target resources to combine). * *

A typical usage would map a URL like "/ResourceServlet" onto an instance * of this servlet, and use the "JSP include" action to include this URL, * with the "resource" parameter indicating the actual target path in the WAR. * *

The defaultUrl property can be set to the internal * resource path of a default URL, to be rendered when the target resource * is not found or not specified in the first place. * *

The "resource" parameter and the defaultUrl property can * also specify a list of target resources to combine. Those resources will be * included one by one to build the response. If last-modified determination * is active, the newest timestamp among those files will be used. * *

The allowedResources property can be set to a URL * pattern of resources that should be available via this servlet. * If not set, any target resource can be requested, including resources * in the WEB-INF directory! * *

If using this servlet for direct access rather than via includes, * the contentType property should be specified to apply a * proper content type. Note that a content type header in the target JSP will * be ignored when including the resource via a RequestDispatcher include. * *

To apply last-modified timestamps for the target resource, set the * applyLastModified property to true. This servlet will then * return the file timestamp of the target resource as last-modified value, * falling back to the startup time of this servlet if not retrievable. * *

Note that applying the last-modified timestamp in the above fashion * just makes sense if the target resource does not generate content that * depends on the HttpSession or cookies; it is just allowed to evaluate * request parameters. * *

A typical case for such last-modified usage is a JSP that just makes * minimal usage of basic means like includes or message resolution to * build quasi-static content. Regenerating such content on every request * is unnecessary; it can be cached as long as the file hasn't changed. * *

Note that this servlet will apply the last-modified timestamp if you * tell it to do so: It's your decision whether the content of the target * resource can be cached in such a fashion. Typical use cases are helper * resources that are not fronted by a controller, like JavaScript files * that are generated by a JSP (without depending on the HttpSession). * * @author Rod Johnson * @author Juergen Hoeller * @see #setDefaultUrl * @see #setAllowedResources * @see #setApplyLastModified */ public class ResourceServlet extends HttpServletBean { /** * Any number of these characters are considered delimiters * between multiple resource paths in a single String value. */ public static final String RESOURCE_URL_DELIMITERS = ",; \t\n"; /** * Name of the parameter that must contain the actual resource path. */ public static final String RESOURCE_PARAM_NAME = "resource"; private String defaultUrl; private String allowedResources; private String contentType; private boolean applyLastModified = false; private PathMatcher pathMatcher; private long startupTime; /** * Set the URL within the current web application from which to * include content if the requested path isn't found, or if none * is specified in the first place. *

If specifying multiple URLs, they will be included one by one * to build the response. If last-modified determination is active, * the newest timestamp among those files will be used. * @see #setApplyLastModified */ public void setDefaultUrl(String defaultUrl) { this.defaultUrl = defaultUrl; } /** * Set allowed resources as URL pattern, e.g. "/WEB-INF/res/*.jsp", * The parameter can be any Ant-style pattern parsable by AntPathMatcher. * @see org.springframework.util.AntPathMatcher */ public void setAllowedResources(String allowedResources) { this.allowedResources = allowedResources; } /** * Set the content type of the target resource (typically a JSP). * Default is none, which is appropriate when including resources. *

For directly accessing resources, for example to leverage this * servlet's last-modified support, specify a content type here. * Note that a content type header in the target JSP will be ignored * when including the resource via a RequestDispatcher include. */ public void setContentType(String contentType) { this.contentType = contentType; } /** * Set whether to apply the file timestamp of the target resource * as last-modified value. Default is "false". *

This is mainly intended for JSP targets that don't generate * session-specific or database-driven content: Such files can be * cached by the browser as long as the last-modified timestamp * of the JSP file doesn't change. *

This will only work correctly with expanded WAR files that * allow access to the file timestamps. Else, the startup time * of this servlet is returned. */ public void setApplyLastModified(boolean applyLastModified) { this.applyLastModified = applyLastModified; } /** * Remember the startup time, using no last-modified time before it. */ protected void initServletBean() { this.pathMatcher = getPathMatcher(); this.startupTime = System.currentTimeMillis(); } /** * Return a PathMatcher to use for matching the "allowedResources" URL pattern. * Default is AntPathMatcher. * @see #setAllowedResources * @see org.springframework.util.AntPathMatcher */ protected PathMatcher getPathMatcher() { return new AntPathMatcher(); } /** * Determine the URL of the target resource and include it. * @see #determineResourceUrl */ protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // determine URL of resource to include String resourceUrl = determineResourceUrl(request); if (resourceUrl != null) { try { doInclude(request, response, resourceUrl); } catch (ServletException ex) { if (logger.isWarnEnabled()) { logger.warn("Failed to include content of resource [" + resourceUrl + "]", ex); } // Try including default URL if appropriate. if (!includeDefaultUrl(request, response)) { throw ex; } } catch (IOException ex) { if (logger.isWarnEnabled()) { logger.warn("Failed to include content of resource [" + resourceUrl + "]", ex); } // Try including default URL if appropriate. if (!includeDefaultUrl(request, response)) { throw ex; } } } // no resource URL specified -> try to include default URL. else if (!includeDefaultUrl(request, response)) { throw new ServletException("No target resource URL found for request"); } } /** * Determine the URL of the target resource of this request. *

Default implementation returns the value of the "resource" parameter. * Can be overridden in subclasses. * @param request current HTTP request * @return the URL of the target resource, or null if none found * @see #RESOURCE_PARAM_NAME */ protected String determineResourceUrl(HttpServletRequest request) { return request.getParameter(RESOURCE_PARAM_NAME); } /** * Include the specified default URL, if appropriate. * @param request current HTTP request * @param response current HTTP response * @return whether a default URL was included * @throws ServletException if thrown by the RequestDispatcher * @throws IOException if thrown by the RequestDispatcher */ private boolean includeDefaultUrl(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (this.defaultUrl == null) { return false; } doInclude(request, response, this.defaultUrl); return true; } /** * Include the specified resource via the RequestDispatcher. * @param request current HTTP request * @param response current HTTP response * @param resourceUrl the URL of the target resource * @throws ServletException if thrown by the RequestDispatcher * @throws IOException if thrown by the RequestDispatcher */ private void doInclude(HttpServletRequest request, HttpServletResponse response, String resourceUrl) throws ServletException, IOException { if (this.contentType != null) { response.setContentType(this.contentType); } String[] resourceUrls = StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS); for (int i = 0; i < resourceUrls.length; i++) { // check whether URL matches allowed resources if (this.allowedResources != null && !this.pathMatcher.match(this.allowedResources, resourceUrls[i])) { throw new ServletException("Resource [" + resourceUrls[i] + "] does not match allowed pattern [" + this.allowedResources + "]"); } if (logger.isDebugEnabled()) { logger.debug("Including resource [" + resourceUrls[i] + "]"); } RequestDispatcher rd = request.getRequestDispatcher(resourceUrls[i]); rd.include(request, response); } } /** * Return the last-modified timestamp of the file that corresponds * to the target resource URL (i.e. typically the request ".jsp" file). * Will simply return -1 if "applyLastModified" is false (the default). *

Returns no last-modified date before the startup time of this servlet, * to allow for message resolution etc that influences JSP contents, * assuming that those background resources might have changed on restart. *

Returns the startup time of this servlet if the file that corresponds * to the target resource URL coudln't be resolved (for example, because * the WAR is not expanded). * @see #determineResourceUrl * @see #getFileTimestamp */ protected final long getLastModified(HttpServletRequest request) { if (this.applyLastModified) { String resourceUrl = determineResourceUrl(request); if (resourceUrl == null) { resourceUrl = this.defaultUrl; } if (resourceUrl != null) { String[] resourceUrls = StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS); long latestTimestamp = -1; for (int i = 0; i < resourceUrls.length; i++) { long timestamp = getFileTimestamp(resourceUrls[i]); if (timestamp > latestTimestamp) { latestTimestamp = timestamp; } } return (latestTimestamp > this.startupTime ? latestTimestamp : this.startupTime); } } return -1; } /** * Return the file timestamp for the given resource. * @param resourceUrl the URL of the resource * @return the file timestamp in milliseconds, * or -1 if not determinable */ protected long getFileTimestamp(String resourceUrl) { try { File resource = new ServletContextResource(getServletContext(), resourceUrl).getFile(); long lastModifiedTime = resource.lastModified(); if (logger.isDebugEnabled()) { logger.debug("Last-modified timestamp of resource file [" + resource.getAbsolutePath() + "] is [" + lastModifiedTime + "]"); } return lastModifiedTime; } catch (IOException ex) { logger.warn("Couldn't retrieve lastModified timestamp of resource [" + resourceUrl + "] - returning ResourceServlet startup time"); return -1; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy