Author: rgielen Date: Sat Apr 25 13:49:50 2009 New Revision: 768529 URL: http://svn.apache.org/viewvc?rev=768529&view=rev Log: WW-3017 - Making it possible to have a configurable list of uri-patterns telling struts not to handle the request - applied patch provided by Andreas Joseph Krogh - made some refactoring to eliminate duplicate code and unneeded method calls - added integration test
Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/InitOperations.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/PrepareOperations.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsExecuteFilter.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareAndExecuteFilter.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareFilter.java struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ng/StrutsPrepareAndExecuteFilterIntegrationTest.java 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=768529&r1=768528&r2=768529&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 Sat Apr 25 13:49:50 2009 @@ -44,6 +44,9 @@ /** The URL extension to use to determine if the request is meant for a Struts action */ public static final String STRUTS_ACTION_EXTENSION = "struts.action.extension"; + /** Comma separated list of patterns (java.util.regex.Pattern) to be excluded from Struts2-processing */ + public static final String STRUTS_ACTION_EXCLUDE_PATTERN = "struts.action.exclude_pattern"; + /** Whether to use the alterative syntax for the tags or not */ public static final String STRUTS_TAG_ALTSYNTAX = "struts.tag.altSyntax"; Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/InitOperations.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/InitOperations.java?rev=768529&r1=768528&r2=768529&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/InitOperations.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/InitOperations.java Sat Apr 25 13:49:50 2009 @@ -25,10 +25,10 @@ import org.apache.struts2.dispatcher.Dispatcher; import org.apache.struts2.dispatcher.StaticContentLoader; import org.apache.struts2.util.ClassLoaderUtils; +import org.apache.struts2.StrutsConstants; -import java.util.HashMap; -import java.util.Map; -import java.util.Iterator; +import java.util.*; +import java.util.regex.Pattern; /** * Contains initialization operations @@ -41,20 +41,20 @@ /** * Initializes the internal Struts logging */ - public void initLogging(HostConfig filterConfig) { + public void initLogging( HostConfig 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) { + } catch ( InstantiationException e ) { System.err.println("Unable to instantiate logger factory: " + factoryName + ", using default"); e.printStackTrace(); - } catch (IllegalAccessException e) { + } catch ( IllegalAccessException e ) { System.err.println("Unable to access logger factory: " + factoryName + ", using default"); e.printStackTrace(); - } catch (ClassNotFoundException e) { + } catch ( ClassNotFoundException e ) { System.err.println("Unable to locate logger factory class: " + factoryName + ", using default"); e.printStackTrace(); } @@ -64,7 +64,7 @@ /** * Creates and initializes the dispatcher */ - public Dispatcher initDispatcher(HostConfig filterConfig) { + public Dispatcher initDispatcher( HostConfig filterConfig ) { Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init(); return dispatcher; @@ -73,7 +73,7 @@ /** * Initializes the static content loader with the filter configuration */ - public StaticContentLoader initStaticContentLoader(HostConfig filterConfig, Dispatcher dispatcher) { + public StaticContentLoader initStaticContentLoader( HostConfig filterConfig, Dispatcher dispatcher ) { StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class); loader.setHostConfig(filterConfig); return loader; @@ -81,6 +81,7 @@ /** * @return The dispatcher on the thread. + * * @throws IllegalStateException If there is no dispatcher available */ public Dispatcher findDispatcherOnThread() { @@ -92,11 +93,11 @@ } /** - * Create a {...@link Dispatcher} + * Create a {...@link Dispatcher} */ - private Dispatcher createDispatcher(HostConfig filterConfig) { + private Dispatcher createDispatcher( HostConfig filterConfig ) { Map<String, String> params = new HashMap<String, String>(); - for (Iterator e = filterConfig.getInitParameterNames(); e.hasNext();) { + for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) { String name = (String) e.next(); String value = filterConfig.getInitParameter(name); params.put(name, value); @@ -107,4 +108,31 @@ public void cleanup() { ActionContext.setContext(null); } + + /** + * Extract a list of patterns to exclude from request filtering + * + * @param dispatcher The dispatcher to check for exclude pattern configuration + * + * @return a List of Patterns for request to exclude if apply, or <tt>null</tt> + * + * @see org.apache.struts2.StrutsConstants#STRUTS_ACTION_EXCLUDE_PATTERN + */ + public List<Pattern> buildExcludedPatternsList( Dispatcher dispatcher ) { + return buildExcludedPatternsList(dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN)); + } + + private List<Pattern> buildExcludedPatternsList( String patterns ) { + if (null != patterns && patterns.trim().length() != 0) { + List<Pattern> list = new ArrayList<Pattern>(); + String[] tokens = patterns.split(","); + for ( String token : tokens ) { + list.add(Pattern.compile(token.trim())); + } + return Collections.unmodifiableList(list); + } else { + return null; + } + } + } Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/PrepareOperations.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/PrepareOperations.java?rev=768529&r1=768528&r2=768529&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/PrepareOperations.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/PrepareOperations.java Sat Apr 25 13:49:50 2009 @@ -24,6 +24,7 @@ import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.StrutsException; +import org.apache.struts2.RequestUtils; import javax.servlet.ServletException; import javax.servlet.ServletContext; @@ -38,6 +39,8 @@ import java.io.IOException; import java.util.HashMap; +import java.util.List; +import java.util.regex.Pattern; /** * Contains preparation operations for a request before execution @@ -179,4 +182,48 @@ } } } + + /** + * Check whether the request matches a list of exclude patterns. + * + * @param request The request to check patterns against + * @param excludedPatterns list of patterns for exclusion + * + * @return <tt>true</tt> if the request URI matches one of the given patterns + */ + public boolean isUrlExcluded( HttpServletRequest request, List<Pattern> excludedPatterns ) { + if (excludedPatterns != null) { + String uri = getUri(request); + for ( Pattern pattern : excludedPatterns ) { + if (pattern.matcher(uri).matches()) { + return true; + } + } + } + return false; + } + + /** + * Gets the uri from the request + * + * @param request The request + * + * @return The uri + */ + private String getUri( HttpServletRequest request ) { + // handle http dispatcher includes. + String uri = (String) request.getAttribute("javax.servlet.include.servlet_path"); + if (uri != null) { + return uri; + } + + uri = RequestUtils.getServletPath(request); + if (uri != null && !"".equals(uri)) { + return uri; + } + + uri = request.getRequestURI(); + return uri.substring(request.getContextPath().length()); + } + } Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsExecuteFilter.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsExecuteFilter.java?rev=768529&r1=768528&r2=768529&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsExecuteFilter.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsExecuteFilter.java Sat Apr 25 13:49:50 2009 @@ -63,8 +63,15 @@ HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; + if (excludeUrl(request)) { + chain.doFilter(request, response); + return; + } + // This is necessary since we need the dispatcher instance, which was created by the prepare filter - lazyInit(); + if (execute == null) { + lazyInit(); + } ActionMapping mapping = prepare.findActionMapping(request, response); if (mapping == null) { @@ -77,9 +84,13 @@ } } + private boolean excludeUrl(HttpServletRequest request) { + return request.getAttribute(StrutsPrepareFilter.REQUEST_EXCLUDED_FROM_ACTION_MAPPING) != null; + } + public void destroy() { prepare = null; execute = null; filterConfig = null; } -} \ No newline at end of file +} Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareAndExecuteFilter.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareAndExecuteFilter.java?rev=768529&r1=768528&r2=768529&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareAndExecuteFilter.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareAndExecuteFilter.java Sat Apr 25 13:49:50 2009 @@ -22,15 +22,17 @@ import org.apache.struts2.StrutsStatics; import org.apache.struts2.dispatcher.Dispatcher; -import org.apache.struts2.dispatcher.ng.PrepareOperations; +import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.dispatcher.ng.ExecuteOperations; import org.apache.struts2.dispatcher.ng.InitOperations; -import org.apache.struts2.dispatcher.mapper.ActionMapping; +import org.apache.struts2.dispatcher.ng.PrepareOperations; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.List; +import java.util.regex.Pattern; /** * Handles both the preparation and execution phases of the Struts dispatching process. This filter is better to use @@ -39,6 +41,7 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { private PrepareOperations prepare; private ExecuteOperations execute; + protected List<Pattern> excludedPatterns = null; public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); @@ -50,6 +53,7 @@ prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); + this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); } finally { init.cleanup(); } @@ -66,15 +70,19 @@ prepare.createActionContext(request, response); prepare.assignDispatcherToThread(); request = prepare.wrapRequest(request); - ActionMapping mapping = prepare.findActionMapping(request, response, true); - if (mapping == null) { - boolean handled = execute.executeStaticResourceRequest(request, response); - if (!handled) { - chain.doFilter(request, response); - } - } else { - execute.executeAction(request, response, mapping); - } + if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { + chain.doFilter(request, response); + } else { + ActionMapping mapping = prepare.findActionMapping(request, response, true); + if (mapping == null) { + boolean handled = execute.executeStaticResourceRequest(request, response); + if (!handled) { + chain.doFilter(request, response); + } + } else { + execute.executeAction(request, response, mapping); + } + } } finally { prepare.cleanupRequest(request); } Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareFilter.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareFilter.java?rev=768529&r1=768528&r2=768529&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareFilter.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareFilter.java Sat Apr 25 13:49:50 2009 @@ -22,21 +22,27 @@ import org.apache.struts2.StrutsStatics; import org.apache.struts2.dispatcher.Dispatcher; -import org.apache.struts2.dispatcher.ng.PrepareOperations; import org.apache.struts2.dispatcher.ng.InitOperations; +import org.apache.struts2.dispatcher.ng.PrepareOperations; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.List; +import java.util.regex.Pattern; /** * Prepares the request for execution by a later {...@link org.apache.struts2.dispatcher.ng.filter.StrutsExecuteFilter} filter instance. */ public class StrutsPrepareFilter implements StrutsStatics, Filter { + + protected static final String REQUEST_EXCLUDED_FROM_ACTION_MAPPING = StrutsPrepareFilter.class.getName() + ".REQUEST_EXCLUDED_FROM_ACTION_MAPPING"; + private PrepareOperations prepare; + private List<Pattern> excludedPatterns = null; - public void init(FilterConfig filterConfig) throws ServletException { + public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); try { FilterHostConfig config = new FilterHostConfig(filterConfig); @@ -44,6 +50,7 @@ Dispatcher dispatcher = init.initDispatcher(config); prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); + this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); } finally { init.cleanup(); } @@ -60,15 +67,18 @@ prepare.createActionContext(request, response); prepare.assignDispatcherToThread(); request = prepare.wrapRequest(request); - prepare.findActionMapping(request, response); - + if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { + request.setAttribute(REQUEST_EXCLUDED_FROM_ACTION_MAPPING, new Object()); + } else { + prepare.findActionMapping(request, response); + } chain.doFilter(request, response); } finally { prepare.cleanupRequest(request); } } - public void destroy() { + public void destroy() { prepare.cleanupDispatcher(); } } \ No newline at end of file Modified: struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ng/StrutsPrepareAndExecuteFilterIntegrationTest.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ng/StrutsPrepareAndExecuteFilterIntegrationTest.java?rev=768529&r1=768528&r2=768529&view=diff ============================================================================== --- struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ng/StrutsPrepareAndExecuteFilterIntegrationTest.java (original) +++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/ng/StrutsPrepareAndExecuteFilterIntegrationTest.java Sat Apr 25 13:49:50 2009 @@ -32,7 +32,10 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.FilterConfig; import java.io.IOException; +import java.util.regex.Pattern; +import java.util.ArrayList; /** * Integration tests for the filter @@ -113,6 +116,32 @@ assertTrue((Boolean) request.getAttribute("__invoked")); } + public void testUriPatternExclusion() throws ServletException, IOException { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterConfig filterConfig = new MockFilterConfig(); + MockFilterChain filterChain = new MockFilterChain() { + @Override + public void doFilter(ServletRequest req, ServletResponse res) { + req.setAttribute("i_was", "invoked"); + } + }; + + request.setRequestURI("/hello.action"); + StrutsPrepareAndExecuteFilter filter = new StrutsPrepareAndExecuteFilter() { + @Override + public void init( FilterConfig filterConfig ) throws ServletException { + super.init(filterConfig); + excludedPatterns = new ArrayList<Pattern>(); + excludedPatterns.add(Pattern.compile(".*hello.*")); + } + }; + filter.init(filterConfig); + filter.doFilter(request, response, filterChain); + assertEquals(200, response.getStatus()); + assertEquals("invoked", request.getAttribute("i_was")); + } + public void testStaticFallthrough() throws ServletException, IOException { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse();