View Javadoc

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