You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2015/06/17 23:09:39 UTC
[39/57] [partial] struts git commit: Merges xwork packages into struts
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 -->
+ *
+ * <-- Basic usage -->
+ * <interceptor name="scopedModelDriven" class="com.opensymphony.interceptor.ScopedModelDrivenInterceptor" />
+ *
+ * <-- Using all available parameters -->
+ * <interceptor name="gangsterForm" class="com.opensymphony.interceptor.ScopedModelDrivenInterceptor">
+ * <param name="scope">session</param>
+ * <param name="name">gangsterForm</param>
+ * <param name="className">com.opensymphony.example.GangsterForm</param>
+ * </interceptor>
+ *
+ * <!-- 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 <param> 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 -->
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="staticParams">
+ * <param name="parse">true</param>
+ * <param name="overwrite">false</param>
+ * </interceptor-ref>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ * <!-- 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 -->
+ * <!-- records only the action's execution time -->
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="completeStack"/>
+ * <interceptor-ref name="timer"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ * <!-- records action's execution time as well as other interceptors-->
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="timer"/>
+ * <interceptor-ref name="completeStack"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * This second example uses our own log category at debug level.
+ *
+ * <pre>
+ * <!-- START SNIPPET: example2 -->
+ * <!-- records only the action's execution time -->
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="completeStack"/>
+ * <interceptor-ref name="timer">
+ * <param name="logLevel">debug</param>
+ * <param name="logCategory">com.mycompany.myapp.actiontime</param>
+ * <interceptor-ref/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ *
+ * <!-- records action's execution time as well as other interceptors-->
+ * <action name="someAction" class="com.examples.SomeAction">
+ * <interceptor-ref name="timer"/>
+ * <interceptor-ref name="completeStack"/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ * <!-- 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 {
+ *
+ * @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> @{@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> @{@link BeforeResult} - will be invoked after the action method but before the result execution</li>
+ * <li> @{@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/>
+ * @Before
+ * public String baseBefore() {
+ * log = log + "baseBefore-";
+ * return null;
+ * }
+ * }
+ * <p/>
+ * public class AnnotatedAction extends BaseAnnotatedAction {
+ * @Before
+ * public String before() {
+ * log = log + "before";
+ * return null;
+ * }
+ * <p/>
+ * public String execute() {
+ * log = log + "-execute";
+ * return Action.SUCCESS;
+ * }
+ * <p/>
+ * @BeforeResult
+ * public void beforeResult() throws Exception {
+ * log = log +"-beforeResult";
+ * }
+ * <p/>
+ * @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 -->
+ * <interceptor-stack name="annotatedStack">
+ * <interceptor-ref name="staticParams"/>
+ * <interceptor-ref name="params"/>
+ * <interceptor-ref name="conversionError"/>
+ * <interceptor-ref name="annotationWorkflow"/>
+ * </interceptor-stack>
+ * <!-- 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 @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 {
+ *
+ * @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 {
+ *
+ * @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
+ * }
+ *
+ * @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);
+ }
+
+}