1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.interceptor;
19
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.LinkedHashMap;
23 import java.util.Map;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import com.opensymphony.xwork2.ActionContext;
29 import com.opensymphony.xwork2.ActionInvocation;
30 import com.opensymphony.xwork2.ValidationAware;
31 import com.opensymphony.xwork2.interceptor.Interceptor;
32
33 /***
34 * <!-- START SNIPPET: description -->
35 *
36 * An interceptor to store {@link ValidationAware} action's messages / errors and field errors into
37 * Http Session, such that it will be retrieveable at a later stage. This allows the action's message /
38 * errors and field errors to be available longer that just the particular http request.
39 *
40 * <p/>
41 *
42 * In the 'STORE' mode, the interceptor will store the {@link ValidationAware} action's message / errors
43 * and field errors into Http session.
44 *
45 * <p/>
46 *
47 * In the 'RETRIEVE' mode, the interceptor will retrieve the stored action's message / errors and field
48 * errors and put them back into the {@link ValidationAware} action.
49 *
50 * <p/>
51 *
52 * The interceptor does nothing in the 'NONE' mode, which is the default.
53 *
54 * <p/>
55 *
56 * The operation mode could be switched using :- <p/>
57 * 1] Setting the iterceptor parameter eg.
58 * <pre>
59 * <action name="submitApplication" ...>
60 * <interceptor-ref name="store">
61 * <param name="operationMode">l;STORE</param>
62 * </interceptor-ref>
63 * <interceptor-ref name="defaultStack" />
64 * ....
65 * </action>
66 * </pre>
67 *
68 * 2] Through request parameter (allowRequestParameterSwitch must be 'true' which is the default)
69 * <pre>
70 * // the request will have the operation mode in 'STORE'
71 * http://localhost:8080/context/submitApplication.action?operationMode=STORE
72 * </pre>
73 *
74 * <!-- END SNIPPET: description -->
75 *
76 *
77 * <!-- START SNIPPET: parameters -->
78 *
79 * <ul>
80 * <li>allowRequestParameterSwitch - To enable request parameter that could switch the operation mode
81 * of this interceptor. </li>
82 * <li>requestParameterSwitch - The request parameter that will indicate what mode this
83 * interceptor is in. </li>
84 * <li>operationMode - The operation mode this interceptor should be in
85 * (either 'STORE', 'RETRIEVE' or 'NONE'). 'NONE' being the default.</li>
86 * </ul>
87 *
88 * <!-- END SNIPPET: parameters -->
89 *
90 * <p/>
91 *
92 * <!-- START SNIPPET: extending -->
93 *
94 * The following method could be overriden :-
95 * <ul>
96 * <li>getRequestOperationMode - get the operation mode of this interceptor based on the request parameters</li>
97 * <li>mergeCollection - merge two collections</li>
98 * <li>mergeMap - merge two map</li>
99 * </ul>
100 *
101 * <!-- END SNIPPET: extending -->
102 *
103 * <pre>
104 * <!-- START SNIPPET: example -->
105 *
106 * <action name="submitApplication" ....>
107 * <interceptor-ref name="store">
108 * <param name="operationMode">STORE</param>
109 * </interceptor-ref>
110 * <interceptor-ref name="defaultStack" />
111 * <result name="input" type="redirect">applicationFailed.action</result>
112 * <result type="dispatcher">applicationSuccess.jsp</result>
113 * </action>
114 *
115 * <action name="applicationFailed" ....>
116 * <interceptor-ref name="store">
117 * <param name="operationMode">RETRIEVE</param>
118 * </interceptor-ref>
119 * <result>applicationFailed.jsp</result>
120 * </action>
121 *
122 * <!-- END SNIPPET: example -->
123 * </pre>
124 *
125 * <!-- START SNIPPET: exampleDescription -->
126 *
127 * With the example above, 'submitApplication.action' will have the action messages / errors / field errors stored
128 * in the Http Session. Later when needed, (in this case, when 'applicationFailed.action' is fired, it
129 * will get the action messages / errors / field errors stored in the Http Session and put them back into
130 * the action.
131 *
132 * <!-- END SNIPPET: exampleDescription -->
133 *
134 * @version $Date: 2006-09-05 23:34:39 -0400 (Tue, 05 Sep 2006) $ $Id: MessageStoreInterceptor.java 440597 2006-09-06 03:34:39Z wsmoak $
135 */
136 public class MessageStoreInterceptor implements Interceptor {
137
138 private static final long serialVersionUID = 4491997514314242420L;
139
140 private static final Log _log = LogFactory.getLog(MessageStoreInterceptor.class);
141
142
143 public static final String STORE_MODE = "STORE";
144 public static final String RETRIEVE_MODE = "RETRIEVE";
145 public static final String NONE = "NONE";
146
147 private boolean allowRequestParameterSwitch = true;
148 private String requestParameterSwitch = "operationMode";
149 private String operationMode = NONE;
150
151 public static String fieldErrorsSessionKey = "__MessageStoreInterceptor_FieldErrors_SessionKey";
152 public static String actionErrorsSessionKey = "__MessageStoreInterceptor_ActionErrors_SessionKey";
153 public static String actionMessagesSessionKey = "__MessageStoreInterceptor_ActionMessages_SessionKey";
154
155
156
157 public void setAllowRequestParameterSwitch(boolean allowRequestParameterSwitch) {
158 this.allowRequestParameterSwitch = allowRequestParameterSwitch;
159 }
160 public boolean getAllowRequestParameterSwitch() {
161 return this.allowRequestParameterSwitch;
162 }
163
164
165 public void setRequestParameterSwitch(String requestParameterSwitch) {
166 this.requestParameterSwitch = requestParameterSwitch;
167 }
168 public String getRequestParameterSwitch() {
169 return this.requestParameterSwitch;
170 }
171
172
173
174 public void setOperationMode(String operationMode) {
175 this.operationMode = operationMode;
176 }
177 public String getOperationModel() {
178 return this.operationMode;
179 }
180
181
182 public void destroy() {
183 }
184
185 public void init() {
186 }
187
188 public String intercept(ActionInvocation invocation) throws Exception {
189 _log.debug("entering MessageStoreInterceptor ...");
190
191 before(invocation);
192 String result = invocation.invoke();
193 after(invocation, result);
194
195 _log.debug("exit executing MessageStoreInterceptor");
196 return result;
197 }
198
199 /***
200 * Handle the retrieving of field errors / action messages / field errors, which is
201 * done before action invocation, and the <code>operationMode</code> is 'RETRIEVE'.
202 *
203 * @param invocation
204 * @throws Exception
205 */
206 protected void before(ActionInvocation invocation) throws Exception {
207 String reqOperationMode = getRequestOperationMode(invocation);
208
209 if (RETRIEVE_MODE.equalsIgnoreCase(reqOperationMode) ||
210 RETRIEVE_MODE.equalsIgnoreCase(operationMode)) {
211
212 Object action = invocation.getAction();
213 if (action instanceof ValidationAware) {
214
215 Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
216 ValidationAware validationAwareAction = (ValidationAware) action;
217
218 _log.debug("retrieve error / message from session to populate into action ["+action+"]");
219
220 Collection actionErrors = (Collection) session.get(actionErrorsSessionKey);
221 Collection actionMessages = (Collection) session.get(actionMessagesSessionKey);
222 Map fieldErrors = (Map) session.get(fieldErrorsSessionKey);
223
224 if (actionErrors != null && actionErrors.size() > 0) {
225 Collection mergedActionErrors = mergeCollection(validationAwareAction.getActionErrors(), actionErrors);
226 validationAwareAction.setActionErrors(mergedActionErrors);
227 }
228
229 if (actionMessages != null && actionMessages.size() > 0) {
230 Collection mergedActionMessages = mergeCollection(validationAwareAction.getActionMessages(), actionMessages);
231 validationAwareAction.setActionMessages(mergedActionMessages);
232 }
233
234 if (fieldErrors != null && fieldErrors.size() > 0) {
235 Map mergedFieldErrors = mergeMap(validationAwareAction.getFieldErrors(), fieldErrors);
236 validationAwareAction.setFieldErrors(mergedFieldErrors);
237 }
238 session.remove(actionErrorsSessionKey);
239 session.remove(actionMessagesSessionKey);
240 session.remove(fieldErrorsSessionKey);
241 }
242 }
243 }
244
245 /***
246 * Handle the storing of field errors / action messages / field errors, which is
247 * done after action invocation, and the <code>operationMode</code> is in 'STORE'.
248 *
249 * @param invocation
250 * @param result
251 * @throws Exception
252 */
253 protected void after(ActionInvocation invocation, String result) throws Exception {
254
255 String reqOperationMode = getRequestOperationMode(invocation);
256 if (STORE_MODE.equalsIgnoreCase(reqOperationMode) ||
257 STORE_MODE.equalsIgnoreCase(operationMode)) {
258
259 Object action = invocation.getAction();
260 if (action instanceof ValidationAware) {
261
262 Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);
263
264 _log.debug("store action ["+action+"] error/messages into session ");
265
266 ValidationAware validationAwareAction = (ValidationAware) action;
267 session.put(actionErrorsSessionKey, validationAwareAction.getActionErrors());
268 session.put(actionMessagesSessionKey, validationAwareAction.getActionMessages());
269 session.put(fieldErrorsSessionKey, validationAwareAction.getFieldErrors());
270 }
271 else {
272 _log.debug("Action ["+action+"] is not ValidationAware, no message / error that are storeable");
273 }
274 }
275 }
276
277
278 /***
279 * Get the operationMode through request paramter, if <code>allowRequestParameterSwitch</code>
280 * is 'true', else it simply returns 'NONE', meaning its neither in the 'STORE_MODE' nor
281 * 'RETRIEVE_MODE'.
282 *
283 * @return String
284 */
285 protected String getRequestOperationMode(ActionInvocation invocation) {
286 String reqOperationMode = NONE;
287 if (allowRequestParameterSwitch) {
288 Map reqParams = (Map) invocation.getInvocationContext().get(ActionContext.PARAMETERS);
289 boolean containsParameter = reqParams.containsKey(requestParameterSwitch);
290 if (containsParameter) {
291 String[] reqParamsArr = (String[]) reqParams.get(requestParameterSwitch);
292 if (reqParamsArr != null && reqParamsArr.length > 0) {
293 reqOperationMode = reqParamsArr[0];
294 }
295 }
296 }
297 return reqOperationMode;
298 }
299
300 /***
301 * Merge <code>col1</code> and <code>col2</code> and return the composite
302 * <code>Collection</code>.
303 *
304 * @param col1
305 * @param col2
306 * @return Collection
307 */
308 protected Collection mergeCollection(Collection col1, Collection col2) {
309 Collection _col1 = (col1 == null ? new ArrayList() : col1);
310 Collection _col2 = (col2 == null ? new ArrayList() : col2);
311 _col1.addAll(_col2);
312 return _col1;
313 }
314
315 /***
316 * Merge <code>map1</code> and <code>map2</code> and return the composite
317 * <code>Map</code>
318 *
319 * @param map1
320 * @param map2
321 * @return Map
322 */
323 protected Map mergeMap(Map map1, Map map2) {
324 Map _map1 = (map1 == null ? new LinkedHashMap() : map1);
325 Map _map2 = (map2 == null ? new LinkedHashMap() : map2);
326 _map1.putAll(_map2);
327 return _map1;
328 }
329
330 }