struts-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lukaszlen...@apache.org
Subject [39/57] [partial] struts git commit: Merges xwork packages into struts
Date Wed, 17 Jun 2015 21:09:39 GMT
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptor.java
new file mode 100644
index 0000000..9d32fbe
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ScopedModelDrivenInterceptor.java
@@ -0,0 +1,164 @@
+/*
+ * 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.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+import com.opensymphony.xwork2.inject.Inject;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * <!-- START SNIPPET: description -->
+ *
+ * An interceptor that enables scoped model-driven actions.
+ *
+ * <p/>This interceptor only activates on actions that implement the {@link ScopedModelDriven} interface.  If
+ * detected, it will retrieve the model class from the configured scope, then provide it to the Action.
+ *  
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Interceptor parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ *
+ * <ul>
+ *
+ * <li>className - The model class name.  Defaults to the class name of the object returned by the getModel() method.</li>
+ *            
+ * <li>name - The key to use when storing or retrieving the instance in a scope.  Defaults to the model
+ *            class name.</li>
+ *
+ * <li>scope - The scope to store and retrieve the model.  Defaults to 'request' but can also be 'session'.</li>
+ * </ul>
+ *
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Extending the interceptor:</u>
+ *
+ * <p/>
+ *
+ * <!-- START SNIPPET: extending -->
+ *
+ * There are no known extension points for this interceptor.
+ *
+ * <!-- END SNIPPET: extending -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * 
+ * &lt;-- Basic usage --&gt;
+ * &lt;interceptor name="scopedModelDriven" class="com.opensymphony.interceptor.ScopedModelDrivenInterceptor" /&gt;
+ * 
+ * &lt;-- Using all available parameters --&gt;
+ * &lt;interceptor name="gangsterForm" class="com.opensymphony.interceptor.ScopedModelDrivenInterceptor"&gt;
+ *      &lt;param name="scope"&gt;session&lt;/param&gt;
+ *      &lt;param name="name"&gt;gangsterForm&lt;/param&gt;
+ *      &lt;param name="className"&gt;com.opensymphony.example.GangsterForm&lt;/param&gt;
+ *  &lt;/interceptor&gt;
+ * 
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ */
+public class ScopedModelDrivenInterceptor extends AbstractInterceptor {
+
+    private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
+    
+    private static final String GET_MODEL = "getModel";
+    private String scope;
+    private String name;
+    private String className;
+    private ObjectFactory objectFactory;
+    
+    @Inject
+    public void setObjectFactory(ObjectFactory factory) {
+        this.objectFactory = factory;
+    }
+    
+    protected Object resolveModel(ObjectFactory factory, ActionContext actionContext, String modelClassName, String modelScope, String modelName) throws Exception {
+        Object model;
+        Map<String, Object> scopeMap = actionContext.getContextMap();
+        if ("session".equals(modelScope)) {
+            scopeMap = actionContext.getSession();
+        }
+        
+        model = scopeMap.get(modelName);
+        if (model == null) {
+            model = factory.buildBean(modelClassName, null);
+            scopeMap.put(modelName, model);
+        }
+        return model;
+    }
+
+    @Override
+    public String intercept(ActionInvocation invocation) throws Exception {
+        Object action = invocation.getAction();
+
+        if (action instanceof ScopedModelDriven) {
+            ScopedModelDriven modelDriven = (ScopedModelDriven) action;
+            if (modelDriven.getModel() == null) {
+                ActionContext ctx = ActionContext.getContext();
+                ActionConfig config = invocation.getProxy().getConfig();
+                
+                String cName = className;
+                if (cName == null) {
+                    try {
+                        Method method = action.getClass().getMethod(GET_MODEL, EMPTY_CLASS_ARRAY);
+                        Class cls = method.getReturnType();
+                        cName = cls.getName();
+                    } catch (NoSuchMethodException e) {
+                        throw new XWorkException("The " + GET_MODEL + "() is not defined in action " + action.getClass() + "", config);
+                    }
+                }
+                String modelName = name;
+                if (modelName == null) {
+                    modelName = cName;
+                }
+                Object model = resolveModel(objectFactory, ctx, cName, scope, modelName);
+                modelDriven.setModel(model);
+                modelDriven.setScopeKey(modelName);
+            }
+        }
+        return invocation.invoke();
+    }
+
+    /**
+     * @param className the className to set
+     */
+    public void setClassName(String className) {
+        this.className = className;
+    }
+
+    /**
+     * @param name the name to set
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * @param scope the scope to set
+     */
+    public void setScope(String scope) {
+        this.scope = scope;
+    }    
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.java
new file mode 100644
index 0000000..25fd8d6
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.java
@@ -0,0 +1,240 @@
+/*
+ * 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.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ValidationAware;
+import com.opensymphony.xwork2.XWorkConstants;
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+import com.opensymphony.xwork2.config.entities.Parameterizable;
+import com.opensymphony.xwork2.inject.Inject;
+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.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+
+/**
+ * <!-- START SNIPPET: description -->
+ *
+ * This interceptor populates the action with the static parameters defined in the action configuration. If the action
+ * implements {@link Parameterizable}, a map of the static parameters will be also be passed directly to the action.
+ * The static params will be added to the request params map, unless "merge" is set to false.
+ *
+ * <p/> Parameters are typically defined with &lt;param&gt; elements within xwork.xml.
+ *
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Interceptor parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ *
+ * <ul>
+ *
+ * <li>None</li>
+ *
+ * </ul>
+ *
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Extending the interceptor:</u>
+ *
+ * <!-- START SNIPPET: extending -->
+ *
+ * <p/>There are no 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="staticParams"&gt;
+ *          &lt;param name="parse"&gt;true&lt;/param&gt;
+ *          &lt;param name="overwrite"&gt;false&lt;/param&gt;
+ *     &lt;/interceptor-ref&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Patrick Lightbody
+ */
+public class StaticParametersInterceptor extends AbstractInterceptor {
+
+    private boolean parse;
+    private boolean overwrite;
+    private boolean merge = true;
+    private boolean devMode = false;
+
+    private static final Logger LOG = LogManager.getLogger(StaticParametersInterceptor.class);
+
+    private ValueStackFactory valueStackFactory;
+
+    @Inject
+    public void setValueStackFactory(ValueStackFactory valueStackFactory) {
+        this.valueStackFactory = valueStackFactory;
+    }
+
+    @Inject(XWorkConstants.DEV_MODE)
+    public void setDevMode(String mode) {
+        devMode = BooleanUtils.toBoolean(mode);
+    }    
+
+    public void setParse(String value) {
+        this.parse = BooleanUtils.toBoolean(value);
+    }
+
+     public void setMerge(String value) {
+         this.merge = BooleanUtils.toBoolean(value);
+    }
+
+    /**
+     * Overwrites already existing parameters from other sources.
+     * Static parameters are the successor over previously set parameters, if true.
+     *
+     * @param value
+     */
+    public void setOverwrite(String value) {
+        this.overwrite = BooleanUtils.toBoolean(value);
+    }
+
+    @Override
+    public String intercept(ActionInvocation invocation) throws Exception {
+        ActionConfig config = invocation.getProxy().getConfig();
+        Object action = invocation.getAction();
+
+        final Map<String, String> parameters = config.getParams();
+
+        LOG.debug("Setting static parameters: {}", parameters);
+
+        // for actions marked as Parameterizable, pass the static parameters directly
+        if (action instanceof Parameterizable) {
+            ((Parameterizable) action).setParams(parameters);
+        }
+
+        if (parameters != null) {
+            ActionContext ac = ActionContext.getContext();
+            Map<String, Object> contextMap = ac.getContextMap();
+            try {
+                ReflectionContextState.setCreatingNullObjects(contextMap, true);
+                ReflectionContextState.setReportingConversionErrors(contextMap, true);
+                final ValueStack stack = ac.getValueStack();
+
+                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));
+                }
+
+                for (Map.Entry<String, String> entry : parameters.entrySet()) {
+                    Object val = entry.getValue();
+                    if (parse && val instanceof String) {
+                        val = TextParseUtil.translateVariables(val.toString(), stack);
+                    }
+                    try {
+                        newStack.setValue(entry.getKey(), val);
+                    } catch (RuntimeException e) {
+                        if (devMode) {
+                            String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
+                                    "Unexpected Exception caught setting '" + entry.getKey() + "' on '" + action.getClass() + ": " + e.getMessage()
+                            });
+                            LOG.error(developerNotification);
+                            if (action instanceof ValidationAware) {
+                                ((ValidationAware) action).addActionMessage(developerNotification);
+                            }
+                        }
+                    }
+                }
+
+                 if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null))
+                    stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS));
+
+                if (merge)
+                    addParametersToContext(ac, parameters);
+            } finally {
+                ReflectionContextState.setCreatingNullObjects(contextMap, false);
+                ReflectionContextState.setReportingConversionErrors(contextMap, false);
+            }
+        }
+        return invocation.invoke();
+    }
+
+
+    /**
+     * @param ac The action context
+     * @return the parameters from the action mapping in the context.  If none found, returns
+     *         an empty map.
+     */
+    protected Map<String, String> retrieveParameters(ActionContext ac) {
+        ActionConfig config = ac.getActionInvocation().getProxy().getConfig();
+        if (config != null) {
+            return config.getParams();
+        } else {
+            return Collections.emptyMap();
+        }
+    }
+
+    /**
+     * Adds the parameters into context's ParameterMap.
+     * As default, static parameters will not overwrite existing paramaters from other sources.
+     * If you want the static parameters as successor over already existing parameters, set overwrite to <tt>true</tt>.
+     *
+     * @param ac        The action context
+     * @param newParams The parameter map to apply
+     */
+    protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
+        Map<String, Object> previousParams = ac.getParameters();
+
+        Map<String, Object> combinedParams;
+        if ( overwrite ) {
+            if (previousParams != null) {
+                combinedParams = new TreeMap<>(previousParams);
+            } else {
+                combinedParams = new TreeMap<>();
+            }
+            if ( newParams != null) {
+                combinedParams.putAll(newParams);
+            }
+        } else {
+            if (newParams != null) {
+                combinedParams = new TreeMap<>(newParams);
+            } else {
+                combinedParams = new TreeMap<>();
+            }
+            if ( previousParams != null) {
+                combinedParams.putAll(previousParams);
+            }
+        }
+        ac.setParameters(combinedParams);
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/TimerInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/TimerInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/TimerInterceptor.java
new file mode 100644
index 0000000..7cc4918
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/TimerInterceptor.java
@@ -0,0 +1,244 @@
+/*
+ * 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.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * This interceptor logs the amount of time in milliseconds. In order for this interceptor to work properly, the
+ * logging framework must be set to at least the <tt>INFO</tt> level.
+ * This interceptor relies on the <a href="http://jakarta.apache.org/commons/logging/">Commons Logging API</a> to
+ * report its execution-time value.
+ * <!-- END SNIPPET: description -->
+ *
+ * <!-- START SNIPPET: parameters -->
+ *
+ * <ul>
+ *
+ * <li>logLevel (optional) - what log level should we use (<code>trace, debug, info, warn, error, fatal</code>)? - defaut is <code>info</code></li>
+ *
+ * <li>logCategory (optional) - If provided we would use this category (eg. <code>com.mycompany.app</code>).
+ * Default is to use <code>com.opensymphony.xwork2.interceptor.TimerInterceptor</code>.</li>
+ *
+ * </ul>
+ *
+ * The parameters above enables us to log all action execution times in our own logfile.
+ *
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <!-- START SNIPPET: extending -->
+ * This interceptor can be extended to provide custom message format. Users should override the
+ * <code>invokeUnderTiming</code> method.
+ * <!-- END SNIPPET: extending -->
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &lt;!-- records only the action's execution time --&gt;
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *     &lt;interceptor-ref name="completeStack"/&gt;
+ *     &lt;interceptor-ref name="timer"/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ *
+ * &lt;!-- records action's execution time as well as other interceptors--&gt;
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *     &lt;interceptor-ref name="timer"/&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>
+ *
+ * This second example uses our own log category at debug level.
+ *
+ * <pre>
+ * <!-- START SNIPPET: example2 -->
+ * &lt;!-- records only the action's execution time --&gt;
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *     &lt;interceptor-ref name="completeStack"/&gt;
+ *     &lt;interceptor-ref name="timer"&gt;
+ *         &lt;param name="logLevel"&gt;debug&lt;/param&gt;
+ *         &lt;param name="logCategory"&gt;com.mycompany.myapp.actiontime&lt;/param&gt;
+ *     &lt;interceptor-ref/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ *
+ * &lt;!-- records action's execution time as well as other interceptors--&gt;
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *     &lt;interceptor-ref name="timer"/&gt;
+ *     &lt;interceptor-ref name="completeStack"/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ * <!-- END SNIPPET: example2 -->
+ * </pre>
+ *
+ * @author Jason Carreira
+ * @author Claus Ibsen
+ */
+public class TimerInterceptor extends AbstractInterceptor {
+    protected static final Logger LOG = LogManager.getLogger(TimerInterceptor.class);
+
+    protected Logger categoryLogger;
+    protected String logCategory;
+    protected String logLevel;
+
+    public String getLogCategory() {
+        return logCategory;
+    }
+
+    public void setLogCategory(String logCatgory) {
+        this.logCategory = logCatgory;
+    }
+
+    public String getLogLevel() {
+        return logLevel;
+    }
+
+    public void setLogLevel(String logLevel) {
+        this.logLevel = logLevel;
+    }
+
+    @Override
+    public String intercept(ActionInvocation invocation) throws Exception {
+        if (! shouldLog()) {
+            return invocation.invoke();
+        } else {
+            return invokeUnderTiming(invocation);
+        }
+    }
+
+    /**
+     * Is called to invoke the action invocation and time the execution time.
+     *
+     * @param invocation  the action invocation.
+     * @return the result of the action execution.
+     * @throws Exception  can be thrown from the action.
+     */
+    protected String invokeUnderTiming(ActionInvocation invocation) throws Exception {
+        long startTime = System.currentTimeMillis();
+        String result = invocation.invoke();
+        long executionTime = System.currentTimeMillis() - startTime;
+
+        StringBuilder message = new StringBuilder(100);
+        message.append("Executed action [");
+        String namespace = invocation.getProxy().getNamespace();
+        if (StringUtils.isNotBlank(namespace)) {
+            message.append(namespace).append("/");
+        }
+        message.append(invocation.getProxy().getActionName());
+        message.append("!");
+        message.append(invocation.getProxy().getMethod());
+        message.append("] took ").append(executionTime).append(" ms.");
+
+        doLog(getLoggerToUse(), message.toString());
+
+        return result;
+    }
+
+    /**
+     * Determines if we should log the time.
+     *
+     * @return  true to log, false to not log.
+     */
+    protected boolean shouldLog() {
+        // default check first
+        if (logLevel == null && logCategory == null) {
+            return LOG.isInfoEnabled();
+        }
+
+        // okay user have set some parameters
+        return isLoggerEnabled(getLoggerToUse(), logLevel);
+    }
+
+    /**
+     * Get's the logger to use.
+     *
+     * @return the logger to use.
+     */
+    protected Logger getLoggerToUse() {
+        if (logCategory != null) {
+            if (categoryLogger == null) {
+                // init category logger
+                categoryLogger = LogManager.getLogger(logCategory);
+                if (logLevel == null) {
+                    logLevel = "info"; // use info as default if not provided
+                }
+            }
+            return categoryLogger;
+        }
+
+        return LOG;
+    }
+
+    /**
+     * Performs the actual logging.
+     *
+     * @param logger  the provided logger to use.
+     * @param message  the message to log.
+     */
+    protected void doLog(Logger logger, String message) {
+        if (logLevel == null) {
+            logger.info(message);
+            return;
+        }
+
+        if ("debug".equalsIgnoreCase(logLevel)) {
+            logger.debug(message);
+        } else if ("info".equalsIgnoreCase(logLevel)) {
+            logger.info(message);
+        } else if ("warn".equalsIgnoreCase(logLevel)) {
+            logger.warn(message);
+        } else if ("error".equalsIgnoreCase(logLevel)) {
+            logger.error(message);
+        } else if ("fatal".equalsIgnoreCase(logLevel)) {
+            logger.fatal(message);
+        } else if ("trace".equalsIgnoreCase(logLevel)) {
+            logger.trace(message);
+        } else {
+            throw new IllegalArgumentException("LogLevel [" + logLevel + "] is not supported");
+        }
+    }
+
+    /**
+     * Is the given logger enalbed at the given level?
+     *
+     * @param logger  the logger.
+     * @param level   the level to check if <code>isXXXEnabled</code>.
+     * @return <tt>true</tt> if enabled, <tt>false</tt> if not.
+     */
+    private static boolean isLoggerEnabled(Logger logger, String level) {
+        if ("debug".equalsIgnoreCase(level)) {
+            return logger.isDebugEnabled();
+        } else if ("info".equalsIgnoreCase(level)) {
+            return logger.isInfoEnabled();
+        } else if ("warn".equalsIgnoreCase(level)) {
+            return logger.isWarnEnabled();
+        } else if ("error".equalsIgnoreCase(level)) {
+            return logger.isErrorEnabled();
+        } else if ("fatal".equalsIgnoreCase(level)) {
+            return logger.isFatalEnabled();
+        } else if ("trace".equalsIgnoreCase(level)) {
+            return logger.isTraceEnabled();
+        } else {
+            throw new IllegalArgumentException("LogLevel [" + level + "] is not supported");
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/ValidationErrorAware.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ValidationErrorAware.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ValidationErrorAware.java
new file mode 100644
index 0000000..5d0fd6b
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ValidationErrorAware.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;
+
+/**
+ * ValidationErrorAware classes can be notified about validation errors
+ * before {@link com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor} will return 'inputResultName' result
+ * to allow change or not the result name
+ *
+ * This interface can be only applied to action which already implements {@link com.opensymphony.xwork2.ValidationAware} interface!
+ *
+ * @since 2.3.15
+ */
+public interface ValidationErrorAware {
+
+    /**
+     * Allows to notify action about occurred action/field errors
+     *
+     * @param currentResultName current result name, action can change it or return the same
+     * @return new result name or passed currentResultName
+     */
+    String actionErrorOccurred(final String currentResultName);
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/ValidationWorkflowAware.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ValidationWorkflowAware.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ValidationWorkflowAware.java
new file mode 100644
index 0000000..51bdf71
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ValidationWorkflowAware.java
@@ -0,0 +1,12 @@
+package com.opensymphony.xwork2.interceptor;
+
+/**
+ * ValidationWorkflowAware classes can programmatically change result name when errors occurred
+ *
+ * This interface can be only applied to action which already implements {@link com.opensymphony.xwork2.ValidationAware} interface!
+ */
+public interface ValidationWorkflowAware {
+
+    String getInputResultName();
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/After.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/After.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/After.java
new file mode 100644
index 0000000..3fc2fcd
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/After.java
@@ -0,0 +1,81 @@
+/*
+ * 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * Marks a action method that needs to be called after the main action method and the result was
+ * executed. Return value is ignored.
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Annotation usage:</u>
+ *
+ * <!-- START SNIPPET: usage -->
+ * The After annotation can be applied at method level.
+ *
+ * <!-- END SNIPPET: usage -->
+ *
+ * <p/> <u>Annotation parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ * <table class='confluenceTable'>
+ * <tr>
+ * <th class='confluenceTh'> Parameter </th>
+ * <th class='confluenceTh'> Required </th>
+ * <th class='confluenceTh'> Default </th>
+ * <th class='confluenceTh'> Notes </th>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>priority</td>
+ * <td class='confluenceTd'>no</td>
+ * <td class='confluenceTd'>10</td>
+ * <td class='confluenceTd'>Priority order of method execution</td>
+ * </tr>
+ * </table>
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * public class SampleAction extends ActionSupport {
+ *
+ *  &#64;After
+ *  public void isValid() throws ValidationException {
+ *    // validate model object, throw exception if failed
+ *  }
+ *
+ *  public String execute() {
+ *     // perform action
+ *     return SUCCESS;
+ *  }
+ * }
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Zsolt Szasz, zsolt at lorecraft dot com
+ * @author Rainer Hermanns
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface After {
+    int priority() default 10; 
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Allowed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Allowed.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Allowed.java
new file mode 100644
index 0000000..4263344
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Allowed.java
@@ -0,0 +1,18 @@
+package com.opensymphony.xwork2.interceptor.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares that it is permitted for the field be mutated through
+ * a HttpRequest parameter.
+ *
+ * @author martin.gilday
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Allowed {
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationParameterFilterIntereptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationParameterFilterIntereptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationParameterFilterIntereptor.java
new file mode 100644
index 0000000..f978e46
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationParameterFilterIntereptor.java
@@ -0,0 +1,95 @@
+package com.opensymphony.xwork2.interceptor.annotations;
+
+
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.interceptor.Interceptor;
+import com.opensymphony.xwork2.interceptor.ParameterFilterInterceptor;
+import com.opensymphony.xwork2.interceptor.ParametersInterceptor;
+import com.opensymphony.xwork2.util.AnnotationUtils;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Annotation based version of {@link ParameterFilterInterceptor}.
+ * <p/>
+ * This {@link Interceptor} must be placed in the stack before the {@link ParametersInterceptor}
+ * When a parameter matches a field that is marked {@link Blocked} then it is removed from
+ * the parameter map.
+ * <p/>
+ * If an {@link Action} class is marked with {@link BlockByDefault} then all parameters are
+ * removed unless a field on the Action exists and is marked with {@link Allowed}
+ *
+ * @author martin.gilday
+ */
+public class AnnotationParameterFilterIntereptor extends AbstractInterceptor {
+
+    /* (non-Javadoc)
+      * @see com.opensymphony.xwork2.interceptor.AbstractInterceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
+      */
+    @Override public String intercept(ActionInvocation invocation) throws Exception {
+
+        final Object action = invocation.getAction();
+        Map<String, Object> parameters = invocation.getInvocationContext().getParameters();
+
+        Object model = invocation.getStack().peek();
+        if (model == action) {
+            model = null;
+        }
+
+        boolean blockByDefault = action.getClass().isAnnotationPresent(BlockByDefault.class);
+        List<Field> annotatedFields = new ArrayList<>();
+        HashSet<String> paramsToRemove = new HashSet<>();
+
+        if (blockByDefault) {
+            AnnotationUtils.addAllFields(Allowed.class, action.getClass(), annotatedFields);
+            if (model != null) {
+                AnnotationUtils.addAllFields(Allowed.class, model.getClass(), annotatedFields);
+            }
+
+            for (String paramName : parameters.keySet()) {
+                boolean allowed = false;
+
+                for (Field field : annotatedFields) {
+                    //TODO only matches exact field names.  need to change to it matches start of ognl expression
+                    //i.e take param name up to first . (period) and match against that
+                    if (field.getName().equals(paramName)) {
+                        allowed = true;
+                        break;
+                    }
+                }
+
+                if (!allowed) {
+                    paramsToRemove.add(paramName);
+                }
+            }
+        } else {
+            AnnotationUtils.addAllFields(Blocked.class, action.getClass(), annotatedFields);
+            if (model != null) {
+                AnnotationUtils.addAllFields(Blocked.class, model.getClass(), annotatedFields);
+            }
+
+            for (String paramName : parameters.keySet()) {
+                for (Field field : annotatedFields) {
+                    //TODO only matches exact field names.  need to change to it matches start of ognl expression
+                    //i.e take param name up to first . (period) and match against that
+                    if (field.getName().equals(paramName)) {
+                        paramsToRemove.add(paramName);
+                    }
+                }
+            }
+        }
+
+        for (String aParamsToRemove : paramsToRemove) {
+            parameters.remove(aParamsToRemove);
+        }
+
+        return invocation.invoke();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationWorkflowInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationWorkflowInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationWorkflowInterceptor.java
new file mode 100644
index 0000000..665ed1c
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/AnnotationWorkflowInterceptor.java
@@ -0,0 +1,192 @@
+/*
+ * 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.annotations;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.interceptor.PreResultListener;
+import com.opensymphony.xwork2.util.AnnotationUtils;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ * <p>Invokes any annotated methods on the action. Specifically, it supports the following
+ * annotations:
+ * <ul>
+ * <li> &#64;{@link Before} - will be invoked before the action method. If the returned value is not null, it is
+ * returned as the action result code</li>
+ * <li> &#64;{@link BeforeResult} - will be invoked after the action method but before the result execution</li>
+ * <li> &#64;{@link After} - will be invoked after the action method and result execution</li>
+ * </ul>
+ * </p>
+ * <p/>
+ * <p>There can be multiple methods marked with the same annotations, but the order of their execution
+ * is not guaranteed. However, the annotated methods on the superclass chain are guaranteed to be invoked before the
+ * annotated method in the current class in the case of a {@link Before} annotations and after, if the annotations is
+ * {@link After}.</p>
+ * <!-- END SNIPPET: javadoc -->
+ * <p/>
+ * <pre>
+ * <!-- START SNIPPET: javacode -->
+ *  public class BaseAnnotatedAction {
+ *  	protected String log = "";
+ * <p/>
+ *  	&#64;Before
+ *  	public String baseBefore() {
+ *  		log = log + "baseBefore-";
+ *  		return null;
+ *  	}
+ *  }
+ * <p/>
+ *  public class AnnotatedAction extends BaseAnnotatedAction {
+ *  	&#64;Before
+ *  	public String before() {
+ *  		log = log + "before";
+ *  		return null;
+ *  	}
+ * <p/>
+ *  	public String execute() {
+ *  		log = log + "-execute";
+ *  		return Action.SUCCESS;
+ *  	}
+ * <p/>
+ *  	&#64;BeforeResult
+ *  	public void beforeResult() throws Exception {
+ *  		log = log +"-beforeResult";
+ *  	}
+ * <p/>
+ *  	&#64;After
+ *  	public void after() {
+ *  		log = log + "-after";
+ *  	}
+ *  }
+ * <!-- END SNIPPET: javacode -->
+ *  </pre>
+ * <p/>
+ * <!-- START SNIPPET: example -->
+ * <p>With the interceptor applied and the action executed on <code>AnnotatedAction</code> the log
+ * instance variable will contain <code>baseBefore-before-execute-beforeResult-after</code>.</p>
+ * <!-- END SNIPPET: example -->
+ * <p/>
+ * <p/>Configure a stack in xwork.xml that replaces the PrepareInterceptor with the AnnotationWorkflowInterceptor:
+ * <pre>
+ * <!-- START SNIPPET: stack -->
+ * &lt;interceptor-stack name="annotatedStack"&gt;
+ * 	&lt;interceptor-ref name="staticParams"/&gt;
+ * 	&lt;interceptor-ref name="params"/&gt;
+ * 	&lt;interceptor-ref name="conversionError"/&gt;
+ * 	&lt;interceptor-ref name="annotationWorkflow"/&gt;
+ * &lt;/interceptor-stack&gt;
+ *  <!-- END SNIPPET: stack -->
+ * </pre>
+ *
+ * @author Zsolt Szasz, zsolt at lorecraft dot com
+ * @author Rainer Hermanns
+ * @author Dan Oxlade, dan d0t oxlade at gmail d0t c0m
+ */
+public class AnnotationWorkflowInterceptor extends AbstractInterceptor implements PreResultListener {
+
+    /**
+     * Discovers annotated methods on the action and calls them according to the workflow
+     *
+     * @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
+     */
+    public String intercept(ActionInvocation invocation) throws Exception {
+        final Object action = invocation.getAction();
+        invocation.addPreResultListener(this);
+        List<Method> methods = new ArrayList<>(AnnotationUtils.getAnnotatedMethods(action.getClass(), Before.class));
+        if (methods.size() > 0) {
+            // methods are only sorted by priority
+            Collections.sort(methods, new Comparator<Method>() {
+                public int compare(Method method1, Method method2) {
+                    return comparePriorities(method1.getAnnotation(Before.class).priority(),
+                                method2.getAnnotation(Before.class).priority());
+                }
+            });
+            for (Method m : methods) {
+                final String resultCode = (String) m.invoke(action, (Object[]) null);
+                if (resultCode != null) {
+                    // shortcircuit execution
+                    return resultCode;
+                }
+            }
+        }
+
+        String invocationResult = invocation.invoke();
+
+        // invoke any @After methods
+        methods = new ArrayList<Method>(AnnotationUtils.getAnnotatedMethods(action.getClass(), After.class));
+
+        if (methods.size() > 0) {
+            // methods are only sorted by priority
+            Collections.sort(methods, new Comparator<Method>() {
+                public int compare(Method method1, Method method2) {
+                    return comparePriorities(method1.getAnnotation(After.class).priority(),
+                                method2.getAnnotation(After.class).priority());
+                }
+            });
+            for (Method m : methods) {
+                m.invoke(action, (Object[]) null);
+            }
+        }
+
+        return invocationResult;
+    }
+
+    protected static int comparePriorities(int val1, int val2) {
+        if (val2 < val1) {
+            return -1;
+        } else if (val2 > val1) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Invokes any &#64;BeforeResult annotated methods
+     *
+     * @see com.opensymphony.xwork2.interceptor.PreResultListener#beforeResult(com.opensymphony.xwork2.ActionInvocation,String)
+     */
+    public void beforeResult(ActionInvocation invocation, String resultCode) {
+        Object action = invocation.getAction();
+        List<Method> methods = new ArrayList<Method>(AnnotationUtils.getAnnotatedMethods(action.getClass(), BeforeResult.class));
+
+        if (methods.size() > 0) {
+            // methods are only sorted by priority
+            Collections.sort(methods, new Comparator<Method>() {
+                public int compare(Method method1, Method method2) {
+                    return comparePriorities(method1.getAnnotation(BeforeResult.class).priority(),
+                                method2.getAnnotation(BeforeResult.class).priority());
+                }
+            });
+            for (Method m : methods) {
+                try {
+                    m.invoke(action, (Object[]) null);
+                } catch (Exception e) {
+                    throw new XWorkException(e);
+                }
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Before.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Before.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Before.java
new file mode 100644
index 0000000..cb0e555
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Before.java
@@ -0,0 +1,80 @@
+/*
+ * 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * Marks a action method that needs to be executed before the main action method.
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Annotation usage:</u>
+ *
+ * <!-- START SNIPPET: usage -->
+ * The Before annotation can be applied at method level.
+ *
+ * <!-- END SNIPPET: usage -->
+ *
+ * <p/> <u>Annotation parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ * <table class='confluenceTable'>
+ * <tr>
+ * <th class='confluenceTh'> Parameter </th>
+ * <th class='confluenceTh'> Required </th>
+ * <th class='confluenceTh'> Default </th>
+ * <th class='confluenceTh'> Notes </th>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>priority</td>
+ * <td class='confluenceTd'>no</td>
+ * <td class='confluenceTd'>10</td>
+ * <td class='confluenceTd'>Priority order of method execution</td>
+ * </tr>
+ * </table>
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * public class SampleAction extends ActionSupport {
+ *
+ *  &#64;Before
+ *  public void isAuthorized() throws AuthenticationException {
+ *    // authorize request, throw exception if failed
+ *  }
+ *
+ *  public String execute() {
+ *     // perform secure action
+ *     return SUCCESS;
+ *  }
+ * }
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Zsolt Szasz, zsolt at lorecraft dot com
+ * @author Rainer Hermanns
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Before {
+    int priority() default 10; 
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/BeforeResult.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/BeforeResult.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/BeforeResult.java
new file mode 100644
index 0000000..faeb400
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/BeforeResult.java
@@ -0,0 +1,80 @@
+/*
+ * 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * Marks a action method that needs to be executed before the result. Return value is ignored.
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Annotation usage:</u>
+ *
+ * <!-- START SNIPPET: usage -->
+ * The BeforeResult annotation can be applied at method level.
+ *
+ * <!-- END SNIPPET: usage -->
+ *
+ * <p/> <u>Annotation parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ * <table class='confluenceTable'>
+ * <tr>
+ * <th class='confluenceTh'> Parameter </th>
+ * <th class='confluenceTh'> Required </th>
+ * <th class='confluenceTh'> Default </th>
+ * <th class='confluenceTh'> Notes </th>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>priority</td>
+ * <td class='confluenceTd'>no</td>
+ * <td class='confluenceTd'>10</td>
+ * <td class='confluenceTd'>Priority order of method execution</td>
+ * </tr>
+ * </table>
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * public class SampleAction extends ActionSupport {
+ *
+ *  &#64;BeforeResult
+ *  public void isValid() throws ValidationException {
+ *    // validate model object, throw exception if failed
+ *  }
+ *
+ *  public String execute() {
+ *     // perform action
+ *     return SUCCESS;
+ *  }
+ * }
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Zsolt Szasz, zsolt at lorecraft dot com
+ * @author Rainer Hermanns
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface BeforeResult {
+    int priority() default 10; 
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/BlockByDefault.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/BlockByDefault.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/BlockByDefault.java
new file mode 100644
index 0000000..a87ac0b
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/BlockByDefault.java
@@ -0,0 +1,21 @@
+package com.opensymphony.xwork2.interceptor.annotations;
+
+import com.opensymphony.xwork2.Action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares that by default fields on the {@link Action} class
+ * are NOT permitted to be set from HttpRequest parameters.
+ * To allow access to a field it must be annotated with {@link Allowed}
+ *
+ * @author martin.gilday
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface BlockByDefault {
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Blocked.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Blocked.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Blocked.java
new file mode 100644
index 0000000..e9a2885
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/Blocked.java
@@ -0,0 +1,18 @@
+package com.opensymphony.xwork2.interceptor.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares that the given field should NOT be able to be mutated through
+ * a HttpRequest parameter.
+ *
+ * @author martin.gilday
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Blocked {
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/InputConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/InputConfig.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/InputConfig.java
new file mode 100644
index 0000000..5d70744
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/InputConfig.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2002-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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.opensymphony.xwork2.Action;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * Marks a action method that if it's not validated by ValidationInterceptor then execute input method or input result.
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Annotation usage:</u>
+ *
+ * <!-- START SNIPPET: usage -->
+ * The InputConfig annotation can be applied at method level.
+ *
+ * <!-- END SNIPPET: usage -->
+ *
+ * <p/> <u>Annotation parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ * <table class='confluenceTable'>
+ * <tr>
+ * <th class='confluenceTh'> Parameter </th>
+ * <th class='confluenceTh'> Required </th>
+ * <th class='confluenceTh'> Default </th>
+ * <th class='confluenceTh'> Notes </th>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>methodName</td>
+ * <td class='confluenceTd'>no</td>
+ * <td class='confluenceTd'></td>
+ * <td class='confluenceTd'>execute this method if specific</td>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>resultName</td>
+ * <td class='confluenceTd'>no</td>
+ * <td class='confluenceTd'></td>
+ * <td class='confluenceTd'>return this result if methodName not specific</td>
+ * </tr>
+ * </table>
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * public class SampleAction extends ActionSupport {
+ *
+ *  public void isValid() throws ValidationException {
+ *    // validate model object, throw exception if failed
+ *  }
+ *
+ *  &#64;InputConfig(methodName="input")
+ *  public String execute() {
+ *     // perform action
+ *     return SUCCESS;
+ *  }
+ *  public String input() {
+ *     // perform some data filling
+ *     return INPUT;
+ *  }
+ * }
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author zhouyanming, zhouyanming@gmail.com
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface InputConfig {
+    String methodName() default "";
+    String resultName() default Action.INPUT;
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/package.html
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/package.html b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/package.html
new file mode 100644
index 0000000..8ac50a8
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/annotations/package.html
@@ -0,0 +1 @@
+<body>Interceptor annotations.</body>

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/interceptor/package.html
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/package.html b/core/src/main/java/com/opensymphony/xwork2/interceptor/package.html
new file mode 100644
index 0000000..505d814
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/package.html
@@ -0,0 +1 @@
+<body>Interceptor classes.</body>

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/mock/MockActionInvocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/mock/MockActionInvocation.java b/core/src/main/java/com/opensymphony/xwork2/mock/MockActionInvocation.java
new file mode 100644
index 0000000..0749689
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/mock/MockActionInvocation.java
@@ -0,0 +1,133 @@
+/*
+ * 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.mock;
+
+import com.opensymphony.xwork2.*;
+import com.opensymphony.xwork2.interceptor.PreResultListener;
+import com.opensymphony.xwork2.util.ValueStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mock for an {@link ActionInvocation}.
+ *
+ * @author plightbo
+ * @author Rainer Hermanns
+ * @author tm_jee
+ * @version $Id$
+ */
+public class MockActionInvocation implements ActionInvocation {
+
+    private Object action;
+    private ActionContext invocationContext;
+    private ActionEventListener actionEventListener;
+    private ActionProxy proxy;
+    private Result result;
+    private String resultCode;
+    private ValueStack stack;
+
+    private List<PreResultListener> preResultListeners = new ArrayList<>();
+
+    public Object getAction() {
+        return action;
+    }
+
+    public void setAction(Object action) {
+        this.action = action;
+    }
+
+    public ActionContext getInvocationContext() {
+        return invocationContext;
+    }
+
+    public void setInvocationContext(ActionContext invocationContext) {
+        this.invocationContext = invocationContext;
+    }
+
+    public ActionProxy getProxy() {
+        return proxy;
+    }
+
+    public void setProxy(ActionProxy proxy) {
+        this.proxy = proxy;
+    }
+
+    public Result getResult() {
+        return result;
+    }
+
+    public void setResult(Result result) {
+        this.result = result;
+    }
+
+    public String getResultCode() {
+        return resultCode;
+    }
+
+    public void setResultCode(String resultCode) {
+        this.resultCode = resultCode;
+    }
+
+    public ValueStack getStack() {
+        return stack;
+    }
+
+    public void setStack(ValueStack stack) {
+        this.stack = stack;
+    }
+
+    public boolean isExecuted() {
+        return false;
+    }
+
+    public void addPreResultListener(PreResultListener listener) {
+    	preResultListeners.add(listener);
+    }
+
+    public String invoke() throws Exception {
+        for (Object preResultListener : preResultListeners) {
+            PreResultListener listener = (PreResultListener) preResultListener;
+            listener.beforeResult(this, resultCode);
+        }
+        return resultCode;
+    }
+
+    public String invokeActionOnly() throws Exception {
+        return resultCode;
+    }
+
+    public void setActionEventListener(ActionEventListener listener) {
+        this.actionEventListener = listener;
+    }
+    
+    public ActionEventListener getActionEventListener() {
+        return this.actionEventListener;
+    }
+
+    public void init(ActionProxy proxy) {
+    }
+
+    public ActionInvocation serialize() {
+        return this;
+    }
+
+    public ActionInvocation deserialize(ActionContext actionContext) {
+        return this;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/mock/MockActionProxy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/mock/MockActionProxy.java b/core/src/main/java/com/opensymphony/xwork2/mock/MockActionProxy.java
new file mode 100644
index 0000000..f373591
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/mock/MockActionProxy.java
@@ -0,0 +1,127 @@
+/*
+ * 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.mock;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Mock for an {@link ActionProxy}.
+ * 
+ * @author Patrick Lightbody (plightbo at gmail dot com)
+ */
+public class MockActionProxy implements ActionProxy {
+    
+    Object action;
+    String actionName;
+    ActionConfig config;
+    boolean executeResult;
+    ActionInvocation invocation;
+    String namespace;
+    String method;
+    boolean executedCalled;
+    String returnedResult;
+    Configuration configuration;
+    boolean methodSpecified;
+
+    public void prepare() throws Exception {}
+    
+    public String execute() throws Exception {
+        executedCalled = true;
+
+        return returnedResult;
+    }
+
+    public void setReturnedResult(String returnedResult) {
+        this.returnedResult = returnedResult;
+    }
+
+    public boolean isExecutedCalled() {
+        return executedCalled;
+    }
+
+    public Object getAction() {
+        return action;
+    }
+
+    public void setAction(Object action) {
+        this.action = action;
+    }
+
+    public String getActionName() {
+        return actionName;
+    }
+
+    public void setActionName(String actionName) {
+        this.actionName = actionName;
+    }
+
+    public ActionConfig getConfig() {
+        return config;
+    }
+
+    public void setConfig(ActionConfig config) {
+        this.config = config;
+    }
+
+    public boolean getExecuteResult() {
+        return executeResult;
+    }
+
+    public void setExecuteResult(boolean executeResult) {
+        this.executeResult = executeResult;
+    }
+
+    public ActionInvocation getInvocation() {
+        return invocation;
+    }
+
+    public void setInvocation(ActionInvocation invocation) {
+        this.invocation = invocation;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public String getMethod() {
+        return method;
+    }
+
+    public void setMethod(String method) {
+        this.method = method;
+        methodSpecified = StringUtils.isNotEmpty(method);
+    }
+
+    public boolean isMethodSpecified()
+    {
+        return methodSpecified;
+    }
+
+    public void setMethodSpecified(boolean methodSpecified)
+    {
+        this.methodSpecified = methodSpecified;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/mock/MockContainer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/mock/MockContainer.java b/core/src/main/java/com/opensymphony/xwork2/mock/MockContainer.java
new file mode 100644
index 0000000..bcad8a9
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/mock/MockContainer.java
@@ -0,0 +1,41 @@
+package com.opensymphony.xwork2.mock;
+
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.Scope;
+
+import java.util.Set;
+
+/**
+ * Mock implementation to be used in unittests
+ */
+public class MockContainer implements Container {
+
+    public void inject(Object o) {
+
+    }
+
+    public <T> T inject(Class<T> implementation) {
+        return null;
+    }
+
+    public <T> T getInstance(Class<T> type, String name) {
+        return null;
+    }
+
+    public <T> T getInstance(Class<T> type) {
+        return null;
+    }
+
+    public Set<String> getInstanceNames(Class<?> type) {
+        return null;
+    }
+
+    public void setScopeStrategy(Scope.Strategy scopeStrategy) {
+
+    }
+
+    public void removeScopeStrategy() {
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/mock/MockInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/mock/MockInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/mock/MockInterceptor.java
new file mode 100644
index 0000000..c244770
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/mock/MockInterceptor.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.mock;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.Interceptor;
+import junit.framework.Assert;
+
+
+/**
+ * Mock for an {@link com.opensymphony.xwork2.interceptor.Interceptor}.
+ *
+ * @author Jason Carreira
+ */
+public class MockInterceptor implements Interceptor {
+
+    private static final long serialVersionUID = 2692551676567227756L;
+    
+    public static final String DEFAULT_FOO_VALUE = "fooDefault";
+
+
+    private String expectedFoo = DEFAULT_FOO_VALUE;
+    private String foo = DEFAULT_FOO_VALUE;
+    private boolean executed = false;
+
+
+    public boolean isExecuted() {
+        return executed;
+    }
+
+    public void setExpectedFoo(String expectedFoo) {
+        this.expectedFoo = expectedFoo;
+    }
+
+    public String getExpectedFoo() {
+        return expectedFoo;
+    }
+
+    public void setFoo(String foo) {
+        this.foo = foo;
+    }
+
+    public String getFoo() {
+        return foo;
+    }
+
+    /**
+     * Called to let an interceptor clean up any resources it has allocated.
+     */
+    public void destroy() {
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof MockInterceptor)) {
+            return false;
+        }
+
+        final MockInterceptor testInterceptor = (MockInterceptor) o;
+
+        if (executed != testInterceptor.executed) {
+            return false;
+        }
+
+        if ((expectedFoo != null) ? (!expectedFoo.equals(testInterceptor.expectedFoo)) : (testInterceptor.expectedFoo != null))
+        {
+            return false;
+        }
+
+        if ((foo != null) ? (!foo.equals(testInterceptor.foo)) : (testInterceptor.foo != null)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        result = ((expectedFoo != null) ? expectedFoo.hashCode() : 0);
+        result = (29 * result) + ((foo != null) ? foo.hashCode() : 0);
+        result = (29 * result) + (executed ? 1 : 0);
+
+        return result;
+    }
+
+    /**
+     * Called after an Interceptor is created, but before any requests are processed using the intercept() methodName. This
+     * gives the Interceptor a chance to initialize any needed resources.
+     */
+    public 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 DefaultActionInvocation or to short-circuit the processing and just return a String return code.
+     */
+    public String intercept(ActionInvocation invocation) throws Exception {
+        executed = true;
+        Assert.assertNotSame(DEFAULT_FOO_VALUE, foo);
+        Assert.assertEquals(expectedFoo, foo);
+
+        return invocation.invoke();
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/mock/MockObjectTypeDeterminer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/mock/MockObjectTypeDeterminer.java b/core/src/main/java/com/opensymphony/xwork2/mock/MockObjectTypeDeterminer.java
new file mode 100644
index 0000000..605aaf2
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/mock/MockObjectTypeDeterminer.java
@@ -0,0 +1,125 @@
+/*
+ * 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.mock;
+
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import ognl.OgnlException;
+import ognl.OgnlRuntime;
+
+import java.util.Map;
+
+/**
+ * Mocks the function of an ObjectTypeDeterminer for testing purposes.
+ *
+ * @author Gabe
+ */
+public class MockObjectTypeDeterminer implements ObjectTypeDeterminer {
+
+    private Class keyClass;
+    private Class elementClass;
+    private String keyProperty;
+    private boolean shouldCreateIfNew;
+
+    public MockObjectTypeDeterminer() {}
+
+
+    /**
+     * @param keyClass
+     * @param elementClass
+     * @param keyProperty
+     * @param shouldCreateIfNew
+     */
+    public MockObjectTypeDeterminer(Class keyClass, Class elementClass,
+                                    String keyProperty, boolean shouldCreateIfNew) {
+        super();
+        this.keyClass = keyClass;
+        this.elementClass = elementClass;
+        this.keyProperty = keyProperty;
+        this.shouldCreateIfNew = shouldCreateIfNew;
+    }
+
+    public Class getKeyClass(Class parentClass, String property) {
+        return getKeyClass();
+    }
+
+    public Class getElementClass(Class parentClass, String property, Object key) {
+        return getElementClass();
+    }
+
+    public String getKeyProperty(Class parentClass, String property) {
+        return getKeyProperty();
+    }
+
+    public boolean shouldCreateIfNew(Class parentClass, String property,
+                                     Object target, String keyProperty, boolean isIndexAccessed) {
+        try {
+            System.out.println("ognl:"+OgnlRuntime.getPropertyAccessor(Map.class)+" this:"+this);
+        } catch (OgnlException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        return isShouldCreateIfNew();
+    }
+
+    /**
+     * @return Returns the elementClass.
+     */
+    public Class getElementClass() {
+        return elementClass;
+    }
+    /**
+     * @param elementClass The elementClass to set.
+     */
+    public void setElementClass(Class elementClass) {
+        this.elementClass = elementClass;
+    }
+    /**
+     * @return Returns the keyClass.
+     */
+    public Class getKeyClass() {
+        return keyClass;
+    }
+    /**
+     * @param keyClass The keyClass to set.
+     */
+    public void setKeyClass(Class keyClass) {
+        this.keyClass = keyClass;
+    }
+    /**
+     * @return Returns the keyProperty.
+     */
+    public String getKeyProperty() {
+        return keyProperty;
+    }
+    /**
+     * @param keyProperty The keyProperty to set.
+     */
+    public void setKeyProperty(String keyProperty) {
+        this.keyProperty = keyProperty;
+    }
+    /**
+     * @return Returns the shouldCreateIfNew.
+     */
+    public boolean isShouldCreateIfNew() {
+        return shouldCreateIfNew;
+    }
+    /**
+     * @param shouldCreateIfNew The shouldCreateIfNew to set.
+     */
+    public void setShouldCreateIfNew(boolean shouldCreateIfNew) {
+        this.shouldCreateIfNew = shouldCreateIfNew;
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/mock/MockResult.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/mock/MockResult.java b/core/src/main/java/com/opensymphony/xwork2/mock/MockResult.java
new file mode 100644
index 0000000..571469c
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/mock/MockResult.java
@@ -0,0 +1,53 @@
+/*
+ * 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.mock;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.Result;
+
+/**
+ * Mock for a {@link Result}.
+ *
+ * @author Mike
+ * @author Rainer Hermanns
+ */
+public class MockResult implements Result {
+
+    public static final String DEFAULT_PARAM = "foo";
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        return o instanceof MockResult;
+    }
+
+    public void execute(ActionInvocation invocation) throws Exception {
+        // no op
+    }
+
+    @Override
+    public int hashCode() {
+        return 10;
+    }
+
+    public void setFoo(String foo) {
+        // no op
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/mock/package.html
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/mock/package.html b/core/src/main/java/com/opensymphony/xwork2/mock/package.html
new file mode 100644
index 0000000..61bdf48
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/mock/package.html
@@ -0,0 +1 @@
+<body>XWork specific mock classes.</body>

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/ErrorMessageBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/ErrorMessageBuilder.java b/core/src/main/java/com/opensymphony/xwork2/ognl/ErrorMessageBuilder.java
new file mode 100644
index 0000000..e136241
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/ErrorMessageBuilder.java
@@ -0,0 +1,58 @@
+package com.opensymphony.xwork2.ognl;
+
+/**
+ * Helper class to build error messages.
+ */
+public class ErrorMessageBuilder {
+
+    private StringBuilder message = new StringBuilder();
+
+    public static ErrorMessageBuilder create() {
+        return new ErrorMessageBuilder();
+    }
+
+    private ErrorMessageBuilder() {
+    }
+
+    public ErrorMessageBuilder errorSettingExpressionWithValue(String expr, Object value) {
+        appenExpression(expr);
+        if (value instanceof Object[]) {
+            appendValueAsArray((Object[]) value, message);
+        } else {
+            appendValue(value);
+        }
+        return this;
+    }
+
+    private void appenExpression(String expr) {
+        message.append("Error setting expression '");
+        message.append(expr);
+        message.append("' with value ");
+    }
+
+    private void appendValue(Object value) {
+        message.append("'");
+        message.append(value);
+        message.append("'");
+    }
+
+    private void appendValueAsArray(Object[] valueArray, StringBuilder msg) {
+        msg.append("[");
+        for (int index = 0; index < valueArray.length; index++) {
+            appendValue(valueArray[index]);
+            if (hasMoreElements(valueArray, index)) {
+                msg.append(", ");
+            }
+        }
+        msg.append("]");
+    }
+
+    private boolean hasMoreElements(Object[] valueArray, int index) {
+        return index < (valueArray.length + 1);
+    }
+
+    public String build() {
+        return message.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/ObjectProxy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/ObjectProxy.java b/core/src/main/java/com/opensymphony/xwork2/ognl/ObjectProxy.java
new file mode 100644
index 0000000..b01fd67
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/ObjectProxy.java
@@ -0,0 +1,55 @@
+/*
+ * 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.ognl;
+
+/**
+ * An Object to use within OGNL to proxy other Objects
+ * usually Collections that you set in a different place
+ * on the ValueStack but want to retain the context information
+ * about where they previously were.
+ *
+ * @author Gabe
+ */
+public class ObjectProxy {
+    private Object value;
+    private Class lastClassAccessed;
+    private String lastPropertyAccessed;
+
+    public Class getLastClassAccessed() {
+        return lastClassAccessed;
+    }
+
+    public void setLastClassAccessed(Class lastClassAccessed) {
+        this.lastClassAccessed = lastClassAccessed;
+    }
+
+    public String getLastPropertyAccessed() {
+        return lastPropertyAccessed;
+    }
+
+    public void setLastPropertyAccessed(String lastPropertyAccessed) {
+        this.lastPropertyAccessed = lastPropertyAccessed;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlNullHandlerWrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlNullHandlerWrapper.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlNullHandlerWrapper.java
new file mode 100644
index 0000000..95a0e35
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlNullHandlerWrapper.java
@@ -0,0 +1,24 @@
+package com.opensymphony.xwork2.ognl;
+
+import com.opensymphony.xwork2.conversion.NullHandler;
+
+import java.util.Map;
+
+public class OgnlNullHandlerWrapper implements ognl.NullHandler {
+
+    private NullHandler wrapped;
+    
+    public OgnlNullHandlerWrapper(NullHandler target) {
+        this.wrapped = target;
+    }
+    
+    public Object nullMethodResult(Map context, Object target,
+            String methodName, Object[] args) {
+        return wrapped.nullMethodResult(context, target, methodName, args);
+    }
+
+    public Object nullPropertyValue(Map context, Object target, Object property) {
+        return wrapped.nullPropertyValue(context, target, property);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlReflectionContextFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlReflectionContextFactory.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlReflectionContextFactory.java
new file mode 100644
index 0000000..03a5537
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlReflectionContextFactory.java
@@ -0,0 +1,14 @@
+package com.opensymphony.xwork2.ognl;
+
+import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
+import ognl.Ognl;
+
+import java.util.Map;
+
+public class OgnlReflectionContextFactory implements ReflectionContextFactory {
+
+    public Map createDefaultContext(Object root) {
+        return Ognl.createDefaultContext(root);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlReflectionProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlReflectionProvider.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlReflectionProvider.java
new file mode 100644
index 0000000..34f3043
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlReflectionProvider.java
@@ -0,0 +1,125 @@
+package com.opensymphony.xwork2.ognl;
+
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.reflection.ReflectionException;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+import ognl.Ognl;
+import ognl.OgnlException;
+import ognl.OgnlRuntime;
+
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Map;
+
+public class OgnlReflectionProvider implements ReflectionProvider {
+    
+    private OgnlUtil ognlUtil;
+    
+    @Inject
+    public void setOgnlUtil(OgnlUtil ognlUtil) {
+        this.ognlUtil = ognlUtil;
+    }
+
+    public Field getField(Class inClass, String name) {
+        return OgnlRuntime.getField(inClass, name);
+    }
+
+    public Method getGetMethod(Class targetClass, String propertyName)
+            throws IntrospectionException, ReflectionException {
+        try {
+            return OgnlRuntime.getGetMethod(null, targetClass, propertyName);
+        } catch (OgnlException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public Method getSetMethod(Class targetClass, String propertyName)
+            throws IntrospectionException, ReflectionException {
+        try {
+            return OgnlRuntime.getSetMethod(null, targetClass, propertyName);
+        } catch (OgnlException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public void setProperties(Map<String, ?> props, Object o, Map<String, Object> context) {
+        ognlUtil.setProperties(props, o, context);
+    }
+
+    public void setProperties(Map<String, ?> props, Object o, Map<String, Object> context, boolean throwPropertyExceptions) throws ReflectionException{
+        ognlUtil.setProperties(props, o, context, throwPropertyExceptions);
+        
+    }
+
+    public void setProperties(Map<String, ?> properties, Object o) {
+        ognlUtil.setProperties(properties, o);
+    }
+
+    public PropertyDescriptor getPropertyDescriptor(Class targetClass,
+            String propertyName) throws IntrospectionException,
+            ReflectionException {
+        try {
+            return OgnlRuntime.getPropertyDescriptor(targetClass, propertyName);
+        } catch (OgnlException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public void copy(Object from, Object to, Map<String, Object> context,
+            Collection<String> exclusions, Collection<String> inclusions) {
+        ognlUtil.copy(from, to, context, exclusions, inclusions);
+    }
+
+    public Object getRealTarget(String property, Map<String, Object> context, Object root)
+            throws ReflectionException {
+        try {
+            return ognlUtil.getRealTarget(property, context, root);
+        } catch (OgnlException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public void setProperty(String name, Object value, Object o, Map<String, Object> context) {
+        ognlUtil.setProperty(name, value, o, context);
+    }
+
+    public void setProperty(String name, Object value, Object o, Map<String, Object> context, boolean throwPropertyExceptions) {
+        ognlUtil.setProperty(name, value, o, context, throwPropertyExceptions);
+    }
+
+    public Map getBeanMap(Object source) throws IntrospectionException,
+            ReflectionException {
+        try {
+            return ognlUtil.getBeanMap(source);
+        } catch (OgnlException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public Object getValue(String expression, Map<String, Object> context, Object root)
+            throws ReflectionException {
+        try {
+            return ognlUtil.getValue(expression, context, root);
+        } catch (OgnlException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public void setValue(String expression, Map<String, Object> context, Object root,
+            Object value) throws ReflectionException {
+        try {
+            Ognl.setValue(expression, context, root, value);
+        } catch (OgnlException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public PropertyDescriptor[] getPropertyDescriptors(Object source)
+            throws IntrospectionException {
+        return ognlUtil.getPropertyDescriptors(source);
+    }
+
+}


Mime
View raw message