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