Author: musachy Date: Mon May 5 06:09:50 2008 New Revision: 653471 URL: http://svn.apache.org/viewvc?rev=653471&view=rev Log: * Refactor static content loading out of FilterDispatcher into DefaultStaticContentLoader, and add an extension point for it.
Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/DefaultStaticContentLoader.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/StaticContentLoader.java Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/BeanSelectionProvider.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java struts/struts2/trunk/core/src/main/resources/struts-default.xml Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java?rev=653471&r1=653470&r2=653471&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java Mon May 5 06:09:50 2008 @@ -184,4 +184,7 @@ /** The [EMAIL PROTECTED] com.opensymphony.xwork2.util.PatternMatcher} implementation class */ public static final String STRUTS_PATTERNMATCHER = "struts.patternMatcher"; + /** The [EMAIL PROTECTED] org.apache.struts2.dispatcher.StaticContentLoader} implementation class */ + public static final String STRUTS_STATIC_CONTENT_LOADER = "struts.staticContentLoader"; + } Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/BeanSelectionProvider.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/BeanSelectionProvider.java?rev=653471&r1=653470&r2=653471&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/BeanSelectionProvider.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/BeanSelectionProvider.java Mon May 5 06:09:50 2008 @@ -26,6 +26,8 @@ import org.apache.struts2.StrutsConstants; import org.apache.struts2.components.UrlRenderer; +import org.apache.struts2.dispatcher.DefaultStaticContentLoader; +import org.apache.struts2.dispatcher.StaticContentLoader; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.multipart.MultiPartRequest; import org.apache.struts2.views.freemarker.FreemarkerManager; @@ -151,6 +153,12 @@ * <td>singleton</td> * <td>Matches patterns, such as action names, generally used in configuration (since 2.1)</td> * </tr> + * <tr> + * <td>org.apache.struts2.views.dispatcher.DefaultStaticContentLoader</td> + * <td>struts.staticContentLoader</td> + * <td>singleton</td> + * <td>Loads static resources (since 2.1)</td> + * </tr> * </table> * * <!-- END SNIPPET: extensionPoints --> @@ -209,6 +217,7 @@ alias(ReflectionProvider.class, StrutsConstants.STRUTS_REFLECTIONPROVIDER, builder, props); alias(ReflectionContextFactory.class, StrutsConstants.STRUTS_REFLECTIONCONTEXTFACTORY, builder, props); alias(PatternMatcher.class, StrutsConstants.STRUTS_PATTERNMATCHER, builder, props); + alias(StaticContentLoader.class, StrutsConstants.STRUTS_STATIC_CONTENT_LOADER, builder, props); if ("true".equalsIgnoreCase(props.getProperty(StrutsConstants.STRUTS_DEVMODE))) { props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, "true"); Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/DefaultStaticContentLoader.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/DefaultStaticContentLoader.java?rev=653471&view=auto ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/DefaultStaticContentLoader.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/DefaultStaticContentLoader.java Mon May 5 06:09:50 2008 @@ -0,0 +1,343 @@ +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.struts2.dispatcher; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.StringTokenizer; + +import javax.servlet.FilterConfig; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.struts2.StrutsConstants; +import org.apache.struts2.util.ClassLoaderUtils; + +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.ClassLoaderUtil; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +public class DefaultStaticContentLoader implements StaticContentLoader { + /** + * Provide a logging instance. + */ + private Logger log; + + /** + * Store set of path prefixes to use with static resources. + */ + protected String[] pathPrefixes; + + /** + * Store state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting. + */ + protected boolean serveStatic; + + /** + * Store state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE setting. + */ + protected boolean serveStaticBrowserCache; + + /** + * Provide a formatted date for setting heading information when caching static content. + */ + protected final Calendar lastModifiedCal = Calendar.getInstance(); + + /** + * Store state of StrutsConstants.STRUTS_I18N_ENCODING setting. + */ + protected String encoding; + + + /** + * Modify state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting. + * + * @param val + * New setting + */ + @Inject(StrutsConstants.STRUTS_SERVE_STATIC_CONTENT) + public void setServeStaticContent(String val) { + serveStatic = "true".equals(val); + } + + /** + * Modify state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE + * setting. + * + * @param val + * New setting + */ + @Inject(StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE) + public void setServeStaticBrowserCache(String val) { + serveStaticBrowserCache = "true".equals(val); + } + + /** + * Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting. + * @param val New setting + */ + @Inject(StrutsConstants.STRUTS_I18N_ENCODING) + public void setEncoding(String val) { + encoding = val; + } + + /* + * (non-Javadoc) + * + * @see org.apache.struts2.dispatcher.StaticResourceLoader#setFilterConfig(javax.servlet.FilterConfig) + */ + public void setFilterConfig(FilterConfig filterConfig) { + String param = filterConfig.getInitParameter("packages"); + String packages = getAdditionalPackages(); + if (param != null) { + packages = param + " " + packages; + } + this.pathPrefixes = parse(packages); + initLogging(filterConfig); + } + + protected String getAdditionalPackages() { + return "org.apache.struts2.static template org.apache.struts2.interceptor.debugging static"; + } + + /** + * Create a string array from a comma-delimited list of packages. + * + * @param packages + * A comma-delimited String listing packages + * @return A string array of packages + */ + 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()]); + } + + /* + * (non-Javadoc) + * + * @see org.apache.struts2.dispatcher.StaticResourceLoader#findStaticResource(java.lang.String, + * javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + public void findStaticResource(String path, HttpServletRequest request, HttpServletResponse response) + throws IOException { + String name = cleanupPath(path); + if (!name.endsWith(".class")) { + for (String pathPrefix : pathPrefixes) { + InputStream is = findInputStream(buildPath(name, pathPrefix)); + if (is != null) { + process(is, path, request, response); + return; + } + } + } + + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + + protected void process(InputStream is, String path, HttpServletRequest request, HttpServletResponse response) throws IOException { + 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(path); + 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; + } + } + + private void initLogging(FilterConfig filterConfig) { + String factoryName = filterConfig.getInitParameter("loggerFactory"); + if (factoryName != null) { + try { + Class cls = ClassLoaderUtils.loadClass(factoryName, this.getClass()); + LoggerFactory fac = (LoggerFactory)cls.newInstance(); + LoggerFactory.setLoggerFactory(fac); + } catch (InstantiationException e) { + System.err.println("Unable to instantiate logger factory: "+factoryName+", using default"); + e.printStackTrace(); + } catch (IllegalAccessException e) { + System.err.println("Unable to access logger factory: "+factoryName+", using default"); + e.printStackTrace(); + } catch (ClassNotFoundException e) { + System.err.println("Unable to locate logger factory class: "+factoryName+", using default"); + e.printStackTrace(); + } + } + + log = LoggerFactory.getLogger(FilterDispatcher.class); + + } + + /** + * Look for a static resource in the classpath. + * + * @param path The resource path + * @return The inputstream of the resource + * @throws IOException If there is a problem locating the resource + */ + protected InputStream findInputStream(String path) throws IOException { + return ClassLoaderUtil.getResourceAsStream(path, getClass()); + } + + /** + * @param name resource name + * @param packagePrefix The package prefix to use to locate the resource + * @return full path + * @throws UnsupportedEncodingException + * @throws IOException + */ + protected String buildPath(String name, String packagePrefix) throws UnsupportedEncodingException { + String resourcePath; + if (packagePrefix.endsWith("/") && name.startsWith("/")) { + resourcePath = packagePrefix + name.substring(1); + } else { + resourcePath = packagePrefix + name; + } + + return URLDecoder.decode(resourcePath, encoding); + } + + + + /** + * Determine the content type for the resource name. + * + * @param name The resource name + * @return The mime type + */ + 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; + } + } + + /** + * Copy bytes from the input stream to the output stream. + * + * @param input + * The input stream + * @param output + * The output stream + * @throws IOException + * If anything goes wrong + */ + 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(); + } + + public boolean canHandle(String resourcePath) { + return serveStatic && (resourcePath.startsWith("/struts") || resourcePath.startsWith("/static")); + } + + /** + * @param path requested path + * @return path without leading "/struts" or "/static" + */ + protected String cleanupPath(String path) { + //path will start with "/struts" or "/static", remove them + return path.substring(7); + } +} Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java?rev=653471&r1=653470&r2=653471&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/FilterDispatcher.java Mon May 5 06:09:50 2008 @@ -157,31 +157,6 @@ private Logger log; /** - * Store set of path prefixes to use with static resources. - */ - private String[] pathPrefixes; - - /** - * Provide a formatted date for setting heading information when caching static content. - */ - private final Calendar lastModifiedCal = Calendar.getInstance(); - - /** - * Store state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting. - */ - private boolean serveStatic; - - /** - * Store state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE setting. - */ - private boolean serveStaticBrowserCache; - - /** - * Store state of StrutsConstants.STRUTS_I18N_ENCODING setting. - */ - private String encoding; - - /** * Provide ActionMapper instance, set by injection. */ private ActionMapper actionMapper; @@ -197,6 +172,11 @@ protected Dispatcher dispatcher; /** + * Loads stattic resources, set by injection + */ + protected StaticContentLoader staticResourceLoader; + + /** * Initializes the filter by creating a default dispatcher * and setting the default packages for static resources. * @@ -212,12 +192,7 @@ dispatcher.init(); dispatcher.getContainer().inject(this); - 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); + staticResourceLoader.setFilterConfig(filterConfig); } finally { ActionContext.setContext(null); } @@ -282,38 +257,16 @@ } /** - * Modify state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting. - * - * @param val New setting - */ - @Inject(StrutsConstants.STRUTS_SERVE_STATIC_CONTENT) - public void setServeStaticContent(String val) { - serveStatic = "true".equals(val); - } - - /** - * Modify state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE setting. - * - * @param val New setting - */ - @Inject(StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE) - public void setServeStaticBrowserCache(String val) { - serveStaticBrowserCache = "true".equals(val); - } - - /** - * Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting. - * - * @param val New setting + * Modify state of StrutsConstants.STRUTS_STATIC_CONTENT_LOADER setting. + * @param staticResourceLoader val New setting */ - @Inject(StrutsConstants.STRUTS_I18N_ENCODING) - public void setEncoding(String val) { - encoding = val; + @Inject + public void setStaticResourceLoader(StaticContentLoader staticResourceLoader) { + this.staticResourceLoader = staticResourceLoader; } /** * Modify ActionMapper instance. - * * @param mapper New instance */ @Inject @@ -384,31 +337,6 @@ } /** - * Create a string array from a comma-delimited list of packages. - * - * @param packages A comma-delimited String listing packages - * @return A string array of packages - */ - 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()]); - } - - - /** * Process an action or handle a request a static resource. * <p/> * The filter tries to match the request to an action mapping. @@ -456,9 +384,8 @@ resourcePath = request.getPathInfo(); } - if (serveStatic && resourcePath.startsWith("/struts")) { - String name = resourcePath.substring("/struts".length()); - findStaticResource(name, request, response); + if (staticResourceLoader.canHandle(resourcePath)) { + staticResourceLoader.findStaticResource(resourcePath, request, response); } else { // this is a normal request, let it pass through chain.doFilter(request, response); @@ -477,137 +404,4 @@ } } } - - /** - * Locate a static resource and copy directly to the response, - * setting the appropriate caching headers. - * - * @param name The resource name - * @param request The request - * @param response The response - * @throws IOException If anything goes wrong - */ - 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); - } - - /** - * Determine the content type for the resource name. - * - * @param name The resource name - * @return The mime type - */ - 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; - } - } - - /** - * Copy bytes from the input stream to the output stream. - * - * @param input The input stream - * @param output The output stream - * @throws IOException If anything goes wrong - */ - 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(); - } - - /** - * Look for a static resource in the classpath. - * - * @param name The resource name - * @param packagePrefix The package prefix to use to locate the resource - * @return The inputstream of the resource - * @throws IOException If there is a problem locating the resource - */ - 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()); - } } Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/StaticContentLoader.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/StaticContentLoader.java?rev=653471&view=auto ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/StaticContentLoader.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/StaticContentLoader.java Mon May 5 06:09:50 2008 @@ -0,0 +1,64 @@ +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.struts2.dispatcher; + +import java.io.IOException; + +import javax.servlet.FilterConfig; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Interface for loading static resources, based on a path + * + */ +public interface StaticContentLoader { + + /** + * @param path + * Requested resource path + * @return true if this loader is able to load this type of resource, false otherwise + */ + public boolean canHandle(String path); + + /** + * @param filterConfig + * The filter configuration + */ + public abstract void setFilterConfig(FilterConfig filterConfig); + + /** + * Locate a static resource and copy directly to the response, setting the + * appropriate caching headers. + * + * @param name + * The resource name + * @param request + * The request + * @param response + * The response + * @throws IOException + * If anything goes wrong + */ + public abstract void findStaticResource(String path, HttpServletRequest request, HttpServletResponse response) + throws IOException; + +} \ No newline at end of file Modified: struts/struts2/trunk/core/src/main/resources/struts-default.xml URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/struts-default.xml?rev=653471&r1=653470&r2=653471&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/resources/struts-default.xml (original) +++ struts/struts2/trunk/core/src/main/resources/struts-default.xml Mon May 5 06:09:50 2008 @@ -95,6 +95,8 @@ <bean class="org.apache.struts2.views.jsp.ui.OgnlTool" /> + <bean type="org.apache.struts2.dispatcher.StaticContentLoader" class="org.apache.struts2.dispatcher.DefaultStaticContentLoader" name="struts" /> + <!-- Silly workarounds for OGNL since there is currently no way to flush its internal caches --> <bean type="ognl.PropertyAccessor" name="java.util.ArrayList" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.HashSet" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />