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