001: /*
002: * $Id: ForwardConfig.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts.config;
022:
023: import java.lang.reflect.InvocationTargetException;
024:
025: /**
026: * <p>A JavaBean representing the configuration information of a
027: * <code><forward></code> element from a Struts configuration file.</p>
028: *
029: * @version $Rev: 471754 $ $Date: 2005-08-14 17:24:39 -0400 (Sun, 14 Aug 2005)
030: * $
031: * @since Struts 1.1
032: */
033: public class ForwardConfig extends BaseConfig {
034: // ------------------------------------------------------------- Properties
035:
036: /**
037: * The name of the ForwardConfig that this object should inherit
038: * properties from.
039: */
040: protected String inherit = null;
041:
042: /**
043: * Have the inheritance values for this class been applied?
044: */
045: protected boolean extensionProcessed = false;
046:
047: /**
048: * The unique identifier of this forward, which is used to reference it in
049: * <code>Action</code> classes.
050: */
051: protected String name = null;
052:
053: /**
054: * <p>The URL to which this <code>ForwardConfig</code> entry points, which
055: * must start with a slash ("/") character. It is interpreted according
056: * to the following rules:</p>
057: *
058: * <ul>
059: *
060: * <li>If <code>contextRelative</code> property is <code>true</code>, the
061: * path is considered to be context-relative within the current web
062: * application (even if we are in a named module). It will be prefixed by
063: * the context path to create a server-relative URL.</li>
064: *
065: * <li>If the <code>contextRelative</code> property is false, the path is
066: * considered to be the module-relative portion of the URL. It will be
067: * used as the replacement for the <code>$P</code> marker in the
068: * <code>forwardPattern</code> property defined on the {@link
069: * ControllerConfig} element for our current module. For the default
070: * <code>forwardPattern</code> value of <code>$C$M$P</code>, the resulting
071: * server-relative URL will be the concatenation of the context path, the
072: * module prefix, and the <code>path</code> from this
073: * <code>ForwardConfig</code>.
074: *
075: * </li>
076: *
077: * </ul>
078: */
079: protected String path = null;
080:
081: /**
082: * <p>The prefix of the module to which this <code>ForwardConfig</code>
083: * entry points, which must start with a slash ("/") character. </p>
084: * <p>Usage note: If a forward config is used in a hyperlink, and a module
085: * is specified, the path must lead to another action and not directly to
086: * a page. This is in keeping with rule that in a modular application all
087: * links must be to an action rather than a page. </p>
088: */
089: protected String module = null;
090:
091: /**
092: * Should a redirect be used to transfer control to the specified path?
093: */
094: protected boolean redirect = false;
095:
096: /**
097: * <p>The name of a <code>commons-chain</code> command which should be
098: * looked up and executed before Struts dispatches control to the view
099: * 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(
267: ModuleConfig moduleConfig, 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) throws ClassNotFoundException,
419: IllegalAccessException, InstantiationException,
420: 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 = (this != moduleConfig
432: .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
445: .findForwardConfig(ancestorName);
446: }
447:
448: // Then check the global forwards
449: if (baseConfig == null) {
450: baseConfig = moduleConfig
451: .findForwardConfig(ancestorName);
452: }
453:
454: if (baseConfig == null) {
455: throw new NullPointerException("Unable to find "
456: + "forward '" + ancestorName + "' to extend.");
457: }
458:
459: // Check for circular inheritance and make sure the base config's
460: // own extends have been processed already
461: if (checkCircularInheritance(moduleConfig, actionConfig)) {
462: throw new IllegalArgumentException(
463: "Circular inheritance detected for forward "
464: + getName());
465: }
466:
467: if (!baseConfig.isExtensionProcessed()) {
468: baseConfig.processExtends(moduleConfig, actionConfig);
469: }
470:
471: // copy values from the base config
472: inheritFrom(baseConfig);
473: }
474:
475: extensionProcessed = true;
476: }
477:
478: /**
479: * Return a String representation of this object.
480: */
481: public String toString() {
482: StringBuffer sb = new StringBuffer("ForwardConfig[");
483:
484: sb.append("name=");
485: sb.append(this .name);
486: sb.append(",path=");
487: sb.append(this .path);
488: sb.append(",redirect=");
489: sb.append(this .redirect);
490: sb.append(",module=");
491: sb.append(this .module);
492: sb.append(",extends=");
493: sb.append(this .inherit);
494: sb.append(",catalog=");
495: sb.append(this .catalog);
496: sb.append(",command=");
497: sb.append(this .command);
498: sb.append("]");
499:
500: return (sb.toString());
501: }
502: }
|