View Javadoc

1   /*
2    * $Id: MessageStoreInterceptor.java 471756 2006-11-06 15:01:43Z husted $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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   *   &lt;action name="submitApplication" ...&gt;
63   *      &lt;interceptor-ref name="store"&gt;
64   *         &lt;param name="operationMode"&gtl;STORE&lt;/param&gt;
65   *      &lt;/interceptor-ref&gt;
66   *      &lt;interceptor-ref name="defaultStack" /&gt;
67   *      ....
68   *   &lt;/action&gt;
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  * &lt;action name="submitApplication" ....&gt;
110  *    &lt;interceptor-ref name="store"&gt;
111  *      &lt;param name="operationMode">STORE&lt;/param&gt;
112  *    &lt;/interceptor-ref&gt;
113  *    &lt;interceptor-ref name="defaultStack" /&gt;
114  *    &lt;result name="input" type="redirect">applicationFailed.action&lt;/result&gt;
115  *    &lt;result type="dispatcher"&gt;applicationSuccess.jsp&lt;/result&gt;
116  * &lt;/action&gt;
117  *
118  * &lt;action name="applicationFailed" ....&gt;
119  *    &lt;interceptor-ref name="store"&gt;
120  *       &lt;param name="operationMode"&gt;RETRIEVE&lt;/param&gt;
121  *    &lt;/interceptor-ref&gt;
122  *    &lt;result&gt;applicationFailed.jsp&lt;/result&gt;
123  * &lt;/action&gt;
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 16:01:43 +0100 (Mo, 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                 // retrieve error / message from session
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                 // store error / messages into session
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 }