View Javadoc

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