001: /*
002: * $Id: ModuleConfigImpl.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.impl;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025: import org.apache.struts.config.ActionConfig;
026: import org.apache.struts.config.ActionConfigMatcher;
027: import org.apache.struts.config.BaseConfig;
028: import org.apache.struts.config.ControllerConfig;
029: import org.apache.struts.config.ExceptionConfig;
030: import org.apache.struts.config.FormBeanConfig;
031: import org.apache.struts.config.ForwardConfig;
032: import org.apache.struts.config.MessageResourcesConfig;
033: import org.apache.struts.config.ModuleConfig;
034: import org.apache.struts.config.PlugInConfig;
035:
036: import java.io.Serializable;
037:
038: import java.util.ArrayList;
039: import java.util.HashMap;
040: import java.util.List;
041:
042: /**
043: * <p>The collection of static configuration information that describes a
044: * Struts-based module. Multiple modules are identified by a <em>prefix</em>
045: * at the beginning of the context relative portion of the request URI. If no
046: * module prefix can be matched, the default configuration (with a prefix
047: * equal to a zero-length string) is selected, which is elegantly backwards
048: * compatible with the previous Struts behavior that only supported one
049: * module.</p>
050: *
051: * @version $Rev: 471754 $ $Date: 2005-12-31 03:57:16 -0500 (Sat, 31 Dec 2005)
052: * $
053: * @since Struts 1.1
054: */
055: public class ModuleConfigImpl extends BaseConfig implements
056: Serializable, ModuleConfig {
057: /**
058: * <p>Commons Logging instance. </p>
059: */
060: protected static Log log = LogFactory
061: .getLog(ModuleConfigImpl.class);
062:
063: // ----------------------------------------------------- Instance Variables
064: // Instance Variables at end to make comparing Interface and implementation easier.
065:
066: /**
067: * <p>The set of action configurations for this module, if any, keyed by
068: * the <code>path</code> property.</p>
069: */
070: protected HashMap actionConfigs = null;
071:
072: /**
073: * <p>The set of action configuration for this module, if any, keyed by
074: * the <code>actionId</code> property.</p>
075: */
076: protected HashMap actionConfigIds = null;
077:
078: /**
079: * <p>The set of action configurations for this module, if any, listed in
080: * the order in which they are added.</p>
081: */
082: protected List actionConfigList = null;
083:
084: /**
085: * <p>The set of exception handling configurations for this module, if
086: * any, keyed by the <code>type</code> property.</p>
087: */
088: protected HashMap exceptions = null;
089:
090: /**
091: * <p>The set of form bean configurations for this module, if any, keyed
092: * by the <code>name</code> property.</p>
093: */
094: protected HashMap formBeans = null;
095:
096: /**
097: * <p>The set of global forward configurations for this module, if any,
098: * keyed by the <code>name</code> property.</p>
099: */
100: protected HashMap forwards = null;
101:
102: /**
103: * <p>The set of message resources configurations for this module, if any,
104: * keyed by the <code>key</code> property.</p>
105: */
106: protected HashMap messageResources = null;
107:
108: /**
109: * <p>The set of configured plug-in Actions for this module, if any, in
110: * the order they were declared and configured.</p>
111: */
112: protected ArrayList plugIns = null;
113:
114: /**
115: * <p>The controller configuration object for this module.</p>
116: */
117: protected ControllerConfig controllerConfig = null;
118:
119: /**
120: * <p>The prefix of the context-relative portion of the request URI, used
121: * to select this configuration versus others supported by the controller
122: * servlet. A configuration with a prefix of a zero-length String is the
123: * default configuration for this web module.</p>
124: */
125: protected String prefix = null;
126:
127: /**
128: * <p>The default class name to be used when creating action form bean
129: * instances.</p>
130: */
131: protected String actionFormBeanClass = "org.apache.struts.action.ActionFormBean";
132:
133: /**
134: * The default class name to be used when creating action mapping
135: * instances.
136: */
137: protected String actionMappingClass = "org.apache.struts.action.ActionMapping";
138:
139: /**
140: * The default class name to be used when creating action forward
141: * instances.
142: */
143: protected String actionForwardClass = "org.apache.struts.action.ActionForward";
144:
145: /**
146: * <p>Matches action config paths against compiled wildcard patterns</p>
147: */
148: protected ActionConfigMatcher matcher = null;
149:
150: /**
151: * <p>Constructor for ModuleConfigImpl. Assumes default
152: * configuration.</p>
153: *
154: * @since Struts 1.2.8
155: */
156: public ModuleConfigImpl() {
157: this ("");
158: }
159:
160: /**
161: * <p>Construct an ModuleConfigImpl object according to the specified
162: * parameter values.</p>
163: *
164: * @param prefix Context-relative URI prefix for this module
165: */
166: public ModuleConfigImpl(String prefix) {
167: super ();
168: this .prefix = prefix;
169: this .actionConfigs = new HashMap();
170: this .actionConfigIds = new HashMap();
171: this .actionConfigList = new ArrayList();
172: this .actionFormBeanClass = "org.apache.struts.action.ActionFormBean";
173: this .actionMappingClass = "org.apache.struts.action.ActionMapping";
174: this .actionForwardClass = "org.apache.struts.action.ActionForward";
175: this .configured = false;
176: this .controllerConfig = null;
177: this .exceptions = new HashMap();
178: this .formBeans = new HashMap();
179: this .forwards = new HashMap();
180: this .messageResources = new HashMap();
181: this .plugIns = new ArrayList();
182: }
183:
184: // --------------------------------------------------------- Public Methods
185:
186: /**
187: * </p> Has this module been completely configured yet. Once this flag
188: * has been set, any attempt to modify the configuration will return an
189: * IllegalStateException.</p>
190: */
191: public boolean getConfigured() {
192: return (this .configured);
193: }
194:
195: /**
196: * <p>The controller configuration object for this module.</p>
197: */
198: public ControllerConfig getControllerConfig() {
199: if (this .controllerConfig == null) {
200: this .controllerConfig = new ControllerConfig();
201: }
202:
203: return (this .controllerConfig);
204: }
205:
206: /**
207: * <p>The controller configuration object for this module.</p>
208: *
209: * @param cc The controller configuration object for this module.
210: */
211: public void setControllerConfig(ControllerConfig cc) {
212: throwIfConfigured();
213: this .controllerConfig = cc;
214: }
215:
216: /**
217: * <p>The prefix of the context-relative portion of the request URI, used
218: * to select this configuration versus others supported by the controller
219: * servlet. A configuration with a prefix of a zero-length String is the
220: * default configuration for this web module.</p>
221: */
222: public String getPrefix() {
223: return (this .prefix);
224: }
225:
226: /**
227: * <p>The prefix of the context-relative portion of the request URI, used
228: * to select this configuration versus others supported by the controller
229: * servlet. A configuration with a prefix of a zero-length String is the
230: * default configuration for this web module.</p>
231: */
232: public void setPrefix(String prefix) {
233: throwIfConfigured();
234: this .prefix = prefix;
235: }
236:
237: /**
238: * <p>The default class name to be used when creating action form bean
239: * instances.</p>
240: */
241: public String getActionFormBeanClass() {
242: return this .actionFormBeanClass;
243: }
244:
245: /**
246: * <p>The default class name to be used when creating action form bean
247: * instances.</p>
248: *
249: * @param actionFormBeanClass default class name to be used when creating
250: * action form bean instances.
251: */
252: public void setActionFormBeanClass(String actionFormBeanClass) {
253: this .actionFormBeanClass = actionFormBeanClass;
254: }
255:
256: /**
257: * <p>The default class name to be used when creating action mapping
258: * instances.</p>
259: */
260: public String getActionMappingClass() {
261: return this .actionMappingClass;
262: }
263:
264: /**
265: * <p> The default class name to be used when creating action mapping
266: * instances. </p>
267: *
268: * @param actionMappingClass default class name to be used when creating
269: * action mapping instances.
270: */
271: public void setActionMappingClass(String actionMappingClass) {
272: this .actionMappingClass = actionMappingClass;
273: }
274:
275: /**
276: * </p> Ad d a new <code>ActionConfig</code> instance to the set
277: * associated with this module. </p>
278: *
279: * @param config The new configuration instance to be added
280: * @throws IllegalStateException if this module configuration has been
281: * frozen
282: */
283: public void addActionConfig(ActionConfig config) {
284: throwIfConfigured();
285: config.setModuleConfig(this );
286:
287: String path = config.getPath();
288: if (actionConfigs.containsKey(path)) {
289: log.warn("Overriding ActionConfig of path " + path);
290: }
291:
292: String actionId = config.getActionId();
293: if ((actionId != null) && !actionId.equals("")) {
294: if (actionConfigIds.containsKey(actionId)) {
295: if (log.isWarnEnabled()) {
296: ActionConfig otherConfig = (ActionConfig) actionConfigIds
297: .get(actionId);
298: StringBuffer msg = new StringBuffer(
299: "Overriding actionId[");
300: msg.append(actionId);
301: msg.append("] for path[");
302: msg.append(otherConfig.getPath());
303: msg.append("] with path[");
304: msg.append(path);
305: msg.append("]");
306: log.warn(msg);
307: }
308: }
309: actionConfigIds.put(actionId, config);
310: }
311:
312: actionConfigs.put(path, config);
313: actionConfigList.add(config);
314: }
315:
316: /**
317: * <p> Add a new <code>ExceptionConfig</code> instance to the set
318: * associated with this module. </p>
319: *
320: * @param config The new configuration instance to be added
321: * @throws IllegalStateException if this module configuration has been
322: * frozen
323: */
324: public void addExceptionConfig(ExceptionConfig config) {
325: throwIfConfigured();
326:
327: String key = config.getType();
328:
329: if (exceptions.containsKey(key)) {
330: log.warn("Overriding ExceptionConfig of type " + key);
331: }
332:
333: exceptions.put(key, config);
334: }
335:
336: /**
337: * <p> Add a new <code>FormBeanConfig</code> instance to the set
338: * associated with this module. </p>
339: *
340: * @param config The new configuration instance to be added
341: * @throws IllegalStateException if this module configuration has been
342: * frozen
343: */
344: public void addFormBeanConfig(FormBeanConfig config) {
345: throwIfConfigured();
346:
347: String key = config.getName();
348:
349: if (formBeans.containsKey(key)) {
350: log.warn("Overriding ActionForm of name " + key);
351: }
352:
353: formBeans.put(key, config);
354: }
355:
356: /**
357: * <p> The default class name to be used when creating action forward
358: * instances. </p>
359: */
360: public String getActionForwardClass() {
361: return this .actionForwardClass;
362: }
363:
364: /**
365: * <p> The default class name to be used when creating action forward
366: * instances. </p>
367: *
368: * @param actionForwardClass default class name to be used when creating
369: * action forward instances.
370: */
371: public void setActionForwardClass(String actionForwardClass) {
372: this .actionForwardClass = actionForwardClass;
373: }
374:
375: /**
376: * <p> Add a new <code>ForwardConfig</code> instance to the set of global
377: * forwards associated with this module. </p>
378: *
379: * @param config The new configuration instance to be added
380: * @throws IllegalStateException if this module configuration has been
381: * frozen
382: */
383: public void addForwardConfig(ForwardConfig config) {
384: throwIfConfigured();
385:
386: String key = config.getName();
387:
388: if (forwards.containsKey(key)) {
389: log.warn("Overriding global ActionForward of name " + key);
390: }
391:
392: forwards.put(key, config);
393: }
394:
395: /**
396: * <p> Add a new <code>MessageResourcesConfig</code> instance to the set
397: * associated with this module. </p>
398: *
399: * @param config The new configuration instance to be added
400: * @throws IllegalStateException if this module configuration has been
401: * frozen
402: */
403: public void addMessageResourcesConfig(MessageResourcesConfig config) {
404: throwIfConfigured();
405:
406: String key = config.getKey();
407:
408: if (messageResources.containsKey(key)) {
409: log
410: .warn("Overriding MessageResources bundle of key "
411: + key);
412: }
413:
414: messageResources.put(key, config);
415: }
416:
417: /**
418: * <p> Add a newly configured {@link org.apache.struts.config.PlugInConfig}
419: * instance to the set of plug-in Actions for this module. </p>
420: *
421: * @param plugInConfig The new configuration instance to be added
422: */
423: public void addPlugInConfig(PlugInConfig plugInConfig) {
424: throwIfConfigured();
425: plugIns.add(plugInConfig);
426: }
427:
428: /**
429: * <p> Return the action configuration for the specified path, first
430: * looking a direct match, then if none found, a wildcard pattern match;
431: * otherwise return <code>null</code>. </p>
432: *
433: * @param path Path of the action configuration to return
434: */
435: public ActionConfig findActionConfig(String path) {
436: ActionConfig config = (ActionConfig) actionConfigs.get(path);
437:
438: // If a direct match cannot be found, try to match action configs
439: // containing wildcard patterns only if a matcher exists.
440: if ((config == null) && (matcher != null)) {
441: config = matcher.match(path);
442: }
443:
444: return config;
445: }
446:
447: /**
448: * <p>Returns the action configuration for the specifed action
449: * action identifier.</p>
450: *
451: * @param actionId the action identifier
452: * @return the action config if found; otherwise <code>null</code>
453: * @see ActionConfig#getActionId()
454: * @since Struts 1.3.6
455: */
456: public ActionConfig findActionConfigId(String actionId) {
457: if (actionId != null) {
458: return (ActionConfig) this .actionConfigIds.get(actionId);
459: }
460: return null;
461: }
462:
463: /**
464: * <p> Return the action configurations for this module. If there are
465: * none, a zero-length array is returned. </p>
466: */
467: public ActionConfig[] findActionConfigs() {
468: ActionConfig[] results = new ActionConfig[actionConfigList
469: .size()];
470:
471: return ((ActionConfig[]) actionConfigList.toArray(results));
472: }
473:
474: /**
475: * <p> Return the exception configuration for the specified type, if any;
476: * otherwise return <code>null</code>. </p>
477: *
478: * @param type Exception class name to find a configuration for
479: */
480: public ExceptionConfig findExceptionConfig(String type) {
481: return ((ExceptionConfig) exceptions.get(type));
482: }
483:
484: /**
485: * <p>Find and return the <code>ExceptionConfig</code> instance defining
486: * how <code>Exceptions</code> of the specified type should be handled.
487: *
488: * <p>In original Struts usage, this was only available in
489: * <code>ActionConfig</code>, but there are cases when an exception could
490: * be thrown before an <code>ActionConfig</code> has been identified,
491: * where global exception handlers may still be pertinent.</p>
492: *
493: * <p>TODO: Look for a way to share this logic with
494: * <code>ActionConfig</code>, although there are subtle differences, and
495: * it certainly doesn't seem like it should be done with inheritance.</p>
496: *
497: * @param type Exception class for which to find a handler
498: * @since Struts 1.3.0
499: */
500: public ExceptionConfig findException(Class type) {
501: // Check through the entire superclass hierarchy as needed
502: ExceptionConfig config = null;
503:
504: while (true) {
505: // Check for a locally defined handler
506: String name = type.getName();
507:
508: log.debug("findException: look locally for " + name);
509: config = findExceptionConfig(name);
510:
511: if (config != null) {
512: return (config);
513: }
514:
515: // Loop again for our superclass (if any)
516: type = type.getSuperclass();
517:
518: if (type == null) {
519: break;
520: }
521: }
522:
523: return (null); // No handler has been configured
524: }
525:
526: /**
527: * <p> Return the exception configurations for this module. If there are
528: * none, a zero-length array is returned. </p>
529: */
530: public ExceptionConfig[] findExceptionConfigs() {
531: ExceptionConfig[] results = new ExceptionConfig[exceptions
532: .size()];
533:
534: return ((ExceptionConfig[]) exceptions.values()
535: .toArray(results));
536: }
537:
538: /**
539: * <p> Return the form bean configuration for the specified key, if any;
540: * otherwise return <code>null</code>. </p>
541: *
542: * @param name Name of the form bean configuration to return
543: */
544: public FormBeanConfig findFormBeanConfig(String name) {
545: return ((FormBeanConfig) formBeans.get(name));
546: }
547:
548: /**
549: * <p> Return the form bean configurations for this module. If there are
550: * none, a zero-length array is returned. </p>
551: */
552: public FormBeanConfig[] findFormBeanConfigs() {
553: FormBeanConfig[] results = new FormBeanConfig[formBeans.size()];
554:
555: return ((FormBeanConfig[]) formBeans.values().toArray(results));
556: }
557:
558: /**
559: * <p> Return the forward configuration for the specified key, if any;
560: * otherwise return <code>null</code>. </p>
561: *
562: * @param name Name of the forward configuration to return
563: */
564: public ForwardConfig findForwardConfig(String name) {
565: return ((ForwardConfig) forwards.get(name));
566: }
567:
568: /**
569: * <p> Return the form bean configurations for this module. If there are
570: * none, a zero-length array is returned. </p>
571: */
572: public ForwardConfig[] findForwardConfigs() {
573: ForwardConfig[] results = new ForwardConfig[forwards.size()];
574:
575: return ((ForwardConfig[]) forwards.values().toArray(results));
576: }
577:
578: /**
579: * <p> Return the message resources configuration for the specified key,
580: * if any; otherwise return <code>null</code>. </p>
581: *
582: * @param key Key of the data source configuration to return
583: */
584: public MessageResourcesConfig findMessageResourcesConfig(String key) {
585: return ((MessageResourcesConfig) messageResources.get(key));
586: }
587:
588: /**
589: * <p> Return the message resources configurations for this module. If
590: * there are none, a zero-length array is returned. </p>
591: */
592: public MessageResourcesConfig[] findMessageResourcesConfigs() {
593: MessageResourcesConfig[] results = new MessageResourcesConfig[messageResources
594: .size()];
595:
596: return ((MessageResourcesConfig[]) messageResources.values()
597: .toArray(results));
598: }
599:
600: /**
601: * <p> Return the configured plug-in actions for this module. If there
602: * are none, a zero-length array is returned. </p>
603: */
604: public PlugInConfig[] findPlugInConfigs() {
605: PlugInConfig[] results = new PlugInConfig[plugIns.size()];
606:
607: return ((PlugInConfig[]) plugIns.toArray(results));
608: }
609:
610: /**
611: * <p> Freeze the configuration of this module. After this method
612: * returns, any attempt to modify the configuration will return an
613: * IllegalStateException. </p>
614: */
615: public void freeze() {
616: super .freeze();
617:
618: ActionConfig[] aconfigs = findActionConfigs();
619:
620: for (int i = 0; i < aconfigs.length; i++) {
621: aconfigs[i].freeze();
622: }
623:
624: matcher = new ActionConfigMatcher(aconfigs);
625:
626: getControllerConfig().freeze();
627:
628: ExceptionConfig[] econfigs = findExceptionConfigs();
629:
630: for (int i = 0; i < econfigs.length; i++) {
631: econfigs[i].freeze();
632: }
633:
634: FormBeanConfig[] fbconfigs = findFormBeanConfigs();
635:
636: for (int i = 0; i < fbconfigs.length; i++) {
637: fbconfigs[i].freeze();
638: }
639:
640: ForwardConfig[] fconfigs = findForwardConfigs();
641:
642: for (int i = 0; i < fconfigs.length; i++) {
643: fconfigs[i].freeze();
644: }
645:
646: MessageResourcesConfig[] mrconfigs = findMessageResourcesConfigs();
647:
648: for (int i = 0; i < mrconfigs.length; i++) {
649: mrconfigs[i].freeze();
650: }
651:
652: PlugInConfig[] piconfigs = findPlugInConfigs();
653:
654: for (int i = 0; i < piconfigs.length; i++) {
655: piconfigs[i].freeze();
656: }
657: }
658:
659: /**
660: * <p> Remove the specified action configuration instance. </p>
661: *
662: * @param config ActionConfig instance to be removed
663: * @throws IllegalStateException if this module configuration has been
664: * frozen
665: */
666: public void removeActionConfig(ActionConfig config) {
667: throwIfConfigured();
668: config.setModuleConfig(null);
669: actionConfigs.remove(config.getPath());
670: actionConfigList.remove(config);
671: }
672:
673: /**
674: * <p> Remove the specified exception configuration instance. </p>
675: *
676: * @param config ActionConfig instance to be removed
677: * @throws IllegalStateException if this module configuration has been
678: * frozen
679: */
680: public void removeExceptionConfig(ExceptionConfig config) {
681: throwIfConfigured();
682: exceptions.remove(config.getType());
683: }
684:
685: /**
686: * <p> Remove the specified form bean configuration instance. </p>
687: *
688: * @param config FormBeanConfig instance to be removed
689: * @throws IllegalStateException if this module configuration has been
690: * frozen
691: */
692: public void removeFormBeanConfig(FormBeanConfig config) {
693: throwIfConfigured();
694: formBeans.remove(config.getName());
695: }
696:
697: /**
698: * <p> Remove the specified forward configuration instance. </p>
699: *
700: * @param config ForwardConfig instance to be removed
701: * @throws IllegalStateException if this module configuration has been
702: * frozen
703: */
704: public void removeForwardConfig(ForwardConfig config) {
705: throwIfConfigured();
706: forwards.remove(config.getName());
707: }
708:
709: /**
710: * <p> Remove the specified message resources configuration instance.
711: * </p>
712: *
713: * @param config MessageResourcesConfig instance to be removed
714: * @throws IllegalStateException if this module configuration has been
715: * frozen
716: */
717: public void removeMessageResourcesConfig(
718: MessageResourcesConfig config) {
719: throwIfConfigured();
720: messageResources.remove(config.getKey());
721: }
722: }
|