001: /*
002: * $Id: MessageStoreInterceptor.java 471756 2006-11-06 15:01:43Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts2.interceptor;
022:
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.LinkedHashMap;
026: import java.util.Map;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030:
031: import com.opensymphony.xwork2.ActionContext;
032: import com.opensymphony.xwork2.ActionInvocation;
033: import com.opensymphony.xwork2.ValidationAware;
034: import com.opensymphony.xwork2.interceptor.Interceptor;
035:
036: /**
037: * <!-- START SNIPPET: description -->
038: *
039: * An interceptor to store {@link ValidationAware} action's messages / errors and field errors into
040: * Http Session, such that it will be retrieveable at a later stage. This allows the action's message /
041: * errors and field errors to be available longer that just the particular http request.
042: *
043: * <p/>
044: *
045: * In the 'STORE' mode, the interceptor will store the {@link ValidationAware} action's message / errors
046: * and field errors into Http session.
047: *
048: * <p/>
049: *
050: * In the 'RETRIEVE' mode, the interceptor will retrieve the stored action's message / errors and field
051: * errors and put them back into the {@link ValidationAware} action.
052: *
053: * <p/>
054: *
055: * The interceptor does nothing in the 'NONE' mode, which is the default.
056: *
057: * <p/>
058: *
059: * The operation mode could be switched using :- <p/>
060: * 1] Setting the iterceptor parameter eg.
061: * <pre>
062: * <action name="submitApplication" ...>
063: * <interceptor-ref name="store">
064: * <param name="operationMode">l;STORE</param>
065: * </interceptor-ref>
066: * <interceptor-ref name="defaultStack" />
067: * ....
068: * </action>
069: * </pre>
070: *
071: * 2] Through request parameter (allowRequestParameterSwitch must be 'true' which is the default)
072: * <pre>
073: * // the request will have the operation mode in 'STORE'
074: * http://localhost:8080/context/submitApplication.action?operationMode=STORE
075: * </pre>
076: *
077: * <!-- END SNIPPET: description -->
078: *
079: *
080: * <!-- START SNIPPET: parameters -->
081: *
082: * <ul>
083: * <li>allowRequestParameterSwitch - To enable request parameter that could switch the operation mode
084: * of this interceptor. </li>
085: * <li>requestParameterSwitch - The request parameter that will indicate what mode this
086: * interceptor is in. </li>
087: * <li>operationMode - The operation mode this interceptor should be in
088: * (either 'STORE', 'RETRIEVE' or 'NONE'). 'NONE' being the default.</li>
089: * </ul>
090: *
091: * <!-- END SNIPPET: parameters -->
092: *
093: * <p/>
094: *
095: * <!-- START SNIPPET: extending -->
096: *
097: * The following method could be overriden :-
098: * <ul>
099: * <li>getRequestOperationMode - get the operation mode of this interceptor based on the request parameters</li>
100: * <li>mergeCollection - merge two collections</li>
101: * <li>mergeMap - merge two map</li>
102: * </ul>
103: *
104: * <!-- END SNIPPET: extending -->
105: *
106: * <pre>
107: * <!-- START SNIPPET: example -->
108: *
109: * <action name="submitApplication" ....>
110: * <interceptor-ref name="store">
111: * <param name="operationMode">STORE</param>
112: * </interceptor-ref>
113: * <interceptor-ref name="defaultStack" />
114: * <result name="input" type="redirect">applicationFailed.action</result>
115: * <result type="dispatcher">applicationSuccess.jsp</result>
116: * </action>
117: *
118: * <action name="applicationFailed" ....>
119: * <interceptor-ref name="store">
120: * <param name="operationMode">RETRIEVE</param>
121: * </interceptor-ref>
122: * <result>applicationFailed.jsp</result>
123: * </action>
124: *
125: * <!-- END SNIPPET: example -->
126: * </pre>
127: *
128: * <!-- START SNIPPET: exampleDescription -->
129: *
130: * With the example above, 'submitApplication.action' will have the action messages / errors / field errors stored
131: * in the Http Session. Later when needed, (in this case, when 'applicationFailed.action' is fired, it
132: * will get the action messages / errors / field errors stored in the Http Session and put them back into
133: * the action.
134: *
135: * <!-- END SNIPPET: exampleDescription -->
136: *
137: * @version $Date: 2006-11-06 10:01:43 -0500 (Mon, 06 Nov 2006) $ $Id: MessageStoreInterceptor.java 471756 2006-11-06 15:01:43Z husted $
138: */
139: public class MessageStoreInterceptor implements Interceptor {
140:
141: private static final long serialVersionUID = 4491997514314242420L;
142:
143: private static final Log _log = LogFactory
144: .getLog(MessageStoreInterceptor.class);
145:
146: public static final String STORE_MODE = "STORE";
147: public static final String RETRIEVE_MODE = "RETRIEVE";
148: public static final String NONE = "NONE";
149:
150: private boolean allowRequestParameterSwitch = true;
151: private String requestParameterSwitch = "operationMode";
152: private String operationMode = NONE;
153:
154: public static String fieldErrorsSessionKey = "__MessageStoreInterceptor_FieldErrors_SessionKey";
155: public static String actionErrorsSessionKey = "__MessageStoreInterceptor_ActionErrors_SessionKey";
156: public static String actionMessagesSessionKey = "__MessageStoreInterceptor_ActionMessages_SessionKey";
157:
158: public void setAllowRequestParameterSwitch(
159: boolean allowRequestParameterSwitch) {
160: this .allowRequestParameterSwitch = allowRequestParameterSwitch;
161: }
162:
163: public boolean getAllowRequestParameterSwitch() {
164: return this .allowRequestParameterSwitch;
165: }
166:
167: public void setRequestParameterSwitch(String requestParameterSwitch) {
168: this .requestParameterSwitch = requestParameterSwitch;
169: }
170:
171: public String getRequestParameterSwitch() {
172: return this .requestParameterSwitch;
173: }
174:
175: public void setOperationMode(String operationMode) {
176: this .operationMode = operationMode;
177: }
178:
179: public String getOperationModel() {
180: return this .operationMode;
181: }
182:
183: public void destroy() {
184: }
185:
186: public void init() {
187: }
188:
189: public String intercept(ActionInvocation invocation)
190: throws Exception {
191: _log.debug("entering MessageStoreInterceptor ...");
192:
193: before(invocation);
194: String result = invocation.invoke();
195: after(invocation, result);
196:
197: _log.debug("exit executing MessageStoreInterceptor");
198: return result;
199: }
200:
201: /**
202: * Handle the retrieving of field errors / action messages / field errors, which is
203: * done before action invocation, and the <code>operationMode</code> is 'RETRIEVE'.
204: *
205: * @param invocation
206: * @throws Exception
207: */
208: protected void before(ActionInvocation invocation) throws Exception {
209: String reqOperationMode = getRequestOperationMode(invocation);
210:
211: if (RETRIEVE_MODE.equalsIgnoreCase(reqOperationMode)
212: || RETRIEVE_MODE.equalsIgnoreCase(operationMode)) {
213:
214: Object action = invocation.getAction();
215: if (action instanceof ValidationAware) {
216: // retrieve error / message from session
217: Map session = (Map) invocation.getInvocationContext()
218: .get(ActionContext.SESSION);
219: ValidationAware validationAwareAction = (ValidationAware) action;
220:
221: _log
222: .debug("retrieve error / message from session to populate into action ["
223: + action + "]");
224:
225: Collection actionErrors = (Collection) session
226: .get(actionErrorsSessionKey);
227: Collection actionMessages = (Collection) session
228: .get(actionMessagesSessionKey);
229: Map fieldErrors = (Map) session
230: .get(fieldErrorsSessionKey);
231:
232: if (actionErrors != null && actionErrors.size() > 0) {
233: Collection mergedActionErrors = mergeCollection(
234: validationAwareAction.getActionErrors(),
235: actionErrors);
236: validationAwareAction
237: .setActionErrors(mergedActionErrors);
238: }
239:
240: if (actionMessages != null && actionMessages.size() > 0) {
241: Collection mergedActionMessages = mergeCollection(
242: validationAwareAction.getActionMessages(),
243: actionMessages);
244: validationAwareAction
245: .setActionMessages(mergedActionMessages);
246: }
247:
248: if (fieldErrors != null && fieldErrors.size() > 0) {
249: Map mergedFieldErrors = mergeMap(
250: validationAwareAction.getFieldErrors(),
251: fieldErrors);
252: validationAwareAction
253: .setFieldErrors(mergedFieldErrors);
254: }
255: session.remove(actionErrorsSessionKey);
256: session.remove(actionMessagesSessionKey);
257: session.remove(fieldErrorsSessionKey);
258: }
259: }
260: }
261:
262: /**
263: * Handle the storing of field errors / action messages / field errors, which is
264: * done after action invocation, and the <code>operationMode</code> is in 'STORE'.
265: *
266: * @param invocation
267: * @param result
268: * @throws Exception
269: */
270: protected void after(ActionInvocation invocation, String result)
271: throws Exception {
272:
273: String reqOperationMode = getRequestOperationMode(invocation);
274: if (STORE_MODE.equalsIgnoreCase(reqOperationMode)
275: || STORE_MODE.equalsIgnoreCase(operationMode)) {
276:
277: Object action = invocation.getAction();
278: if (action instanceof ValidationAware) {
279: // store error / messages into session
280: Map session = (Map) invocation.getInvocationContext()
281: .get(ActionContext.SESSION);
282:
283: _log.debug("store action [" + action
284: + "] error/messages into session ");
285:
286: ValidationAware validationAwareAction = (ValidationAware) action;
287: session.put(actionErrorsSessionKey,
288: validationAwareAction.getActionErrors());
289: session.put(actionMessagesSessionKey,
290: validationAwareAction.getActionMessages());
291: session.put(fieldErrorsSessionKey,
292: validationAwareAction.getFieldErrors());
293: } else {
294: _log
295: .debug("Action ["
296: + action
297: + "] is not ValidationAware, no message / error that are storeable");
298: }
299: }
300: }
301:
302: /**
303: * Get the operationMode through request paramter, if <code>allowRequestParameterSwitch</code>
304: * is 'true', else it simply returns 'NONE', meaning its neither in the 'STORE_MODE' nor
305: * 'RETRIEVE_MODE'.
306: *
307: * @return String
308: */
309: protected String getRequestOperationMode(ActionInvocation invocation) {
310: String reqOperationMode = NONE;
311: if (allowRequestParameterSwitch) {
312: Map reqParams = (Map) invocation.getInvocationContext()
313: .get(ActionContext.PARAMETERS);
314: boolean containsParameter = reqParams
315: .containsKey(requestParameterSwitch);
316: if (containsParameter) {
317: String[] reqParamsArr = (String[]) reqParams
318: .get(requestParameterSwitch);
319: if (reqParamsArr != null && reqParamsArr.length > 0) {
320: reqOperationMode = reqParamsArr[0];
321: }
322: }
323: }
324: return reqOperationMode;
325: }
326:
327: /**
328: * Merge <code>col1</code> and <code>col2</code> and return the composite
329: * <code>Collection</code>.
330: *
331: * @param col1
332: * @param col2
333: * @return Collection
334: */
335: protected Collection mergeCollection(Collection col1,
336: Collection col2) {
337: Collection _col1 = (col1 == null ? new ArrayList() : col1);
338: Collection _col2 = (col2 == null ? new ArrayList() : col2);
339: _col1.addAll(_col2);
340: return _col1;
341: }
342:
343: /**
344: * Merge <code>map1</code> and <code>map2</code> and return the composite
345: * <code>Map</code>
346: *
347: * @param map1
348: * @param map2
349: * @return Map
350: */
351: protected Map mergeMap(Map map1, Map map2) {
352: Map _map1 = (map1 == null ? new LinkedHashMap() : map1);
353: Map _map2 = (map2 == null ? new LinkedHashMap() : map2);
354: _map1.putAll(_map2);
355: return _map1;
356: }
357:
358: }
|