org.apache.struts2.dispatcher
public class: FilterDispatcher [javadoc |
source]
java.lang.Object
org.apache.struts2.dispatcher.FilterDispatcher
All Implemented Interfaces:
Filter, StrutsStatics
Direct Known Subclasses:
FilterDispatcherCompatWeblogic61
Master filter for Struts that handles four distinct
responsibilities:
- Executing actions
- Cleaning up the ActionContext (see note)
- Serving static content
- Kicking off XWork's interceptor chain for the request lifecycle
IMPORTANT: this filter must be mapped to all requests. Unless you know exactly what you are doing, always
map to this URL pattern: /*
Executing actions
This filter executes actions by consulting the
ActionMapper and determining if the requested URL should
invoke an action. If the mapper indicates it should,
the rest of the filter chain is stopped and the action is
invoked. This is important, as it means that filters like the SiteMesh filter must be placed
before this
filter or they will not be able to decorate the output of actions.
Cleaning up the ActionContext
This filter will also automatically clean up the
ActionContext for you, ensuring that no memory leaks
take place. However, this can sometimes cause problems integrating with other products like SiteMesh. See
ActionContextCleanUp for more information on how to deal with this.
Serving static content
This filter also serves common static content needed when using various parts of Struts, such as JavaScript
files, CSS files, etc. It works by looking for requests to /struts/*, and then mapping the value after "/struts/"
to common packages in Struts and, optionally, in your class path. By default, the following packages are
automatically searched:
- org.apache.struts2.static
- template
This means that you can simply request /struts/xhtml/styles.css and the XHTML UI theme's default stylesheet
will be returned. Likewise, many of the AJAX UI components require various JavaScript files, which are found in the
org.apache.struts2.static package. If you wish to add additional packages to be searched, you can add a comma
separated (space, tab and new line will do as well) list in the filter init parameter named "packages".
Be
careful, however, to expose any packages that may have sensitive information, such as properties file with
database access credentials.
This filter supports the following init-params:
To use a custom
Dispatcher , the
createDispatcher() method could be overriden by
the subclass.
Also see:
- ActionMapper
- ActionContextCleanUp
- version:
$ - Date: 2007-04-19 17:00:20 +0200 (Do, 19 Apr 2007) $ $Id: FilterDispatcher.java 530439 2007-04-19 15:00:20Z hermanns $
| Field Summary |
|---|
| protected Dispatcher | dispatcher | Expose Dispatcher instance to subclass. |
| Method from org.apache.struts2.dispatcher.FilterDispatcher Summary: |
|---|
|
copy, createDispatcher, destroy, doFilter, findInputStream, findStaticResource, getContentType, getFilterConfig, getServletContext, init, parse, prepareDispatcherAndWrapRequest, setActionMapper, setEncoding, setServeStaticBrowserCache, setServeStaticContent |
| Method from org.apache.struts2.dispatcher.FilterDispatcher Detail: |
protected void copy(InputStream input,
OutputStream output) throws IOException {
final byte[] buffer = new byte[4096];
int n;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
output.flush(); // WW-1526
}
Copy bytes from the input stream to the output stream. |
protected Dispatcher createDispatcher(FilterConfig filterConfig) {
Map< String,String > params = new HashMap< String,String >();
for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements(); ) {
String name = (String) e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
return new Dispatcher(filterConfig.getServletContext(), params);
}
Create a default Dispatcher that subclasses can override
with a custom Dispatcher, if needed. |
public void destroy() {
if (dispatcher == null) {
LOG.warn("something is seriously wrong, Dispatcher is not initialized (null) ");
} else {
dispatcher.cleanup();
}
}
Calls dispatcher.cleanup,
which in turn releases local threads and destroys any DispatchListeners. |
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
ServletContext servletContext = getServletContext();
String timerKey = "FilterDispatcher_doFilter: ";
try {
UtilTimerStack.push(timerKey);
request = prepareDispatcherAndWrapRequest(request, response);
ActionMapping mapping;
try {
mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
} catch (Exception ex) {
LOG.error("error getting ActionMapping", ex);
dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
return;
}
if (mapping == null) {
// there is no action in this request, should we look for a static resource?
String resourcePath = RequestUtils.getServletPath(request);
if ("".equals(resourcePath) && null != request.getPathInfo()) {
resourcePath = request.getPathInfo();
}
if (serveStatic && resourcePath.startsWith("/struts")) {
String name = resourcePath.substring("/struts".length());
findStaticResource(name, request, response);
} else {
// this is a normal request, let it pass through
chain.doFilter(request, response);
}
// The framework did its job here
return;
}
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
}
}
Process an action or handle a request a static resource.
The filter tries to match the request to an action mapping.
If mapping is found, the action processes is delegated to the dispatcher's serviceAction method.
If action processing fails, doFilter will try to create an error page via the dispatcher.
Otherwise, if the request is for a static resource,
the resource is copied directly to the response, with the appropriate caching headers set.
If the request does not match an action mapping, or a static resource page,
then it passes through. |
protected InputStream findInputStream(String name,
String packagePrefix) throws IOException {
String resourcePath;
if (packagePrefix.endsWith("/") && name.startsWith("/")) {
resourcePath = packagePrefix + name.substring(1);
} else {
resourcePath = packagePrefix + name;
}
resourcePath = URLDecoder.decode(resourcePath, encoding);
return ClassLoaderUtil.getResourceAsStream(resourcePath, getClass());
}
Look for a static resource in the classpath. |
protected void findStaticResource(String name,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
if (!name.endsWith(".class")) {
for (String pathPrefix : pathPrefixes) {
InputStream is = findInputStream(name, pathPrefix);
if (is != null) {
Calendar cal = Calendar.getInstance();
// check for if-modified-since, prior to any other headers
long ifModifiedSince = 0;
try {
ifModifiedSince = request.getDateHeader("If-Modified-Since");
} catch (Exception e) {
LOG.warn("Invalid If-Modified-Since header value: '" + request.getHeader("If-Modified-Since") + "', ignoring");
}
long lastModifiedMillis = lastModifiedCal.getTimeInMillis();
long now = cal.getTimeInMillis();
cal.add(Calendar.DAY_OF_MONTH, 1);
long expires = cal.getTimeInMillis();
if (ifModifiedSince > 0 && ifModifiedSince < = lastModifiedMillis) {
// not modified, content is not sent - only basic headers and status SC_NOT_MODIFIED
response.setDateHeader("Expires", expires);
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
is.close();
return;
}
// set the content-type header
String contentType = getContentType(name);
if (contentType != null) {
response.setContentType(contentType);
}
if (serveStaticBrowserCache) {
// set heading information for caching static content
response.setDateHeader("Date", now);
response.setDateHeader("Expires", expires);
response.setDateHeader("Retry-After", expires);
response.setHeader("Cache-Control", "public");
response.setDateHeader("Last-Modified", lastModifiedMillis);
} else {
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "-1");
}
try {
copy(is, response.getOutputStream());
} finally {
is.close();
}
return;
}
}
}
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
Locate a static resource and copy directly to the response,
setting the appropriate caching headers. |
protected String getContentType(String name) {
// NOT using the code provided activation.jar to avoid adding yet another dependency
// this is generally OK, since these are the main files we server up
if (name.endsWith(".js")) {
return "text/javascript";
} else if (name.endsWith(".css")) {
return "text/css";
} else if (name.endsWith(".html")) {
return "text/html";
} else if (name.endsWith(".txt")) {
return "text/plain";
} else if (name.endsWith(".gif")) {
return "image/gif";
} else if (name.endsWith(".jpg") || name.endsWith(".jpeg")) {
return "image/jpeg";
} else if (name.endsWith(".png")) {
return "image/png";
} else {
return null;
}
}
Determine the content type for the resource name. |
protected FilterConfig getFilterConfig() {
return filterConfig;
}
Expose the FilterConfig instance. |
protected ServletContext getServletContext() {
return filterConfig.getServletContext();
}
Provide a workaround for some versions of WebLogic.
Servlet 2.3 specifies that the servlet context can be retrieved from the session. Unfortunately, some versions of
WebLogic can only retrieve the servlet context from the filter config. Hence, this method enables subclasses to
retrieve the servlet context from other sources. |
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
dispatcher = createDispatcher(filterConfig);
dispatcher.init();
String param = filterConfig.getInitParameter("packages");
String packages = "org.apache.struts2.static template org.apache.struts2.interceptor.debugging";
if (param != null) {
packages = param + " " + packages;
}
this.pathPrefixes = parse(packages);
}
Initializes the filter by creating a default dispatcher
and setting the default packages for static resources. |
protected String[] parse(String packages) {
if (packages == null) {
return null;
}
List< String > pathPrefixes = new ArrayList< String >();
StringTokenizer st = new StringTokenizer(packages, ", \n\t");
while (st.hasMoreTokens()) {
String pathPrefix = st.nextToken().replace('.", '/");
if (!pathPrefix.endsWith("/")) {
pathPrefix += "/";
}
pathPrefixes.add(pathPrefix);
}
return pathPrefixes.toArray(new String[pathPrefixes.size()]);
}
Create a string array from a comma-delimited list of packages. |
protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException {
Dispatcher du = Dispatcher.getInstance();
// Prepare and wrap the request if the cleanup filter hasn't already, cleanup filter should be
// configured first before struts2 dispatcher filter, hence when its cleanup filter's turn,
// static instance of Dispatcher should be null.
if (du == null) {
Dispatcher.setInstance(dispatcher);
// prepare the request no matter what - this ensures that the proper character encoding
// is used before invoking the mapper (see WW-9127)
dispatcher.prepare(request, response);
} else {
dispatcher = du;
}
try {
// Wrap request first, just in case it is multipart/form-data
// parameters might not be accessible through before encoding (ww-1278)
request = dispatcher.wrapRequest(request, getServletContext());
} catch (IOException e) {
String message = "Could not wrap servlet request with MultipartRequestWrapper!";
LOG.error(message, e);
throw new ServletException(message, e);
}
return request;
}
Wrap and return the given request, if needed, so as to to transparently
handle multipart data as a wrapped class around the given request. |
public static void setActionMapper(ActionMapper mapper) {
actionMapper = mapper;
}
Modify ActionMapper instance. |
public static void setEncoding(String val) {
encoding = val;
}
Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting. |
public static void setServeStaticBrowserCache(String val) {
serveStaticBrowserCache = "true".equals(val);
}
Modify state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE setting. |
public static void setServeStaticContent(String val) {
serveStatic = "true".equals(val);
}
Modify state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting. |