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