View Javadoc

    1   /*
    2    * $Id: ActionContextBase.java 471754 2006-11-06 14:55:09Z 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.struts.chain.contexts;
    22
    23  import org.apache.commons.chain.Context;
    24  import org.apache.commons.chain.impl.ContextBase;
    25  import org.apache.commons.logging.Log;
    26  import org.apache.commons.logging.LogFactory;
    27  import org.apache.struts.action.Action;
    28  import org.apache.struts.action.ActionForm;
    29  import org.apache.struts.action.ActionMessages;
    30  import org.apache.struts.chain.Constants;
    31  import org.apache.struts.config.ActionConfig;
    32  import org.apache.struts.config.FormBeanConfig;
    33  import org.apache.struts.config.ForwardConfig;
    34  import org.apache.struts.config.ModuleConfig;
    35  import org.apache.struts.util.MessageResources;
    36  import org.apache.struts.util.TokenProcessor;
    37
    38  import java.util.Locale;
    39  import java.util.Map;
    40
    41  /**
    42   * <p> Provide an abstract but semi-complete implementation of ActionContext
    43   * to serve as the base for concrete implementations. </p> <p> The abstract
    44   * methods to implement are the accessors for the named states,
    45   * <code>getApplicationScope</code>, <code>getRequestScope</code>, and
    46   * <code>getSessionScope</code>. </p>
    47   */
    48  public abstract class ActionContextBase extends ContextWrapper
    49      implements ActionContext {
    50      /**
    51       * @see Constants.ACTION_KEY
    52       */
    53      public static final String ACTION_KEY = Constants.ACTION_KEY;
    54
    55      /**
    56       * @see
    57       */
    58      public static final String ACTION_CONFIG_KEY = Constants.ACTION_CONFIG_KEY;
    59
    60      /**
    61       * @see Constants.ACTION_FORM_KEY
    62       */
    63      public static final String ACTION_FORM_KEY = Constants.ACTION_FORM_KEY;
    64
    65      /**
    66       * @see Constants.FORWARD_CONFIG_KEY
    67       */
    68      public static final String FORWARD_CONFIG_KEY =
    69          Constants.FORWARD_CONFIG_KEY;
    70
    71      /**
    72       * @see Constants.MODULE_CONFIG_KEY
    73       */
    74      public static final String MODULE_CONFIG_KEY = Constants.MODULE_CONFIG_KEY;
    75
    76      /**
    77       * @see Constants.EXCEPTION_KEY
    78       */
    79      public static final String EXCEPTION_KEY = Constants.EXCEPTION_KEY;
    80
    81      /**
    82       * <p> Provide the default context attribute under which to store the
    83       * ActionMessage cache for errors. </p>
    84       */
    85      public static final String ERROR_ACTION_MESSAGES_KEY = "errors";
    86
    87      /**
    88       * <p> Provide the default context attribute under which to store the
    89       * ActionMessage cache. </p>
    90       */
    91      public static final String MESSAGE_ACTION_MESSAGES_KEY = "messages";
    92
    93      /**
    94       * @see Constants.MESSAGE_RESOURCES_KEY
    95       */
    96      public static final String MESSAGE_RESOURCES_KEY =
    97          Constants.MESSAGE_RESOURCES_KEY;
    98
    99      /**
    100      * @see Constants.INCLUDE_KEY
    101      */
    102     public static final String INCLUDE_KEY = Constants.INCLUDE_KEY;
    103
    104     /**
    105      * @see Constants.LOCALE_KEY
    106      */
    107     public static final String LOCALE_KEY = Constants.LOCALE_KEY;
    108
    109     /**
    110      * @see Constants.CANCEL_KEY
    111      */
    112     public static final String CANCEL_KEY = Constants.CANCEL_KEY;
    113
    114     /**
    115      * @see Constants.VALID_KEY
    116      */
    117     public static final String VALID_KEY = Constants.VALID_KEY;
    118
    119     /**
    120      * Provide the default context attribute under which to store the
    121      * transaction token key.
    122      */
    123     public static final String TRANSACTION_TOKEN_KEY = "TRANSACTION_TOKEN_KEY";
    124
    125     /**
    126      * Provide the default context attribute under which to store the token
    127      * key.
    128      */
    129     public static final String TOKEN_KEY = "TOKEN_KEY";
    130
    131     /**
    132      * Store the TokenProcessor instance for this Context.
    133      */
    134     protected TokenProcessor token = null;
    135
    136     /**
    137      * Store the Log instance for this Context.
    138      */
    139     private Log logger = null;
    140
    141     /**
    142      * Instantiate ActionContextBase, wrapping the given Context.
    143      *
    144      * @param context Context to wrap
    145      */
    146     public ActionContextBase(Context context) {
    147         super(context);
    148         token = TokenProcessor.getInstance();
    149         logger = LogFactory.getLog(this.getClass());
    150     }
    151
    152     /**
    153      * Instantiate ActionContextBase, wrapping a default ContextBase
    154      * instance.
    155      */
    156     public ActionContextBase() {
    157         this(new ContextBase());
    158     }
    159
    160     // -------------------------------
    161     // General Application Support
    162     // -------------------------------
    163     public void release() {
    164         this.token = null;
    165     }
    166
    167     public abstract Map getApplicationScope();
    168
    169     public abstract Map getRequestScope();
    170
    171     public abstract Map getSessionScope();
    172
    173     public Map getScope(String scopeName) {
    174         if (REQUEST_SCOPE.equals(scopeName)) {
    175             return this.getRequestScope();
    176         }
    177
    178         if (SESSION_SCOPE.equals(scopeName)) {
    179             return this.getSessionScope();
    180         }
    181
    182         if (APPLICATION_SCOPE.equals(scopeName)) {
    183             return this.getApplicationScope();
    184         }
    185
    186         throw new IllegalArgumentException("Invalid scope: " + scopeName);
    187     }
    188
    189     // -------------------------------
    190     // General Struts properties
    191     // -------------------------------
    192     public void setAction(Action action) {
    193         this.put(ACTION_KEY, action);
    194     }
    195
    196     public Action getAction() {
    197         return (Action) this.get(ACTION_KEY);
    198     }
    199
    200     public void setActionForm(ActionForm form) {
    201         this.put(ACTION_FORM_KEY, form);
    202     }
    203
    204     public ActionForm getActionForm() {
    205         return (ActionForm) this.get(ACTION_FORM_KEY);
    206     }
    207
    208     public void setActionConfig(ActionConfig config) {
    209         this.put(ACTION_CONFIG_KEY, config);
    210     }
    211
    212     public ActionConfig getActionConfig() {
    213         return (ActionConfig) this.get(ACTION_CONFIG_KEY);
    214     }
    215
    216     public void setForwardConfig(ForwardConfig forward) {
    217         this.put(FORWARD_CONFIG_KEY, forward);
    218     }
    219
    220     public ForwardConfig getForwardConfig() {
    221         return (ForwardConfig) this.get(FORWARD_CONFIG_KEY);
    222     }
    223
    224     public void setInclude(String include) {
    225         this.put(INCLUDE_KEY, include);
    226     }
    227
    228     public String getInclude() {
    229         return (String) this.get(INCLUDE_KEY);
    230     }
    231
    232     public Boolean getFormValid() {
    233         return (Boolean) this.get(VALID_KEY);
    234     }
    235
    236     public void setFormValid(Boolean valid) {
    237         this.put(VALID_KEY, valid);
    238     }
    239
    240     public ModuleConfig getModuleConfig() {
    241         return (ModuleConfig) this.get(MODULE_CONFIG_KEY);
    242     }
    243
    244     public void setModuleConfig(ModuleConfig config) {
    245         this.put(MODULE_CONFIG_KEY, config);
    246     }
    247
    248     public Exception getException() {
    249         return (Exception) this.get(EXCEPTION_KEY);
    250     }
    251
    252     public void setException(Exception e) {
    253         this.put(EXCEPTION_KEY, e);
    254     }
    255
    256     // -------------------------------
    257     // ActionMessage Processing
    258     // -------------------------------
    259     public void addMessages(ActionMessages messages) {
    260         this.addActionMessages(MESSAGE_ACTION_MESSAGES_KEY, messages);
    261     }
    262
    263     public void addErrors(ActionMessages errors) {
    264         this.addActionMessages(ERROR_ACTION_MESSAGES_KEY, errors);
    265     }
    266
    267     public ActionMessages getErrors() {
    268         return (ActionMessages) this.get(ERROR_ACTION_MESSAGES_KEY);
    269     }
    270
    271     public ActionMessages getMessages() {
    272         return (ActionMessages) this.get(MESSAGE_ACTION_MESSAGES_KEY);
    273     }
    274
    275     public void saveErrors(ActionMessages errors) {
    276         this.saveActionMessages(ERROR_ACTION_MESSAGES_KEY, errors);
    277     }
    278
    279     public void saveMessages(ActionMessages messages) {
    280         this.saveActionMessages(MESSAGE_ACTION_MESSAGES_KEY, messages);
    281     }
    282
    283     // ISSUE: do we want to add this to the public API?
    284
    285     /**
    286      * <p> Add the given messages to a cache stored in this Context, under
    287      * key. </p>
    288      *
    289      * @param key      The attribute name for the message cache
    290      * @param messages The ActionMessages to add
    291      */
    292     public void addActionMessages(String key, ActionMessages messages) {
    293         if (messages == null) {
    294             // bad programmer! *slap*
    295             return;
    296         }
    297
    298         // get any existing messages from the request, or make a new one
    299         ActionMessages requestMessages = (ActionMessages) this.get(key);
    300
    301         if (requestMessages == null) {
    302             requestMessages = new ActionMessages();
    303         }
    304
    305         // add incoming messages
    306         requestMessages.add(messages);
    307
    308         // if still empty, just wipe it out from the request
    309         this.remove(key);
    310
    311         // save the messages
    312         this.saveActionMessages(key, requestMessages);
    313     }
    314
    315     // ISSUE: do we want to add this to the public API?
    316
    317     /**
    318      * <p> Save the given ActionMessages into the request scope under the
    319      * given key, clearing the attribute if the messages are empty or null.
    320      * </p>
    321      *
    322      * @param key      The attribute name for the message cache
    323      * @param messages The ActionMessages to add
    324      */
    325     public void saveActionMessages(String key, ActionMessages messages) {
    326         this.saveActionMessages(REQUEST_SCOPE, key, messages);
    327     }
    328
    329     /**
    330      * <p>Save the given <code>messages</code> into the map identified by the
    331      * given <code>scopeId</code> under the given <code>key</code>.</p>
    332      *
    333      * @param scopeId
    334      * @param key
    335      * @param messages
    336      */
    337     public void saveActionMessages(String scopeId, String key,
    338         ActionMessages messages) {
    339         Map scope = getScope(scopeId);
    340
    341         if ((messages == null) || messages.isEmpty()) {
    342             scope.remove(key);
    343
    344             return;
    345         }
    346
    347         scope.put(key, messages);
    348     }
    349
    350     // ISSUE: Should we deprecate this method, since it is misleading?
    351     // Do we need it for backward compatibility?
    352
    353     /**
    354      * <p> Adapt a legacy form of SaveMessages to the ActionContext API by
    355      * storing the ActoinMessages under the default scope.
    356      *
    357      * @param scope    The scope for the internal cache
    358      * @param messages ActionMesssages to cache
    359      */
    360     public void saveMessages(String scope, ActionMessages messages) {
    361         this.saveMessages(messages);
    362     }
    363
    364     // -------------------------------
    365     // Token Processing
    366     // -------------------------------
    367     // ISSUE: Should there be a getToken method?
    368     // Is there a problem trying to map this method from Action
    369     // to ActionContext when we aren't necessarily sure how token
    370     // processing maps into a context with an ill-defined "session"?
    371     // There's no getToken() method, but maybe there should be. *
    372     public void saveToken() {
    373         String token = this.generateToken();
    374
    375         this.put(TRANSACTION_TOKEN_KEY, token);
    376     }
    377
    378     public String generateToken() {
    379         return token.generateToken(getTokenGeneratorId());
    380     }
    381
    382     // ISSUE: The original implementation was based on the HttpSession
    383     // identifier; what would be a way to do that without depending on the
    384     // Servlet API?
    385     // REPLY: uuid's
    386     // http://java.sun.com/products/jini/2.0/doc/specs/api/net/jini/id/Uuid.html
    387     protected String getTokenGeneratorId() {
    388         return "";
    389     }
    390
    391     public boolean isTokenValid() {
    392         return this.isTokenValid(false);
    393     }
    394
    395     public boolean isTokenValid(boolean reset) {
    396         // Retrieve the transaction token from this session, and
    397         // reset it if requested
    398         String saved = (String) this.get(TRANSACTION_TOKEN_KEY);
    399
    400         if (saved == null) {
    401             return false;
    402         }
    403
    404         if (reset) {
    405             this.resetToken();
    406         }
    407
    408         // Retrieve the transaction token included in this request
    409         String token = (String) this.get(TOKEN_KEY);
    410
    411         if (token == null) {
    412             return false;
    413         }
    414
    415         return saved.equals(token);
    416     }
    417
    418     public void resetToken() {
    419         this.remove(TRANSACTION_TOKEN_KEY);
    420     }
    421
    422     // -------------------------------
    423     // Cancel Processing
    424     // -------------------------------
    425     public Boolean getCancelled() {
    426         return (Boolean) this.get(CANCEL_KEY);
    427     }
    428
    429     public void setCancelled(Boolean cancelled) {
    430         this.put(CANCEL_KEY, cancelled);
    431     }
    432
    433     // -------------------------------
    434     // MessageResources Processing
    435     // -------------------------------
    436     public void setMessageResources(MessageResources messageResources) {
    437         this.put(MESSAGE_RESOURCES_KEY, messageResources);
    438     }
    439
    440     public MessageResources getMessageResources() {
    441         return (MessageResources) this.get(MESSAGE_RESOURCES_KEY);
    442     }
    443
    444     public MessageResources getMessageResources(String key) {
    445         return (MessageResources) this.get(key);
    446     }
    447
    448     // -------------------------------
    449     // Locale Processing
    450     // -------------------------------
    451     public void setLocale(Locale locale) {
    452         this.put(LOCALE_KEY, locale);
    453     }
    454
    455     public Locale getLocale() {
    456         return (Locale) this.get(LOCALE_KEY);
    457     }
    458
    459     // -------------------------------
    460     // Convenience Methods: these are not part of the formal ActionContext API,
    461     // but are likely to be commonly useful.
    462     // -------------------------------
    463
    464     /**
    465      * <p> Provide the currently configured commons-logging <code>Log</code>
    466      * instance. </p>
    467      *
    468      * @return Log instance for this context
    469      */
    470     public Log getLogger() {
    471         return this.logger;
    472     }
    473
    474     /**
    475      * <p> Set the commons-logging <code>Log</code> instance which should be
    476      * used to LOG messages. This is initialized at instantiation time but may
    477      * be overridden. Be advised not to set the value to null, as
    478      * <code>ActionContextBase</code> uses the logger for some of its own
    479      * operations. </p>
    480      */
    481     public void setLogger(Log logger) {
    482         this.logger = logger;
    483     }
    484
    485     /**
    486      * <p> Using this <code>ActionContext</code>'s default
    487      * <code>ModuleConfig</code>, return an existing <code>ActionForm</code>
    488      * in the specified scope, or create a new one and add it to the specified
    489      * scope. </p>
    490      *
    491      * @param formName  The name attribute of our ActionForm
    492      * @param scopeName The scope identier (request, session)
    493      * @return The ActionForm for this request
    494      * @throws IllegalAccessException If object cannot be created
    495      * @throws InstantiationException If object cannot be created
    496      * @see this.findOrCreateActionForm(String, String, ModuleConfig)
    497      */
    498     public ActionForm findOrCreateActionForm(String formName, String scopeName)
    499         throws IllegalAccessException, InstantiationException {
    500         return this.findOrCreateActionForm(formName, scopeName,
    501             this.getModuleConfig());
    502     }
    503
    504     /**
    505      * <p> In the context of the given <code>ModuleConfig</code> and this
    506      * <code>ActionContext</code>, look for an existing
    507      * <code>ActionForm</code> in the specified scope. If one is found, return
    508      * it; otherwise, create a new instance, add it to that scope, and then
    509      * return it. </p>
    510      *
    511      * @param formName  The name attribute of our ActionForm
    512      * @param scopeName The scope identier (request, session)
    513      * @return The ActionForm for this request
    514      * @throws IllegalAccessException   If object cannot be created
    515      * @throws InstantiationException   If object cannot be created
    516      * @throws IllegalArgumentException If form config is missing from module
    517      *                                  or scopeName is invalid
    518      */
    519     public ActionForm findOrCreateActionForm(String formName, String scopeName,
    520         ModuleConfig moduleConfig)
    521         throws IllegalAccessException, InstantiationException {
    522         Map scope = this.getScope(scopeName);
    523
    524         ActionForm instance;
    525         FormBeanConfig formBeanConfig =
    526             moduleConfig.findFormBeanConfig(formName);
    527
    528         if (formBeanConfig == null) {
    529             throw new IllegalArgumentException("No form config found under "
    530                 + formName + " in module " + moduleConfig.getPrefix());
    531         }
    532
    533         instance = (ActionForm) scope.get(formName);
    534
    535         // ISSUE: Can we recycle the existing instance (if any)?
    536         if (instance != null) {
    537             getLogger().trace("Found an instance in scope " + scopeName
    538                 + "; test for reusability");
    539
    540             if (formBeanConfig.canReuse(instance)) {
    541                 return instance;
    542             }
    543         }
    544
    545         ActionForm form = formBeanConfig.createActionForm(this);
    546
    547         // ISSUE: Should we check this call to put?
    548         scope.put(formName, form);
    549
    550         return form;
    551     }
    552 }