View Javadoc

    1   /*
    2    * $Id: ExceptionConfig.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;
    22
    23  import java.lang.reflect.InvocationTargetException;
    24
    25  /**
    26   * <p>A JavaBean representing the configuration information of an
    27   * <code>&lt;exception&gt;</code> element from a Struts configuration
    28   * file.</p>
    29   *
    30   * @version $Rev: 471754 $ $Date: 2005-08-06 18:03:30 -0400 (Sat, 06 Aug 2005)
    31   *          $
    32   * @since Struts 1.1
    33   */
    34  public class ExceptionConfig extends BaseConfig {
    35      // ------------------------------------------------------------- Properties
    36
    37      /**
    38       * The servlet context attribute under which the message resources bundle
    39       * to be used for this exception is located.  If not set, the default
    40       * message resources for the current module is assumed.
    41       */
    42      protected String bundle = null;
    43
    44      /**
    45       * The type of the ExceptionConfig that this object should inherit
    46       * properties from.
    47       */
    48      protected String inherit = null;
    49
    50      /**
    51       * Have the inheritance values for this class been applied?
    52       */
    53      protected boolean extensionProcessed = false;
    54
    55      /**
    56       * The fully qualified Java class name of the exception handler class
    57       * which should be instantiated to handle this exception.
    58       */
    59      protected String handler = "org.apache.struts.action.ExceptionHandler";
    60
    61      /**
    62       * The message resources key specifying the error message associated with
    63       * this exception.
    64       */
    65      protected String key = null;
    66
    67      /**
    68       * The module-relative path of the resource to forward to if this
    69       * exception occurs during an <code>Action</code>.
    70       */
    71      protected String path = null;
    72
    73      /**
    74       * The scope in which we should expose the ActionMessage for this
    75       * exception handler.
    76       */
    77      protected String scope = "request";
    78
    79      /**
    80       * The fully qualified Java class name of the exception that is to be
    81       * handled by this handler.
    82       */
    83      protected String type = null;
    84
    85      public String getBundle() {
    86          return (this.bundle);
    87      }
    88
    89      public void setBundle(String bundle) {
    90          if (configured) {
    91              throw new IllegalStateException("Configuration is frozen");
    92          }
    93
    94          this.bundle = bundle;
    95      }
    96
    97      public String getExtends() {
    98          return (this.inherit);
    99      }
    100
    101     public void setExtends(String inherit) {
    102         if (configured) {
    103             throw new IllegalStateException("Configuration is frozen");
    104         }
    105
    106         this.inherit = inherit;
    107     }
    108
    109     public boolean isExtensionProcessed() {
    110         return extensionProcessed;
    111     }
    112
    113     public String getHandler() {
    114         return (this.handler);
    115     }
    116
    117     public void setHandler(String handler) {
    118         if (configured) {
    119             throw new IllegalStateException("Configuration is frozen");
    120         }
    121
    122         this.handler = handler;
    123     }
    124
    125     public String getKey() {
    126         return (this.key);
    127     }
    128
    129     public void setKey(String key) {
    130         if (configured) {
    131             throw new IllegalStateException("Configuration is frozen");
    132         }
    133
    134         this.key = key;
    135     }
    136
    137     public String getPath() {
    138         return (this.path);
    139     }
    140
    141     public void setPath(String path) {
    142         if (configured) {
    143             throw new IllegalStateException("Configuration is frozen");
    144         }
    145
    146         this.path = path;
    147     }
    148
    149     public String getScope() {
    150         return (this.scope);
    151     }
    152
    153     public void setScope(String scope) {
    154         if (configured) {
    155             throw new IllegalStateException("Configuration is frozen");
    156         }
    157
    158         this.scope = scope;
    159     }
    160
    161     public String getType() {
    162         return (this.type);
    163     }
    164
    165     public void setType(String type) {
    166         if (configured) {
    167             throw new IllegalStateException("Configuration is frozen");
    168         }
    169
    170         this.type = type;
    171     }
    172
    173     // ------------------------------------------------------ Protected Methods
    174
    175     /**
    176      * <p>Traces the hierarchy of this object to check if any of the ancestors
    177      * are extending this instance.</p>
    178      *
    179      * @param moduleConfig The {@link ModuleConfig} that this config is from.
    180      * @param actionConfig The {@link ActionConfig} that this config is from,
    181      *                     if applicable.  This parameter must be null if this
    182      *                     is a global handler.
    183      * @return true if circular inheritance was detected.
    184      */
    185     protected boolean checkCircularInheritance(ModuleConfig moduleConfig,
    186         ActionConfig actionConfig) {
    187         String ancestorType = getExtends();
    188
    189         if (ancestorType == null) {
    190             return false;
    191         }
    192
    193         // Find our ancestor
    194         ExceptionConfig ancestor = null;
    195
    196         // First check the action config
    197         if (actionConfig != null) {
    198             ancestor = actionConfig.findExceptionConfig(ancestorType);
    199
    200             // If we found *this*, set ancestor to null to check for a global def
    201             if (ancestor == this) {
    202                 ancestor = null;
    203             }
    204         }
    205
    206         // Then check the global handlers
    207         if (ancestor == null) {
    208             ancestor = moduleConfig.findExceptionConfig(ancestorType);
    209
    210             if (ancestor != null) {
    211                 // If the ancestor is a global handler, set actionConfig
    212                 //  to null so further searches are only done among
    213                 //  global handlers.
    214                 actionConfig = null;
    215             }
    216         }
    217
    218         while (ancestor != null) {
    219             // Check if an ancestor is extending *this*
    220             if (ancestor == this) {
    221                 return true;
    222             }
    223
    224             // Get our ancestor's ancestor
    225             ancestorType = ancestor.getExtends();
    226
    227             // check against ancestors extending same typed ancestors
    228             if (ancestor.getType().equals(ancestorType)) {
    229                 // If the ancestor is extending a config for the same type,
    230                 //  make sure we look for its ancestor in the global handlers.
    231                 //  If we're already at that level, we return false.
    232                 if (actionConfig == null) {
    233                     return false;
    234                 } else {
    235                     // Set actionConfig = null to force us to look for global
    236                     //  forwards
    237                     actionConfig = null;
    238                 }
    239             }
    240
    241             ancestor = null;
    242
    243             // First check the action config
    244             if (actionConfig != null) {
    245                 ancestor = actionConfig.findExceptionConfig(ancestorType);
    246             }
    247
    248             // Then check the global handlers
    249             if (ancestor == null) {
    250                 ancestor = moduleConfig.findExceptionConfig(ancestorType);
    251
    252                 if (ancestor != null) {
    253                     // Limit further checks to moduleConfig.
    254                     actionConfig = null;
    255                 }
    256             }
    257         }
    258
    259         return false;
    260     }
    261
    262     // --------------------------------------------------------- Public Methods
    263
    264     /**
    265      * <p>Inherit values that have not been overridden from the provided
    266      * config object.  Subclasses overriding this method should verify that
    267      * the given parameter is of a class that contains a property it is trying
    268      * to inherit:</p>
    269      *
    270      * <pre>
    271      * if (config instanceof MyCustomConfig) {
    272      *     MyCustomConfig myConfig =
    273      *         (MyCustomConfig) config;
    274      *
    275      *     if (getMyCustomProp() == null) {
    276      *         setMyCustomProp(myConfig.getMyCustomProp());
    277      *     }
    278      * }
    279      * </pre>
    280      *
    281      * <p>If the given <code>config</code> is extending another object, those
    282      * extensions should be resolved before it's used as a parameter to this
    283      * method.</p>
    284      *
    285      * @param config The object that this instance will be inheriting its
    286      *               values from.
    287      * @see #processExtends(ModuleConfig, ActionConfig)
    288      */
    289     public void inheritFrom(ExceptionConfig config)
    290         throws ClassNotFoundException, IllegalAccessException,
    291             InstantiationException, InvocationTargetException {
    292         if (configured) {
    293             throw new IllegalStateException("Configuration is frozen");
    294         }
    295
    296         // Inherit values that have not been overridden
    297         if (getBundle() == null) {
    298             setBundle(config.getBundle());
    299         }
    300
    301         if (getHandler().equals("org.apache.struts.action.ExceptionHandler")) {
    302             setHandler(config.getHandler());
    303         }
    304
    305         if (getKey() == null) {
    306             setKey(config.getKey());
    307         }
    308
    309         if (getPath() == null) {
    310             setPath(config.getPath());
    311         }
    312
    313         if (getScope().equals("request")) {
    314             setScope(config.getScope());
    315         }
    316
    317         if (getType() == null) {
    318             setType(config.getType());
    319         }
    320
    321         inheritProperties(config);
    322     }
    323
    324     /**
    325      * <p>Inherit configuration information from the ExceptionConfig that this
    326      * instance is extending.  This method verifies that any exception config
    327      * object that it inherits from has also had its processExtends() method
    328      * called.</p>
    329      *
    330      * @param moduleConfig The {@link ModuleConfig} that this config is from.
    331      * @param actionConfig The {@link ActionConfig} that this config is from,
    332      *                     if applicable.  This must be null for global
    333      *                     forwards.
    334      * @see #inheritFrom(ExceptionConfig)
    335      */
    336     public void processExtends(ModuleConfig moduleConfig,
    337         ActionConfig actionConfig)
    338         throws ClassNotFoundException, IllegalAccessException,
    339             InstantiationException, InvocationTargetException {
    340         if (configured) {
    341             throw new IllegalStateException("Configuration is frozen");
    342         }
    343
    344         String ancestorType = getExtends();
    345
    346         if ((!extensionProcessed) && (ancestorType != null)) {
    347             ExceptionConfig baseConfig = null;
    348
    349             // We only check the action config if we're not a global handler
    350             boolean checkActionConfig =
    351                 (this != moduleConfig.findExceptionConfig(getType()));
    352
    353             // ... and the action config was provided
    354             checkActionConfig &= (actionConfig != null);
    355
    356             // ... and we're not extending a config with the same type value
    357             // (because if we are, that means we're an action-level handler
    358             //  extending a global handler).
    359             checkActionConfig &= !ancestorType.equals(getType());
    360
    361             // We first check in the action config's exception handlers
    362             if (checkActionConfig) {
    363                 baseConfig = actionConfig.findExceptionConfig(ancestorType);
    364             }
    365
    366             // Then check the global exception handlers
    367             if (baseConfig == null) {
    368                 baseConfig = moduleConfig.findExceptionConfig(ancestorType);
    369             }
    370
    371             if (baseConfig == null) {
    372                 throw new NullPointerException("Unable to find "
    373                     + "handler for '" + ancestorType + "' to extend.");
    374             }
    375
    376             // Check for circular inheritance and make sure the base config's
    377             //  own inheritance has been processed already
    378             if (checkCircularInheritance(moduleConfig, actionConfig)) {
    379                 throw new IllegalArgumentException(
    380                     "Circular inheritance detected for forward " + getType());
    381             }
    382
    383             if (!baseConfig.isExtensionProcessed()) {
    384                 baseConfig.processExtends(moduleConfig, actionConfig);
    385             }
    386
    387             // copy values from the base config
    388             inheritFrom(baseConfig);
    389         }
    390
    391         extensionProcessed = true;
    392     }
    393
    394     /**
    395      * Return a String representation of this object.
    396      */
    397     public String toString() {
    398         StringBuffer sb = new StringBuffer("ExceptionConfig[");
    399
    400         sb.append("type=");
    401         sb.append(this.type);
    402
    403         if (this.bundle != null) {
    404             sb.append(",bundle=");
    405             sb.append(this.bundle);
    406         }
    407
    408         if (this.inherit != null) {
    409             sb.append(",extends=");
    410             sb.append(this.inherit);
    411         }
    412
    413         sb.append(",handler=");
    414         sb.append(this.handler);
    415         sb.append(",key=");
    416         sb.append(this.key);
    417         sb.append(",path=");
    418         sb.append(this.path);
    419         sb.append(",scope=");
    420         sb.append(this.scope);
    421         sb.append("]");
    422
    423         return (sb.toString());
    424     }
    425 }