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