View Javadoc

    1   /*
    2    * $Id: ForwardConfig.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 a
    27   * <code>&lt;forward&gt;</code> element from a Struts configuration file.</p>
    28   *
    29   * @version $Rev: 471754 $ $Date: 2005-08-14 17:24:39 -0400 (Sun, 14 Aug 2005)
    30   *          $
    31   * @since Struts 1.1
    32   */
    33  public class ForwardConfig extends BaseConfig {
    34      // ------------------------------------------------------------- Properties
    35
    36      /**
    37       * The name of the ForwardConfig that this object should inherit
    38       * properties from.
    39       */
    40      protected String inherit = null;
    41
    42      /**
    43       * Have the inheritance values for this class been applied?
    44       */
    45      protected boolean extensionProcessed = false;
    46
    47      /**
    48       * The unique identifier of this forward, which is used to reference it in
    49       * <code>Action</code> classes.
    50       */
    51      protected String name = null;
    52
    53      /**
    54       * <p>The URL to which this <code>ForwardConfig</code> entry points, which
    55       * must start with a slash ("/") character.  It is interpreted according
    56       * to the following rules:</p>
    57       *
    58       * <ul>
    59       *
    60       * <li>If <code>contextRelative</code> property is <code>true</code>, the
    61       * path is considered to be context-relative within the current web
    62       * application (even if we are in a named module).  It will be prefixed by
    63       * the context path to create a server-relative URL.</li>
    64       *
    65       * <li>If the <code>contextRelative</code> property is false, the path is
    66       * considered to be the module-relative portion of the URL. It will be
    67       * used as the replacement for the <code>$P</code> marker in the
    68       * <code>forwardPattern</code> property defined on the {@link
    69       * ControllerConfig} element for our current module. For the default
    70       * <code>forwardPattern</code> value of <code>$C$M$P</code>, the resulting
    71       * server-relative URL will be the concatenation of the context path, the
    72       * module prefix, and the <code>path</code> from this
    73       * <code>ForwardConfig</code>.
    74       *
    75       * </li>
    76       *
    77       * </ul>
    78       */
    79      protected String path = null;
    80
    81      /**
    82       * <p>The prefix of the module to which this <code>ForwardConfig</code>
    83       * entry points, which must start with a slash ("/") character.  </p>
    84       * <p>Usage note: If a forward config is used in a hyperlink, and a module
    85       * is specified, the path must lead to another action and not directly to
    86       * a page. This is in keeping with rule that in a modular application all
    87       * links must be to an action rather than a page. </p>
    88       */
    89      protected String module = null;
    90
    91      /**
    92       * Should a redirect be used to transfer control to the specified path?
    93       */
    94      protected boolean redirect = false;
    95
    96      /**
    97       * <p>The name of a <code>commons-chain</code> command which should be
    98       * looked up and executed before Struts dispatches control to the view
    99       * represented by this config.</p>
    100      */
    101     protected String command = null;
    102
    103     /**
    104      * <p>The name of a <code>commons-chain</code> catalog in which
    105      * <code>command</code> should be looked up.  If this value is undefined,
    106      * then the command will be looked up in the "default" catalog.  This
    107      * value has no meaning except in the context of the <code>command</code>
    108      * property.</p>
    109      */
    110     protected String catalog = null;
    111
    112     // ----------------------------------------------------------- Constructors
    113
    114     /**
    115      * Construct a new instance with default values.
    116      */
    117     public ForwardConfig() {
    118         super();
    119     }
    120
    121     /**
    122      * Construct a new instance with the specified values.
    123      *
    124      * @param name     Name of this forward
    125      * @param path     Path to which control should be forwarded or
    126      *                 redirected
    127      * @param redirect Should we do a redirect?
    128      */
    129     public ForwardConfig(String name, String path, boolean redirect) {
    130         super();
    131         setName(name);
    132         setPath(path);
    133         setRedirect(redirect);
    134     }
    135
    136     /**
    137      * <p>Construct a new instance with the specified values.</p>
    138      *
    139      * @param name     Name of this forward
    140      * @param path     Path to which control should be forwarded or
    141      *                 redirected
    142      * @param redirect Should we do a redirect?
    143      * @param module   Module prefix, if any
    144      */
    145     public ForwardConfig(String name, String path, boolean redirect,
    146         String module) {
    147         super();
    148         setName(name);
    149         setPath(path);
    150         setRedirect(redirect);
    151         setModule(module);
    152     }
    153
    154     /**
    155      * <p>Construct a new instance based on the values of another
    156      * ForwardConfig.</p>
    157      *
    158      * @param copyMe A ForwardConfig instance to copy
    159      * @since Struts 1.3.6
    160      */
    161     public ForwardConfig(ForwardConfig copyMe) {
    162         this(copyMe.getName(), copyMe.getPath(), copyMe.getRedirect(),
    163             copyMe.getModule());
    164     }
    165
    166     public String getExtends() {
    167         return (this.inherit);
    168     }
    169
    170     public void setExtends(String inherit) {
    171         if (configured) {
    172             throw new IllegalStateException("Configuration is frozen");
    173         }
    174
    175         this.inherit = inherit;
    176     }
    177
    178     public boolean isExtensionProcessed() {
    179         return extensionProcessed;
    180     }
    181
    182     public String getName() {
    183         return (this.name);
    184     }
    185
    186     public void setName(String name) {
    187         if (configured) {
    188             throw new IllegalStateException("Configuration is frozen");
    189         }
    190
    191         this.name = name;
    192     }
    193
    194     public String getPath() {
    195         return (this.path);
    196     }
    197
    198     public void setPath(String path) {
    199         if (configured) {
    200             throw new IllegalStateException("Configuration is frozen");
    201         }
    202
    203         this.path = path;
    204     }
    205
    206     public String getModule() {
    207         return (this.module);
    208     }
    209
    210     public void setModule(String module) {
    211         if (configured) {
    212             throw new IllegalStateException("Configuration is frozen");
    213         }
    214
    215         this.module = module;
    216     }
    217
    218     public boolean getRedirect() {
    219         return (this.redirect);
    220     }
    221
    222     public void setRedirect(boolean redirect) {
    223         if (configured) {
    224             throw new IllegalStateException("Configuration is frozen");
    225         }
    226
    227         this.redirect = redirect;
    228     }
    229
    230     public String getCommand() {
    231         return (this.command);
    232     }
    233
    234     public void setCommand(String command) {
    235         if (configured) {
    236             throw new IllegalStateException("Configuration is frozen");
    237         }
    238
    239         this.command = command;
    240     }
    241
    242     public String getCatalog() {
    243         return (this.catalog);
    244     }
    245
    246     public void setCatalog(String catalog) {
    247         if (configured) {
    248             throw new IllegalStateException("Configuration is frozen");
    249         }
    250
    251         this.catalog = catalog;
    252     }
    253
    254     // ------------------------------------------------------ Protected Methods
    255
    256     /**
    257      * <p>Traces the hierarchy of this object to check if any of the ancestors
    258      * are extending this instance.</p>
    259      *
    260      * @param moduleConfig The {@link ModuleConfig} that this config is from.
    261      * @param actionConfig The {@link ActionConfig} that this config is from,
    262      *                     if applicable.  This parameter must be null if this
    263      *                     forward config is a global forward.
    264      * @return true if circular inheritance was detected.
    265      */
    266     protected boolean checkCircularInheritance(ModuleConfig moduleConfig,
    267         ActionConfig actionConfig) {
    268         String ancestorName = getExtends();
    269
    270         if (ancestorName == null) {
    271             return false;
    272         }
    273
    274         // Find our ancestor
    275         ForwardConfig ancestor = null;
    276
    277         // First check the action config
    278         if (actionConfig != null) {
    279             ancestor = actionConfig.findForwardConfig(ancestorName);
    280
    281             // If we found *this*, set ancestor to null to check for a global def
    282             if (ancestor == this) {
    283                 ancestor = null;
    284             }
    285         }
    286
    287         // Then check the global forwards
    288         if (ancestor == null) {
    289             ancestor = moduleConfig.findForwardConfig(ancestorName);
    290
    291             if (ancestor != null) {
    292                 // If the ancestor is a global forward, set actionConfig
    293                 //  to null so further searches are only done among
    294                 //  global forwards.
    295                 actionConfig = null;
    296             }
    297         }
    298
    299         while (ancestor != null) {
    300             // Check if an ancestor is extending *this*
    301             if (ancestor == this) {
    302                 return true;
    303             }
    304
    305             // Get our ancestor's ancestor
    306             ancestorName = ancestor.getExtends();
    307
    308             // check against ancestors extending same named ancestors
    309             if (ancestor.getName().equals(ancestorName)) {
    310                 // If the ancestor is extending a config with the same name
    311                 //  make sure we look for its ancestor in the global forwards.
    312                 //  If we're already at that level, we return false.
    313                 if (actionConfig == null) {
    314                     return false;
    315                 } else {
    316                     // Set actionConfig = null to force us to look for global
    317                     //  forwards
    318                     actionConfig = null;
    319                 }
    320             }
    321
    322             ancestor = null;
    323
    324             // First check the action config
    325             if (actionConfig != null) {
    326                 ancestor = actionConfig.findForwardConfig(ancestorName);
    327             }
    328
    329             // Then check the global forwards
    330             if (ancestor == null) {
    331                 ancestor = moduleConfig.findForwardConfig(ancestorName);
    332
    333                 if (ancestor != null) {
    334                     // Limit further checks to moduleConfig.
    335                     actionConfig = null;
    336                 }
    337             }
    338         }
    339
    340         return false;
    341     }
    342
    343     // --------------------------------------------------------- Public Methods
    344
    345     /**
    346      * <p>Inherit values that have not been overridden from the provided
    347      * config object.  Subclasses overriding this method should verify that
    348      * the given parameter is of a class that contains a property it is trying
    349      * to inherit:</p>
    350      *
    351      * <pre>
    352      * if (config instanceof MyCustomConfig) {
    353      *     MyCustomConfig myConfig =
    354      *         (MyCustomConfig) config;
    355      *
    356      *     if (getMyCustomProp() == null) {
    357      *         setMyCustomProp(myConfig.getMyCustomProp());
    358      *     }
    359      * }
    360      * </pre>
    361      *
    362      * <p>If the given <code>config</code> is extending another object, those
    363      * extensions should be resolved before it's used as a parameter to this
    364      * method.</p>
    365      *
    366      * @param config The object that this instance will be inheriting its
    367      *               values from.
    368      * @see #processExtends(ModuleConfig, ActionConfig)
    369      */
    370     public void inheritFrom(ForwardConfig config)
    371         throws ClassNotFoundException, IllegalAccessException,
    372             InstantiationException, InvocationTargetException {
    373         if (configured) {
    374             throw new IllegalStateException("Configuration is frozen");
    375         }
    376
    377         // Inherit values that have not been overridden
    378         if (getCatalog() == null) {
    379             setCatalog(config.getCatalog());
    380         }
    381
    382         if (getCommand() == null) {
    383             setCommand(config.getCommand());
    384         }
    385
    386         if (getModule() == null) {
    387             setModule(config.getModule());
    388         }
    389
    390         if (getName() == null) {
    391             setName(config.getName());
    392         }
    393
    394         if (getPath() == null) {
    395             setPath(config.getPath());
    396         }
    397
    398         if (!getRedirect()) {
    399             setRedirect(config.getRedirect());
    400         }
    401
    402         inheritProperties(config);
    403     }
    404
    405     /**
    406      * <p>Inherit configuration information from the ForwardConfig that this
    407      * instance is extending.  This method verifies that any forward config
    408      * object that it inherits from has also had its processExtends() method
    409      * called.</p>
    410      *
    411      * @param moduleConfig The {@link ModuleConfig} that this config is from.
    412      * @param actionConfig The {@link ActionConfig} that this config is from,
    413      *                     if applicable.  This must be null for global
    414      *                     forwards.
    415      * @see #inheritFrom(ForwardConfig)
    416      */
    417     public void processExtends(ModuleConfig moduleConfig,
    418         ActionConfig actionConfig)
    419         throws ClassNotFoundException, IllegalAccessException,
    420             InstantiationException, InvocationTargetException {
    421         if (configured) {
    422             throw new IllegalStateException("Configuration is frozen");
    423         }
    424
    425         String ancestorName = getExtends();
    426
    427         if ((!extensionProcessed) && (ancestorName != null)) {
    428             ForwardConfig baseConfig = null;
    429
    430             // We only check the action config if we're not a global forward
    431             boolean checkActionConfig =
    432                 (this != moduleConfig.findForwardConfig(getName()));
    433
    434             // ... and the action config was provided
    435             checkActionConfig &= (actionConfig != null);
    436
    437             // ... and we're not extending a config with the same name
    438             // (because if we are, that means we're an action-level forward
    439             //  extending a global forward).
    440             checkActionConfig &= !ancestorName.equals(getName());
    441
    442             // We first check in the action config's forwards
    443             if (checkActionConfig) {
    444                 baseConfig = actionConfig.findForwardConfig(ancestorName);
    445             }
    446
    447             // Then check the global forwards
    448             if (baseConfig == null) {
    449                 baseConfig = moduleConfig.findForwardConfig(ancestorName);
    450             }
    451
    452             if (baseConfig == null) {
    453                 throw new NullPointerException("Unable to find " + "forward '"
    454                     + ancestorName + "' to extend.");
    455             }
    456
    457             // Check for circular inheritance and make sure the base config's
    458             //  own extends have been processed already
    459             if (checkCircularInheritance(moduleConfig, actionConfig)) {
    460                 throw new IllegalArgumentException(
    461                     "Circular inheritance detected for forward " + getName());
    462             }
    463
    464             if (!baseConfig.isExtensionProcessed()) {
    465                 baseConfig.processExtends(moduleConfig, actionConfig);
    466             }
    467
    468             // copy values from the base config
    469             inheritFrom(baseConfig);
    470         }
    471
    472         extensionProcessed = true;
    473     }
    474
    475     /**
    476      * Return a String representation of this object.
    477      */
    478     public String toString() {
    479         StringBuffer sb = new StringBuffer("ForwardConfig[");
    480
    481         sb.append("name=");
    482         sb.append(this.name);
    483         sb.append(",path=");
    484         sb.append(this.path);
    485         sb.append(",redirect=");
    486         sb.append(this.redirect);
    487         sb.append(",module=");
    488         sb.append(this.module);
    489         sb.append(",extends=");
    490         sb.append(this.inherit);
    491         sb.append(",catalog=");
    492         sb.append(this.catalog);
    493         sb.append(",command=");
    494         sb.append(this.command);
    495         sb.append("]");
    496
    497         return (sb.toString());
    498     }
    499 }