1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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><forward></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
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
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
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
260 ForwardConfig ancestor = null;
261
262
263 if (actionConfig != null) {
264 ancestor = actionConfig.findForwardConfig(ancestorName);
265
266
267 if (ancestor == this) {
268 ancestor = null;
269 }
270 }
271
272
273 if (ancestor == null) {
274 ancestor = moduleConfig.findForwardConfig(ancestorName);
275
276 if (ancestor != null) {
277
278
279
280 actionConfig = null;
281 }
282 }
283
284 while (ancestor != null) {
285
286 if (ancestor == this) {
287 return true;
288 }
289
290
291 ancestorName = ancestor.getExtends();
292
293
294 if (ancestor.getName().equals(ancestorName)) {
295
296
297
298 if (actionConfig == null) {
299 return false;
300 } else {
301
302
303 actionConfig = null;
304 }
305 }
306
307 ancestor = null;
308
309
310 if (actionConfig != null) {
311 ancestor = actionConfig.findForwardConfig(ancestorName);
312 }
313
314
315 if (ancestor == null) {
316 ancestor = moduleConfig.findForwardConfig(ancestorName);
317
318 if (ancestor != null) {
319
320 actionConfig = null;
321 }
322 }
323 }
324
325 return false;
326 }
327
328
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
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
416 boolean checkActionConfig =
417 (this != moduleConfig.findForwardConfig(getName()));
418
419
420 checkActionConfig &= (actionConfig != null);
421
422
423
424
425 checkActionConfig &= !ancestorName.equals(getName());
426
427
428 if (checkActionConfig) {
429 baseConfig = actionConfig.findForwardConfig(ancestorName);
430 }
431
432
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
443
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
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 }