1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts.config;
22
23 import org.apache.commons.beanutils.BeanUtils;
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.struts.util.RequestUtils;
27
28 import java.lang.reflect.InvocationTargetException;
29
30 import java.util.ArrayList;
31 import java.util.HashMap;
32
33 /**
34 * <p>A JavaBean representing the configuration information of an
35 * <code><action></code> element from a Struts module configuration
36 * file.</p>
37 *
38 * @version $Rev: 480593 $ $Date: 2006-11-29 09:17:52 -0600 (Wed, 29 Nov 2006) $
39 * @since Struts 1.1
40 */
41 public class ActionConfig extends BaseConfig {
42 private static final Log log = LogFactory.getLog(ActionConfig.class);
43
44
45
46 /**
47 * <p> The set of exception handling configurations for this action, if
48 * any, keyed by the <code>type</code> property. </p>
49 */
50 protected HashMap exceptions = new HashMap();
51
52 /**
53 * <p> The set of local forward configurations for this action, if any,
54 * keyed by the <code>name</code> property. </p>
55 */
56 protected HashMap forwards = new HashMap();
57
58
59
60 /**
61 * <p> The module configuration with which we are associated. </p>
62 */
63 protected ModuleConfig moduleConfig = null;
64
65 /**
66 * <p> The request-scope or session-scope attribute name under which our
67 * form bean is accessed, if it is different from the form bean's
68 * specified <code>name</code>. </p>
69 */
70 protected String attribute = null;
71
72 /**
73 * <p>The internal identification of this action mapping. Identifications are
74 * not inheritable and must be unique within a module.</p>
75 *
76 * @since Struts 1.3.6
77 */
78 protected String actionId = null;
79
80 /**
81 * <p>The path of the ActionConfig that this object should inherit
82 * properties from.</p> </p>
83 */
84 protected String inherit = null;
85
86 /**
87 * Indicates whether the "cancellable " property has been set or not.
88 */
89 private boolean cancellableSet = false;
90
91 /**
92 * <p>Can this Action be cancelled? [false]</p> <p> By default, when an
93 * Action is cancelled, validation is bypassed and the Action should not
94 * execute the business operation. If a request tries to cancel an Action
95 * when cancellable is not set, a "InvalidCancelException" is thrown.
96 * </p>
97 */
98 protected boolean cancellable = false;
99
100 /**
101 * <p> Have the inheritance values for this class been applied?</p>
102 */
103 protected boolean extensionProcessed = false;
104
105 /**
106 * <p> Context-relative path of the web application resource that will
107 * process this request via RequestDispatcher.forward(), instead of
108 * instantiating and calling the <code>Action</code> class specified by
109 * "type". Exactly one of <code>forward</code>, <code>include</code>, or
110 * <code>type</code> must be specified. </p>
111 */
112 protected String forward = null;
113
114 /**
115 * <p> Context-relative path of the web application resource that will
116 * process this request via RequestDispatcher.include(), instead of
117 * instantiating and calling the <code>Action</code> class specified by
118 * "type". Exactly one of <code>forward</code>, <code>include</code>, or
119 * <code>type</code> must be specified. </p>
120 */
121 protected String include = null;
122
123 /**
124 * <p> Context-relative path of the input form to which control should be
125 * returned if a validation error is encountered. Required if "name" is
126 * specified and the input bean returns validation errors. </p>
127 */
128 protected String input = null;
129
130 /**
131 * <p> Fully qualified Java class name of the <code>MultipartRequestHandler</code>
132 * implementation class used to process multi-part request data for this
133 * Action. </p>
134 */
135 protected String multipartClass = null;
136
137 /**
138 * <p> Name of the form bean, if any, associated with this Action. </p>
139 */
140 protected String name = null;
141
142 /**
143 * <p> General purpose configuration parameter that can be used to pass
144 * extra information to the Action instance selected by this Action.
145 * Struts does not itself use this value in any way. </p>
146 */
147 protected String parameter = null;
148
149 /**
150 * <p> Context-relative path of the submitted request, starting with a
151 * slash ("/") character, and omitting any filename extension if extension
152 * mapping is being used. </p>
153 */
154 protected String path = null;
155
156 /**
157 * <p> Prefix used to match request parameter names to form bean property
158 * names, if any. </p>
159 */
160 protected String prefix = null;
161
162 /**
163 * <p> Comma-delimited list of security role names allowed to request this
164 * Action. </p>
165 */
166 protected String roles = null;
167
168 /**
169 * <p> The set of security role names used to authorize access to this
170 * Action, as an array for faster access. </p>
171 */
172 protected String[] roleNames = new String[0];
173
174 /**
175 * <p> Identifier of the scope ("request" or "session") within which our
176 * form bean is accessed, if any. </p>
177 */
178 protected String scope = "session";
179
180 /**
181 * <p> Suffix used to match request parameter names to form bean property
182 * names, if any. </p>
183 */
184 protected String suffix = null;
185
186 /**
187 * <p> Fully qualified Java class name of the <code>Action</code> class to
188 * be used to process requests for this mapping if the
189 * <code>forward</code> and <code>include</code> properties are not set.
190 * Exactly one of <code>forward</code>, <code>include</code>, or
191 * <code>type</code> must be specified.
192 */
193 protected String type = null;
194
195 /**
196 * <p> Indicates Action be configured as the default one for this module,
197 * when true.
198 */
199 protected boolean unknown = false;
200
201 /**
202 * Indicates whether the "validate" property has been set or not.
203 */
204 private boolean validateSet = false;
205
206 /**
207 * <p> Should the <code>validate()</code> method of the form bean
208 * associated with this action be called?
209 */
210 protected boolean validate = true;
211
212 /**
213 * <p> The name of a <code>commons-chain</code> command which should be
214 * executed as part of the processing of this action.
215 *
216 * @since Struts 1.3.0
217 */
218 protected String command = null;
219
220 /**
221 * <p> The name of a <code>commons-chain</code> catalog in which
222 * <code>command</code> should be sought. If a <code>command</code> is
223 * defined and this property is undefined, the "default" catalog will be
224 * used. This is likely to be infrequently used after a future release of
225 * <code>commons-chain</code> supports a one-string expression of a
226 * catalog/chain combination.
227 *
228 * @since Struts 1.3.0
229 */
230 protected String catalog = null;
231
232 /**
233 * <p>The internal name of this action mapping. If an action has a name, it may be used
234 * as a shortcut in a URI. For example, an action with an identification of "editPerson"
235 * may be internally forwarded as "editPerson?id=1" which will then resolve to the
236 * real URI path at execution time.</p>
237 *
238 * @return the actionId
239 * @since Struts 1.3.6
240 */
241 public String getActionId() {
242 return this.actionId;
243 }
244
245 /**
246 * <p>The internal name of this action mapping. The name is not inheritable,
247 * may not contain a forward slash, and must be unique within a module. </p>
248 *
249 * @param actionId the action identifier
250 * @since Struts 1.3.6
251 * @throws IllegalStateException if the configuration is frozen
252 * @throws IllegalArgumentException if the identifier contains a forward slash
253 */
254 public void setActionId(String actionId) {
255 if (configured) {
256 throw new IllegalStateException("Configuration is frozen");
257 }
258
259 if ((actionId != null) && (actionId.indexOf("/") > -1)) {
260 throw new IllegalArgumentException("actionId '" + actionId + "' may not contain a forward slash");
261 }
262
263 this.actionId = actionId;
264 }
265
266 /**
267 * <p> The module configuration with which we are associated.
268 */
269 public ModuleConfig getModuleConfig() {
270 return (this.moduleConfig);
271 }
272
273 /**
274 * <p> The module configuration with which we are associated.
275 */
276 public void setModuleConfig(ModuleConfig moduleConfig) {
277 if (configured) {
278 throw new IllegalStateException("Configuration is frozen");
279 }
280
281 this.moduleConfig = moduleConfig;
282 }
283
284 /**
285 * <p> Returns the request-scope or session-scope attribute name under
286 * which our form bean is accessed, if it is different from the form
287 * bean's specified <code>name</code>.
288 *
289 * @return attribute name under which our form bean is accessed.
290 */
291 public String getAttribute() {
292 if (this.attribute == null) {
293 return (this.name);
294 } else {
295 return (this.attribute);
296 }
297 }
298
299 /**
300 * <p> Set the request-scope or session-scope attribute name under which
301 * our form bean is accessed, if it is different from the form bean's
302 * specified <code>name</code>.
303 *
304 * @param attribute the request-scope or session-scope attribute name
305 * under which our form bean is access.
306 */
307 public void setAttribute(String attribute) {
308 if (configured) {
309 throw new IllegalStateException("Configuration is frozen");
310 }
311
312 this.attribute = attribute;
313 }
314
315 /**
316 * <p>Accessor for cancellable property</p>
317 *
318 * @return True if Action can be cancelled
319 */
320 public boolean getCancellable() {
321 return (this.cancellable);
322 }
323
324 /**
325 * <p>Mutator for for cancellable property</p>
326 *
327 * @param cancellable
328 */
329 public void setCancellable(boolean cancellable) {
330 if (configured) {
331 throw new IllegalStateException("Configuration is frozen");
332 }
333
334 this.cancellable = cancellable;
335 this.cancellableSet = true;
336 }
337
338 /**
339 * <p>Returns the path of the ActionConfig that this object should inherit
340 * properties from.</p>
341 *
342 * @return the path of the ActionConfig that this object should inherit
343 * properties from.
344 */
345 public String getExtends() {
346 return (this.inherit);
347 }
348
349 /**
350 * <p>Set the path of the ActionConfig that this object should inherit
351 * properties from.</p>
352 *
353 * @param inherit the path of the ActionConfig that this object should
354 * inherit properties from.
355 */
356 public void setExtends(String inherit) {
357 if (configured) {
358 throw new IllegalStateException("Configuration is frozen");
359 }
360
361 this.inherit = inherit;
362 }
363
364 public boolean isExtensionProcessed() {
365 return extensionProcessed;
366 }
367
368 /**
369 * <p> Returns context-relative path of the web application resource that
370 * will process this request.
371 *
372 * @return context-relative path of the web application resource that will
373 * process this request.
374 */
375 public String getForward() {
376 return (this.forward);
377 }
378
379 /**
380 * <p> Set the context-relative path of the web application resource that
381 * will process this request. Exactly one of <code>forward</code>,
382 * <code>include</code>, or <code>type</code> must be specified.
383 *
384 * @param forward context-relative path of the web application resource
385 * that will process this request.
386 */
387 public void setForward(String forward) {
388 if (configured) {
389 throw new IllegalStateException("Configuration is frozen");
390 }
391
392 this.forward = forward;
393 }
394
395 /**
396 * <p> Context-relative path of the web application resource that will
397 * process this request.
398 *
399 * @return Context-relative path of the web application resource that will
400 * process this request.
401 */
402 public String getInclude() {
403 return (this.include);
404 }
405
406 /**
407 * <p> Set context-relative path of the web application resource that will
408 * process this request. Exactly one of <code>forward</code>,
409 * <code>include</code>, or <code>type</code> must be specified.
410 *
411 * @param include context-relative path of the web application resource
412 * that will process this request.
413 */
414 public void setInclude(String include) {
415 if (configured) {
416 throw new IllegalStateException("Configuration is frozen");
417 }
418
419 this.include = include;
420 }
421
422 /**
423 * <p> Get the context-relative path of the input form to which control
424 * should be returned if a validation error is encountered.
425 *
426 * @return context-relative path of the input form to which control should
427 * be returned if a validation error is encountered.
428 */
429 public String getInput() {
430 return (this.input);
431 }
432
433 /**
434 * <p> Set the context-relative path of the input form to which control
435 * should be returned if a validation error is encountered. Required if
436 * "name" is specified and the input bean returns validation errors.
437 *
438 * @param input context-relative path of the input form to which control
439 * should be returned if a validation error is encountered.
440 */
441 public void setInput(String input) {
442 if (configured) {
443 throw new IllegalStateException("Configuration is frozen");
444 }
445
446 this.input = input;
447 }
448
449 /**
450 * <p> Return the fully qualified Java class name of the
451 * <code>MultipartRequestHandler</code> implementation class used to
452 * process multi-part request data for this Action.
453 */
454 public String getMultipartClass() {
455 return (this.multipartClass);
456 }
457
458 /**
459 * <p> Set the fully qualified Java class name of the
460 * <code>MultipartRequestHandler</code> implementation class used to
461 * process multi-part request data for this Action.
462 *
463 * @param multipartClass fully qualified class name of the
464 * <code>MultipartRequestHandler</code>
465 * implementation class.
466 */
467 public void setMultipartClass(String multipartClass) {
468 if (configured) {
469 throw new IllegalStateException("Configuration is frozen");
470 }
471
472 this.multipartClass = multipartClass;
473 }
474
475 /**
476 * <p> Return name of the form bean, if any, associated with this Action.
477 */
478 public String getName() {
479 return (this.name);
480 }
481
482 /**
483 * @param name name of the form bean associated with this Action.
484 */
485 public void setName(String name) {
486 if (configured) {
487 throw new IllegalStateException("Configuration is frozen");
488 }
489
490 this.name = name;
491 }
492
493 /**
494 * <p> Return general purpose configuration parameter that can be used to
495 * pass extra information to the Action instance selected by this Action.
496 * Struts does not itself use this value in any way.
497 */
498 public String getParameter() {
499 return (this.parameter);
500 }
501
502 /**
503 * <p> General purpose configuration parameter that can be used to pass
504 * extra information to the Action instance selected by this Action.
505 * Struts does not itself use this value in any way.
506 *
507 * @param parameter General purpose configuration parameter.
508 */
509 public void setParameter(String parameter) {
510 if (configured) {
511 throw new IllegalStateException("Configuration is frozen");
512 }
513
514 this.parameter = parameter;
515 }
516
517 /**
518 * <p> Return context-relative path of the submitted request, starting
519 * with a slash ("/") character, and omitting any filename extension if
520 * extension mapping is being used.
521 */
522 public String getPath() {
523 return (this.path);
524 }
525
526 /**
527 * <p> Set context-relative path of the submitted request, starting with a
528 * slash ("/") character, and omitting any filename extension if extension
529 * mapping is being used.
530 *
531 * @param path context-relative path of the submitted request.
532 */
533 public void setPath(String path) {
534 if (configured) {
535 throw new IllegalStateException("Configuration is frozen");
536 }
537
538 this.path = path;
539 }
540
541 /**
542 * <p> Retruns prefix used to match request parameter names to form bean
543 * property names, if any.
544 */
545 public String getPrefix() {
546 return (this.prefix);
547 }
548
549 /**
550 * @param prefix Prefix used to match request parameter names to form bean
551 * property names, if any.
552 */
553 public void setPrefix(String prefix) {
554 if (configured) {
555 throw new IllegalStateException("Configuration is frozen");
556 }
557
558 this.prefix = prefix;
559 }
560
561 public String getRoles() {
562 return (this.roles);
563 }
564
565 public void setRoles(String roles) {
566 if (configured) {
567 throw new IllegalStateException("Configuration is frozen");
568 }
569
570 this.roles = roles;
571
572 if (roles == null) {
573 roleNames = new String[0];
574
575 return;
576 }
577
578 ArrayList list = new ArrayList();
579
580 while (true) {
581 int comma = roles.indexOf(',');
582
583 if (comma < 0) {
584 break;
585 }
586
587 list.add(roles.substring(0, comma).trim());
588 roles = roles.substring(comma + 1);
589 }
590
591 roles = roles.trim();
592
593 if (roles.length() > 0) {
594 list.add(roles);
595 }
596
597 roleNames = (String[]) list.toArray(new String[list.size()]);
598 }
599
600 /**
601 * <p> Get array of security role names used to authorize access to this
602 * Action.
603 */
604 public String[] getRoleNames() {
605 return (this.roleNames);
606 }
607
608 /**
609 * <p> Get the scope ("request" or "session") within which our form bean
610 * is accessed, if any.
611 */
612 public String getScope() {
613 return (this.scope);
614 }
615
616 /**
617 * @param scope scope ("request" or "session") within which our form bean
618 * is accessed, if any.
619 */
620 public void setScope(String scope) {
621 if (configured) {
622 throw new IllegalStateException("Configuration is frozen");
623 }
624
625 this.scope = scope;
626 }
627
628 /**
629 * <p> Return suffix used to match request parameter names to form bean
630 * property names, if any. </p>
631 */
632 public String getSuffix() {
633 return (this.suffix);
634 }
635
636 /**
637 * @param suffix Suffix used to match request parameter names to form bean
638 * property names, if any.
639 */
640 public void setSuffix(String suffix) {
641 if (configured) {
642 throw new IllegalStateException("Configuration is frozen");
643 }
644
645 this.suffix = suffix;
646 }
647
648 public String getType() {
649 return (this.type);
650 }
651
652 public void setType(String type) {
653 if (configured) {
654 throw new IllegalStateException("Configuration is frozen");
655 }
656
657 this.type = type;
658 }
659
660 /**
661 * <p> Determine whether Action is configured as the default one for this
662 * module. </p>
663 */
664 public boolean getUnknown() {
665 return (this.unknown);
666 }
667
668 /**
669 * @param unknown Indicates Action is configured as the default one for
670 * this module, when true.
671 */
672 public void setUnknown(boolean unknown) {
673 if (configured) {
674 throw new IllegalStateException("Configuration is frozen");
675 }
676
677 this.unknown = unknown;
678 }
679
680 public boolean getValidate() {
681 return (this.validate);
682 }
683
684 public void setValidate(boolean validate) {
685 if (configured) {
686 throw new IllegalStateException("Configuration is frozen");
687 }
688
689 this.validate = validate;
690 this.validateSet = true;
691 }
692
693 /**
694 * <p> Get the name of a <code>commons-chain</code> command which should
695 * be executed as part of the processing of this action. </p>
696 *
697 * @return name of a <code>commons-chain</code> command which should be
698 * executed as part of the processing of this action.
699 * @since Struts 1.3.0
700 */
701 public String getCommand() {
702 return (this.command);
703 }
704
705 /**
706 * <p> Get the name of a <code>commons-chain</code> catalog in which a
707 * specified command should be sought. This is likely to be infrequently
708 * used after a future release of <code>commons-chain</code> supports a
709 * one-string expression of a catalog/chain combination. </p>
710 *
711 * @return name of a <code>commons-chain</code> catalog in which a
712 * specified command should be sought.
713 * @since Struts 1.3.0
714 */
715 public String getCatalog() {
716 return (this.catalog);
717 }
718
719 /**
720 * <p> Set the name of a <code>commons-chain</code> command which should
721 * be executed as part of the processing of this action. </p>
722 *
723 * @param command name of a <code>commons-chain</code> command which
724 * should be executed as part of the processing of this
725 * action.
726 * @since Struts 1.3.0
727 */
728 public void setCommand(String command) {
729 if (configured) {
730 throw new IllegalStateException("Configuration is frozen");
731 }
732
733 this.command = command;
734 }
735
736 /**
737 * <p> Set the name of a <code>commons-chain</code> catalog in which a
738 * specified command should be sought. This is likely to be infrequently
739 * used after a future release of <code>commons-chain</code> supports a
740 * one-string expression of a catalog/chain combination. </p>
741 *
742 * @param catalog name of a <code>commons-chain</code> catalog in which a
743 * specified command should be sought.
744 * @since Struts 1.3.0
745 */
746 public void setCatalog(String catalog) {
747 if (configured) {
748 throw new IllegalStateException("Configuration is frozen");
749 }
750
751 this.catalog = catalog;
752 }
753
754
755
756 /**
757 * <p>Traces the hierarchy of this object to check if any of the ancestors
758 * is extending this instance.</p>
759 *
760 * @param moduleConfig The configuration for the module being configured.
761 * @return true if circular inheritance was detected.
762 */
763 protected boolean checkCircularInheritance(ModuleConfig moduleConfig) {
764 String ancestorPath = getExtends();
765
766 while (ancestorPath != null) {
767
768 if (getPath().equals(ancestorPath)) {
769 return true;
770 }
771
772
773 ActionConfig ancestor = moduleConfig.findActionConfig(ancestorPath);
774
775 if (ancestor != null) {
776 ancestorPath = ancestor.getExtends();
777 } else {
778 ancestorPath = null;
779 }
780 }
781
782 return false;
783 }
784
785 /**
786 * <p>Compare the exception handlers of this action with that of the given
787 * and copy those that are not present.</p>
788 *
789 * @param baseConfig The action config to copy handlers from.
790 * @see #inheritFrom(ActionConfig)
791 */
792 protected void inheritExceptionHandlers(ActionConfig baseConfig)
793 throws ClassNotFoundException, IllegalAccessException,
794 InstantiationException, InvocationTargetException {
795 if (configured) {
796 throw new IllegalStateException("Configuration is frozen");
797 }
798
799
800 ExceptionConfig[] baseHandlers = baseConfig.findExceptionConfigs();
801
802 for (int i = 0; i < baseHandlers.length; i++) {
803 ExceptionConfig baseHandler = baseHandlers[i];
804
805
806 ExceptionConfig copy =
807 this.findExceptionConfig(baseHandler.getType());
808
809 if (copy == null) {
810
811 copy =
812 (ExceptionConfig) RequestUtils.applicationInstance(baseHandler.getClass()
813 .getName());
814
815 BeanUtils.copyProperties(copy, baseHandler);
816 this.addExceptionConfig(copy);
817 copy.setProperties(baseHandler.copyProperties());
818 } else {
819
820 copy.processExtends(getModuleConfig(), this);
821 }
822 }
823 }
824
825 /**
826 * <p>Compare the forwards of this action with that of the given and copy
827 * those that are not present.</p>
828 *
829 * @param baseConfig The action config to copy forwards from.
830 * @see #inheritFrom(ActionConfig)
831 */
832 protected void inheritForwards(ActionConfig baseConfig)
833 throws ClassNotFoundException, IllegalAccessException,
834 InstantiationException, InvocationTargetException {
835 if (configured) {
836 throw new IllegalStateException("Configuration is frozen");
837 }
838
839
840 ForwardConfig[] baseForwards = baseConfig.findForwardConfigs();
841
842 for (int i = 0; i < baseForwards.length; i++) {
843 ForwardConfig baseForward = baseForwards[i];
844
845
846 ForwardConfig copy = this.findForwardConfig(baseForward.getName());
847
848 if (copy == null) {
849
850 copy =
851 (ForwardConfig) RequestUtils.applicationInstance(baseForward.getClass()
852 .getName());
853 BeanUtils.copyProperties(copy, baseForward);
854
855 this.addForwardConfig(copy);
856 copy.setProperties(baseForward.copyProperties());
857 } else {
858
859 copy.processExtends(getModuleConfig(), this);
860 }
861 }
862 }
863
864
865
866 /**
867 * <p> Add a new <code>ExceptionConfig</code> instance to the set
868 * associated with this action. </p>
869 *
870 * @param config The new configuration instance to be added
871 * @throws IllegalStateException if this module configuration has been
872 * frozen
873 */
874 public void addExceptionConfig(ExceptionConfig config) {
875 if (configured) {
876 throw new IllegalStateException("Configuration is frozen");
877 }
878
879 exceptions.put(config.getType(), config);
880 }
881
882 /**
883 * <p> Add a new <code>ForwardConfig</code> instance to the set of global
884 * forwards associated with this action. </p>
885 *
886 * @param config The new configuration instance to be added
887 * @throws IllegalStateException if this module configuration has been
888 * frozen
889 */
890 public void addForwardConfig(ForwardConfig config) {
891 if (configured) {
892 throw new IllegalStateException("Configuration is frozen");
893 }
894
895 forwards.put(config.getName(), config);
896 }
897
898 /**
899 * <p> Return the exception configuration for the specified type, if any;
900 * otherwise return <code>null</code>. </p>
901 *
902 * @param type Exception class name to find a configuration for
903 */
904 public ExceptionConfig findExceptionConfig(String type) {
905 return ((ExceptionConfig) exceptions.get(type));
906 }
907
908 /**
909 * <p> Return the exception configurations for this action. If there are
910 * none, a zero-length array is returned. </p>
911 */
912 public ExceptionConfig[] findExceptionConfigs() {
913 ExceptionConfig[] results = new ExceptionConfig[exceptions.size()];
914
915 return ((ExceptionConfig[]) exceptions.values().toArray(results));
916 }
917
918 /**
919 * <p>Find and return the <code>ExceptionConfig</code> instance defining
920 * how <code>Exceptions</code> of the specified type should be handled.
921 * This is performed by checking local and then global configurations for
922 * the specified exception's class, and then looking up the superclass
923 * chain (again checking local and then global configurations). If no
924 * handler configuration can be found, return <code>null</code>.</p>
925 *
926 * <p>Introduced in <code>ActionMapping</code> in Struts 1.1, but pushed
927 * up to <code>ActionConfig</code> in Struts 1.2.0.</p>
928 *
929 * @param type Exception class for which to find a handler
930 * @since Struts 1.2.0
931 */
932 public ExceptionConfig findException(Class type) {
933
934 ExceptionConfig config;
935
936 while (true) {
937
938 String name = type.getName();
939
940 log.debug("findException: look locally for " + name);
941 config = findExceptionConfig(name);
942
943 if (config != null) {
944 return (config);
945 }
946
947
948 log.debug("findException: look globally for " + name);
949 config = getModuleConfig().findExceptionConfig(name);
950
951 if (config != null) {
952 return (config);
953 }
954
955
956 type = type.getSuperclass();
957
958 if (type == null) {
959 break;
960 }
961 }
962
963 return (null);
964 }
965
966 /**
967 * <p> Return the forward configuration for the specified key, if any;
968 * otherwise return <code>null</code>. </p>
969 *
970 * @param name Name of the forward configuration to return
971 */
972 public ForwardConfig findForwardConfig(String name) {
973 return ((ForwardConfig) forwards.get(name));
974 }
975
976 /**
977 * <p> Return all forward configurations for this module. If there are
978 * none, a zero-length array is returned. </p>
979 */
980 public ForwardConfig[] findForwardConfigs() {
981 ForwardConfig[] results = new ForwardConfig[forwards.size()];
982
983 return ((ForwardConfig[]) forwards.values().toArray(results));
984 }
985
986 /**
987 * <p> Freeze the configuration of this action. </p>
988 */
989 public void freeze() {
990 super.freeze();
991
992 ExceptionConfig[] econfigs = findExceptionConfigs();
993
994 for (int i = 0; i < econfigs.length; i++) {
995 econfigs[i].freeze();
996 }
997
998 ForwardConfig[] fconfigs = findForwardConfigs();
999
1000 for (int i = 0; i < fconfigs.length; i++) {
1001 fconfigs[i].freeze();
1002 }
1003 }
1004
1005 /**
1006 * <p>Inherit values that have not been overridden from the provided
1007 * config object. Subclasses overriding this method should verify that
1008 * the given parameter is of a class that contains a property it is trying
1009 * to inherit:</p>
1010 *
1011 * <pre>
1012 * if (config instanceof MyCustomConfig) {
1013 * MyCustomConfig myConfig =
1014 * (MyCustomConfig) config;
1015 *
1016 * if (getMyCustomProp() == null) {
1017 * setMyCustomProp(myConfig.getMyCustomProp());
1018 * }
1019 * }
1020 * </pre>
1021 *
1022 * <p>If the given <code>config</code> is extending another object, those
1023 * extensions should be resolved before it's used as a parameter to this
1024 * method.</p>
1025 *
1026 * @param config The object that this instance will be inheriting its
1027 * values from.
1028 * @see #processExtends(ModuleConfig)
1029 */
1030 public void inheritFrom(ActionConfig config)
1031 throws ClassNotFoundException, IllegalAccessException,
1032 InstantiationException, InvocationTargetException {
1033 if (configured) {
1034 throw new IllegalStateException("Configuration is frozen");
1035 }
1036
1037
1038 if (getAttribute() == null) {
1039 setAttribute(config.getAttribute());
1040 }
1041
1042 if (!cancellableSet) {
1043 setCancellable(config.getCancellable());
1044 }
1045
1046 if (getCatalog() == null) {
1047 setCatalog(config.getCatalog());
1048 }
1049
1050 if (getCommand() == null) {
1051 setCommand(config.getCommand());
1052 }
1053
1054 if (getForward() == null) {
1055 setForward(config.getForward());
1056 }
1057
1058 if (getInclude() == null) {
1059 setInclude(config.getInclude());
1060 }
1061
1062 if (getInput() == null) {
1063 setInput(config.getInput());
1064 }
1065
1066 if (getMultipartClass() == null) {
1067 setMultipartClass(config.getMultipartClass());
1068 }
1069
1070 if (getName() == null) {
1071 setName(config.getName());
1072 }
1073
1074 if (getParameter() == null) {
1075 setParameter(config.getParameter());
1076 }
1077
1078 if (getPath() == null) {
1079 setPath(config.getPath());
1080 }
1081
1082 if (getPrefix() == null) {
1083 setPrefix(config.getPrefix());
1084 }
1085
1086 if (getRoles() == null) {
1087 setRoles(config.getRoles());
1088 }
1089
1090 if (getScope().equals("session")) {
1091 setScope(config.getScope());
1092 }
1093
1094 if (getSuffix() == null) {
1095 setSuffix(config.getSuffix());
1096 }
1097
1098 if (getType() == null) {
1099 setType(config.getType());
1100 }
1101
1102 if (!getUnknown()) {
1103 setUnknown(config.getUnknown());
1104 }
1105
1106 if (!validateSet) {
1107 setValidate(config.getValidate());
1108 }
1109
1110 inheritExceptionHandlers(config);
1111 inheritForwards(config);
1112 inheritProperties(config);
1113 }
1114
1115 /**
1116 * <p>Inherit configuration information from the ActionConfig that this
1117 * instance is extending. This method verifies that any action config
1118 * object that it inherits from has also had its processExtends() method
1119 * called.</p>
1120 *
1121 * @param moduleConfig The {@link ModuleConfig} that this bean is from.
1122 * @see #inheritFrom(ActionConfig)
1123 */
1124 public void processExtends(ModuleConfig moduleConfig)
1125 throws ClassNotFoundException, IllegalAccessException,
1126 InstantiationException, InvocationTargetException {
1127 if (configured) {
1128 throw new IllegalStateException("Configuration is frozen");
1129 }
1130
1131 String ancestorPath = getExtends();
1132
1133 if ((!extensionProcessed) && (ancestorPath != null)) {
1134 ActionConfig baseConfig =
1135 moduleConfig.findActionConfig(ancestorPath);
1136
1137 if (baseConfig == null) {
1138 throw new NullPointerException("Unable to find "
1139 + "action for '" + ancestorPath + "' to extend.");
1140 }
1141
1142
1143
1144 if (checkCircularInheritance(moduleConfig)) {
1145 throw new IllegalArgumentException(
1146 "Circular inheritance detected for action " + getPath());
1147 }
1148
1149
1150 if (!baseConfig.isExtensionProcessed()) {
1151 baseConfig.processExtends(moduleConfig);
1152 }
1153
1154
1155 inheritFrom(baseConfig);
1156 }
1157
1158 extensionProcessed = true;
1159 }
1160
1161 /**
1162 * <p> Remove the specified exception configuration instance. </p>
1163 *
1164 * @param config ExceptionConfig instance to be removed
1165 * @throws IllegalStateException if this module configuration has been
1166 * frozen
1167 */
1168 public void removeExceptionConfig(ExceptionConfig config) {
1169 if (configured) {
1170 throw new IllegalStateException("Configuration is frozen");
1171 }
1172
1173 exceptions.remove(config.getType());
1174 }
1175
1176 /**
1177 * <p> Remove the specified forward configuration instance. </p>
1178 *
1179 * @param config ForwardConfig instance to be removed
1180 * @throws IllegalStateException if this module configuration has been
1181 * frozen
1182 */
1183 public void removeForwardConfig(ForwardConfig config) {
1184 if (configured) {
1185 throw new IllegalStateException("Configuration is frozen");
1186 }
1187
1188 forwards.remove(config.getName());
1189 }
1190
1191 /**
1192 * <p> Return a String representation of this object. </p>
1193 */
1194 public String toString() {
1195 StringBuffer sb = new StringBuffer("ActionConfig[");
1196
1197 sb.append("cancellable=");
1198 sb.append(cancellable);
1199
1200 sb.append(",path=");
1201 sb.append(path);
1202
1203 sb.append(",validate=");
1204 sb.append(validate);
1205
1206 if (actionId != null) {
1207 sb.append(",actionId=");
1208 sb.append(actionId);
1209 }
1210
1211 if (attribute != null) {
1212 sb.append(",attribute=");
1213 sb.append(attribute);
1214 }
1215
1216 if (catalog != null) {
1217 sb.append(",catalog=");
1218 sb.append(catalog);
1219 }
1220
1221 if (command != null) {
1222 sb.append(",command=");
1223 sb.append(command);
1224 }
1225
1226 if (inherit != null) {
1227 sb.append(",extends=");
1228 sb.append(inherit);
1229 }
1230
1231 if (forward != null) {
1232 sb.append(",forward=");
1233 sb.append(forward);
1234 }
1235
1236 if (include != null) {
1237 sb.append(",include=");
1238 sb.append(include);
1239 }
1240
1241 if (input != null) {
1242 sb.append(",input=");
1243 sb.append(input);
1244 }
1245
1246 if (multipartClass != null) {
1247 sb.append(",multipartClass=");
1248 sb.append(multipartClass);
1249 }
1250
1251 if (name != null) {
1252 sb.append(",name=");
1253 sb.append(name);
1254 }
1255
1256 if (parameter != null) {
1257 sb.append(",parameter=");
1258 sb.append(parameter);
1259 }
1260
1261 if (prefix != null) {
1262 sb.append(",prefix=");
1263 sb.append(prefix);
1264 }
1265
1266 if (roles != null) {
1267 sb.append(",roles=");
1268 sb.append(roles);
1269 }
1270
1271 if (scope != null) {
1272 sb.append(",scope=");
1273 sb.append(scope);
1274 }
1275
1276 if (suffix != null) {
1277 sb.append(",suffix=");
1278 sb.append(suffix);
1279 }
1280
1281 if (type != null) {
1282 sb.append(",type=");
1283 sb.append(type);
1284 }
1285
1286 return (sb.toString());
1287 }
1288 }