1 changed files with 0 additions and 347 deletions
@ -1,347 +0,0 @@
@@ -1,347 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2016 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.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. |
||||
* |
||||
* <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"; |
||||
|
||||
|
||||
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. |
||||
* <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 {@link PathMatcher} to use for matching the "allowedResources" URL pattern. |
||||
* <p>The default is {@link 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 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. |
||||
* <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 |
||||
*/ |
||||
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 && !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) { |
||||
if (logger.isWarnEnabled()) { |
||||
logger.warn("Couldn't retrieve last-modified timestamp of " + resource + |
||||
" - using ResourceServlet startup time"); |
||||
} |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue