Remove ResourceServlet deprecated in 4.3.x
Issue: SPR-15984
This commit is contained in:
parent
9c216c2144
commit
132022861e
|
@ -1,346 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2017 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.IOException;
|
|
||||||
import javax.servlet.RequestDispatcher;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.util.AntPathMatcher;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* <p>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).
|
|
||||||
*
|
|
||||||
* <p>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.
|
|
||||||
*
|
|
||||||
* <p>The {@code 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.
|
|
||||||
*
|
|
||||||
* <p>The "resource" parameter and the {@code 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.
|
|
||||||
*
|
|
||||||
* <p>The {@code 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!
|
|
||||||
*
|
|
||||||
* <p>If using this servlet for direct access rather than via includes,
|
|
||||||
* the {@code 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.
|
|
||||||
*
|
|
||||||
* <p>To apply last-modified timestamps for the target resource, set the
|
|
||||||
* {@code 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.
|
|
||||||
*
|
|
||||||
* <p>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.
|
|
||||||
*
|
|
||||||
* <p>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.
|
|
||||||
*
|
|
||||||
* <p>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 Juergen Hoeller
|
|
||||||
* @author Rod Johnson
|
|
||||||
* @see #setDefaultUrl
|
|
||||||
* @see #setAllowedResources
|
|
||||||
* @see #setApplyLastModified
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
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";
|
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String defaultUrl;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String allowedResources;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String contentType;
|
|
||||||
|
|
||||||
private boolean applyLastModified = false;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
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.
|
|
||||||
* <p>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.
|
|
||||||
* <p>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".
|
|
||||||
* <p>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.
|
|
||||||
* <p>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.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
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 | 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.
|
|
||||||
* <p>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 {@code null} if none found
|
|
||||||
* @see #RESOURCE_PARAM_NAME
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
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 (String url : resourceUrls) {
|
|
||||||
// check whether URL matches allowed resources
|
|
||||||
if (this.allowedResources != null) {
|
|
||||||
Assert.state(this.pathMatcher != null, "No PathMatcher available");
|
|
||||||
if (!this.pathMatcher.match(this.allowedResources, url)) {
|
|
||||||
throw new ServletException("Resource [" + url +
|
|
||||||
"] does not match allowed pattern [" + this.allowedResources + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Including resource [" + url + "]");
|
|
||||||
}
|
|
||||||
RequestDispatcher rd = request.getRequestDispatcher(url);
|
|
||||||
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).
|
|
||||||
* <p>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.
|
|
||||||
* <p>Returns the startup time of this servlet if the file that corresponds
|
|
||||||
* to the target resource URL couldn't be resolved (for example, because
|
|
||||||
* the WAR is not expanded).
|
|
||||||
* @see #determineResourceUrl
|
|
||||||
* @see #getFileTimestamp
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
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 (String url : resourceUrls) {
|
|
||||||
long timestamp = getFileTimestamp(url);
|
|
||||||
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) {
|
|
||||||
ServletContextResource resource = new ServletContextResource(getServletContext(), resourceUrl);
|
|
||||||
try {
|
|
||||||
long lastModifiedTime = resource.lastModified();
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Last-modified timestamp of " + resource + " is " + lastModifiedTime);
|
|
||||||
}
|
|
||||||
return lastModifiedTime;
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
logger.warn("Couldn't retrieve last-modified timestamp of [" + resource +
|
|
||||||
"] - using ResourceServlet startup time");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue