View Javadoc

1   /*
2    * $Id: MessageStoreInterceptor.java 440597 2006-09-06 03:34:39Z wsmoak $
3    *
4    * Copyright 2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
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   *   &lt;action name="submitApplication" ...&gt;
60   *      &lt;interceptor-ref name="store"&gt;
61   *         &lt;param name="operationMode"&gtl;STORE&lt;/param&gt;
62   *      &lt;/interceptor-ref&gt;
63   *      &lt;interceptor-ref name="defaultStack" /&gt;
64   *      ....
65   *   &lt;/action&gt;
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  * &lt;action name="submitApplication" ....&gt;
107  *    &lt;interceptor-ref name="store"&gt;
108  *    	&lt;param name="operationMode">STORE&lt;/param&gt;
109  *    &lt;/interceptor-ref&gt;
110  *    &lt;interceptor-ref name="defaultStack" /&gt;
111  *    &lt;result name="input" type="redirect">applicationFailed.action&lt;/result&gt;
112  *    &lt;result type="dispatcher"&gt;applicationSuccess.jsp&lt;/result&gt;
113  * &lt;/action&gt;
114  * 
115  * &lt;action name="applicationFailed" ....&gt;
116  *    &lt;interceptor-ref name="store"&gt;
117  *       &lt;param name="operationMode"&gt;RETRIEVE&lt;/param&gt;
118  *    &lt;/interceptor-ref&gt;
119  *    &lt;result&gt;applicationFailed.jsp&lt;/result&gt;
120  * &lt;/action&gt;
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 				// retrieve error / message from session
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 				// store error / messages into session
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 }