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
This commit is contained in:
René Gielen
2009-04-25 13:49:50 +00:00
parent 15ac7fe985
commit 85bf223e44
7 changed files with 167 additions and 31 deletions
@@ -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";
@@ -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<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 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 <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;
}
}
}
@@ -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 <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());
}
}
@@ -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;
}
}
}
@@ -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<Pattern> 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);
}
@@ -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<Pattern> 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();
}
}
@@ -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<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();