struts-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lukaszlen...@apache.org
Subject [40/57] [partial] struts git commit: Merges xwork packages into struts
Date Wed, 17 Jun 2015 21:09:40 GMT
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/Interceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/Interceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/Interceptor.java
new file mode 100644
index 0000000..4266236
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/Interceptor.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+
+import java.io.Serializable;
+
+
+/**
+ * <!-- START SNIPPET: introduction -->
+ * <p/>
+ * An interceptor is a stateless class that follows the interceptor pattern, as
+ * found in {@link  javax.servlet.Filter} and in AOP languages.
+ * <p/>
+ * <p/>
+ * <p/>
+ * Interceptors are objects that dynamically intercept Action invocations.
+ * They provide the developer with the opportunity to define code that can be executed
+ * before and/or after the execution of an action. They also have the ability
+ * to prevent an action from executing. Interceptors provide developers a way to
+ * encapulate common functionality in a re-usable form that can be applied to
+ * one or more Actions.
+ * <p/>
+ * <p/>
+ * <p/>
+ * Interceptors <b>must</b> be stateless and not assume that a new instance will be created for each request or Action.
+ * Interceptors may choose to either short-circuit the {@link ActionInvocation} execution and return a return code
+ * (such as {@link com.opensymphony.xwork2.Action#SUCCESS}), or it may choose to do some processing before
+ * and/or after delegating the rest of the procesing using {@link ActionInvocation#invoke()}.
+ * <p/>
+ * <!-- END SNIPPET: introduction -->
+ * <p/>
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: parameterOverriding -->
+ * <p/>
+ * Interceptor's parameter could be overriden through the following ways :-
+ * <p/>
+ * <p/>
+ * <p/>
+ * <b>Method 1:</b>
+ * <pre>
+ * &lt;action name="myAction" class="myActionClass"&gt;
+ *     &lt;interceptor-ref name="exception"/&gt;
+ *     &lt;interceptor-ref name="alias"/&gt;
+ *     &lt;interceptor-ref name="params"/&gt;
+ *     &lt;interceptor-ref name="servletConfig"/&gt;
+ *     &lt;interceptor-ref name="prepare"/&gt;
+ *     &lt;interceptor-ref name="i18n"/&gt;
+ *     &lt;interceptor-ref name="chain"/&gt;
+ *     &lt;interceptor-ref name="modelDriven"/&gt;
+ *     &lt;interceptor-ref name="fileUpload"/&gt;
+ *     &lt;interceptor-ref name="staticParams"/&gt;
+ *     &lt;interceptor-ref name="params"/&gt;
+ *     &lt;interceptor-ref name="conversionError"/&gt;
+ *     &lt;interceptor-ref name="validation"&gt;
+ *     &lt;param name="excludeMethods"&gt;myValidationExcudeMethod&lt;/param&gt;
+ *     &lt;/interceptor-ref&gt;
+ *     &lt;interceptor-ref name="workflow"&gt;
+ *     &lt;param name="excludeMethods"&gt;myWorkflowExcludeMethod&lt;/param&gt;
+ *     &lt;/interceptor-ref&gt;
+ * &lt;/action&gt;
+ * </pre>
+ * <p/>
+ * <b>Method 2:</b>
+ * <pre>
+ * &lt;action name="myAction" class="myActionClass"&gt;
+ *   &lt;interceptor-ref name="defaultStack"&gt;
+ *     &lt;param name="validation.excludeMethods"&gt;myValidationExcludeMethod&lt;/param&gt;
+ *     &lt;param name="workflow.excludeMethods"&gt;myWorkflowExcludeMethod&lt;/param&gt;
+ *   &lt;/interceptor-ref&gt;
+ * &lt;/action&gt;
+ * </pre>
+ * <p/>
+ * <p/>
+ * <p/>
+ * In the first method, the whole default stack is copied and the parameter then
+ * changed accordingly.
+ * <p/>
+ * <p/>
+ * <p/>
+ * In the second method, the 'interceptor-ref' refer to an existing
+ * interceptor-stack, namely defaultStack in this example, and override the validator
+ * and workflow interceptor excludeMethods typically in this case. Note that in the
+ * 'param' tag, the name attribute contains a dot (.) the word before the dot(.)
+ * specifies the interceptor name whose parameter is to be overridden and the word after
+ * the dot (.) specifies the parameter itself. Essetially it is as follows :-
+ * <p/>
+ * <pre>
+ *    &lt;interceptor-name&gt;.&lt;parameter-name&gt;
+ * </pre>
+ * <p/>
+ * <b>Note</b> also that in this case the 'interceptor-ref' name attribute
+ * is used to indicate an interceptor stack which makes sense as if it is referring
+ * to the interceptor itself it would be just using Method 1 describe above.
+ * <p/>
+ * <!-- END SNIPPET: parameterOverriding -->
+ * <p/>
+ * <p/>
+ * <b>Nested Interceptor param overriding</b>
+ * <p/>
+ * <!-- START SNIPPET: nestedParameterOverriding -->
+ * <p/>
+ * Interceptor stack parameter overriding could be nested into as many level as possible, though it would
+ * be advisable not to nest it too deep as to avoid confusion, For example,
+ * <pre>
+ * &lt;interceptor name="interceptor1" class="foo.bar.Interceptor1" /&gt;
+ * &lt;interceptor name="interceptor2" class="foo.bar.Interceptor2" /&gt;
+ * &lt;interceptor name="interceptor3" class="foo.bar.Interceptor3" /&gt;
+ * &lt;interceptor name="interceptor4" class="foo.bar.Interceptor4" /&gt;
+ * &lt;interceptor-stack name="stack1"&gt;
+ *     &lt;interceptor-ref name="interceptor1" /&gt;
+ * &lt;/interceptor-stack&gt;
+ * &lt;interceptor-stack name="stack2"&gt;
+ *     &lt;interceptor-ref name="intercetor2" /&gt;
+ *     &lt;interceptor-ref name="stack1" /&gt;
+ * &lt;/interceptor-stack&gt;
+ * &lt;interceptor-stack name="stack3"&gt;
+ *     &lt;interceptor-ref name="interceptor3" /&gt;
+ *     &lt;interceptor-ref name="stack2" /&gt;
+ * &lt;/interceptor-stack&gt;
+ * &lt;interceptor-stack name="stack4"&gt;
+ *     &lt;interceptor-ref name="interceptor4" /&gt;
+ *     &lt;interceptor-ref name="stack3" /&gt;
+ *  &lt;/interceptor-stack&gt;
+ * </pre>
+ * Assuming the interceptor has the following properties
+ * <table border="1" width="100%">
+ * <tr>
+ * <td>Interceptor</td>
+ * <td>property</td>
+ * </tr>
+ * <tr>
+ * <td>Interceptor1</td>
+ * <td>param1</td>
+ * </tr>
+ * <tr>
+ * <td>Interceptor2</td>
+ * <td>param2</td>
+ * </tr>
+ * <tr>
+ * <td>Interceptor3</td>
+ * <td>param3</td>
+ * </tr>
+ * <tr>
+ * <td>Interceptor4</td>
+ * <td>param4</td>
+ * </tr>
+ * </table>
+ * We could override them as follows :-
+ * <pre>
+ *    &lt;action ... &gt;
+ *        &lt;!-- to override parameters of interceptor located directly in the stack  --&gt;
+ *        &lt;interceptor-ref name="stack4"&gt;
+ *           &lt;param name="interceptor4.param4"&gt; ... &lt;/param&gt;
+ *        &lt;/interceptor-ref&gt;
+ *    &lt;/action&gt;
+ * <p/>
+ *    &lt;action ... &gt;
+ *        &lt;!-- to override parameters of interceptor located under nested stack --&gt;
+ *        &lt;interceptor-ref name="stack4"&gt;
+ *            &lt;param name="stack3.interceptor3.param3"&gt; ... &lt;/param&gt;
+ *            &lt;param name="stack3.stack2.interceptor2.param2"&gt; ... &lt;/param&gt;
+ *            &lt;param name="stack3.stack2.stack1.interceptor1.param1"&gt; ... &lt;/param&gt;
+ *        &lt;/interceptor-ref&gt;
+ *    &lt;/action&gt;
+ *  </pre>
+ * <p/>
+ * <!-- END SNIPPET: nestedParameterOverriding -->
+ *
+ * @author Jason Carreira
+ * @author tmjee
+ * @version $Date$ $Id$
+ */
+public interface Interceptor extends Serializable {
+
+    /**
+     * Called to let an interceptor clean up any resources it has allocated.
+     */
+    void destroy();
+
+    /**
+     * Called after an interceptor is created, but before any requests are processed using
+     * {@link #intercept(com.opensymphony.xwork2.ActionInvocation) intercept} , giving
+     * the Interceptor a chance to initialize any needed resources.
+     */
+    void init();
+
+    /**
+     * Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the
+     * request by the {@link ActionInvocation} or to short-circuit the processing and just return a String return code.
+     *
+     * @param invocation the action invocation
+     * @return the return code, either returned from {@link ActionInvocation#invoke()}, or from the interceptor itself.
+     * @throws Exception any system-level error, as defined in {@link com.opensymphony.xwork2.Action#execute()}.
+     */
+    String intercept(ActionInvocation invocation) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/LoggingInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/LoggingInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/LoggingInterceptor.java
new file mode 100644
index 0000000..c82532f
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/LoggingInterceptor.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+
+/**
+ * <!-- START SNIPPET: description -->
+ * This interceptor logs the start and end of the execution an action (in English-only, not internationalized).
+ * <br/>
+ * <b>Note:</b>: This interceptor will log at <tt>INFO</tt> level.
+ * <p/>
+ * <!-- END SNIPPET: description -->
+ *
+ * <!-- START SNIPPET: parameters -->
+ * There are no parameters for this interceptor.
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <!-- START SNIPPET: extending -->
+ * There are no obvious extensions to the existing interceptor.
+ * <!-- END SNIPPET: extending -->
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &lt;!-- prints out a message before and after the immediate action execution --&gt;
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *     &lt;interceptor-ref name="completeStack"/&gt;
+ *     &lt;interceptor-ref name="logger"/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ *
+ * &lt;!-- prints out a message before any more interceptors continue and after they have finished --&gt;
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *     &lt;interceptor-ref name="logger"/&gt;
+ *     &lt;interceptor-ref name="completeStack"/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Jason Carreira
+ */
+public class LoggingInterceptor extends AbstractInterceptor {
+    private static final Logger LOG = LogManager.getLogger(LoggingInterceptor.class);
+    private static final String FINISH_MESSAGE = "Finishing execution stack for action ";
+    private static final String START_MESSAGE = "Starting execution stack for action ";
+
+    @Override
+    public String intercept(ActionInvocation invocation) throws Exception {
+        logMessage(invocation, START_MESSAGE);
+        String result = invocation.invoke();
+        logMessage(invocation, FINISH_MESSAGE);
+        return result;
+    }
+
+    private void logMessage(ActionInvocation invocation, String baseMessage) {
+        if (LOG.isInfoEnabled()) {
+            StringBuilder message = new StringBuilder(baseMessage);
+            String namespace = invocation.getProxy().getNamespace();
+
+            if ((namespace != null) && (namespace.trim().length() > 0)) {
+                message.append(namespace).append("/");
+            }
+
+            message.append(invocation.getProxy().getActionName());
+        	LOG.info(message.toString());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/MethodFilterInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/MethodFilterInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/MethodFilterInterceptor.java
new file mode 100644
index 0000000..a46cf34
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/MethodFilterInterceptor.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.TextParseUtil;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Collections;
+import java.util.Set;
+
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ * 
+ * MethodFilterInterceptor is an abstract <code>Interceptor</code> used as
+ * a base class for interceptors that will filter execution based on method 
+ * names according to specified included/excluded method lists.
+ * 
+ * <p/>
+ * 
+ * Settable parameters are as follows:
+ * 
+ * <ul>
+ * 		<li>excludeMethods - method names to be excluded from interceptor processing</li>
+ * 		<li>includeMethods - method names to be included in interceptor processing</li>
+ * </ul>
+ * 
+ * <p/>
+ * 
+ * <b>NOTE:</b> If method name are available in both includeMethods and 
+ * excludeMethods, it will be considered as an included method: 
+ * includeMethods takes precedence over excludeMethods.
+ * 
+ * <p/>
+ * 
+ * Interceptors that extends this capability include:
+ * 
+ * <ul>
+ *    <li>TokenInterceptor</li>
+ *    <li>TokenSessionStoreInterceptor</li>
+ *    <li>DefaultWorkflowInterceptor</li>
+ *    <li>ValidationInterceptor</li>
+ * </ul>
+ * 
+ * <!-- END SNIPPET: javadoc -->
+ * 
+ * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
+ * @author Rainer Hermanns
+ * 
+ * @see org.apache.struts2.interceptor.TokenInterceptor
+ * @see org.apache.struts2.interceptor.TokenSessionStoreInterceptor
+ * @see com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor
+ * @see com.opensymphony.xwork2.validator.ValidationInterceptor
+ * 
+ * @version $Date$ $Id$
+ */
+public abstract class MethodFilterInterceptor extends AbstractInterceptor {
+    protected transient Logger log = LogManager.getLogger(getClass());
+    
+    protected Set<String> excludeMethods = Collections.emptySet();
+    protected Set<String> includeMethods = Collections.emptySet();
+
+    public void setExcludeMethods(String excludeMethods) {
+        this.excludeMethods = TextParseUtil.commaDelimitedStringToSet(excludeMethods);
+    }
+    
+    public Set<String> getExcludeMethodsSet() {
+    	return excludeMethods;
+    }
+
+    public void setIncludeMethods(String includeMethods) {
+        this.includeMethods = TextParseUtil.commaDelimitedStringToSet(includeMethods);
+    }
+    
+    public Set<String> getIncludeMethodsSet() {
+    	return includeMethods;
+    }
+
+    @Override
+    public String intercept(ActionInvocation invocation) throws Exception {
+        if (applyInterceptor(invocation)) {
+            return doIntercept(invocation);
+        } 
+        return invocation.invoke();
+    }
+
+    protected boolean applyInterceptor(ActionInvocation invocation) {
+        String method = invocation.getProxy().getMethod();
+        // ValidationInterceptor
+        boolean applyMethod = MethodFilterInterceptorUtil.applyMethod(excludeMethods, includeMethods, method);
+        if (!applyMethod) {
+            log.debug("Skipping Interceptor... Method [{}] found in exclude list.", method);
+        }
+        return applyMethod;
+    }
+    
+    /**
+     * Subclasses must override to implement the interceptor logic.
+     * 
+     * @param invocation the action invocation
+     * @return the result of invocation
+     * @throws Exception
+     */
+    protected abstract String doIntercept(ActionInvocation invocation) throws Exception;
+    
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/MethodFilterInterceptorUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/MethodFilterInterceptorUtil.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/MethodFilterInterceptorUtil.java
new file mode 100644
index 0000000..987d782
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/MethodFilterInterceptorUtil.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.util.TextParseUtil;
+import com.opensymphony.xwork2.util.WildcardHelper;
+
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * Utility class contains common methods used by 
+ * {@link com.opensymphony.xwork2.interceptor.MethodFilterInterceptor}.
+ * 
+ * @author tm_jee
+ */
+public class MethodFilterInterceptorUtil {
+
+	/**
+     * Static method to decide if the specified <code>method</code> should be
+     * apply (not filtered) depending on the set of <code>excludeMethods</code> and 
+     * <code>includeMethods</code>. 
+     *
+     * <ul>
+     * <li>
+     * 	<code>includeMethods</code> takes precedence over <code>excludeMethods</code>
+     * </li>
+     * </ul>
+     * <b>Note:</b> Supports wildcard listings in includeMethods/excludeMethods
+     *
+     * @param excludeMethods  list of methods to exclude.
+     * @param includeMethods  list of methods to include.
+     * @param method the specified method to check
+     * @return <tt>true</tt> if the method should be applied.
+     */
+    public static boolean applyMethod(Set<String> excludeMethods, Set<String> includeMethods, String method) {
+        
+        // quick check to see if any actual pattern matching is needed
+        boolean needsPatternMatch = false;
+        for (String includeMethod : includeMethods) {
+            if (!"*".equals(includeMethod) && includeMethod.contains("*")) {
+                needsPatternMatch = true;
+                break;
+            }
+        }
+        
+        for (String excludeMethod : excludeMethods) {
+            if (!"*".equals(excludeMethod) && excludeMethod.contains("*")) {
+                needsPatternMatch = true;
+                break;
+            }
+        }
+
+        // this section will try to honor the original logic, while 
+        // still allowing for wildcards later
+        if (!needsPatternMatch && (includeMethods.contains("*") || includeMethods.size() == 0) ) {
+            if (excludeMethods != null 
+                    && excludeMethods.contains(method) 
+                    && !includeMethods.contains(method) ) {
+                return false;
+            }
+        }
+        
+        // test the methods using pattern matching
+        WildcardHelper wildcard = new WildcardHelper();
+        String methodCopy ;
+        if (method == null ) { // no method specified
+            methodCopy = "";
+        }
+        else {
+            methodCopy = new String(method);
+        }
+        for (String pattern : includeMethods) {
+            if (pattern.contains("*")) {
+                int[] compiledPattern = wildcard.compilePattern(pattern);
+                HashMap<String, String> matchedPatterns = new HashMap<>();
+                boolean matches = wildcard.match(matchedPatterns, methodCopy, compiledPattern);
+                if (matches) {
+                    return true; // run it, includeMethods takes precedence
+                }
+            }
+            else {
+                if (pattern.equals(methodCopy)) {
+                    return true; // run it, includeMethods takes precedence
+                }
+            }
+        }
+        if (excludeMethods.contains("*") ) {
+            return false;
+        }
+
+        // CHECK ME: Previous implementation used include method 
+        for ( String pattern : excludeMethods) {
+            if (pattern.contains("*")) {
+                int[] compiledPattern = wildcard.compilePattern(pattern);
+                HashMap<String, String> matchedPatterns = new HashMap<>();
+                boolean matches = wildcard.match(matchedPatterns, methodCopy, compiledPattern);
+                if (matches) {
+                    // if found, and wasn't included earlier, don't run it
+                    return false; 
+                }
+            }
+            else {
+                if (pattern.equals(methodCopy)) {
+                    // if found, and wasn't included earlier, don't run it
+                    return false; 
+                }
+            }
+        }
+    
+
+        // default fall-back from before changes
+        return includeMethods.size() == 0 || includeMethods.contains(method) || includeMethods.contains("*");
+    }
+    
+    /**
+     * Same as {@link #applyMethod(Set, Set, String)}, except that <code>excludeMethods</code>
+     * and <code>includeMethods</code> are supplied as comma separated string.
+     * 
+     * @param excludeMethods  comma seperated string of methods to exclude.
+     * @param includeMethods  comma seperated string of methods to include.
+     * @param method the specified method to check
+     * @return <tt>true</tt> if the method should be applied.
+     */
+    public static boolean applyMethod(String excludeMethods, String includeMethods, String method) {
+    	Set<String> includeMethodsSet = TextParseUtil.commaDelimitedStringToSet(includeMethods == null? "" : includeMethods);
+    	Set<String> excludeMethodsSet = TextParseUtil.commaDelimitedStringToSet(excludeMethods == null? "" : excludeMethods);
+    	
+    	return applyMethod(excludeMethodsSet, includeMethodsSet, method);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/ModelDrivenInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ModelDrivenInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ModelDrivenInterceptor.java
new file mode 100644
index 0000000..06a91b5
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ModelDrivenInterceptor.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ModelDriven;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * <!-- START SNIPPET: description -->
+ *
+ * Watches for {@link ModelDriven} actions and adds the action's model on to the value stack.
+ *
+ * <p/> <b>Note:</b>  The ModelDrivenInterceptor must come before the both {@link StaticParametersInterceptor} and
+ * {@link ParametersInterceptor} if you want the parameters to be applied to the model.
+ * 
+ * <p/> <b>Note:</b>  The ModelDrivenInterceptor will only push the model into the stack when the
+ * model is not null, else it will be ignored.
+ *
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Interceptor parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ *
+ * <ul>
+ *
+ * <li>refreshModelBeforeResult - set to true if you want the model to be refreshed on the value stack after action
+ * execution and before result execution.  The setting is useful if you want to change the model instance during the
+ * action execution phase, like when loading it from the data layer.  This will result in getModel() being called at
+ * least twice.</li>
+ *
+ * </ul>
+ *
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Extending the interceptor:</u>
+ *
+ * <p/>
+ *
+ * <!-- START SNIPPET: extending -->
+ *
+ * There are no known extension points to this interceptor.
+ *
+ * <!-- END SNIPPET: extending -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *     &lt;interceptor-ref name="modelDriven"/&gt;
+ *     &lt;interceptor-ref name="basicStack"/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ * 
+ * @author tm_jee
+ * @version $Date$ $Id$
+ */
+public class ModelDrivenInterceptor extends AbstractInterceptor {
+
+    protected boolean refreshModelBeforeResult = false;
+
+    public void setRefreshModelBeforeResult(boolean val) {
+        this.refreshModelBeforeResult = val;
+    }
+
+    @Override
+    public String intercept(ActionInvocation invocation) throws Exception {
+        Object action = invocation.getAction();
+
+        if (action instanceof ModelDriven) {
+            ModelDriven modelDriven = (ModelDriven) action;
+            ValueStack stack = invocation.getStack();
+            Object model = modelDriven.getModel();
+            if (model !=  null) {
+            	stack.push(model);
+            }
+            if (refreshModelBeforeResult) {
+                invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
+            }
+        }
+        return invocation.invoke();
+    }
+
+    /**
+     * Refreshes the model instance on the value stack, if it has changed
+     */
+    protected static class RefreshModelBeforeResult implements PreResultListener {
+        private Object originalModel = null;
+        protected ModelDriven action;
+
+
+        public RefreshModelBeforeResult(ModelDriven action, Object model) {
+            this.originalModel = model;
+            this.action = action;
+        }
+
+        public void beforeResult(ActionInvocation invocation, String resultCode) {
+            ValueStack stack = invocation.getStack();
+            CompoundRoot root = stack.getRoot();
+
+            boolean needsRefresh = true;
+            Object newModel = action.getModel();
+
+            // Check to see if the new model instance is already on the stack
+            for (Object item : root) {
+                if (item.equals(newModel)) {
+                    needsRefresh = false;
+                    break;
+                }
+            }
+
+            // Add the new model on the stack
+            if (needsRefresh) {
+
+                // Clear off the old model instance
+                if (originalModel != null) {
+                    root.remove(originalModel);
+                }
+                if (newModel != null) {
+                    stack.push(newModel);
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/NoParameters.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/NoParameters.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/NoParameters.java
new file mode 100644
index 0000000..8db8fbc
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/NoParameters.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+
+/**
+ * Marker interface to incidate no auto setting of parameters.
+ * <p/>
+ * This marker interface should be implemented by actions that do not want any
+ * request parameters set on them automatically (by the ParametersInterceptor).
+ * This may be useful if one is using the action tag and want to supply
+ * the parameters to the action manually using the param tag.
+ * It may also be useful if one for security reasons wants to make sure that
+ * parameters cannot be set by malicious users.
+ *
+ * @author Dick Zetterberg (dick@transitor.se)
+ */
+public interface NoParameters {
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterFilterInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterFilterInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterFilterInterceptor.java
new file mode 100644
index 0000000..46767d5
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterFilterInterceptor.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.TextParseUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * <!-- START SNIPPET: description -->
+ *
+ * The Parameter Filter Interceptor blocks parameters from getting
+ * to the rest of the stack or your action. You can use multiple 
+ * parameter filter interceptors for a given action, so, for example,
+ * you could use one in your default stack that filtered parameters
+ * you wanted blocked from every action and those you wanted blocked 
+ * from an individual action you could add an additional interceptor
+ * for each action.
+ * 
+ * <!-- END SNIPPET: description -->
+ * 
+ * <!-- START SNIPPET: parameters -->
+ *
+ * <ul>
+ * <li>allowed - a comma delimited list of parameter prefixes
+ *  that are allowed to pass to the action</li>
+ * <li>blocked - a comma delimited list of parameter prefixes 
+ * that are not allowed to pass to the action</li>
+ * <li>defaultBlock - boolean (default to false) whether by
+ * default a given parameter is blocked. If true, then a parameter
+ * must have a prefix in the allowed list in order to be able 
+ * to pass to the action
+ * </ul>
+ * 
+ * <p>The way parameters are filtered for the least configuration is that
+ * if a string is in the allowed or blocked lists, then any parameter
+ * that is a member of the object represented by the parameter is allowed
+ * or blocked respectively.</p>
+ * 
+ * <p>For example, if the parameters are:
+ * <ul>
+ * <li>blocked: person,person.address.createDate,personDao</li>
+ * <li>allowed: person.address</li>
+ * <li>defaultBlock: false</li>
+ * </ul>
+ * <br>
+ * The parameters person.name, person.phoneNum etc would be blocked 
+ * because 'person' is in the blocked list. However, person.address.street
+ * and person.address.city would be allowed because person.address is
+ * in the allowed list (the longer string determines permissions).</p> 
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <!-- START SNIPPET: extending -->
+ * There are no known extension points to this interceptor.
+ * <!-- END SNIPPET: extending -->
+ * 
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &lt;interceptors&gt;
+ *   ...
+ *   &lt;interceptor name="parameterFilter" class="com.opensymphony.xwork2.interceptor.ParameterFilterInterceptor"/&gt;
+ *   ...
+ * &lt;/interceptors&gt;
+ * 
+ * &lt;action ....&gt;
+ *   ...
+ *   &lt;interceptor-ref name="parameterFilter"&gt;
+ *     &lt;param name="blocked"&gt;person,person.address.createDate,personDao&lt;/param&gt;
+ *   &lt;/interceptor-ref&gt;
+ *   ...
+ * &lt;/action&gt;
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ * 
+ * @author Gabe
+ */
+public class ParameterFilterInterceptor extends AbstractInterceptor {
+
+    private static final Logger LOG = LogManager.getLogger(ParameterFilterInterceptor.class);
+
+    private Collection<String> allowed;
+    private Collection<String> blocked;
+    private Map<String, Boolean> includesExcludesMap;
+    private boolean defaultBlock = false;
+
+    @Override
+    public String intercept(ActionInvocation invocation) throws Exception {
+
+        Map<String, Object> parameters = invocation.getInvocationContext().getParameters();
+        HashSet<String> paramsToRemove = new HashSet<>();
+
+        Map<String, Boolean> includesExcludesMap = getIncludesExcludesMap();
+
+        for (String param : parameters.keySet()) {
+            boolean currentAllowed = !isDefaultBlock();
+
+            for (String currRule : includesExcludesMap.keySet()) {
+                if (param.startsWith(currRule)
+                        && (param.length() == currRule.length()
+                        || isPropertySeparator(param.charAt(currRule.length())))) {
+                    currentAllowed = includesExcludesMap.get(currRule).booleanValue();
+                }
+            }
+            if (!currentAllowed) {
+                paramsToRemove.add(param);
+            }
+        }
+
+        LOG.debug("Params to remove: {}", paramsToRemove);
+
+        for (Object aParamsToRemove : paramsToRemove) {
+            parameters.remove(aParamsToRemove);
+        }
+
+        return invocation.invoke();
+    }
+
+    /**
+     * Tests if the given char is a property separator char <code>.([</code>.
+     *
+     * @param c the char
+     * @return <tt>true</tt>, if char is property separator, <tt>false</tt> otherwise.
+     */
+    private static boolean isPropertySeparator(char c) {
+        return c == '.' || c == '(' || c == '[';
+    }
+
+    private Map<String, Boolean> getIncludesExcludesMap() {
+        if (this.includesExcludesMap == null) {
+            this.includesExcludesMap = new TreeMap<>();
+
+            if (getAllowedCollection() != null) {
+                for (String e : getAllowedCollection()) {
+                    this.includesExcludesMap.put(e, Boolean.TRUE);
+                }
+            }
+            if (getBlockedCollection() != null) {
+                for (String b : getBlockedCollection()) {
+                    this.includesExcludesMap.put(b, Boolean.FALSE);
+                }
+            }
+        }
+
+        return this.includesExcludesMap;
+    }
+
+    /**
+     * @return Returns the defaultBlock.
+     */
+    public boolean isDefaultBlock() {
+        return defaultBlock;
+    }
+
+    /**
+     * @param defaultExclude The defaultExclude to set.
+     */
+    public void setDefaultBlock(boolean defaultExclude) {
+        this.defaultBlock = defaultExclude;
+    }
+
+    /**
+     * @return Returns the blocked.
+     */
+    public Collection<String> getBlockedCollection() {
+        return blocked;
+    }
+
+    /**
+     * @param blocked The blocked to set.
+     */
+    public void setBlockedCollection(Collection<String> blocked) {
+        this.blocked = blocked;
+    }
+
+    /**
+     * @param blocked The blocked paramters as comma separated String.
+     */
+    public void setBlocked(String blocked) {
+        setBlockedCollection(asCollection(blocked));
+    }
+
+    /**
+     * @return Returns the allowed.
+     */
+    public Collection<String> getAllowedCollection() {
+        return allowed;
+    }
+
+    /**
+     * @param allowed The allowed to set.
+     */
+    public void setAllowedCollection(Collection<String> allowed) {
+        this.allowed = allowed;
+    }
+
+    /**
+     * @param allowed The allowed paramters as comma separated String.
+     */
+    public void setAllowed(String allowed) {
+        setAllowedCollection(asCollection(allowed));
+    }
+
+    /**
+     * Return a collection from the comma delimited String.
+     *
+     * @param commaDelim the comma delimited String.
+     * @return A collection from the comma delimited String. Returns <tt>null</tt> if the string is empty.
+     */
+    private Collection<String> asCollection(String commaDelim) {
+        if (StringUtils.isBlank(commaDelim)) {
+            return null;
+        }
+        return TextParseUtil.commaDelimitedStringToSet(commaDelim);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterNameAware.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterNameAware.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterNameAware.java
new file mode 100644
index 0000000..46ba188
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterNameAware.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ * This interface is implemented by actions that want to declare acceptable parameters. Works in conjunction with {@link
+ * ParametersInterceptor}. For example, actions may want to create a whitelist of parameters they will accept or a
+ * blacklist of paramters they will reject to prevent clients from setting other unexpected (and possibly dangerous)
+ * parameters.
+ * <!-- END SNIPPET: javadoc -->
+ *
+ * @author Bob Lee (crazybob@google.com)
+ */
+public interface ParameterNameAware {
+
+    /**
+     * Tests if the the action will accept the parameter with the given name.
+     *
+     * @param parameterName  the parameter name
+     * @return <tt> if accepted, <tt>false</tt> otherwise
+     */
+    boolean acceptableParameterName(String parameterName);
+    
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterRemoverInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterRemoverInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterRemoverInterceptor.java
new file mode 100644
index 0000000..68bb154
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParameterRemoverInterceptor.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.TextParseUtil;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * This is a simple XWork interceptor that allows parameters (matching
+ * one of the paramNames attribute csv value) to be 
+ * removed from the parameter map if they match a certain value
+ * (matching one of the paramValues attribute csv value), before they 
+ * are set on the action. A typical usage would be to want a dropdown/select 
+ * to map onto a boolean value on an action. The select had the options 
+ * none, yes and no with values -1, true and false. The true and false would 
+ * map across correctly. However the -1 would be set to false. 
+ * This was not desired as one might needed the value on the action to stay null. 
+ * This interceptor fixes this by preventing the parameter from ever reaching 
+ * the action.
+ * <!-- END SNIPPET: description -->
+ * 
+ * 
+ * <!-- START SNIPPET: parameters -->
+ * <ul>
+ * 	<li>paramNames - A comma separated value (csv) indicating the parameter name 
+ * 								    whose param value should be considered that if they match any of the
+ *                                     comma separated value (csv) from paramValues attribute, shall be 
+ *                                     removed from the parameter map such that they will not be applied
+ *                                     to the action</li>
+ * 	<li>paramValues - A comma separated value (csv) indicating the parameter value that if 
+ * 							      matched shall have its parameter be removed from the parameter map 
+ * 							      such that they will not be applied to the action</li>
+ * </ul>
+ * <!-- END SNIPPET: parameters -->
+ * 
+ * 
+ * <!-- START SNIPPET: extending -->
+ * No intended extension point
+ * <!-- END SNIPPET: extending -->
+ * 
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ *	
+ * &lt;action name="sample" class="org.martingilday.Sample"&gt;
+ * 	&lt;interceptor-ref name="paramRemover"&gt;
+ *   		&lt;param name="paramNames"&gt;aParam,anotherParam&lt;/param&gt;
+ *   		&lt;param name="paramValues"&gt;--,-1&lt;/param&gt;
+ * 	&lt;/interceptor-ref&gt;
+ * 	&lt;interceptor-ref name="defaultStack" /&gt;
+ * 	...
+ * &lt;/action&gt;
+ *  
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *  
+ *  
+ * @author martin.gilday
+ */
+public class ParameterRemoverInterceptor extends AbstractInterceptor {
+
+	private static final Logger LOG = LogManager.getLogger(ParameterRemoverInterceptor.class);
+
+	private static final long serialVersionUID = 1;
+
+	private Set<String> paramNames = Collections.emptySet();
+	private Set<String> paramValues = Collections.emptySet();
+
+	
+	/**
+	 * Decide if the parameter should be removed from the parameter map based on
+	 * <code>paramNames</code> and <code>paramValues</code>.
+	 * 
+	 * @see com.opensymphony.xwork2.interceptor.AbstractInterceptor
+	 */
+	@Override
+	public String intercept(ActionInvocation invocation) throws Exception {
+		if (!(invocation.getAction() instanceof NoParameters)
+				&& (null != this.paramNames)) {
+			ActionContext ac = invocation.getInvocationContext();
+			final Map<String, Object> parameters = ac.getParameters();
+
+			if (parameters != null) {
+                for (String removeName : paramNames) {
+                    // see if the field is in the parameter map
+                    if (parameters.containsKey(removeName)) {
+
+                        try {
+							String[] values = (String[]) parameters.get(removeName);
+							String value = values[0];
+							if (null != value && this.paramValues.contains(value)) {
+                                parameters.remove(removeName);
+                            }
+                        } catch (Exception e) {
+							LOG.error("Failed to convert parameter to string", e);
+						}
+					}
+                }
+			}
+		}
+		return invocation.invoke();
+	}
+
+	/**
+	 * Allows <code>paramNames</code> attribute to be set as comma-separated-values (csv).
+	 * 
+	 * @param paramNames the paramNames to set
+	 */
+	public void setParamNames(String paramNames) {
+		this.paramNames = TextParseUtil.commaDelimitedStringToSet(paramNames);
+	}
+
+
+	/**
+	 * Allows <code>paramValues</code> attribute to be set as a comma-separated-values (csv).
+	 * 
+	 * @param paramValues the paramValues to set
+	 */
+	public void setParamValues(String paramValues) {
+		this.paramValues = TextParseUtil.commaDelimitedStringToSet(paramValues);
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java
new file mode 100644
index 0000000..8496610
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java
@@ -0,0 +1,492 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ *
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ValidationAware;
+import com.opensymphony.xwork2.XWorkConstants;
+import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
+import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
+import com.opensymphony.xwork2.util.*;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.TreeMap;
+
+
+/**
+ * <!-- START SNIPPET: description -->
+ * This interceptor sets all parameters on the value stack.
+ *
+ * This interceptor gets all parameters from {@link ActionContext#getParameters()} and sets them on the value stack by
+ * calling {@link ValueStack#setValue(String, Object)}, typically resulting in the values submitted in a form
+ * request being applied to an action in the value stack. Note that the parameter map must contain a String key and
+ * often containers a String[] for the value.
+ *
+ * The interceptor takes one parameter named 'ordered'. When set to true action properties are guaranteed to be
+ * set top-down which means that top action's properties are set first. Then it's subcomponents properties are set.
+ * The reason for this order is to enable a 'factory' pattern. For example, let's assume that one has an action
+ * that contains a property named 'modelClass' that allows to choose what is the underlying implementation of model.
+ * By assuring that modelClass property is set before any model properties are set, it's possible to choose model
+ * implementation during action.setModelClass() call. Similiarily it's possible to use action.setPrimaryKey()
+ * property set call to actually load the model class from persistent storage. Without any assumption on parameter
+ * order you have to use patterns like 'Preparable'.
+ *
+ * Because parameter names are effectively OGNL statements, it is important that security be taken in to account.
+ * This interceptor will not apply any values in the parameters map if the expression contains an assignment (=),
+ * multiple expressions (,), or references any objects in the context (#). This is all done in the {@link
+ * #acceptableName(String)} method. In addition to this method, if the action being invoked implements the {@link
+ * ParameterNameAware} interface, the action will be consulted to determine if the parameter should be set.
+ *
+ * In addition to these restrictions, a flag ({@link ReflectionContextState#DENY_METHOD_EXECUTION}) is set such that
+ * no methods are allowed to be invoked. That means that any expression such as <i>person.doSomething()</i> or
+ * <i>person.getName()</i> will be explicitely forbidden. This is needed to make sure that your application is not
+ * exposed to attacks by malicious users.
+ *
+ * While this interceptor is being invoked, a flag ({@link ReflectionContextState#CREATE_NULL_OBJECTS}) is turned
+ * on to ensure that any null reference is automatically created - if possible. See the type conversion documentation
+ * and the {@link InstantiatingNullHandler} javadocs for more information.
+ *
+ * Finally, a third flag ({@link XWorkConverter#REPORT_CONVERSION_ERRORS}) is set that indicates any errors when
+ * converting the the values to their final data type (String[] -&gt; int) an unrecoverable error occured. With this
+ * flag set, the type conversion errors will be reported in the action context. See the type conversion documentation
+ * and the {@link XWorkConverter} javadocs for more information.
+ *
+ * If you are looking for detailed logging information about your parameters, turn on DEBUG level logging for this
+ * interceptor. A detailed log of all the parameter keys and values will be reported.
+ *
+ * <b>Note:</b> Since XWork 2.0.2, this interceptor extends {@link MethodFilterInterceptor}, therefore being
+ * able to deal with excludeMethods / includeMethods parameters. See [Workflow Interceptor]
+ * (class {@link DefaultWorkflowInterceptor}) for documentation and examples on how to use this feature.
+ * <!-- END SNIPPET: description -->
+ *
+ * <u>Interceptor parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ *
+ * <ul>
+ * <li>ordered - set to true if you want the top-down property setter behaviour</li>
+ * <li>acceptParamNames - a comma delimited list of regular expressions to describe a whitelist of accepted parameter names.
+ * Don't change the default unless you know what you are doing in terms of security implications</li>
+ * <li>excludeParams - a comma delimited list of regular expressions to describe a blacklist of not allowed parameter names</li>
+ * <li>paramNameMaxLength - the maximum length of parameter names; parameters with longer names will be ignored; the default is 100 characters</li>
+ * </ul>
+ *
+ * <!-- END SNIPPET: parameters -->
+ *
+ *  <u>Extending the interceptor:</u>
+ *
+ * <!-- START SNIPPET: extending -->
+ *
+ *  The best way to add behavior to this interceptor is to utilize the {@link ParameterNameAware} interface in your
+ * actions. However, if you wish to apply a global rule that isn't implemented in your action, then you could extend
+ * this interceptor and override the {@link #acceptableName(String)} method.
+ *
+ * <!-- END SNIPPET: extending -->
+ *
+ *
+ * <!-- START SNIPPET: extending-warning -->
+ * Using {@link ParameterNameAware} could be dangerous as {@link ParameterNameAware#acceptableParameterName(String)} takes precedence
+ * over ParametersInterceptor which means if ParametersInterceptor excluded given parameter name you can accept it with
+ * {@link ParameterNameAware#acceptableParameterName(String)}.
+ *
+ * The best idea is to define very tight restrictions with ParametersInterceptor and relax them per action with
+ * {@link ParameterNameAware#acceptableParameterName(String)}
+ * <!-- END SNIPPET: extending-warning -->
+ *
+ *
+ * <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *     &lt;interceptor-ref name="params"/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Patrick Lightbody
+ */
+public class ParametersInterceptor extends MethodFilterInterceptor {
+
+    private static final Logger LOG = LogManager.getLogger(ParametersInterceptor.class);
+
+    protected static final int PARAM_NAME_MAX_LENGTH = 100;
+
+    private int paramNameMaxLength = PARAM_NAME_MAX_LENGTH;
+    private boolean devMode = false;
+
+    protected boolean ordered = false;
+
+    private ValueStackFactory valueStackFactory;
+    private ExcludedPatternsChecker excludedPatterns;
+    private AcceptedPatternsChecker acceptedPatterns;
+
+    @Inject
+    public void setValueStackFactory(ValueStackFactory valueStackFactory) {
+        this.valueStackFactory = valueStackFactory;
+    }
+
+    @Inject(XWorkConstants.DEV_MODE)
+    public void setDevMode(String mode) {
+        this.devMode = BooleanUtils.toBoolean(mode);
+    }
+
+    @Inject
+    public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
+        this.excludedPatterns = excludedPatterns;
+    }
+
+    @Inject
+    public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
+        this.acceptedPatterns = acceptedPatterns;
+    }
+
+    /**
+     * If the param name exceeds the configured maximum length it will not be
+     * accepted.
+     *
+     * @param paramNameMaxLength Maximum length of param names
+     */
+    public void setParamNameMaxLength(int paramNameMaxLength) {
+        this.paramNameMaxLength = paramNameMaxLength;
+    }
+
+    static private int countOGNLCharacters(String s) {
+        int count = 0;
+        for (int i = s.length() - 1; i >= 0; i--) {
+            char c = s.charAt(i);
+            if (c == '.' || c == '[') count++;
+        }
+        return count;
+    }
+
+    /**
+     * Compares based on number of '.' and '[' characters (fewer is higher)
+     */
+    static final Comparator<String> rbCollator = new Comparator<String>() {
+        public int compare(String s1, String s2) {
+            int l1 = countOGNLCharacters(s1),
+                l2 = countOGNLCharacters(s2);
+            return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
+        }
+
+    };
+
+    @Override
+    public String doIntercept(ActionInvocation invocation) throws Exception {
+        Object action = invocation.getAction();
+        if (!(action instanceof NoParameters)) {
+            ActionContext ac = invocation.getInvocationContext();
+            final Map<String, Object> parameters = retrieveParameters(ac);
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Setting params {}", getParameterLogMap(parameters));
+            }
+
+            if (parameters != null) {
+                Map<String, Object> contextMap = ac.getContextMap();
+                try {
+                    ReflectionContextState.setCreatingNullObjects(contextMap, true);
+                    ReflectionContextState.setDenyMethodExecution(contextMap, true);
+                    ReflectionContextState.setReportingConversionErrors(contextMap, true);
+
+                    ValueStack stack = ac.getValueStack();
+                    setParameters(action, stack, parameters);
+                } finally {
+                    ReflectionContextState.setCreatingNullObjects(contextMap, false);
+                    ReflectionContextState.setDenyMethodExecution(contextMap, false);
+                    ReflectionContextState.setReportingConversionErrors(contextMap, false);
+                }
+            }
+        }
+        return invocation.invoke();
+    }
+
+    /**
+     * Gets the parameter map to apply from wherever appropriate
+     *
+     * @param ac The action context
+     * @return The parameter map to apply
+     */
+    protected Map<String, Object> retrieveParameters(ActionContext ac) {
+        return ac.getParameters();
+    }
+
+
+    /**
+     * Adds the parameters into context's ParameterMap
+     *
+     * @param ac        The action context
+     * @param newParams The parameter map to apply
+     *                  <p/>
+     *                  In this class this is a no-op, since the parameters were fetched from the same location.
+     *                  In subclasses both retrieveParameters() and addParametersToContext() should be overridden.
+     */
+    protected void addParametersToContext(ActionContext ac, Map<String, Object> newParams) {
+    }
+
+    protected void setParameters(final Object action, ValueStack stack, final Map<String, Object> parameters) {
+        Map<String, Object> params;
+        Map<String, Object> acceptableParameters;
+        if (ordered) {
+            params = new TreeMap<>(getOrderedComparator());
+            acceptableParameters = new TreeMap<>(getOrderedComparator());
+            params.putAll(parameters);
+        } else {
+            params = new TreeMap<>(parameters);
+            acceptableParameters = new TreeMap<>();
+        }
+
+        for (Map.Entry<String, Object> entry : params.entrySet()) {
+            String name = entry.getKey();
+            Object value = entry.getValue();
+            if (isAcceptableParameter(name, action) && isAcceptableValue(value)) {
+                acceptableParameters.put(name, entry.getValue());
+            }
+        }
+
+        ValueStack newStack = valueStackFactory.createValueStack(stack);
+        boolean clearableStack = newStack instanceof ClearableValueStack;
+        if (clearableStack) {
+            //if the stack's context can be cleared, do that to prevent OGNL
+            //from having access to objects in the stack, see XW-641
+            ((ClearableValueStack)newStack).clearContextValues();
+            Map<String, Object> context = newStack.getContext();
+            ReflectionContextState.setCreatingNullObjects(context, true);
+            ReflectionContextState.setDenyMethodExecution(context, true);
+            ReflectionContextState.setReportingConversionErrors(context, true);
+
+            //keep locale from original context
+            context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE));
+        }
+
+        boolean memberAccessStack = newStack instanceof MemberAccessValueStack;
+        if (memberAccessStack) {
+            //block or allow access to properties
+            //see WW-2761 for more details
+            MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack;
+            accessValueStack.setAcceptProperties(acceptedPatterns.getAcceptedPatterns());
+            accessValueStack.setExcludeProperties(excludedPatterns.getExcludedPatterns());
+        }
+
+        for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) {
+            String name = entry.getKey();
+            Object value = entry.getValue();
+            try {
+                newStack.setParameter(name, value);
+            } catch (RuntimeException e) {
+                if (devMode) {
+                    notifyDeveloperParameterException(action, name, e.getMessage());
+                }
+            }
+        }
+
+        if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null))
+            stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS));
+
+        addParametersToContext(ActionContext.getContext(), acceptableParameters);
+    }
+
+    protected void notifyDeveloperParameterException(Object action, String property, String message) {
+        String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification",
+                ActionContext.getContext().getLocale(), "Developer Notification:\n{0}",
+                new Object[]{
+                        "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message
+                }
+        );
+        LOG.error(developerNotification);
+        // see https://issues.apache.org/jira/browse/WW-4066
+        if (action instanceof ValidationAware) {
+            Collection<String> messages = ((ValidationAware) action).getActionMessages();
+            messages.add(message);
+            ((ValidationAware) action).setActionMessages(messages);
+        }
+    }
+
+    /**
+     * Checks if name of parameter can be accepted or thrown away
+     *
+     * @param name parameter name
+     * @param action current action
+     * @return true if parameter is accepted
+     */
+    protected boolean isAcceptableParameter(String name, Object action) {
+        ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware) ? (ParameterNameAware) action : null;
+        return acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
+    }
+
+    /**
+     * Checks if given value doesn't match global excluded patterns to avoid passing malicious code
+     *
+     * @param value incoming parameter's value
+     * @return true if value is safe
+     *
+     * FIXME: can be removed when parameters won't be represented as simple Strings
+     */
+    protected boolean isAcceptableValue(Object value) {
+        if (value == null) {
+            return true;
+        }
+        Object[] values;
+        if (value.getClass().isArray()) {
+            values = (Object[]) value;
+        } else {
+            values = new Object[] { value };
+        }
+        boolean result = true;
+        for (Object obj : values) {
+            if (isExcluded(String.valueOf(obj))) {
+                result = false;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Gets an instance of the comparator to use for the ordered sorting.  Override this
+     * method to customize the ordering of the parameters as they are set to the
+     * action.
+     *
+     * @return A comparator to sort the parameters
+     */
+    protected Comparator<String> getOrderedComparator() {
+        return rbCollator;
+    }
+
+    protected String getParameterLogMap(Map<String, Object> parameters) {
+        if (parameters == null) {
+            return "NONE";
+        }
+
+        StringBuilder logEntry = new StringBuilder();
+        for (Map.Entry entry : parameters.entrySet()) {
+            logEntry.append(String.valueOf(entry.getKey()));
+            logEntry.append(" => ");
+            if (entry.getValue() instanceof Object[]) {
+                Object[] valueArray = (Object[]) entry.getValue();
+                logEntry.append("[ ");
+                if (valueArray.length > 0 ) {
+                    for (int indexA = 0; indexA < (valueArray.length - 1); indexA++) {
+                        Object valueAtIndex = valueArray[indexA];
+                        logEntry.append(String.valueOf(valueAtIndex));
+                        logEntry.append(", ");
+                    }
+                    logEntry.append(String.valueOf(valueArray[valueArray.length - 1]));
+                }
+                logEntry.append(" ] ");
+            } else {
+                logEntry.append(String.valueOf(entry.getValue()));
+            }
+        }
+
+        return logEntry.toString();
+    }
+
+    protected boolean acceptableName(String name) {
+        boolean accepted = isWithinLengthLimit(name) && !isExcluded(name) && isAccepted(name);
+        if (devMode && accepted) { // notify only when in devMode
+            LOG.debug("Parameter [{}] was accepted and will be appended to action!", name);
+        }
+        return accepted;
+    }
+
+	protected boolean isWithinLengthLimit( String name ) {
+        boolean matchLength = name.length() <= paramNameMaxLength;
+        if (!matchLength) {
+            notifyDeveloper("Parameter [{}] is too long, allowed length is [{}]", name, String.valueOf(paramNameMaxLength));
+        }
+        return matchLength;
+	}
+
+    protected boolean isAccepted(String paramName) {
+        AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
+        if (result.isAccepted()) {
+            return true;
+        }
+        notifyDeveloper("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
+        return false;
+    }
+
+    protected boolean isExcluded(String paramName) {
+        ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
+        if (result.isExcluded()) {
+            notifyDeveloper("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
+            return true;
+        }
+        return false;
+    }
+
+    private void notifyDeveloper(String message, String... parameters) {
+        if (devMode) {
+            LOG.warn(message, parameters);
+        } else {
+            LOG.debug(message, parameters);
+        }
+    }
+
+    /**
+     * Whether to order the parameters or not
+     *
+     * @return True to order
+     */
+    public boolean isOrdered() {
+        return ordered;
+    }
+
+    /**
+     * Set whether to order the parameters by object depth or not
+     *
+     * @param ordered True to order them
+     */
+    public void setOrdered(boolean ordered) {
+        this.ordered = ordered;
+    }
+
+    /**
+     * Sets a comma-delimited list of regular expressions to match
+     * parameters that are allowed in the parameter map (aka whitelist).
+     * <p/>
+     * Don't change the default unless you know what you are doing in terms
+     * of security implications.
+     *
+     * @param commaDelim A comma-delimited list of regular expressions
+     */
+    public void setAcceptParamNames(String commaDelim) {
+        acceptedPatterns.setAcceptedPatterns(commaDelim);
+    }
+
+    /**
+     * Sets a comma-delimited list of regular expressions to match
+     * parameters that should be removed from the parameter map.
+     *
+     * @param commaDelim A comma-delimited list of regular expressions
+     */
+    public void setExcludeParams(String commaDelim) {
+        excludedPatterns.setExcludedPatterns(commaDelim);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/PreResultListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/PreResultListener.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/PreResultListener.java
new file mode 100644
index 0000000..12643a3
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/PreResultListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+
+
+/**
+ * PreResultListeners may be registered with an {@link ActionInvocation} to get a callback after the
+ * {@link com.opensymphony.xwork2.Action} has been executed but before the {@link com.opensymphony.xwork2.Result}
+ * is executed.
+ *
+ * @author Jason Carreira
+ */
+public interface PreResultListener {
+
+    /**
+     * This callback method will be called after the {@link com.opensymphony.xwork2.Action} execution and
+     * before the {@link com.opensymphony.xwork2.Result} execution.
+     *
+     * @param invocation  the action invocation
+     * @param resultCode  the result code returned by the action (eg. <code>success</code>).
+     */
+    void beforeResult(ActionInvocation invocation, String resultCode);
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/PrefixMethodInvocationUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/PrefixMethodInvocationUtil.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/PrefixMethodInvocationUtil.java
new file mode 100644
index 0000000..5a6e5a1
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/PrefixMethodInvocationUtil.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * A utility class for invoking prefixed methods in action class.
+ * 
+ * Interceptors that made use of this class are:
+ * <ul>
+ * 	 <li>DefaultWorkflowInterceptor</li>
+ *   <li>PrepareInterceptor</li>
+ * </ul>
+ * 
+ * <p/>
+ * 
+ * <!-- START SNIPPET: javadocDefaultWorkflowInterceptor -->
+ * 
+ * <b>In DefaultWorkflowInterceptor</b>
+ * <p>applies only when action implements {@link com.opensymphony.xwork2.Validateable}</p>
+ * <ol>
+ *    <li>if the action class have validate{MethodName}(), it will be invoked</li>
+ *    <li>else if the action class have validateDo{MethodName}(), it will be invoked</li>
+ *    <li>no matter if 1] or 2] is performed, if alwaysInvokeValidate property of the interceptor is "true" (which is by default "true"), validate() will be invoked.</li>
+ * </ol>
+ * 
+ * <!-- END SNIPPET: javadocDefaultWorkflowInterceptor -->
+ * 
+ * 
+ * <!-- START SNIPPET: javadocPrepareInterceptor -->
+ * 
+ * <b>In PrepareInterceptor</b>
+ * <p>Applies only when action implements Preparable</p>
+ * <ol>
+ *    <li>if the action class have prepare{MethodName}(), it will be invoked</li>
+ *    <li>else if the action class have prepareDo(MethodName()}(), it will be invoked</li>
+ *    <li>no matter if 1] or 2] is performed, if alwaysinvokePrepare property of the interceptor is "true" (which is by default "true"), prepare() will be invoked.</li>
+ * </ol>
+ * 
+ * <!-- END SNIPPET: javadocPrepareInterceptor -->
+ * 
+ * @author Philip Luppens
+ * @author tm_jee
+ */
+public class PrefixMethodInvocationUtil {
+	
+	private static final Logger LOG = LogManager.getLogger(PrefixMethodInvocationUtil.class);
+
+    private static final String DEFAULT_INVOCATION_METHODNAME = "execute";
+
+    private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
+
+    /**
+	 * This method will prefix <code>actionInvocation</code>'s <code>ActionProxy</code>'s
+	 * <code>method</code> with <code>prefixes</code> before invoking the prefixed method.
+	 * Order of the <code>prefixes</code> is important, as this method will return once 
+	 * a prefixed method is found in the action class.
+	 * 
+	 * <p/>
+	 * 
+	 * For example, with
+	 * <pre>
+	 *   invokePrefixMethod(actionInvocation, new String[] { "prepare", "prepareDo" });
+	 * </pre>
+	 * 
+	 * Assuming <code>actionInvocation.getProxy(),getMethod()</code> returns "submit", 
+	 * the order of invocation would be as follows:-
+	 * <ol>
+	 *   <li>prepareSubmit()</li>
+	 *   <li>prepareDoSubmit()</li>
+	 * </ol>
+	 * 
+	 * If <code>prepareSubmit()</code> exists, it will be invoked and this method 
+	 * will return, <code>prepareDoSubmit()</code> will NOT be invoked. 
+	 * 
+	 * <p/>
+	 * 
+	 * On the other hand, if <code>prepareDoSubmit()</code> does not exists, and 
+	 * <code>prepareDoSubmit()</code> exists, it will be invoked.
+	 * 
+	 * <p/>
+	 * 
+	 * If none of those two methods exists, nothing will be invoked.
+	 * 
+	 * @param actionInvocation  the action invocation
+	 * @param prefixes  prefixes for method names
+	 * @throws InvocationTargetException is thrown if invocation of a method failed.
+	 * @throws IllegalAccessException  is thrown if invocation of a method failed.
+	 */
+	public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {
+		Object action = actionInvocation.getAction();
+		
+		String methodName = actionInvocation.getProxy().getMethod();
+		
+		if (methodName == null) {
+			// if null returns (possible according to the docs), use the default execute 
+	        methodName = DEFAULT_INVOCATION_METHODNAME;
+		}
+		
+		Method method = getPrefixedMethod(prefixes, methodName, action);
+		if (method != null) {
+			method.invoke(action, new Object[0]);
+		}
+	}
+	
+	
+	/**
+	 * This method returns a {@link Method} in <code>action</code>. The method 
+	 * returned is found by searching for method in <code>action</code> whose method name
+	 * is equals to the result of appending each <code>prefixes</code>
+	 * to <code>methodName</code>. Only the first method found will be returned, hence
+	 * the order of <code>prefixes</code> is important. If none is found this method
+	 * will return null.
+	 * 
+	 * @param prefixes the prefixes to prefix the <code>methodName</code>
+	 * @param methodName the method name to be prefixed with <code>prefixes</code>
+	 * @param action the action class of which the prefixed method is to be search for.
+	 * @return a {@link Method} if one is found, else <tt>null</tt>.
+	 */
+	public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {
+		assert(prefixes != null);
+		String capitalizedMethodName = capitalizeMethodName(methodName);
+        for (String prefixe : prefixes) {
+            String prefixedMethodName = prefixe + capitalizedMethodName;
+            try {
+                return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);
+            }
+            catch (NoSuchMethodException e) {
+                // hmm -- OK, try next prefix
+                LOG.debug("Cannot find method [{}] in action [{}]", prefixedMethodName, action);
+            }
+        }
+		return null;
+	}
+	
+	/**
+	 * This method capitalized the first character of <code>methodName</code>.
+	 * <br/>
+	 * eg. <code>capitalizeMethodName("someMethod");</code> will return <code>"SomeMethod"</code>.
+	 * 
+	 * @param methodName the method name
+	 * @return capitalized method name
+	 */
+	public static String capitalizeMethodName(String methodName) {
+		assert(methodName != null);
+		return methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/PrepareInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/PrepareInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/PrepareInterceptor.java
new file mode 100644
index 0000000..6a327c3
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/PrepareInterceptor.java
@@ -0,0 +1,174 @@
+/*
+ * $Id$
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.Preparable;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+import java.lang.reflect.InvocationTargetException;
+
+
+/**
+ * <!-- START SNIPPET: description -->
+ *
+ * This interceptor calls <code>prepare()</code> on actions which implement
+ * {@link Preparable}. This interceptor is very useful for any situation where
+ * you need to ensure some logic runs before the actual execute method runs.
+ *
+ * <p/> A typical use of this is to run some logic to load an object from the
+ * database so that when parameters are set they can be set on this object. For
+ * example, suppose you have a User object with two properties: <i>id</i> and
+ * <i>name</i>. Provided that the params interceptor is called twice (once
+ * before and once after this interceptor), you can load the User object using
+ * the id property, and then when the second params interceptor is called the
+ * parameter <i>user.name</i> will be set, as desired, on the actual object
+ * loaded from the database. See the example for more info.
+ *
+ * <p/>
+ * <b>Note:</b> Since XWork 2.0.2, this interceptor extends {@link MethodFilterInterceptor}, therefore being
+ * able to deal with excludeMethods / includeMethods parameters. See [Workflow Interceptor]
+ * (class {@link DefaultWorkflowInterceptor}) for documentation and examples on how to use this feature.
+ *
+ * <p/><b>Update</b>: Added logic to execute a prepare{MethodName} and conditionally
+ * the a general prepare() Method, depending on the 'alwaysInvokePrepare' parameter/property
+ * which is by default true. This allows us to run some logic based on the method
+ * name we specify in the {@link com.opensymphony.xwork2.ActionProxy}. For example, you can specify a
+ * prepareInput() method that will be run before the invocation of the input method.
+ *
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Interceptor parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ *
+ * <ul>
+ *
+ * <li>alwaysInvokePrepare - Default to true. If true, prepare will always be invoked,
+ * otherwise it will not.</li>
+ *
+ * </ul>
+ *
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Extending the interceptor:</u>
+ *
+ * <p/>
+ *
+ * <!-- START SNIPPET: extending -->
+ *
+ * There are no known extension points to this interceptor.
+ *
+ * <!-- END SNIPPET: extending -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &lt;!-- Calls the params interceptor twice, allowing you to
+ *       pre-load data for the second time parameters are set --&gt;
+ *  &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *      &lt;interceptor-ref name="params"/&gt;
+ *      &lt;interceptor-ref name="prepare"/&gt;
+ *      &lt;interceptor-ref name="basicStack"/&gt;
+ *      &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ *  &lt;/action&gt;
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Jason Carreira
+ * @author Philip Luppens
+ * @author tm_jee
+ * @see com.opensymphony.xwork2.Preparable
+ */
+public class PrepareInterceptor extends MethodFilterInterceptor {
+
+    private static final long serialVersionUID = -5216969014510719786L;
+
+    private final static String PREPARE_PREFIX = "prepare";
+    private final static String ALT_PREPARE_PREFIX = "prepareDo";
+
+    private boolean alwaysInvokePrepare = true;
+    private boolean firstCallPrepareDo = false;
+
+    /**
+     * Sets if the <code>preapare</code> method should always be executed.
+     * <p/>
+     * Default is <tt>true</tt>.
+     *
+     * @param alwaysInvokePrepare if <code>prepare</code> should always be executed or not.
+     */
+    public void setAlwaysInvokePrepare(String alwaysInvokePrepare) {
+        this.alwaysInvokePrepare = Boolean.parseBoolean(alwaysInvokePrepare);
+    }
+
+    /**
+     * Sets if the <code>prepareDoXXX</code> method should be called first
+     * <p/>
+     * Default is <tt>false</tt> for backward compatibility
+     *
+     * @param firstCallPrepareDo if <code>prepareDoXXX</code> should be called first
+     */
+    public void setFirstCallPrepareDo(String firstCallPrepareDo) {
+        this.firstCallPrepareDo = Boolean.parseBoolean(firstCallPrepareDo);
+    }
+
+    @Override
+    public String doIntercept(ActionInvocation invocation) throws Exception {
+        Object action = invocation.getAction();
+
+        if (action instanceof Preparable) {
+            try {
+                String[] prefixes;
+                if (firstCallPrepareDo) {
+                    prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};
+                } else {
+                    prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};
+                }
+                PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
+            }
+            catch (InvocationTargetException e) {
+                /*
+                 * The invoked method threw an exception and reflection wrapped it
+                 * in an InvocationTargetException.
+                 * If possible re-throw the original exception so that normal
+                 * exception handling will take place.
+                 */
+                Throwable cause = e.getCause();
+                if (cause instanceof Exception) {
+                    throw (Exception) cause;
+                } else if(cause instanceof Error) {
+                    throw (Error) cause;
+                } else {
+                    /*
+                     * The cause is not an Exception or Error (must be Throwable) so
+                     * just re-throw the wrapped exception.
+                     */
+                    throw e;
+                }
+            }
+
+            if (alwaysInvokePrepare) {
+                ((Preparable) action).prepare();
+            }
+        }
+
+        return invocation.invoke();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/ScopedModelDriven.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ScopedModelDriven.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ScopedModelDriven.java
new file mode 100644
index 0000000..e381373
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ScopedModelDriven.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.interceptor;
+
+import com.opensymphony.xwork2.ModelDriven;
+
+/**
+ * Adds the ability to set a model, probably retrieved from a given state.
+ */
+public interface ScopedModelDriven<T> extends ModelDriven<T> {
+
+    /**
+     * Sets the model
+     */
+    void setModel(T model);
+    
+    /**
+     * Sets the key under which the model is stored
+     * @param key The model key
+     */
+    void setScopeKey(String key);
+    
+    /**
+     * Gets the key under which the model is stored
+     */
+    String getScopeKey();
+}


Mime
View raw message