View Javadoc

    1   /*
    2    * $Id: ModuleConfigImpl.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.config.impl;
    22
    23  import org.apache.commons.logging.Log;
    24  import org.apache.commons.logging.LogFactory;
    25  import org.apache.struts.config.ActionConfig;
    26  import org.apache.struts.config.ActionConfigMatcher;
    27  import org.apache.struts.config.BaseConfig;
    28  import org.apache.struts.config.ControllerConfig;
    29  import org.apache.struts.config.ExceptionConfig;
    30  import org.apache.struts.config.FormBeanConfig;
    31  import org.apache.struts.config.ForwardConfig;
    32  import org.apache.struts.config.MessageResourcesConfig;
    33  import org.apache.struts.config.ModuleConfig;
    34  import org.apache.struts.config.PlugInConfig;
    35
    36  import java.io.Serializable;
    37
    38  import java.util.ArrayList;
    39  import java.util.HashMap;
    40  import java.util.List;
    41
    42  /**
    43   * <p>The collection of static configuration information that describes a
    44   * Struts-based module.  Multiple modules are identified by a <em>prefix</em>
    45   * at the beginning of the context relative portion of the request URI.  If no
    46   * module prefix can be matched, the default configuration (with a prefix
    47   * equal to a zero-length string) is selected, which is elegantly backwards
    48   * compatible with the previous Struts behavior that only supported one
    49   * module.</p>
    50   *
    51   * @version $Rev: 471754 $ $Date: 2005-12-31 03:57:16 -0500 (Sat, 31 Dec 2005)
    52   *          $
    53   * @since Struts 1.1
    54   */
    55  public class ModuleConfigImpl extends BaseConfig implements Serializable,
    56      ModuleConfig {
    57      /**
    58       * <p>Commons Logging instance. </p>
    59       */
    60      protected static Log log = LogFactory.getLog(ModuleConfigImpl.class);
    61
    62      // ----------------------------------------------------- Instance Variables
    63      // Instance Variables at end to make comparing Interface and implementation easier.
    64
    65      /**
    66       * <p>The set of action configurations for this module, if any, keyed by
    67       * the <code>path</code> property.</p>
    68       */
    69      protected HashMap actionConfigs = null;
    70
    71      /**
    72       * <p>The set of action configuration for this module, if any, keyed by
    73       * the <code>actionId</code> property.</p>
    74       */
    75      protected HashMap actionConfigIds = null;
    76
    77      /**
    78       * <p>The set of action configurations for this module, if any, listed in
    79       * the order in which they are added.</p>
    80       */
    81      protected List actionConfigList = null;
    82
    83      /**
    84       * <p>The set of exception handling configurations for this module, if
    85       * any, keyed by the <code>type</code> property.</p>
    86       */
    87      protected HashMap exceptions = null;
    88
    89      /**
    90       * <p>The set of form bean configurations for this module, if any, keyed
    91       * by the <code>name</code> property.</p>
    92       */
    93      protected HashMap formBeans = null;
    94
    95      /**
    96       * <p>The set of global forward configurations for this module, if any,
    97       * keyed by the <code>name</code> property.</p>
    98       */
    99      protected HashMap forwards = null;
    100
    101     /**
    102      * <p>The set of message resources configurations for this module, if any,
    103      * keyed by the <code>key</code> property.</p>
    104      */
    105     protected HashMap messageResources = null;
    106
    107     /**
    108      * <p>The set of configured plug-in Actions for this module, if any, in
    109      * the order they were declared and configured.</p>
    110      */
    111     protected ArrayList plugIns = null;
    112
    113     /**
    114      * <p>The controller configuration object for this module.</p>
    115      */
    116     protected ControllerConfig controllerConfig = null;
    117
    118     /**
    119      * <p>The prefix of the context-relative portion of the request URI, used
    120      * to select this configuration versus others supported by the controller
    121      * servlet.  A configuration with a prefix of a zero-length String is the
    122      * default configuration for this web module.</p>
    123      */
    124     protected String prefix = null;
    125
    126     /**
    127      * <p>The default class name to be used when creating action form bean
    128      * instances.</p>
    129      */
    130     protected String actionFormBeanClass =
    131         "org.apache.struts.action.ActionFormBean";
    132
    133     /**
    134      * The default class name to be used when creating action mapping
    135      * instances.
    136      */
    137     protected String actionMappingClass =
    138         "org.apache.struts.action.ActionMapping";
    139
    140     /**
    141      * The default class name to be used when creating action forward
    142      * instances.
    143      */
    144     protected String actionForwardClass =
    145         "org.apache.struts.action.ActionForward";
    146
    147     /**
    148      * <p>Matches action config paths against compiled wildcard patterns</p>
    149      */
    150     protected ActionConfigMatcher matcher = null;
    151
    152     /**
    153      * <p>Constructor for ModuleConfigImpl.  Assumes default
    154      * configuration.</p>
    155      *
    156      * @since Struts 1.2.8
    157      */
    158     public ModuleConfigImpl() {
    159         this("");
    160     }
    161
    162     /**
    163      * <p>Construct an ModuleConfigImpl object according to the specified
    164      * parameter values.</p>
    165      *
    166      * @param prefix Context-relative URI prefix for this module
    167      */
    168     public ModuleConfigImpl(String prefix) {
    169         super();
    170         this.prefix = prefix;
    171         this.actionConfigs = new HashMap();
    172         this.actionConfigIds = new HashMap();
    173         this.actionConfigList = new ArrayList();
    174         this.actionFormBeanClass = "org.apache.struts.action.ActionFormBean";
    175         this.actionMappingClass = "org.apache.struts.action.ActionMapping";
    176         this.actionForwardClass = "org.apache.struts.action.ActionForward";
    177         this.configured = false;
    178         this.controllerConfig = null;
    179         this.exceptions = new HashMap();
    180         this.formBeans = new HashMap();
    181         this.forwards = new HashMap();
    182         this.messageResources = new HashMap();
    183         this.plugIns = new ArrayList();
    184     }
    185
    186     // --------------------------------------------------------- Public Methods
    187
    188     /**
    189      * </p> Has this module been completely configured yet.  Once this flag
    190      * has been set, any attempt to modify the configuration will return an
    191      * IllegalStateException.</p>
    192      */
    193     public boolean getConfigured() {
    194         return (this.configured);
    195     }
    196
    197     /**
    198      * <p>The controller configuration object for this module.</p>
    199      */
    200     public ControllerConfig getControllerConfig() {
    201         if (this.controllerConfig == null) {
    202             this.controllerConfig = new ControllerConfig();
    203         }
    204
    205         return (this.controllerConfig);
    206     }
    207
    208     /**
    209      * <p>The controller configuration object for this module.</p>
    210      *
    211      * @param cc The controller configuration object for this module.
    212      */
    213     public void setControllerConfig(ControllerConfig cc) {
    214         throwIfConfigured();
    215         this.controllerConfig = cc;
    216     }
    217
    218     /**
    219      * <p>The prefix of the context-relative portion of the request URI, used
    220      * to select this configuration versus others supported by the controller
    221      * servlet.  A configuration with a prefix of a zero-length String is the
    222      * default configuration for this web module.</p>
    223      */
    224     public String getPrefix() {
    225         return (this.prefix);
    226     }
    227
    228     /**
    229      * <p>The prefix of the context-relative portion of the request URI, used
    230      * to select this configuration versus others supported by the controller
    231      * servlet.  A configuration with a prefix of a zero-length String is the
    232      * default configuration for this web module.</p>
    233      */
    234     public void setPrefix(String prefix) {
    235         throwIfConfigured();
    236         this.prefix = prefix;
    237     }
    238
    239     /**
    240      * <p>The default class name to be used when creating action form bean
    241      * instances.</p>
    242      */
    243     public String getActionFormBeanClass() {
    244         return this.actionFormBeanClass;
    245     }
    246
    247     /**
    248      * <p>The default class name to be used when creating action form bean
    249      * instances.</p>
    250      *
    251      * @param actionFormBeanClass default class name to be used when creating
    252      *                            action form bean instances.
    253      */
    254     public void setActionFormBeanClass(String actionFormBeanClass) {
    255         this.actionFormBeanClass = actionFormBeanClass;
    256     }
    257
    258     /**
    259      * <p>The default class name to be used when creating action mapping
    260      * instances.</p>
    261      */
    262     public String getActionMappingClass() {
    263         return this.actionMappingClass;
    264     }
    265
    266     /**
    267      * <p> The default class name to be used when creating action mapping
    268      * instances. </p>
    269      *
    270      * @param actionMappingClass default class name to be used when creating
    271      *                           action mapping instances.
    272      */
    273     public void setActionMappingClass(String actionMappingClass) {
    274         this.actionMappingClass = actionMappingClass;
    275     }
    276
    277     /**
    278      * </p> Ad   d a new <code>ActionConfig</code> instance to the set
    279      * associated with this module. </p>
    280      *
    281      * @param config The new configuration instance to be added
    282      * @throws IllegalStateException if this module configuration has been
    283      *                               frozen
    284      */
    285     public void addActionConfig(ActionConfig config) {
    286         throwIfConfigured();
    287         config.setModuleConfig(this);
    288
    289         String path = config.getPath();
    290         if (actionConfigs.containsKey(path)) {
    291             log.warn("Overriding ActionConfig of path " + path);
    292         }
    293
    294         String actionId = config.getActionId();
    295         if ((actionId != null) && !actionId.equals("")) {
    296             if (actionConfigIds.containsKey(actionId)) {
    297                 if (log.isWarnEnabled()) {
    298                     ActionConfig otherConfig = (ActionConfig) actionConfigIds.get(actionId);
    299                     StringBuffer msg = new StringBuffer("Overriding actionId[");
    300                     msg.append(actionId);
    301                     msg.append("] for path[");
    302                     msg.append(otherConfig.getPath());
    303                     msg.append("] with path[");
    304                     msg.append(path);
    305                     msg.append("]");
    306                     log.warn(msg);
    307                 }
    308             }
    309             actionConfigIds.put(actionId, config);
    310         }
    311
    312         actionConfigs.put(path, config);
    313         actionConfigList.add(config);
    314     }
    315
    316     /**
    317      * <p> Add a new <code>ExceptionConfig</code> instance to the set
    318      * associated with this module. </p>
    319      *
    320      * @param config The new configuration instance to be added
    321      * @throws IllegalStateException if this module configuration has been
    322      *                               frozen
    323      */
    324     public void addExceptionConfig(ExceptionConfig config) {
    325         throwIfConfigured();
    326
    327         String key = config.getType();
    328
    329         if (exceptions.containsKey(key)) {
    330             log.warn("Overriding ExceptionConfig of type " + key);
    331         }
    332
    333         exceptions.put(key, config);
    334     }
    335
    336     /**
    337      * <p> Add a new <code>FormBeanConfig</code> instance to the set
    338      * associated with this module. </p>
    339      *
    340      * @param config The new configuration instance to be added
    341      * @throws IllegalStateException if this module configuration has been
    342      *                               frozen
    343      */
    344     public void addFormBeanConfig(FormBeanConfig config) {
    345         throwIfConfigured();
    346
    347         String key = config.getName();
    348
    349         if (formBeans.containsKey(key)) {
    350             log.warn("Overriding ActionForm of name " + key);
    351         }
    352
    353         formBeans.put(key, config);
    354     }
    355
    356     /**
    357      * <p> The default class name to be used when creating action forward
    358      * instances. </p>
    359      */
    360     public String getActionForwardClass() {
    361         return this.actionForwardClass;
    362     }
    363
    364     /**
    365      * <p> The default class name to be used when creating action forward
    366      * instances. </p>
    367      *
    368      * @param actionForwardClass default class name to be used when creating
    369      *                           action forward instances.
    370      */
    371     public void setActionForwardClass(String actionForwardClass) {
    372         this.actionForwardClass = actionForwardClass;
    373     }
    374
    375     /**
    376      * <p> Add a new <code>ForwardConfig</code> instance to the set of global
    377      * forwards associated with this module. </p>
    378      *
    379      * @param config The new configuration instance to be added
    380      * @throws IllegalStateException if this module configuration has been
    381      *                               frozen
    382      */
    383     public void addForwardConfig(ForwardConfig config) {
    384         throwIfConfigured();
    385
    386         String key = config.getName();
    387
    388         if (forwards.containsKey(key)) {
    389             log.warn("Overriding global ActionForward of name " + key);
    390         }
    391
    392         forwards.put(key, config);
    393     }
    394
    395     /**
    396      * <p> Add a new <code>MessageResourcesConfig</code> instance to the set
    397      * associated with this module. </p>
    398      *
    399      * @param config The new configuration instance to be added
    400      * @throws IllegalStateException if this module configuration has been
    401      *                               frozen
    402      */
    403     public void addMessageResourcesConfig(MessageResourcesConfig config) {
    404         throwIfConfigured();
    405
    406         String key = config.getKey();
    407
    408         if (messageResources.containsKey(key)) {
    409             log.warn("Overriding MessageResources bundle of key " + key);
    410         }
    411
    412         messageResources.put(key, config);
    413     }
    414
    415     /**
    416      * <p> Add a newly configured {@link org.apache.struts.config.PlugInConfig}
    417      * instance to the set of plug-in Actions for this module. </p>
    418      *
    419      * @param plugInConfig The new configuration instance to be added
    420      */
    421     public void addPlugInConfig(PlugInConfig plugInConfig) {
    422         throwIfConfigured();
    423         plugIns.add(plugInConfig);
    424     }
    425
    426     /**
    427      * <p> Return the action configuration for the specified path, first
    428      * looking a direct match, then if none found, a wildcard pattern match;
    429      * otherwise return <code>null</code>. </p>
    430      *
    431      * @param path Path of the action configuration to return
    432      */
    433     public ActionConfig findActionConfig(String path) {
    434         ActionConfig config = (ActionConfig) actionConfigs.get(path);
    435
    436         // If a direct match cannot be found, try to match action configs
    437         // containing wildcard patterns only if a matcher exists.
    438         if ((config == null) && (matcher != null)) {
    439             config = matcher.match(path);
    440         }
    441
    442         return config;
    443     }
    444
    445     /**
    446      * <p>Returns the action configuration for the specifed action
    447      * action identifier.</p>
    448      *
    449      * @param actionId the action identifier
    450      * @return the action config if found; otherwise <code>null</code>
    451      * @see ActionConfig#getActionId()
    452      * @since Struts 1.3.6
    453      */
    454     public ActionConfig findActionConfigId(String actionId) {
    455         if (actionId != null) {
    456             return (ActionConfig) this.actionConfigIds.get(actionId);
    457         }
    458         return null;
    459     }
    460
    461     /**
    462      * <p> Return the action configurations for this module.  If there are
    463      * none, a zero-length array is returned. </p>
    464      */
    465     public ActionConfig[] findActionConfigs() {
    466         ActionConfig[] results = new ActionConfig[actionConfigList.size()];
    467
    468         return ((ActionConfig[]) actionConfigList.toArray(results));
    469     }
    470
    471     /**
    472      * <p> Return the exception configuration for the specified type, if any;
    473      * otherwise return <code>null</code>. </p>
    474      *
    475      * @param type Exception class name to find a configuration for
    476      */
    477     public ExceptionConfig findExceptionConfig(String type) {
    478         return ((ExceptionConfig) exceptions.get(type));
    479     }
    480
    481     /**
    482      * <p>Find and return the <code>ExceptionConfig</code> instance defining
    483      * how <code>Exceptions</code> of the specified type should be handled.
    484      *
    485      * <p>In original Struts usage, this was only available in
    486      * <code>ActionConfig</code>, but there are cases when an exception could
    487      * be thrown before an <code>ActionConfig</code> has been identified,
    488      * where global exception handlers may still be pertinent.</p>
    489      *
    490      * <p>TODO: Look for a way to share this logic with
    491      * <code>ActionConfig</code>, although there are subtle differences, and
    492      * it certainly doesn't seem like it should be done with inheritance.</p>
    493      *
    494      * @param type Exception class for which to find a handler
    495      * @since Struts 1.3.0
    496      */
    497     public ExceptionConfig findException(Class type) {
    498         // Check through the entire superclass hierarchy as needed
    499         ExceptionConfig config = null;
    500
    501         while (true) {
    502             // Check for a locally defined handler
    503             String name = type.getName();
    504
    505             log.debug("findException: look locally for " + name);
    506             config = findExceptionConfig(name);
    507
    508             if (config != null) {
    509                 return (config);
    510             }
    511
    512             // Loop again for our superclass (if any)
    513             type = type.getSuperclass();
    514
    515             if (type == null) {
    516                 break;
    517             }
    518         }
    519
    520         return (null); // No handler has been configured
    521     }
    522
    523     /**
    524      * <p> Return the exception configurations for this module.  If there are
    525      * none, a zero-length array is returned. </p>
    526      */
    527     public ExceptionConfig[] findExceptionConfigs() {
    528         ExceptionConfig[] results = new ExceptionConfig[exceptions.size()];
    529
    530         return ((ExceptionConfig[]) exceptions.values().toArray(results));
    531     }
    532
    533     /**
    534      * <p> Return the form bean configuration for the specified key, if any;
    535      * otherwise return <code>null</code>. </p>
    536      *
    537      * @param name Name of the form bean configuration to return
    538      */
    539     public FormBeanConfig findFormBeanConfig(String name) {
    540         return ((FormBeanConfig) formBeans.get(name));
    541     }
    542
    543     /**
    544      * <p> Return the form bean configurations for this module.  If there are
    545      * none, a zero-length array is returned. </p>
    546      */
    547     public FormBeanConfig[] findFormBeanConfigs() {
    548         FormBeanConfig[] results = new FormBeanConfig[formBeans.size()];
    549
    550         return ((FormBeanConfig[]) formBeans.values().toArray(results));
    551     }
    552
    553     /**
    554      * <p> Return the forward configuration for the specified key, if any;
    555      * otherwise return <code>null</code>. </p>
    556      *
    557      * @param name Name of the forward configuration to return
    558      */
    559     public ForwardConfig findForwardConfig(String name) {
    560         return ((ForwardConfig) forwards.get(name));
    561     }
    562
    563     /**
    564      * <p> Return the form bean configurations for this module.  If there are
    565      * none, a zero-length array is returned. </p>
    566      */
    567     public ForwardConfig[] findForwardConfigs() {
    568         ForwardConfig[] results = new ForwardConfig[forwards.size()];
    569
    570         return ((ForwardConfig[]) forwards.values().toArray(results));
    571     }
    572
    573     /**
    574      * <p> Return the message resources configuration for the specified key,
    575      * if any; otherwise return <code>null</code>. </p>
    576      *
    577      * @param key Key of the data source configuration to return
    578      */
    579     public MessageResourcesConfig findMessageResourcesConfig(String key) {
    580         return ((MessageResourcesConfig) messageResources.get(key));
    581     }
    582
    583     /**
    584      * <p> Return the message resources configurations for this module. If
    585      * there are none, a zero-length array is returned. </p>
    586      */
    587     public MessageResourcesConfig[] findMessageResourcesConfigs() {
    588         MessageResourcesConfig[] results =
    589             new MessageResourcesConfig[messageResources.size()];
    590
    591         return ((MessageResourcesConfig[]) messageResources.values().toArray(results));
    592     }
    593
    594     /**
    595      * <p> Return the configured plug-in actions for this module.  If there
    596      * are none, a zero-length array is returned. </p>
    597      */
    598     public PlugInConfig[] findPlugInConfigs() {
    599         PlugInConfig[] results = new PlugInConfig[plugIns.size()];
    600
    601         return ((PlugInConfig[]) plugIns.toArray(results));
    602     }
    603
    604     /**
    605      * <p> Freeze the configuration of this module.  After this method
    606      * returns, any attempt to modify the configuration will return an
    607      * IllegalStateException. </p>
    608      */
    609     public void freeze() {
    610         super.freeze();
    611
    612         ActionConfig[] aconfigs = findActionConfigs();
    613
    614         for (int i = 0; i < aconfigs.length; i++) {
    615             aconfigs[i].freeze();
    616         }
    617
    618         matcher = new ActionConfigMatcher(aconfigs);
    619
    620         getControllerConfig().freeze();
    621
    622         ExceptionConfig[] econfigs = findExceptionConfigs();
    623
    624         for (int i = 0; i < econfigs.length; i++) {
    625             econfigs[i].freeze();
    626         }
    627
    628         FormBeanConfig[] fbconfigs = findFormBeanConfigs();
    629
    630         for (int i = 0; i < fbconfigs.length; i++) {
    631             fbconfigs[i].freeze();
    632         }
    633
    634         ForwardConfig[] fconfigs = findForwardConfigs();
    635
    636         for (int i = 0; i < fconfigs.length; i++) {
    637             fconfigs[i].freeze();
    638         }
    639
    640         MessageResourcesConfig[] mrconfigs = findMessageResourcesConfigs();
    641
    642         for (int i = 0; i < mrconfigs.length; i++) {
    643             mrconfigs[i].freeze();
    644         }
    645
    646         PlugInConfig[] piconfigs = findPlugInConfigs();
    647
    648         for (int i = 0; i < piconfigs.length; i++) {
    649             piconfigs[i].freeze();
    650         }
    651     }
    652
    653     /**
    654      * <p> Remove the specified action configuration instance. </p>
    655      *
    656      * @param config ActionConfig instance to be removed
    657      * @throws IllegalStateException if this module configuration has been
    658      *                               frozen
    659      */
    660     public void removeActionConfig(ActionConfig config) {
    661         throwIfConfigured();
    662         config.setModuleConfig(null);
    663         actionConfigs.remove(config.getPath());
    664         actionConfigList.remove(config);
    665     }
    666
    667     /**
    668      * <p> Remove the specified exception configuration instance. </p>
    669      *
    670      * @param config ActionConfig instance to be removed
    671      * @throws IllegalStateException if this module configuration has been
    672      *                               frozen
    673      */
    674     public void removeExceptionConfig(ExceptionConfig config) {
    675         throwIfConfigured();
    676         exceptions.remove(config.getType());
    677     }
    678
    679     /**
    680      * <p> Remove the specified form bean configuration instance. </p>
    681      *
    682      * @param config FormBeanConfig instance to be removed
    683      * @throws IllegalStateException if this module configuration has been
    684      *                               frozen
    685      */
    686     public void removeFormBeanConfig(FormBeanConfig config) {
    687         throwIfConfigured();
    688         formBeans.remove(config.getName());
    689     }
    690
    691     /**
    692      * <p> Remove the specified forward configuration instance. </p>
    693      *
    694      * @param config ForwardConfig instance to be removed
    695      * @throws IllegalStateException if this module configuration has been
    696      *                               frozen
    697      */
    698     public void removeForwardConfig(ForwardConfig config) {
    699         throwIfConfigured();
    700         forwards.remove(config.getName());
    701     }
    702
    703     /**
    704      * <p> Remove the specified message resources configuration instance.
    705      * </p>
    706      *
    707      * @param config MessageResourcesConfig instance to be removed
    708      * @throws IllegalStateException if this module configuration has been
    709      *                               frozen
    710      */
    711     public void removeMessageResourcesConfig(MessageResourcesConfig config) {
    712         throwIfConfigured();
    713         messageResources.remove(config.getKey());
    714     }
    715 }