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 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                 // 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 }