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.action;
22
23 import org.apache.commons.beanutils.BeanUtils;
24 import org.apache.commons.beanutils.ConvertUtils;
25 import org.apache.commons.beanutils.PropertyUtils;
26 import org.apache.commons.beanutils.converters.BigDecimalConverter;
27 import org.apache.commons.beanutils.converters.BigIntegerConverter;
28 import org.apache.commons.beanutils.converters.BooleanConverter;
29 import org.apache.commons.beanutils.converters.ByteConverter;
30 import org.apache.commons.beanutils.converters.CharacterConverter;
31 import org.apache.commons.beanutils.converters.DoubleConverter;
32 import org.apache.commons.beanutils.converters.FloatConverter;
33 import org.apache.commons.beanutils.converters.IntegerConverter;
34 import org.apache.commons.beanutils.converters.LongConverter;
35 import org.apache.commons.beanutils.converters.ShortConverter;
36 import org.apache.commons.chain.CatalogFactory;
37 import org.apache.commons.chain.config.ConfigParser;
38 import org.apache.commons.digester.Digester;
39 import org.apache.commons.digester.RuleSet;
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42 import org.apache.struts.Globals;
43 import org.apache.struts.config.ActionConfig;
44 import org.apache.struts.config.ConfigRuleSet;
45 import org.apache.struts.config.ExceptionConfig;
46 import org.apache.struts.config.FormBeanConfig;
47 import org.apache.struts.config.FormPropertyConfig;
48 import org.apache.struts.config.ForwardConfig;
49 import org.apache.struts.config.MessageResourcesConfig;
50 import org.apache.struts.config.ModuleConfig;
51 import org.apache.struts.config.ModuleConfigFactory;
52 import org.apache.struts.config.PlugInConfig;
53 import org.apache.struts.util.MessageResources;
54 import org.apache.struts.util.MessageResourcesFactory;
55 import org.apache.struts.util.ModuleUtils;
56 import org.apache.struts.util.RequestUtils;
57 import org.xml.sax.InputSource;
58 import org.xml.sax.SAXException;
59
60 import javax.servlet.ServletContext;
61 import javax.servlet.ServletException;
62 import javax.servlet.UnavailableException;
63 import javax.servlet.http.HttpServlet;
64 import javax.servlet.http.HttpServletRequest;
65 import javax.servlet.http.HttpServletResponse;
66
67 import java.io.IOException;
68 import java.io.InputStream;
69
70 import java.math.BigDecimal;
71 import java.math.BigInteger;
72
73 import java.net.MalformedURLException;
74 import java.net.URL;
75 import java.net.URLConnection;
76
77 import java.util.ArrayList;
78 import java.util.Enumeration;
79 import java.util.Iterator;
80 import java.util.List;
81 import java.util.MissingResourceException;
82
83 /**
84 * <p><strong>ActionServlet</strong> provides the "controller" in the
85 * Model-View-Controller (MVC) design pattern for web applications that is
86 * commonly known as "Model 2". This nomenclature originated with a
87 * description in the JavaServerPages Specification, version 0.92, and has
88 * persisted ever since (in the absence of a better name).</p>
89 *
90 * <p>Generally, a "Model 2" application is architected as follows:</p>
91 *
92 * <ul>
93 *
94 * <li>The user interface will generally be created with server pages, which
95 * will not themselves contain any business logic. These pages represent the
96 * "view" component of an MVC architecture.</li>
97 *
98 * <li>Forms and hyperlinks in the user interface that require business logic
99 * to be executed will be submitted to a request URI that is mapped to this
100 * servlet.</li>
101 *
102 * <li>There can be <b>one</b> instance of this servlet class, which receives
103 * and processes all requests that change the state of a user's interaction
104 * with the application. The servlet delegates the handling of a request to a
105 * {@link RequestProcessor} object. This component represents the "controller"
106 * component of an MVC architecture. </li>
107 *
108 * <li>The <code>RequestProcessor</code> selects and invokes an {@link Action}
109 * class to perform the requested business logic, or delegates the response to
110 * another resource.</li>
111 *
112 * <li>The <code>Action</code> classes can manipulate the state of the
113 * application's interaction with the user, typically by creating or modifying
114 * JavaBeans that are stored as request or session attributes (depending on
115 * how long they need to be available). Such JavaBeans represent the "model"
116 * component of an MVC architecture.</li>
117 *
118 * <li>Instead of producing the next page of the user interface directly,
119 * <code>Action</code> classes generally return an {@link ActionForward} to
120 * indicate which resource should handle the response. If the
121 * <code>Action</code> does not return null, the <code>RequestProcessor</code>
122 * forwards or redirects to the specified resource (by utilizing
123 * <code>RequestDispatcher.forward</code> or <code>Response.sendRedirect</code>)
124 * so as to produce the next page of the user interface.</li>
125 *
126 * </ul>
127 *
128 * <p>The standard version of <code>RequestsProcessor</code> implements the
129 * following logic for each incoming HTTP request. You can override some or
130 * all of this functionality by subclassing this object and implementing your
131 * own version of the processing.</p>
132 *
133 * <ul>
134 *
135 * <li>Identify, from the incoming request URI, the substring that will be
136 * used to select an action procedure.</li>
137 *
138 * <li>Use this substring to map to the Java class name of the corresponding
139 * action class (an implementation of the <code>Action</code> interface).
140 * </li>
141 *
142 * <li>If this is the first request for a particular <code>Action</code>
143 * class, instantiate an instance of that class and cache it for future
144 * use.</li>
145 *
146 * <li>Optionally populate the properties of an <code>ActionForm</code> bean
147 * associated with this mapping.</li>
148 *
149 * <li>Call the <code>execute</code> method of this <code>Action</code> class,
150 * passing on a reference to the mapping that was used, the relevant form-bean
151 * (if any), and the request and the response that were passed to the
152 * controller by the servlet container (thereby providing access to any
153 * specialized properties of the mapping itself as well as to the
154 * ServletContext). </li>
155 *
156 * </ul>
157 *
158 * <p>The standard version of <code>ActionServlet</code> is configured based
159 * on the following servlet initialization parameters, which you will specify
160 * in the web application deployment descriptor (<code>/WEB-INF/web.xml</code>)
161 * for your application. Subclasses that specialize this servlet are free to
162 * define additional initialization parameters. </p>
163 *
164 * <ul>
165 *
166 * <li><strong>config</strong> - Comma-separated list of context-relative
167 * path(s) to the XML resource(s) containing the configuration information for
168 * the default module. (Multiple files support since Struts 1.1)
169 * [/WEB-INF/struts-config.xml].</li>
170 *
171 * <li><strong>config/${module}</strong> - Comma-separated list of
172 * Context-relative path(s) to the XML resource(s) containing the
173 * configuration information for the module that will use the specified prefix
174 * (/${module}). This can be repeated as many times as required for multiple
175 * modules. (Since Struts 1.1)</li>
176 *
177 * <li><strong>configFactory</strong> - The Java class name of the
178 * <code>ModuleConfigFactory</code> used to create the implementation of the
179 * ModuleConfig interface. </li>
180 *
181 * <li><strong>convertNull</strong> - Force simulation of the Struts 1.0
182 * behavior when populating forms. If set to true, the numeric Java wrapper
183 * class types (like <code>java.lang.Integer</code>) will default to null
184 * (rather than 0). (Since Struts 1.1) [false] </li>
185 *
186 * <li><strong>rulesets </strong> - Comma-delimited list of fully qualified
187 * classnames of additional <code>org.apache.commons.digester.RuleSet</code>
188 * instances that should be added to the <code>Digester</code> that will be
189 * processing <code>struts-config.xml</code> files. By default, only the
190 * <code>RuleSet</code> for the standard configuration elements is loaded.
191 * (Since Struts 1.1)</li>
192 *
193 * <li><strong>validating</strong> - Should we use a validating XML parser to
194 * process the configuration file (strongly recommended)? [true]</li>
195 *
196 * <li><strong>chainConfig</strong> - Comma-separated list of either
197 * context-relative or classloader path(s) to load commons-chain catalog
198 * definitions from. If none specified, the default Struts catalog that is
199 * provided with Struts will be used.</li>
200 *
201 * </ul>
202 *
203 * @version $Rev: 483039 $ $Date: 2005-10-14 19:54:16 -0400 (Fri, 14 Oct 2005)
204 * $
205 */
206 public class ActionServlet extends HttpServlet {
207 /**
208 * <p>Commons Logging instance.</p>
209 *
210 * @since Struts 1.1
211 */
212 protected static Log log = LogFactory.getLog(ActionServlet.class);
213
214
215
216 /**
217 * <p>Comma-separated list of context-relative path(s) to our
218 * configuration resource(s) for the default module.</p>
219 */
220 protected String config = "/WEB-INF/struts-config.xml";
221
222 /**
223 * <p>Comma-separated list of context or classloader-relative path(s) that
224 * contain the configuration for the default commons-chain
225 * catalog(s).</p>
226 */
227 protected String chainConfig = "org/apache/struts/chain/chain-config.xml";
228
229 /**
230 * <p>The Digester used to produce ModuleConfig objects from a Struts
231 * configuration file.</p>
232 *
233 * @since Struts 1.1
234 */
235 protected Digester configDigester = null;
236
237 /**
238 * <p>The flag to request backwards-compatible conversions for form bean
239 * properties of the Java wrapper class types.</p>
240 *
241 * @since Struts 1.1
242 */
243 protected boolean convertNull = false;
244
245 /**
246 * <p>The resources object for our internal resources.</p>
247 */
248 protected MessageResources internal = null;
249
250 /**
251 * <p>The Java base name of our internal resources.</p>
252 *
253 * @since Struts 1.1
254 */
255 protected String internalName = "org.apache.struts.action.ActionResources";
256
257 /**
258 * <p>The set of public identifiers, and corresponding resource names, for
259 * the versions of the configuration file DTDs that we know about. There
260 * <strong>MUST</strong> be an even number of Strings in this list!</p>
261 */
262 protected String[] registrations =
263 {
264 "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",
265 "/org/apache/struts/resources/struts-config_1_0.dtd",
266 "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN",
267 "/org/apache/struts/resources/struts-config_1_1.dtd",
268 "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",
269 "/org/apache/struts/resources/struts-config_1_2.dtd",
270 "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",
271 "/org/apache/struts/resources/struts-config_1_3.dtd",
272 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
273 "/org/apache/struts/resources/web-app_2_3.dtd"
274 };
275
276 /**
277 * <p>The URL pattern to which we are mapped in our web application
278 * deployment descriptor.</p>
279 */
280 protected String servletMapping = null;
281
282 /**
283 * <p>The servlet name under which we are registered in our web
284 * application deployment descriptor.</p>
285 */
286 protected String servletName = null;
287
288
289
290 /**
291 * <p>Gracefully shut down this controller servlet, releasing any
292 * resources that were allocated at initialization.</p>
293 */
294 public void destroy() {
295 if (log.isDebugEnabled()) {
296 log.debug(internal.getMessage("finalizing"));
297 }
298
299 destroyModules();
300 destroyInternal();
301 getServletContext().removeAttribute(Globals.ACTION_SERVLET_KEY);
302
303
304 ClassLoader classLoader =
305 Thread.currentThread().getContextClassLoader();
306
307 if (classLoader == null) {
308 classLoader = ActionServlet.class.getClassLoader();
309 }
310
311 try {
312 LogFactory.release(classLoader);
313 } catch (Throwable t) {
314 ;
315
316
317
318
319
320
321
322
323
324
325
326 }
327
328 CatalogFactory.clear();
329 PropertyUtils.clearDescriptors();
330 }
331
332 /**
333 * <p>Initialize this servlet. Most of the processing has been factored
334 * into support methods so that you can override particular functionality
335 * at a fairly granular level.</p>
336 *
337 * @throws ServletException if we cannot configure ourselves correctly
338 */
339 public void init() throws ServletException {
340 final String configPrefix = "config/";
341 final int configPrefixLength = configPrefix.length() - 1;
342
343
344
345
346 try {
347 initInternal();
348 initOther();
349 initServlet();
350 initChain();
351
352 getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
353 initModuleConfigFactory();
354
355
356 ModuleConfig moduleConfig = initModuleConfig("", config);
357
358 initModuleMessageResources(moduleConfig);
359 initModulePlugIns(moduleConfig);
360 initModuleFormBeans(moduleConfig);
361 initModuleForwards(moduleConfig);
362 initModuleExceptionConfigs(moduleConfig);
363 initModuleActions(moduleConfig);
364 moduleConfig.freeze();
365
366 Enumeration names = getServletConfig().getInitParameterNames();
367
368 while (names.hasMoreElements()) {
369 String name = (String) names.nextElement();
370
371 if (!name.startsWith(configPrefix)) {
372 continue;
373 }
374
375 String prefix = name.substring(configPrefixLength);
376
377 moduleConfig =
378 initModuleConfig(prefix,
379 getServletConfig().getInitParameter(name));
380 initModuleMessageResources(moduleConfig);
381 initModulePlugIns(moduleConfig);
382 initModuleFormBeans(moduleConfig);
383 initModuleForwards(moduleConfig);
384 initModuleExceptionConfigs(moduleConfig);
385 initModuleActions(moduleConfig);
386 moduleConfig.freeze();
387 }
388
389 this.initModulePrefixes(this.getServletContext());
390
391 this.destroyConfigDigester();
392 } catch (UnavailableException ex) {
393 throw ex;
394 } catch (Throwable t) {
395
396
397
398 log.error("Unable to initialize Struts ActionServlet due to an "
399 + "unexpected exception or error thrown, so marking the "
400 + "servlet as unavailable. Most likely, this is due to an "
401 + "incorrect or missing library dependency.", t);
402 throw new UnavailableException(t.getMessage());
403 }
404 }
405
406 /**
407 * <p>Saves a String[] of module prefixes in the ServletContext under
408 * Globals.MODULE_PREFIXES_KEY. <strong>NOTE</strong> - the "" prefix for
409 * the default module is not included in this list.</p>
410 *
411 * @param context The servlet context.
412 * @since Struts 1.2
413 */
414 protected void initModulePrefixes(ServletContext context) {
415 ArrayList prefixList = new ArrayList();
416
417 Enumeration names = context.getAttributeNames();
418
419 while (names.hasMoreElements()) {
420 String name = (String) names.nextElement();
421
422 if (!name.startsWith(Globals.MODULE_KEY)) {
423 continue;
424 }
425
426 String prefix = name.substring(Globals.MODULE_KEY.length());
427
428 if (prefix.length() > 0) {
429 prefixList.add(prefix);
430 }
431 }
432
433 String[] prefixes =
434 (String[]) prefixList.toArray(new String[prefixList.size()]);
435
436 context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes);
437 }
438
439 /**
440 * <p>Process an HTTP "GET" request.</p>
441 *
442 * @param request The servlet request we are processing
443 * @param response The servlet response we are creating
444 * @throws IOException if an input/output error occurs
445 * @throws ServletException if a servlet exception occurs
446 */
447 public void doGet(HttpServletRequest request, HttpServletResponse response)
448 throws IOException, ServletException {
449 process(request, response);
450 }
451
452 /**
453 * <p>Process an HTTP "POST" request.</p>
454 *
455 * @param request The servlet request we are processing
456 * @param response The servlet response we are creating
457 * @throws IOException if an input/output error occurs
458 * @throws ServletException if a servlet exception occurs
459 */
460 public void doPost(HttpServletRequest request, HttpServletResponse response)
461 throws IOException, ServletException {
462 process(request, response);
463 }
464
465
466
467 /**
468 * <p>Remember a servlet mapping from our web application deployment
469 * descriptor, if it is for this servlet.</p>
470 *
471 * @param servletName The name of the servlet being mapped
472 * @param urlPattern The URL pattern to which this servlet is mapped
473 */
474 public void addServletMapping(String servletName, String urlPattern) {
475 if (servletName == null) {
476 return;
477 }
478
479 if (servletName.equals(this.servletName)) {
480 if (log.isDebugEnabled()) {
481 log.debug("Process servletName=" + servletName
482 + ", urlPattern=" + urlPattern);
483 }
484
485 this.servletMapping = urlPattern;
486 }
487 }
488
489 /**
490 * <p>Return the <code>MessageResources</code> instance containing our
491 * internal message strings.</p>
492 *
493 * @return the <code>MessageResources</code> instance containing our
494 * internal message strings.
495 * @since Struts 1.1
496 */
497 public MessageResources getInternal() {
498 return (this.internal);
499 }
500
501
502
503 /**
504 * <p>Gracefully terminate use of any modules associated with this
505 * application (if any).</p>
506 *
507 * @since Struts 1.1
508 */
509 protected void destroyModules() {
510 ArrayList values = new ArrayList();
511 Enumeration names = getServletContext().getAttributeNames();
512
513 while (names.hasMoreElements()) {
514 values.add(names.nextElement());
515 }
516
517 Iterator keys = values.iterator();
518
519 while (keys.hasNext()) {
520 String name = (String) keys.next();
521 Object value = getServletContext().getAttribute(name);
522
523 if (!(value instanceof ModuleConfig)) {
524 continue;
525 }
526
527 ModuleConfig config = (ModuleConfig) value;
528
529 if (this.getProcessorForModule(config) != null) {
530 this.getProcessorForModule(config).destroy();
531 }
532
533 getServletContext().removeAttribute(name);
534
535 PlugIn[] plugIns =
536 (PlugIn[]) getServletContext().getAttribute(Globals.PLUG_INS_KEY
537 + config.getPrefix());
538
539 if (plugIns != null) {
540 for (int i = 0; i < plugIns.length; i++) {
541 int j = plugIns.length - (i + 1);
542
543 plugIns[j].destroy();
544 }
545
546 getServletContext().removeAttribute(Globals.PLUG_INS_KEY
547 + config.getPrefix());
548 }
549 }
550 }
551
552 /**
553 * <p>Gracefully release any configDigester instance that we have created.
554 * </p>
555 *
556 * @since Struts 1.1
557 */
558 protected void destroyConfigDigester() {
559 configDigester = null;
560 }
561
562 /**
563 * <p>Gracefully terminate use of the internal MessageResources.</p>
564 */
565 protected void destroyInternal() {
566 internal = null;
567 }
568
569 /**
570 * <p>Return the module configuration object for the currently selected
571 * module.</p>
572 *
573 * @param request The servlet request we are processing
574 * @return The module configuration object for the currently selected
575 * module.
576 * @since Struts 1.1
577 */
578 protected ModuleConfig getModuleConfig(HttpServletRequest request) {
579 ModuleConfig config =
580 (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);
581
582 if (config == null) {
583 config =
584 (ModuleConfig) getServletContext().getAttribute(Globals.MODULE_KEY);
585 }
586
587 return (config);
588 }
589
590 /**
591 * <p>Look up and return the {@link RequestProcessor} responsible for the
592 * specified module, creating a new one if necessary.</p>
593 *
594 * @param config The module configuration for which to acquire and return
595 * a RequestProcessor.
596 * @return The {@link RequestProcessor} responsible for the specified
597 * module,
598 * @throws ServletException If we cannot instantiate a RequestProcessor
599 * instance a {@link UnavailableException} is
600 * thrown, meaning your application is not loaded
601 * and will not be available.
602 * @since Struts 1.1
603 */
604 protected synchronized RequestProcessor getRequestProcessor(
605 ModuleConfig config) throws ServletException {
606 RequestProcessor processor = this.getProcessorForModule(config);
607
608 if (processor == null) {
609 try {
610 processor =
611 (RequestProcessor) RequestUtils.applicationInstance(config.getControllerConfig()
612 .getProcessorClass());
613 } catch (Exception e) {
614 throw new UnavailableException(
615 "Cannot initialize RequestProcessor of class "
616 + config.getControllerConfig().getProcessorClass() + ": "
617 + e);
618 }
619
620 processor.init(this, config);
621
622 String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix();
623
624 getServletContext().setAttribute(key, processor);
625 }
626
627 return (processor);
628 }
629
630 /**
631 * <p>Returns the RequestProcessor for the given module or null if one
632 * does not exist. This method will not create a RequestProcessor.</p>
633 *
634 * @param config The ModuleConfig.
635 * @return The <code>RequestProcessor</code> for the given module, or
636 * <code>null</code> if one does not exist.
637 */
638 private RequestProcessor getProcessorForModule(ModuleConfig config) {
639 String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix();
640
641 return (RequestProcessor) getServletContext().getAttribute(key);
642 }
643
644 /**
645 * <p>Initialize the factory used to create the module configuration.</p>
646 *
647 * @since Struts 1.2
648 */
649 protected void initModuleConfigFactory() {
650 String configFactory =
651 getServletConfig().getInitParameter("configFactory");
652
653 if (configFactory != null) {
654 ModuleConfigFactory.setFactoryClass(configFactory);
655 }
656 }
657
658 /**
659 * <p>Initialize the module configuration information for the specified
660 * module.</p>
661 *
662 * @param prefix Module prefix for this module
663 * @param paths Comma-separated list of context-relative resource path(s)
664 * for this modules's configuration resource(s)
665 * @return The new module configuration instance.
666 * @throws ServletException if initialization cannot be performed
667 * @since Struts 1.1
668 */
669 protected ModuleConfig initModuleConfig(String prefix, String paths)
670 throws ServletException {
671 if (log.isDebugEnabled()) {
672 log.debug("Initializing module path '" + prefix
673 + "' configuration from '" + paths + "'");
674 }
675
676
677 ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
678 ModuleConfig config = factoryObject.createModuleConfig(prefix);
679
680
681 Digester digester = initConfigDigester();
682
683 List urls = splitAndResolvePaths(paths);
684 URL url;
685
686 for (Iterator i = urls.iterator(); i.hasNext();) {
687 url = (URL) i.next();
688 digester.push(config);
689 this.parseModuleConfigFile(digester, url);
690 }
691
692 getServletContext().setAttribute(Globals.MODULE_KEY
693 + config.getPrefix(), config);
694
695 return config;
696 }
697
698 /**
699 * <p>Parses one module config file.</p>
700 *
701 * @param digester Digester instance that does the parsing
702 * @param path The path to the config file to parse.
703 * @throws UnavailableException if file cannot be read or parsed
704 * @since Struts 1.2
705 * @deprecated use parseModuleConfigFile(Digester digester, URL url)
706 * instead
707 */
708 protected void parseModuleConfigFile(Digester digester, String path)
709 throws UnavailableException {
710 try {
711 List paths = splitAndResolvePaths(path);
712
713 if (paths.size() > 0) {
714
715 URL url = (URL) paths.get(0);
716
717 parseModuleConfigFile(digester, url);
718 } else {
719 throw new UnavailableException("Cannot locate path " + path);
720 }
721 } catch (UnavailableException ex) {
722 throw ex;
723 } catch (ServletException ex) {
724 handleConfigException(path, ex);
725 }
726 }
727
728 /**
729 * <p>Parses one module config file.</p>
730 *
731 * @param digester Digester instance that does the parsing
732 * @param url The url to the config file to parse.
733 * @throws UnavailableException if file cannot be read or parsed
734 * @since Struts 1.3
735 */
736 protected void parseModuleConfigFile(Digester digester, URL url)
737 throws UnavailableException {
738
739 try {
740 digester.parse(url);
741 } catch (IOException e) {
742 handleConfigException(url.toString(), e);
743 } catch (SAXException e) {
744 handleConfigException(url.toString(), e);
745 }
746 }
747
748 /**
749 * <p>Simplifies exception handling in the parseModuleConfigFile
750 * method.<p>
751 *
752 * @param path The path to which the exception relates.
753 * @param e The exception to be wrapped and thrown.
754 * @throws UnavailableException as a wrapper around Exception
755 */
756 private void handleConfigException(String path, Exception e)
757 throws UnavailableException {
758 String msg = internal.getMessage("configParse", path);
759
760 log.error(msg, e);
761 throw new UnavailableException(msg);
762 }
763
764 /**
765 * <p>Handle errors related to creating an instance of the specified
766 * class.</p>
767 *
768 * @param className The className that could not be instantiated.
769 * @param e The exception that was caught.
770 * @throws ServletException to communicate the error.
771 */
772 private void handleCreationException(String className, Exception e)
773 throws ServletException {
774 String errorMessage =
775 internal.getMessage("configExtends.creation", className);
776
777 log.error(errorMessage, e);
778 throw new UnavailableException(errorMessage);
779 }
780
781 /**
782 * <p>General handling for exceptions caught while inheriting config
783 * information.</p>
784 *
785 * @param configType The type of configuration object of configName.
786 * @param configName The name of the config that could not be extended.
787 * @param e The exception that was caught.
788 * @throws ServletException to communicate the error.
789 */
790 private void handleGeneralExtensionException(String configType,
791 String configName, Exception e)
792 throws ServletException {
793 String errorMessage =
794 internal.getMessage("configExtends", configType, configName);
795
796 log.error(errorMessage, e);
797 throw new UnavailableException(errorMessage);
798 }
799
800 /**
801 * <p>Handle errors caused by required fields that were not
802 * specified.</p>
803 *
804 * @param field The name of the required field that was not found.
805 * @param configType The type of configuration object of configName.
806 * @param configName The name of the config that's missing the required
807 * value.
808 * @throws ServletException to communicate the error.
809 */
810 private void handleValueRequiredException(String field, String configType,
811 String configName) throws ServletException {
812 String errorMessage =
813 internal.getMessage("configFieldRequired", field, configType,
814 configName);
815
816 log.error(errorMessage);
817 throw new UnavailableException(errorMessage);
818 }
819
820 /**
821 * <p>Initialize the plug ins for the specified module.</p>
822 *
823 * @param config ModuleConfig information for this module
824 * @throws ServletException if initialization cannot be performed
825 * @since Struts 1.1
826 */
827 protected void initModulePlugIns(ModuleConfig config)
828 throws ServletException {
829 if (log.isDebugEnabled()) {
830 log.debug("Initializing module path '" + config.getPrefix()
831 + "' plug ins");
832 }
833
834 PlugInConfig[] plugInConfigs = config.findPlugInConfigs();
835 PlugIn[] plugIns = new PlugIn[plugInConfigs.length];
836
837 getServletContext().setAttribute(Globals.PLUG_INS_KEY
838 + config.getPrefix(), plugIns);
839
840 for (int i = 0; i < plugIns.length; i++) {
841 try {
842 plugIns[i] =
843 (PlugIn) RequestUtils.applicationInstance(plugInConfigs[i]
844 .getClassName());
845 BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties());
846
847
848
849
850 try {
851 PropertyUtils.setProperty(plugIns[i],
852 "currentPlugInConfigObject", plugInConfigs[i]);
853 } catch (Exception e) {
854 ;
855
856
857
858
859
860 /**
861 * Between version 1.138-1.140 cedric made these changes.
862 * The exceptions are caught to deal with containers
863 * applying strict security. This was in response to bug
864 * #15736
865 *
866 * Recommend that we make the currentPlugInConfigObject part
867 * of the PlugIn Interface if we can, Rob
868 */
869 }
870
871 plugIns[i].init(this, config);
872 } catch (ServletException e) {
873 throw e;
874 } catch (Exception e) {
875 String errMsg =
876 internal.getMessage("plugIn.init",
877 plugInConfigs[i].getClassName());
878
879 log(errMsg, e);
880 throw new UnavailableException(errMsg);
881 }
882 }
883 }
884
885 /**
886 * <p>Initialize the form beans for the specified module.</p>
887 *
888 * @param config ModuleConfig information for this module
889 * @throws ServletException if initialization cannot be performed
890 * @since Struts 1.3
891 */
892 protected void initModuleFormBeans(ModuleConfig config)
893 throws ServletException {
894 if (log.isDebugEnabled()) {
895 log.debug("Initializing module path '" + config.getPrefix()
896 + "' form beans");
897 }
898
899
900 FormBeanConfig[] formBeans = config.findFormBeanConfigs();
901
902 for (int i = 0; i < formBeans.length; i++) {
903 FormBeanConfig beanConfig = formBeans[i];
904
905 processFormBeanExtension(beanConfig, config);
906 }
907
908 for (int i = 0; i < formBeans.length; i++) {
909 FormBeanConfig formBean = formBeans[i];
910
911
912 if (formBean.getType() == null) {
913 handleValueRequiredException("type", formBean.getName(),
914 "form bean");
915 }
916
917
918 FormPropertyConfig[] fpcs = formBean.findFormPropertyConfigs();
919
920 for (int j = 0; j < fpcs.length; j++) {
921 FormPropertyConfig property = fpcs[j];
922
923 if (property.getType() == null) {
924 handleValueRequiredException("type", property.getName(),
925 "form property");
926 }
927 }
928
929
930
931 if (formBean.getDynamic()) {
932 formBean.getDynaActionFormClass();
933 }
934 }
935 }
936
937 /**
938 * <p>Extend the form bean's configuration as necessary.</p>
939 *
940 * @param beanConfig the configuration to process.
941 * @param moduleConfig the module configuration for this module.
942 * @throws ServletException if initialization cannot be performed.
943 */
944 protected void processFormBeanExtension(FormBeanConfig beanConfig,
945 ModuleConfig moduleConfig)
946 throws ServletException {
947 try {
948 if (!beanConfig.isExtensionProcessed()) {
949 if (log.isDebugEnabled()) {
950 log.debug("Processing extensions for '"
951 + beanConfig.getName() + "'");
952 }
953
954 beanConfig =
955 processFormBeanConfigClass(beanConfig, moduleConfig);
956
957 beanConfig.processExtends(moduleConfig);
958 }
959 } catch (ServletException e) {
960 throw e;
961 } catch (Exception e) {
962 handleGeneralExtensionException("FormBeanConfig",
963 beanConfig.getName(), e);
964 }
965 }
966
967 /**
968 * <p>Checks if the current beanConfig is using the correct class based on
969 * the class of its ancestor form bean config.</p>
970 *
971 * @param beanConfig The form bean to check.
972 * @param moduleConfig The config for the current module.
973 * @return The form bean config using the correct class as determined by
974 * the config's ancestor and its own overridden value.
975 * @throws UnavailableException if an instance of the form bean config
976 * class cannot be created.
977 * @throws ServletException on class creation error
978 */
979 protected FormBeanConfig processFormBeanConfigClass(
980 FormBeanConfig beanConfig, ModuleConfig moduleConfig)
981 throws ServletException {
982 String ancestor = beanConfig.getExtends();
983
984 if (ancestor == null) {
985
986 return beanConfig;
987 }
988
989
990 FormBeanConfig baseConfig = moduleConfig.findFormBeanConfig(ancestor);
991
992 if (baseConfig == null) {
993 throw new UnavailableException("Unable to find " + "form bean '"
994 + ancestor + "' to extend.");
995 }
996
997
998 if (beanConfig.getClass().equals(FormBeanConfig.class)) {
999
1000 if (!baseConfig.getClass().equals(beanConfig.getClass())) {
1001
1002 FormBeanConfig newBeanConfig = null;
1003 String baseConfigClassName = baseConfig.getClass().getName();
1004
1005 try {
1006 newBeanConfig =
1007 (FormBeanConfig) RequestUtils.applicationInstance(baseConfigClassName);
1008
1009
1010 BeanUtils.copyProperties(newBeanConfig, beanConfig);
1011
1012 FormPropertyConfig[] fpc =
1013 beanConfig.findFormPropertyConfigs();
1014
1015 for (int i = 0; i < fpc.length; i++) {
1016 newBeanConfig.addFormPropertyConfig(fpc[i]);
1017 }
1018 } catch (Exception e) {
1019 handleCreationException(baseConfigClassName, e);
1020 }
1021
1022
1023 moduleConfig.removeFormBeanConfig(beanConfig);
1024 moduleConfig.addFormBeanConfig(newBeanConfig);
1025 beanConfig = newBeanConfig;
1026 }
1027 }
1028
1029 return beanConfig;
1030 }
1031
1032 /**
1033 * <p>Initialize the forwards for the specified module.</p>
1034 *
1035 * @param config ModuleConfig information for this module
1036 * @throws ServletException if initialization cannot be performed
1037 */
1038 protected void initModuleForwards(ModuleConfig config)
1039 throws ServletException {
1040 if (log.isDebugEnabled()) {
1041 log.debug("Initializing module path '" + config.getPrefix()
1042 + "' forwards");
1043 }
1044
1045
1046 ForwardConfig[] forwards = config.findForwardConfigs();
1047
1048 for (int i = 0; i < forwards.length; i++) {
1049 ForwardConfig forward = forwards[i];
1050
1051 processForwardExtension(forward, config, null);
1052 }
1053
1054 for (int i = 0; i < forwards.length; i++) {
1055 ForwardConfig forward = forwards[i];
1056
1057
1058 if (forward.getPath() == null) {
1059 handleValueRequiredException("path", forward.getName(),
1060 "global forward");
1061 }
1062 }
1063 }
1064
1065 /**
1066 * <p>Extend the forward's configuration as necessary. If actionConfig is
1067 * provided, then this method will process the forwardConfig as part
1068 * of that actionConfig. If actionConfig is null, the forwardConfig
1069 * will be processed as a global forward.</p>
1070 *
1071 * @param forwardConfig the configuration to process.
1072 * @param moduleConfig the module configuration for this module.
1073 * @param actionConfig If applicable, the config for the current action.
1074 * @throws ServletException if initialization cannot be performed.
1075 */
1076 protected void processForwardExtension(ForwardConfig forwardConfig,
1077 ModuleConfig moduleConfig, ActionConfig actionConfig)
1078 throws ServletException {
1079 try {
1080 if (!forwardConfig.isExtensionProcessed()) {
1081 if (log.isDebugEnabled()) {
1082 log.debug("Processing extensions for '"
1083 + forwardConfig.getName() + "'");
1084 }
1085
1086 forwardConfig =
1087 processForwardConfigClass(forwardConfig, moduleConfig,
1088 actionConfig);
1089
1090 forwardConfig.processExtends(moduleConfig, actionConfig);
1091 }
1092 } catch (ServletException e) {
1093 throw e;
1094 } catch (Exception e) {
1095 handleGeneralExtensionException("Forward", forwardConfig.getName(),
1096 e);
1097 }
1098 }
1099
1100 /**
1101 * <p>Checks if the current forwardConfig is using the correct class based
1102 * on the class of its configuration ancestor. If actionConfig is
1103 * provided, then this method will process the forwardConfig as part
1104 * of that actionConfig. If actionConfig is null, the forwardConfig
1105 * will be processed as a global forward.</p>
1106 *
1107 * @param forwardConfig The forward to check.
1108 * @param moduleConfig The config for the current module.
1109 * @param actionConfig If applicable, the config for the current action.
1110 * @return The forward config using the correct class as determined by the
1111 * config's ancestor and its own overridden value.
1112 * @throws UnavailableException if an instance of the forward config class
1113 * cannot be created.
1114 * @throws ServletException on class creation error
1115 */
1116 protected ForwardConfig processForwardConfigClass(
1117 ForwardConfig forwardConfig, ModuleConfig moduleConfig,
1118 ActionConfig actionConfig)
1119 throws ServletException {
1120 String ancestor = forwardConfig.getExtends();
1121
1122 if (ancestor == null) {
1123
1124 return forwardConfig;
1125 }
1126
1127
1128 ForwardConfig baseConfig = null;
1129 if (actionConfig != null) {
1130
1131 baseConfig = actionConfig.findForwardConfig(ancestor);
1132 }
1133
1134 if (baseConfig == null) {
1135
1136
1137 baseConfig = moduleConfig.findForwardConfig(ancestor);
1138 }
1139
1140 if (baseConfig == null) {
1141 throw new UnavailableException("Unable to find " + "forward '"
1142 + ancestor + "' to extend.");
1143 }
1144
1145
1146 if (forwardConfig.getClass().equals(ActionForward.class)) {
1147
1148 if (!baseConfig.getClass().equals(forwardConfig.getClass())) {
1149
1150 ForwardConfig newForwardConfig = null;
1151 String baseConfigClassName = baseConfig.getClass().getName();
1152
1153 try {
1154 newForwardConfig =
1155 (ForwardConfig) RequestUtils.applicationInstance(
1156 baseConfigClassName);
1157
1158
1159 BeanUtils.copyProperties(newForwardConfig, forwardConfig);
1160 } catch (Exception e) {
1161 handleCreationException(baseConfigClassName, e);
1162 }
1163
1164
1165 if (actionConfig != null) {
1166 actionConfig.removeForwardConfig(forwardConfig);
1167 actionConfig.addForwardConfig(newForwardConfig);
1168 } else {
1169
1170 moduleConfig.removeForwardConfig(forwardConfig);
1171 moduleConfig.addForwardConfig(newForwardConfig);
1172 }
1173 forwardConfig = newForwardConfig;
1174 }
1175 }
1176
1177 return forwardConfig;
1178 }
1179
1180 /**
1181 * <p>Initialize the exception handlers for the specified module.</p>
1182 *
1183 * @param config ModuleConfig information for this module
1184 * @throws ServletException if initialization cannot be performed
1185 * @since Struts 1.3
1186 */
1187 protected void initModuleExceptionConfigs(ModuleConfig config)
1188 throws ServletException {
1189 if (log.isDebugEnabled()) {
1190 log.debug("Initializing module path '" + config.getPrefix()
1191 + "' forwards");
1192 }
1193
1194
1195 ExceptionConfig[] exceptions = config.findExceptionConfigs();
1196
1197 for (int i = 0; i < exceptions.length; i++) {
1198 ExceptionConfig exception = exceptions[i];
1199
1200 processExceptionExtension(exception, config, null);
1201 }
1202
1203 for (int i = 0; i < exceptions.length; i++) {
1204 ExceptionConfig exception = exceptions[i];
1205
1206
1207 if (exception.getKey() == null) {
1208 handleValueRequiredException("key", exception.getType(),
1209 "global exception config");
1210 }
1211 }
1212 }
1213
1214 /**
1215 * <p>Extend the exception's configuration as necessary. If actionConfig is
1216 * provided, then this method will process the exceptionConfig as part
1217 * of that actionConfig. If actionConfig is null, the exceptionConfig
1218 * will be processed as a global forward.</p>
1219 *
1220 * @param exceptionConfig the configuration to process.
1221 * @param moduleConfig the module configuration for this module.
1222 * @param actionConfig If applicable, the config for the current action.
1223 * @throws ServletException if initialization cannot be performed.
1224 */
1225 protected void processExceptionExtension(ExceptionConfig exceptionConfig,
1226 ModuleConfig moduleConfig, ActionConfig actionConfig)
1227 throws ServletException {
1228 try {
1229 if (!exceptionConfig.isExtensionProcessed()) {
1230 if (log.isDebugEnabled()) {
1231 log.debug("Processing extensions for '"
1232 + exceptionConfig.getType() + "'");
1233 }
1234
1235 exceptionConfig =
1236 processExceptionConfigClass(exceptionConfig, moduleConfig,
1237 actionConfig);
1238
1239 exceptionConfig.processExtends(moduleConfig, actionConfig);
1240 }
1241 } catch (ServletException e) {
1242 throw e;
1243 } catch (Exception e) {
1244 handleGeneralExtensionException("Exception",
1245 exceptionConfig.getType(), e);
1246 }
1247 }
1248
1249 /**
1250 * <p>Checks if the current exceptionConfig is using the correct class
1251 * based on the class of its configuration ancestor. If actionConfig is
1252 * provided, then this method will process the exceptionConfig as part
1253 * of that actionConfig. If actionConfig is null, the exceptionConfig
1254 * will be processed as a global forward.</p>
1255 *
1256 * @param exceptionConfig The config to check.
1257 * @param moduleConfig The config for the current module.
1258 * @param actionConfig If applicable, the config for the current action.
1259 * @return The exception config using the correct class as determined by
1260 * the config's ancestor and its own overridden value.
1261 * @throws ServletException if an instance of the exception config class
1262 * cannot be created.
1263 */
1264 protected ExceptionConfig processExceptionConfigClass(
1265 ExceptionConfig exceptionConfig, ModuleConfig moduleConfig,
1266 ActionConfig actionConfig)
1267 throws ServletException {
1268 String ancestor = exceptionConfig.getExtends();
1269
1270 if (ancestor == null) {
1271
1272 return exceptionConfig;
1273 }
1274
1275
1276 ExceptionConfig baseConfig = null;
1277 if (actionConfig != null) {
1278 baseConfig = actionConfig.findExceptionConfig(ancestor);
1279 }
1280
1281 if (baseConfig == null) {
1282
1283
1284 baseConfig = moduleConfig.findExceptionConfig(ancestor);
1285 }
1286
1287 if (baseConfig == null) {
1288 throw new UnavailableException("Unable to find "
1289 + "exception config '" + ancestor + "' to extend.");
1290 }
1291
1292
1293 if (exceptionConfig.getClass().equals(ExceptionConfig.class)) {
1294
1295 if (!baseConfig.getClass().equals(exceptionConfig.getClass())) {
1296
1297 ExceptionConfig newExceptionConfig = null;
1298 String baseConfigClassName = baseConfig.getClass().getName();
1299
1300 try {
1301 newExceptionConfig =
1302 (ExceptionConfig) RequestUtils.applicationInstance(
1303 baseConfigClassName);
1304
1305
1306 BeanUtils.copyProperties(newExceptionConfig,
1307 exceptionConfig);
1308 } catch (Exception e) {
1309 handleCreationException(baseConfigClassName, e);
1310 }
1311
1312
1313 if (actionConfig != null) {
1314 actionConfig.removeExceptionConfig(exceptionConfig);
1315 actionConfig.addExceptionConfig(newExceptionConfig);
1316 } else {
1317 moduleConfig.removeExceptionConfig(exceptionConfig);
1318 moduleConfig.addExceptionConfig(newExceptionConfig);
1319 }
1320 exceptionConfig = newExceptionConfig;
1321 }
1322 }
1323
1324 return exceptionConfig;
1325 }
1326
1327 /**
1328 * <p>Initialize the action configs for the specified module.</p>
1329 *
1330 * @param config ModuleConfig information for this module
1331 * @throws ServletException if initialization cannot be performed
1332 * @since Struts 1.3
1333 */
1334 protected void initModuleActions(ModuleConfig config)
1335 throws ServletException {
1336 if (log.isDebugEnabled()) {
1337 log.debug("Initializing module path '" + config.getPrefix()
1338 + "' action configs");
1339 }
1340
1341
1342 ActionConfig[] actionConfigs = config.findActionConfigs();
1343
1344 for (int i = 0; i < actionConfigs.length; i++) {
1345 ActionConfig actionConfig = actionConfigs[i];
1346
1347 processActionConfigExtension(actionConfig, config);
1348 }
1349
1350 for (int i = 0; i < actionConfigs.length; i++) {
1351 ActionConfig actionConfig = actionConfigs[i];
1352
1353
1354
1355 ForwardConfig[] forwards = actionConfig.findForwardConfigs();
1356
1357 for (int j = 0; j < forwards.length; j++) {
1358 ForwardConfig forward = forwards[j];
1359
1360 if (forward.getPath() == null) {
1361 handleValueRequiredException("path", forward.getName(),
1362 "action forward");
1363 }
1364 }
1365
1366
1367 ExceptionConfig[] exceptions = actionConfig.findExceptionConfigs();
1368
1369 for (int j = 0; j < exceptions.length; j++) {
1370 ExceptionConfig exception = exceptions[j];
1371
1372 if (exception.getKey() == null) {
1373 handleValueRequiredException("key", exception.getType(),
1374 "action exception config");
1375 }
1376 }
1377 }
1378 }
1379
1380 /**
1381 * <p>Extend the action's configuration as necessary.</p>
1382 *
1383 * @param actionConfig the configuration to process.
1384 * @param moduleConfig the module configuration for this module.
1385 * @throws ServletException if initialization cannot be performed.
1386 */
1387 protected void processActionConfigExtension(ActionConfig actionConfig,
1388 ModuleConfig moduleConfig)
1389 throws ServletException {
1390 try {
1391 if (!actionConfig.isExtensionProcessed()) {
1392 if (log.isDebugEnabled()) {
1393 log.debug("Processing extensions for '"
1394 + actionConfig.getPath() + "'");
1395 }
1396
1397 actionConfig =
1398 processActionConfigClass(actionConfig, moduleConfig);
1399
1400 actionConfig.processExtends(moduleConfig);
1401 }
1402
1403
1404 ForwardConfig[] forwards = actionConfig.findForwardConfigs();
1405 for (int i = 0; i < forwards.length; i++) {
1406 ForwardConfig forward = forwards[i];
1407 processForwardExtension(forward, moduleConfig, actionConfig);
1408 }
1409
1410
1411 ExceptionConfig[] exceptions = actionConfig.findExceptionConfigs();
1412 for (int i = 0; i < exceptions.length; i++) {
1413 ExceptionConfig exception = exceptions[i];
1414 processExceptionExtension(exception, moduleConfig,
1415 actionConfig);
1416 }
1417 } catch (ServletException e) {
1418 throw e;
1419 } catch (Exception e) {
1420 handleGeneralExtensionException("Action", actionConfig.getPath(), e);
1421 }
1422 }
1423
1424 /**
1425 * <p>Checks if the current actionConfig is using the correct class based
1426 * on the class of its ancestor ActionConfig.</p>
1427 *
1428 * @param actionConfig The action config to check.
1429 * @param moduleConfig The config for the current module.
1430 * @return The config object using the correct class as determined by the
1431 * config's ancestor and its own overridden value.
1432 * @throws ServletException if an instance of the action config class
1433 * cannot be created.
1434 */
1435 protected ActionConfig processActionConfigClass(ActionConfig actionConfig,
1436 ModuleConfig moduleConfig)
1437 throws ServletException {
1438 String ancestor = actionConfig.getExtends();
1439
1440 if (ancestor == null) {
1441
1442 return actionConfig;
1443 }
1444
1445
1446 ActionConfig baseConfig = moduleConfig.findActionConfig(ancestor);
1447
1448 if (baseConfig == null) {
1449 throw new UnavailableException("Unable to find "
1450 + "action config for '" + ancestor + "' to extend.");
1451 }
1452
1453
1454 if (actionConfig.getClass().equals(ActionMapping.class)) {
1455
1456 if (!baseConfig.getClass().equals(actionConfig.getClass())) {
1457
1458 ActionConfig newActionConfig = null;
1459 String baseConfigClassName = baseConfig.getClass().getName();
1460
1461 try {
1462 newActionConfig =
1463 (ActionConfig) RequestUtils.applicationInstance(baseConfigClassName);
1464
1465
1466 BeanUtils.copyProperties(newActionConfig, actionConfig);
1467
1468
1469 ForwardConfig[] forwards =
1470 actionConfig.findForwardConfigs();
1471
1472 for (int i = 0; i < forwards.length; i++) {
1473 newActionConfig.addForwardConfig(forwards[i]);
1474 }
1475
1476 ExceptionConfig[] exceptions =
1477 actionConfig.findExceptionConfigs();
1478
1479 for (int i = 0; i < exceptions.length; i++) {
1480 newActionConfig.addExceptionConfig(exceptions[i]);
1481 }
1482 } catch (Exception e) {
1483 handleCreationException(baseConfigClassName, e);
1484 }
1485
1486
1487 moduleConfig.removeActionConfig(actionConfig);
1488 moduleConfig.addActionConfig(newActionConfig);
1489 actionConfig = newActionConfig;
1490 }
1491 }
1492
1493 return actionConfig;
1494 }
1495
1496 /**
1497 * <p>Initialize the application <code>MessageResources</code> for the
1498 * specified module.</p>
1499 *
1500 * @param config ModuleConfig information for this module
1501 * @throws ServletException if initialization cannot be performed
1502 * @since Struts 1.1
1503 */
1504 protected void initModuleMessageResources(ModuleConfig config)
1505 throws ServletException {
1506 MessageResourcesConfig[] mrcs = config.findMessageResourcesConfigs();
1507
1508 for (int i = 0; i < mrcs.length; i++) {
1509 if ((mrcs[i].getFactory() == null)
1510 || (mrcs[i].getParameter() == null)) {
1511 continue;
1512 }
1513
1514 if (log.isDebugEnabled()) {
1515 log.debug("Initializing module path '" + config.getPrefix()
1516 + "' message resources from '" + mrcs[i].getParameter()
1517 + "'");
1518 }
1519
1520 String factory = mrcs[i].getFactory();
1521
1522 MessageResourcesFactory.setFactoryClass(factory);
1523
1524 MessageResourcesFactory factoryObject =
1525 MessageResourcesFactory.createFactory();
1526
1527 factoryObject.setConfig(mrcs[i]);
1528
1529 MessageResources resources =
1530 factoryObject.createResources(mrcs[i].getParameter());
1531
1532 resources.setReturnNull(mrcs[i].getNull());
1533 resources.setEscape(mrcs[i].isEscape());
1534 getServletContext().setAttribute(mrcs[i].getKey()
1535 + config.getPrefix(), resources);
1536 }
1537 }
1538
1539 /**
1540 * <p>Create (if needed) and return a new <code>Digester</code> instance
1541 * that has been initialized to process Struts module configuration files
1542 * and configure a corresponding <code>ModuleConfig</code> object (which
1543 * must be pushed on to the evaluation stack before parsing begins).</p>
1544 *
1545 * @return A new configured <code>Digester</code> instance.
1546 * @throws ServletException if a Digester cannot be configured
1547 * @since Struts 1.1
1548 */
1549 protected Digester initConfigDigester()
1550 throws ServletException {
1551
1552
1553 if (configDigester != null) {
1554 return (configDigester);
1555 }
1556
1557
1558 configDigester = new Digester();
1559 configDigester.setNamespaceAware(true);
1560 configDigester.setValidating(this.isValidating());
1561 configDigester.setUseContextClassLoader(true);
1562 configDigester.addRuleSet(new ConfigRuleSet());
1563
1564 for (int i = 0; i < registrations.length; i += 2) {
1565 URL url = this.getClass().getResource(registrations[i + 1]);
1566
1567 if (url != null) {
1568 configDigester.register(registrations[i], url.toString());
1569 }
1570 }
1571
1572 this.addRuleSets();
1573
1574
1575 return (configDigester);
1576 }
1577
1578 /**
1579 * <p>Add any custom RuleSet instances to configDigester that have been
1580 * specified in the <code>rulesets</code> init parameter.</p>
1581 *
1582 * @throws ServletException if an error occurs
1583 */
1584 private void addRuleSets()
1585 throws ServletException {
1586 String rulesets = getServletConfig().getInitParameter("rulesets");
1587
1588 if (rulesets == null) {
1589 rulesets = "";
1590 }
1591
1592 rulesets = rulesets.trim();
1593
1594 String ruleset;
1595
1596 while (rulesets.length() > 0) {
1597 int comma = rulesets.indexOf(",");
1598
1599 if (comma < 0) {
1600 ruleset = rulesets.trim();
1601 rulesets = "";
1602 } else {
1603 ruleset = rulesets.substring(0, comma).trim();
1604 rulesets = rulesets.substring(comma + 1).trim();
1605 }
1606
1607 if (log.isDebugEnabled()) {
1608 log.debug("Configuring custom Digester Ruleset of type "
1609 + ruleset);
1610 }
1611
1612 try {
1613 RuleSet instance =
1614 (RuleSet) RequestUtils.applicationInstance(ruleset);
1615
1616 this.configDigester.addRuleSet(instance);
1617 } catch (Exception e) {
1618 log.error("Exception configuring custom Digester RuleSet", e);
1619 throw new ServletException(e);
1620 }
1621 }
1622 }
1623
1624 /**
1625 * <p>Check the status of the <code>validating</code> initialization
1626 * parameter.</p>
1627 *
1628 * @return true if the module Digester should validate.
1629 */
1630 private boolean isValidating() {
1631 boolean validating = true;
1632 String value = getServletConfig().getInitParameter("validating");
1633
1634 if ("false".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value)
1635 || "n".equalsIgnoreCase(value) || "0".equalsIgnoreCase(value)) {
1636 validating = false;
1637 }
1638
1639 return validating;
1640 }
1641
1642 /**
1643 * <p>Initialize our internal MessageResources bundle.</p>
1644 *
1645 * @throws ServletException if we cannot initialize these resources
1646 * @throws UnavailableException if we cannot load resources
1647 */
1648 protected void initInternal()
1649 throws ServletException {
1650 try {
1651 internal = MessageResources.getMessageResources(internalName);
1652 } catch (MissingResourceException e) {
1653 log.error("Cannot load internal resources from '" + internalName
1654 + "'", e);
1655 throw new UnavailableException(
1656 "Cannot load internal resources from '" + internalName + "'");
1657 }
1658 }
1659
1660 /**
1661 * <p>Parse the configuration documents specified by the
1662 * <code>chainConfig</code> init-param to configure the default {@link
1663 * org.apache.commons.chain.Catalog} that is registered in the {@link
1664 * CatalogFactory} instance for this application.</p>
1665 *
1666 * @throws ServletException if an error occurs.
1667 */
1668 protected void initChain()
1669 throws ServletException {
1670
1671 try {
1672 String value;
1673
1674 value = getServletConfig().getInitParameter("chainConfig");
1675
1676 if (value != null) {
1677 chainConfig = value;
1678 }
1679
1680 ConfigParser parser = new ConfigParser();
1681 List urls = splitAndResolvePaths(chainConfig);
1682 URL resource;
1683
1684 for (Iterator i = urls.iterator(); i.hasNext();) {
1685 resource = (URL) i.next();
1686 log.info("Loading chain catalog from " + resource);
1687 parser.parse(resource);
1688 }
1689 } catch (Exception e) {
1690 log.error("Exception loading resources", e);
1691 throw new ServletException(e);
1692 }
1693 }
1694
1695 /**
1696 * <p>Initialize other global characteristics of the controller
1697 * servlet.</p>
1698 *
1699 * @throws ServletException if we cannot initialize these resources
1700 */
1701 protected void initOther()
1702 throws ServletException {
1703 String value;
1704
1705 value = getServletConfig().getInitParameter("config");
1706
1707 if (value != null) {
1708 config = value;
1709 }
1710
1711
1712
1713 value = getServletConfig().getInitParameter("convertNull");
1714
1715 if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value)
1716 || "on".equalsIgnoreCase(value) || "y".equalsIgnoreCase(value)
1717 || "1".equalsIgnoreCase(value)) {
1718 convertNull = true;
1719 }
1720
1721 if (convertNull) {
1722 ConvertUtils.deregister();
1723 ConvertUtils.register(new BigDecimalConverter(null),
1724 BigDecimal.class);
1725 ConvertUtils.register(new BigIntegerConverter(null),
1726 BigInteger.class);
1727 ConvertUtils.register(new BooleanConverter(null), Boolean.class);
1728 ConvertUtils.register(new ByteConverter(null), Byte.class);
1729 ConvertUtils.register(new CharacterConverter(null), Character.class);
1730 ConvertUtils.register(new DoubleConverter(null), Double.class);
1731 ConvertUtils.register(new FloatConverter(null), Float.class);
1732 ConvertUtils.register(new IntegerConverter(null), Integer.class);
1733 ConvertUtils.register(new LongConverter(null), Long.class);
1734 ConvertUtils.register(new ShortConverter(null), Short.class);
1735 }
1736 }
1737
1738 /**
1739 * <p>Initialize the servlet mapping under which our controller servlet is
1740 * being accessed. This will be used in the <code>&html:form></code>
1741 * tag to generate correct destination URLs for form submissions.</p>
1742 *
1743 * @throws ServletException if error happens while scanning web.xml
1744 */
1745 protected void initServlet()
1746 throws ServletException {
1747
1748 this.servletName = getServletConfig().getServletName();
1749
1750
1751 Digester digester = new Digester();
1752
1753 digester.push(this);
1754 digester.setNamespaceAware(true);
1755 digester.setValidating(false);
1756
1757
1758 for (int i = 0; i < registrations.length; i += 2) {
1759 URL url = this.getClass().getResource(registrations[i + 1]);
1760
1761 if (url != null) {
1762 digester.register(registrations[i], url.toString());
1763 }
1764 }
1765
1766
1767 digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2);
1768 digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
1769 digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
1770
1771
1772 if (log.isDebugEnabled()) {
1773 log.debug("Scanning web.xml for controller servlet mapping");
1774 }
1775
1776 InputStream input =
1777 getServletContext().getResourceAsStream("/WEB-INF/web.xml");
1778
1779 if (input == null) {
1780 log.error(internal.getMessage("configWebXml"));
1781 throw new ServletException(internal.getMessage("configWebXml"));
1782 }
1783
1784 try {
1785 digester.parse(input);
1786 } catch (IOException e) {
1787 log.error(internal.getMessage("configWebXml"), e);
1788 throw new ServletException(e);
1789 } catch (SAXException e) {
1790 log.error(internal.getMessage("configWebXml"), e);
1791 throw new ServletException(e);
1792 } finally {
1793 try {
1794 input.close();
1795 } catch (IOException e) {
1796 log.error(internal.getMessage("configWebXml"), e);
1797 throw new ServletException(e);
1798 }
1799 }
1800
1801
1802 if (log.isDebugEnabled()) {
1803 log.debug("Mapping for servlet '" + servletName + "' = '"
1804 + servletMapping + "'");
1805 }
1806
1807 if (servletMapping != null) {
1808 getServletContext().setAttribute(Globals.SERVLET_KEY, servletMapping);
1809 }
1810 }
1811
1812 /**
1813 * <p>Takes a comma-delimited string and splits it into paths, then
1814 * resolves those paths using the ServletContext and appropriate
1815 * ClassLoader. When loading from the classloader, multiple resources per
1816 * path are supported to support, for example, multiple jars containing
1817 * the same named config file.</p>
1818 *
1819 * @param paths A comma-delimited string of paths
1820 * @return A list of resolved URL's for all found resources
1821 * @throws ServletException if a servlet exception is thrown
1822 */
1823 protected List splitAndResolvePaths(String paths)
1824 throws ServletException {
1825 ClassLoader loader = Thread.currentThread().getContextClassLoader();
1826
1827 if (loader == null) {
1828 loader = this.getClass().getClassLoader();
1829 }
1830
1831 ArrayList resolvedUrls = new ArrayList();
1832
1833 URL resource;
1834 String path = null;
1835
1836 try {
1837
1838 while (paths.length() > 0) {
1839 resource = null;
1840
1841 int comma = paths.indexOf(',');
1842
1843 if (comma >= 0) {
1844 path = paths.substring(0, comma).trim();
1845 paths = paths.substring(comma + 1);
1846 } else {
1847 path = paths.trim();
1848 paths = "";
1849 }
1850
1851 if (path.length() < 1) {
1852 break;
1853 }
1854
1855 if (path.charAt(0) == '/') {
1856 resource = getServletContext().getResource(path);
1857 }
1858
1859 if (resource == null) {
1860 if (log.isDebugEnabled()) {
1861 log.debug("Unable to locate " + path
1862 + " in the servlet context, "
1863 + "trying classloader.");
1864 }
1865
1866 Enumeration e = loader.getResources(path);
1867
1868 if (!e.hasMoreElements()) {
1869 String msg = internal.getMessage("configMissing", path);
1870
1871 log.error(msg);
1872 throw new UnavailableException(msg);
1873 } else {
1874 while (e.hasMoreElements()) {
1875 resolvedUrls.add(e.nextElement());
1876 }
1877 }
1878 } else {
1879 resolvedUrls.add(resource);
1880 }
1881 }
1882 } catch (MalformedURLException e) {
1883 handleConfigException(path, e);
1884 } catch (IOException e) {
1885 handleConfigException(path, e);
1886 }
1887
1888 return resolvedUrls;
1889 }
1890
1891 /**
1892 * <p>Perform the standard request processing for this request, and create
1893 * the corresponding response.</p>
1894 *
1895 * @param request The servlet request we are processing
1896 * @param response The servlet response we are creating
1897 * @throws IOException if an input/output error occurs
1898 * @throws ServletException if a servlet exception is thrown
1899 */
1900 protected void process(HttpServletRequest request,
1901 HttpServletResponse response)
1902 throws IOException, ServletException {
1903 ModuleUtils.getInstance().selectModule(request, getServletContext());
1904
1905 ModuleConfig config = getModuleConfig(request);
1906
1907 RequestProcessor processor = getProcessorForModule(config);
1908
1909 if (processor == null) {
1910 processor = getRequestProcessor(config);
1911 }
1912
1913 processor.process(request, response);
1914 }
1915 }