From 85bf223e4466f1734d280538757b7329236ce878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Gielen?= Date: Sat, 25 Apr 2009 13:49:50 +0000 Subject: [PATCH] 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 git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@768529 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/struts2/StrutsConstants.java | 3 ++ .../struts2/dispatcher/ng/InitOperations.java | 52 ++++++++++++++----- .../dispatcher/ng/PrepareOperations.java | 47 +++++++++++++++++ .../ng/filter/StrutsExecuteFilter.java | 15 +++++- .../filter/StrutsPrepareAndExecuteFilter.java | 30 +++++++---- .../ng/filter/StrutsPrepareFilter.java | 22 +++++--- ...repareAndExecuteFilterIntegrationTest.java | 29 +++++++++++ 7 files changed, 167 insertions(+), 31 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index 2fd50c5f5..c409ba566 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -44,6 +44,9 @@ public final class StrutsConstants { /** 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"; diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ng/InitOperations.java b/core/src/main/java/org/apache/struts2/dispatcher/ng/InitOperations.java index 95bdeaceb..f0d76161f 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/ng/InitOperations.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/ng/InitOperations.java @@ -25,10 +25,10 @@ import com.opensymphony.xwork2.ActionContext; 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 @@ public class InitOperations { /** * 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 @@ public class InitOperations { /** * 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 @@ public class InitOperations { /** * 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 @@ public class InitOperations { /** * @return The dispatcher on the thread. + * * @throws IllegalStateException If there is no dispatcher available */ public Dispatcher findDispatcherOnThread() { @@ -92,11 +93,11 @@ public class InitOperations { } /** - * Create a {@link Dispatcher} + * Create a {@link Dispatcher} */ - private Dispatcher createDispatcher(HostConfig filterConfig) { + private Dispatcher createDispatcher( HostConfig filterConfig ) { Map params = new HashMap(); - 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 class InitOperations { 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 null + * + * @see org.apache.struts2.StrutsConstants#STRUTS_ACTION_EXCLUDE_PATTERN + */ + public List buildExcludedPatternsList( Dispatcher dispatcher ) { + return buildExcludedPatternsList(dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN)); + } + + private List buildExcludedPatternsList( String patterns ) { + if (null != patterns && patterns.trim().length() != 0) { + List list = new ArrayList(); + String[] tokens = patterns.split(","); + for ( String token : tokens ) { + list.add(Pattern.compile(token.trim())); + } + return Collections.unmodifiableList(list); + } else { + return null; + } + } + } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ng/PrepareOperations.java b/core/src/main/java/org/apache/struts2/dispatcher/ng/PrepareOperations.java index e4856e089..e089368ee 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/ng/PrepareOperations.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/ng/PrepareOperations.java @@ -24,6 +24,7 @@ import org.apache.struts2.dispatcher.Dispatcher; 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 com.opensymphony.xwork2.util.logging.LoggerFactory; 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 @@ public class PrepareOperations { } } } + + /** + * 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 true if the request URI matches one of the given patterns + */ + public boolean isUrlExcluded( HttpServletRequest request, List 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()); + } + } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsExecuteFilter.java b/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsExecuteFilter.java index 4016baf95..0cd965192 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsExecuteFilter.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsExecuteFilter.java @@ -63,8 +63,15 @@ public class StrutsExecuteFilter implements StrutsStatics, Filter { 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 @@ public class StrutsExecuteFilter implements StrutsStatics, Filter { } } + 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 +} diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareAndExecuteFilter.java b/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareAndExecuteFilter.java index 18510bdf1..e27164b6c 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareAndExecuteFilter.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareAndExecuteFilter.java @@ -22,15 +22,17 @@ package org.apache.struts2.dispatcher.ng.filter; 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 @@ import java.io.IOException; public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { private PrepareOperations prepare; private ExecuteOperations execute; + protected List excludedPatterns = null; public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); @@ -50,6 +53,7 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); + this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); } finally { init.cleanup(); } @@ -66,15 +70,19 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { 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); } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareFilter.java b/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareFilter.java index 7028e6ab4..f288fa9f0 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareFilter.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/ng/filter/StrutsPrepareFilter.java @@ -22,21 +22,27 @@ package org.apache.struts2.dispatcher.ng.filter; 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 { - private PrepareOperations prepare; - public void init(FilterConfig filterConfig) throws ServletException { + protected static final String REQUEST_EXCLUDED_FROM_ACTION_MAPPING = StrutsPrepareFilter.class.getName() + ".REQUEST_EXCLUDED_FROM_ACTION_MAPPING"; + + private PrepareOperations prepare; + private List excludedPatterns = null; + + public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); try { FilterHostConfig config = new FilterHostConfig(filterConfig); @@ -44,6 +50,7 @@ public class StrutsPrepareFilter implements StrutsStatics, Filter { Dispatcher dispatcher = init.initDispatcher(config); prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); + this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); } finally { init.cleanup(); } @@ -60,15 +67,18 @@ public class StrutsPrepareFilter implements StrutsStatics, Filter { 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 diff --git a/core/src/test/java/org/apache/struts2/dispatcher/ng/StrutsPrepareAndExecuteFilterIntegrationTest.java b/core/src/test/java/org/apache/struts2/dispatcher/ng/StrutsPrepareAndExecuteFilterIntegrationTest.java index 7ea502317..9cd9bcf41 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/ng/StrutsPrepareAndExecuteFilterIntegrationTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/ng/StrutsPrepareAndExecuteFilterIntegrationTest.java @@ -32,7 +32,10 @@ import org.springframework.mock.web.MockHttpServletResponse; 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 @@ public class StrutsPrepareAndExecuteFilterIntegrationTest extends TestCase { 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(); + 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();