diff --git a/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java new file mode 100644 index 000000000..f8a752b24 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/interceptor/MessageStoreInterceptor.java @@ -0,0 +1,331 @@ +/* + * $Id: FileUploadInterceptor.java 421776 2006-07-14 00:47:56Z tmjee $ + * + * Copyright 2006 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 org.apache.struts2.interceptor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.ValidationAware; +import com.opensymphony.xwork2.interceptor.Interceptor; + +/** + * + * + * An interceptor to store {@link ValidationAware} action's messages / errors and field errors into + * Http Session, such that it will be retrieveable at a later stage. This allows the action's message / + * errors and field errors to be available longer that just the particular http request. + * + *
+ * + * In the 'STORE' mode, the interceptor will store the {@link ValidationAware} action's message / errors + * and field errors into Http session. + * + * + * + * In the 'RETRIEVE' mode, the interceptor will retrieve the stored action's message / errors and field + * errors and put them back into the {@link ValidationAware} action. + * + * + * + * The interceptor does nothing in the 'NONE' mode, which is the default. + * + * + * + * The operation mode could be switched using :- + * 1] Setting the iterceptor parameter eg. + *+ * <action name="submitApplication" ...> + * <interceptor-ref name="store"> + * <param name="operationMode">l;STORE</param> + * </interceptor-ref> + * <interceptor-ref name="defaultStack" /> + * .... + * </action> + *+ * + * 2] Through request parameter (allowRequestParameterSwitch must be 'true' which is the default) + *
+ * // the request will have the operation mode in 'STORE' + * http://localhost:8080/context/submitApplication.action?operationMode=STORE + *+ * + * + * + * + * + * + *
+ * + * + * <action name="submitApplication" ....> + * <interceptor-ref name="store"> + * <param name="operationMode">STORE</param> + * </interceptor-ref> + * <interceptor-ref name="defaultStack" /> + * <result name="input" type="redirect">applicationFailed.action</result> + * <result type="dispatcher">applicationSuccess.jsp</result> + * </action> + * + * <action name="applicationFailed" ....> + * <interceptor-ref name="store"> + * <param name="operationMode">RETRIEVE</param> + * </interceptor-ref> + * <result>applicationFailed.jsp</result> + * </action> + * + * + *+ * + * + * + * With the example above, 'submitApplication.action' will have the action messages / errors / field errors stored + * in the Http Session. Later when needed, (in this case, when 'applicationFailed.action' is fired, it + * will get the action messages / errors / field errors stored in the Http Session and put them back into + * the action. + * + * + * + * @version $Date$ $Id$ + */ +public class MessageStoreInterceptor implements Interceptor { + + private static final long serialVersionUID = 4491997514314242420L; + + private static final Log _log = LogFactory.getLog(MessageStoreInterceptor.class); + + + public static final String STORE_MODE = "STORE"; + public static final String RETRIEVE_MODE = "RETRIEVE"; + public static final String NONE = "NONE"; + + private boolean allowRequestParameterSwitch = true; + private String requestParameterSwitch = "operationMode"; + private String operationMode = NONE; + + public static String fieldErrorsSessionKey = "__MessageStoreInterceptor_FieldErrors_SessionKey"; + public static String actionErrorsSessionKey = "__MessageStoreInterceptor_ActionErrors_SessionKey"; + public static String actionMessagesSessionKey = "__MessageStoreInterceptor_ActionMessages_SessionKey"; + + + + public void setAllowRequestParameterSwitch(boolean allowRequestParameterSwitch) { + this.allowRequestParameterSwitch = allowRequestParameterSwitch; + } + public boolean getAllowRequestParameterSwitch() { + return this.allowRequestParameterSwitch; + } + + + public void setRequestParameterSwitch(String requestParameterSwitch) { + this.requestParameterSwitch = requestParameterSwitch; + } + public String getRequestParameterSwitch() { + return this.requestParameterSwitch; + } + + + + public void setOperationMode(String operationMode) { + this.operationMode = operationMode; + } + public String getOperationModel() { + return this.operationMode; + } + + + public void destroy() { + } + + public void init() { + } + + public String intercept(ActionInvocation invocation) throws Exception { + _log.debug("entering MessageStoreInterceptor ..."); + + before(invocation); + String result = invocation.invoke(); + after(invocation, result); + + _log.debug("exit executing MessageStoreInterceptor"); + return result; + } + + /** + * Handle the retrieving of field errors / action messages / field errors, which is + * done before action invocation, and the
operationMode is 'RETRIEVE'.
+ *
+ * @param invocation
+ * @throws Exception
+ */
+ protected void before(ActionInvocation invocation) throws Exception {
+ String reqOperationMode = getRequestOperationMode(invocation);
+
+ if (RETRIEVE_MODE.equalsIgnoreCase(reqOperationMode) ||
+ RETRIEVE_MODE.equalsIgnoreCase(operationMode)) {
+
+ Object action = invocation.getAction();
+ if (action instanceof ValidationAware) {
+ // retrieve error / message from session
+ Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
+ ValidationAware validationAwareAction = (ValidationAware) action;
+
+ _log.debug("retrieve error / message from session to populate into action ["+action+"]");
+
+ ValidationAware vaidationAwareAction = (ValidationAware) action;
+ Collection actionErrors = (Collection) session.get(actionErrorsSessionKey);
+ Collection actionMessages = (Collection) session.get(actionMessagesSessionKey);
+ Map fieldErrors = (Map) session.get(fieldErrorsSessionKey);
+
+ if (actionErrors != null && actionErrors.size() > 0) {
+ Collection mergedActionErrors = mergeCollection(validationAwareAction.getActionErrors(), actionErrors);
+ validationAwareAction.setActionErrors(mergedActionErrors);
+ }
+
+ if (actionMessages != null && actionMessages.size() > 0) {
+ Collection mergedActionMessages = mergeCollection(validationAwareAction.getActionMessages(), actionMessages);
+ validationAwareAction.setActionMessages(mergedActionMessages);
+ }
+
+ if (fieldErrors != null && fieldErrors.size() > 0) {
+ Map mergedFieldErrors = mergeMap(validationAwareAction.getFieldErrors(), fieldErrors);
+ validationAwareAction.setFieldErrors(mergedFieldErrors);
+ }
+ session.remove(actionErrorsSessionKey);
+ session.remove(actionMessagesSessionKey);
+ session.remove(fieldErrorsSessionKey);
+ }
+ }
+ }
+
+ /**
+ * Handle the storing of field errors / action messages / field errors, which is
+ * done after action invocation, and the operationMode is in 'STORE'.
+ *
+ * @param invocation
+ * @param result
+ * @throws Exception
+ */
+ protected void after(ActionInvocation invocation, String result) throws Exception {
+
+ String reqOperationMode = getRequestOperationMode(invocation);
+ if (STORE_MODE.equalsIgnoreCase(reqOperationMode) ||
+ STORE_MODE.equalsIgnoreCase(operationMode)) {
+
+ Object action = invocation.getAction();
+ if (action instanceof ValidationAware) {
+ // store error / messages into session
+ Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
+
+ _log.debug("store action ["+action+"] error/messages into session ");
+
+ ValidationAware validationAwareAction = (ValidationAware) action;
+ session.put(actionErrorsSessionKey, validationAwareAction.getActionErrors());
+ session.put(actionMessagesSessionKey, validationAwareAction.getActionMessages());
+ session.put(fieldErrorsSessionKey, validationAwareAction.getFieldErrors());
+ }
+ else {
+ _log.debug("Action ["+action+"] is not ValidationAware, no message / error that are storeable");
+ }
+ }
+ }
+
+
+ /**
+ * Get the operationMode through request paramter, if allowRequestParameterSwitch
+ * is 'true', else it simply returns 'NONE', meaning its neither in the 'STORE_MODE' nor
+ * 'RETRIEVE_MODE'.
+ *
+ * @return String
+ */
+ protected String getRequestOperationMode(ActionInvocation invocation) {
+ String reqOperationMode = NONE;
+ if (allowRequestParameterSwitch) {
+ Map reqParams = (Map) invocation.getInvocationContext().get(ActionContext.PARAMETERS);
+ boolean containsParameter = reqParams.containsKey(requestParameterSwitch);
+ if (containsParameter) {
+ String[] reqParamsArr = (String[]) reqParams.get(requestParameterSwitch);
+ if (reqParamsArr != null && reqParamsArr.length > 0) {
+ reqOperationMode = reqParamsArr[0];
+ }
+ }
+ }
+ return reqOperationMode;
+ }
+
+ /**
+ * Merge col1 and col2 and return the composite
+ * Collection.
+ *
+ * @param col1
+ * @param col2
+ * @return Collection
+ */
+ protected Collection mergeCollection(Collection col1, Collection col2) {
+ Collection _col1 = (col1 == null ? new ArrayList() : col1);
+ Collection _col2 = (col2 == null ? new ArrayList() : col2);
+ _col1.addAll(_col2);
+ return _col1;
+ }
+
+ /**
+ * Merge map1 and map2 and return the composite
+ * Map
+ *
+ * @param map1
+ * @param map2
+ * @return Map
+ */
+ protected Map mergeMap(Map map1, Map map2) {
+ Map _map1 = (map1 == null ? new LinkedHashMap() : map1);
+ Map _map2 = (map2 == null ? new LinkedHashMap() : map2);
+ _map1.putAll(_map2);
+ return _map1;
+ }
+
+}
diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml
index 57b9d8c94..c1d55647a 100644
--- a/core/src/main/resources/struts-default.xml
+++ b/core/src/main/resources/struts-default.xml
@@ -50,6 +50,7 @@