0001: /*
0002: * $Id: ActionServlet.java 483039 2006-12-06 11:34:28Z niallp $
0003: *
0004: * Licensed to the Apache Software Foundation (ASF) under one
0005: * or more contributor license agreements. See the NOTICE file
0006: * distributed with this work for additional information
0007: * regarding copyright ownership. The ASF licenses this file
0008: * to you under the Apache License, Version 2.0 (the
0009: * "License"); you may not use this file except in compliance
0010: * with the License. You may obtain a copy of the License at
0011: *
0012: * http://www.apache.org/licenses/LICENSE-2.0
0013: *
0014: * Unless required by applicable law or agreed to in writing,
0015: * software distributed under the License is distributed on an
0016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0017: * KIND, either express or implied. See the License for the
0018: * specific language governing permissions and limitations
0019: * under the License.
0020: */
0021: package org.apache.struts.action;
0022:
0023: import org.apache.commons.beanutils.BeanUtils;
0024: import org.apache.commons.beanutils.ConvertUtils;
0025: import org.apache.commons.beanutils.PropertyUtils;
0026: import org.apache.commons.beanutils.converters.BigDecimalConverter;
0027: import org.apache.commons.beanutils.converters.BigIntegerConverter;
0028: import org.apache.commons.beanutils.converters.BooleanConverter;
0029: import org.apache.commons.beanutils.converters.ByteConverter;
0030: import org.apache.commons.beanutils.converters.CharacterConverter;
0031: import org.apache.commons.beanutils.converters.DoubleConverter;
0032: import org.apache.commons.beanutils.converters.FloatConverter;
0033: import org.apache.commons.beanutils.converters.IntegerConverter;
0034: import org.apache.commons.beanutils.converters.LongConverter;
0035: import org.apache.commons.beanutils.converters.ShortConverter;
0036: import org.apache.commons.chain.CatalogFactory;
0037: import org.apache.commons.chain.config.ConfigParser;
0038: import org.apache.commons.digester.Digester;
0039: import org.apache.commons.digester.RuleSet;
0040: import org.apache.commons.logging.Log;
0041: import org.apache.commons.logging.LogFactory;
0042: import org.apache.struts.Globals;
0043: import org.apache.struts.config.ActionConfig;
0044: import org.apache.struts.config.ConfigRuleSet;
0045: import org.apache.struts.config.ExceptionConfig;
0046: import org.apache.struts.config.FormBeanConfig;
0047: import org.apache.struts.config.FormPropertyConfig;
0048: import org.apache.struts.config.ForwardConfig;
0049: import org.apache.struts.config.MessageResourcesConfig;
0050: import org.apache.struts.config.ModuleConfig;
0051: import org.apache.struts.config.ModuleConfigFactory;
0052: import org.apache.struts.config.PlugInConfig;
0053: import org.apache.struts.util.MessageResources;
0054: import org.apache.struts.util.MessageResourcesFactory;
0055: import org.apache.struts.util.ModuleUtils;
0056: import org.apache.struts.util.RequestUtils;
0057: import org.xml.sax.InputSource;
0058: import org.xml.sax.SAXException;
0059:
0060: import javax.servlet.ServletContext;
0061: import javax.servlet.ServletException;
0062: import javax.servlet.UnavailableException;
0063: import javax.servlet.http.HttpServlet;
0064: import javax.servlet.http.HttpServletRequest;
0065: import javax.servlet.http.HttpServletResponse;
0066:
0067: import java.io.IOException;
0068: import java.io.InputStream;
0069:
0070: import java.math.BigDecimal;
0071: import java.math.BigInteger;
0072:
0073: import java.net.MalformedURLException;
0074: import java.net.URL;
0075: import java.net.URLConnection;
0076:
0077: import java.util.ArrayList;
0078: import java.util.Enumeration;
0079: import java.util.Iterator;
0080: import java.util.List;
0081: import java.util.MissingResourceException;
0082:
0083: /**
0084: * <p><strong>ActionServlet</strong> provides the "controller" in the
0085: * Model-View-Controller (MVC) design pattern for web applications that is
0086: * commonly known as "Model 2". This nomenclature originated with a
0087: * description in the JavaServerPages Specification, version 0.92, and has
0088: * persisted ever since (in the absence of a better name).</p>
0089: *
0090: * <p>Generally, a "Model 2" application is architected as follows:</p>
0091: *
0092: * <ul>
0093: *
0094: * <li>The user interface will generally be created with server pages, which
0095: * will not themselves contain any business logic. These pages represent the
0096: * "view" component of an MVC architecture.</li>
0097: *
0098: * <li>Forms and hyperlinks in the user interface that require business logic
0099: * to be executed will be submitted to a request URI that is mapped to this
0100: * servlet.</li>
0101: *
0102: * <li>There can be <b>one</b> instance of this servlet class, which receives
0103: * and processes all requests that change the state of a user's interaction
0104: * with the application. The servlet delegates the handling of a request to a
0105: * {@link RequestProcessor} object. This component represents the "controller"
0106: * component of an MVC architecture. </li>
0107: *
0108: * <li>The <code>RequestProcessor</code> selects and invokes an {@link Action}
0109: * class to perform the requested business logic, or delegates the response to
0110: * another resource.</li>
0111: *
0112: * <li>The <code>Action</code> classes can manipulate the state of the
0113: * application's interaction with the user, typically by creating or modifying
0114: * JavaBeans that are stored as request or session attributes (depending on
0115: * how long they need to be available). Such JavaBeans represent the "model"
0116: * component of an MVC architecture.</li>
0117: *
0118: * <li>Instead of producing the next page of the user interface directly,
0119: * <code>Action</code> classes generally return an {@link ActionForward} to
0120: * indicate which resource should handle the response. If the
0121: * <code>Action</code> does not return null, the <code>RequestProcessor</code>
0122: * forwards or redirects to the specified resource (by utilizing
0123: * <code>RequestDispatcher.forward</code> or <code>Response.sendRedirect</code>)
0124: * so as to produce the next page of the user interface.</li>
0125: *
0126: * </ul>
0127: *
0128: * <p>The standard version of <code>RequestsProcessor</code> implements the
0129: * following logic for each incoming HTTP request. You can override some or
0130: * all of this functionality by subclassing this object and implementing your
0131: * own version of the processing.</p>
0132: *
0133: * <ul>
0134: *
0135: * <li>Identify, from the incoming request URI, the substring that will be
0136: * used to select an action procedure.</li>
0137: *
0138: * <li>Use this substring to map to the Java class name of the corresponding
0139: * action class (an implementation of the <code>Action</code> interface).
0140: * </li>
0141: *
0142: * <li>If this is the first request for a particular <code>Action</code>
0143: * class, instantiate an instance of that class and cache it for future
0144: * use.</li>
0145: *
0146: * <li>Optionally populate the properties of an <code>ActionForm</code> bean
0147: * associated with this mapping.</li>
0148: *
0149: * <li>Call the <code>execute</code> method of this <code>Action</code> class,
0150: * passing on a reference to the mapping that was used, the relevant form-bean
0151: * (if any), and the request and the response that were passed to the
0152: * controller by the servlet container (thereby providing access to any
0153: * specialized properties of the mapping itself as well as to the
0154: * ServletContext). </li>
0155: *
0156: * </ul>
0157: *
0158: * <p>The standard version of <code>ActionServlet</code> is configured based
0159: * on the following servlet initialization parameters, which you will specify
0160: * in the web application deployment descriptor (<code>/WEB-INF/web.xml</code>)
0161: * for your application. Subclasses that specialize this servlet are free to
0162: * define additional initialization parameters. </p>
0163: *
0164: * <ul>
0165: *
0166: * <li><strong>config</strong> - Comma-separated list of context-relative
0167: * path(s) to the XML resource(s) containing the configuration information for
0168: * the default module. (Multiple files support since Struts 1.1)
0169: * [/WEB-INF/struts-config.xml].</li>
0170: *
0171: * <li><strong>config/${module}</strong> - Comma-separated list of
0172: * Context-relative path(s) to the XML resource(s) containing the
0173: * configuration information for the module that will use the specified prefix
0174: * (/${module}). This can be repeated as many times as required for multiple
0175: * modules. (Since Struts 1.1)</li>
0176: *
0177: * <li><strong>configFactory</strong> - The Java class name of the
0178: * <code>ModuleConfigFactory</code> used to create the implementation of the
0179: * ModuleConfig interface. </li>
0180: *
0181: * <li><strong>convertNull</strong> - Force simulation of the Struts 1.0
0182: * behavior when populating forms. If set to true, the numeric Java wrapper
0183: * class types (like <code>java.lang.Integer</code>) will default to null
0184: * (rather than 0). (Since Struts 1.1) [false] </li>
0185: *
0186: * <li><strong>rulesets </strong> - Comma-delimited list of fully qualified
0187: * classnames of additional <code>org.apache.commons.digester.RuleSet</code>
0188: * instances that should be added to the <code>Digester</code> that will be
0189: * processing <code>struts-config.xml</code> files. By default, only the
0190: * <code>RuleSet</code> for the standard configuration elements is loaded.
0191: * (Since Struts 1.1)</li>
0192: *
0193: * <li><strong>validating</strong> - Should we use a validating XML parser to
0194: * process the configuration file (strongly recommended)? [true]</li>
0195: *
0196: * <li><strong>chainConfig</strong> - Comma-separated list of either
0197: * context-relative or classloader path(s) to load commons-chain catalog
0198: * definitions from. If none specified, the default Struts catalog that is
0199: * provided with Struts will be used.</li>
0200: *
0201: * </ul>
0202: *
0203: * @version $Rev: 483039 $ $Date: 2005-10-14 19:54:16 -0400 (Fri, 14 Oct 2005)
0204: * $
0205: */
0206: public class ActionServlet extends HttpServlet {
0207: /**
0208: * <p>Commons Logging instance.</p>
0209: *
0210: * @since Struts 1.1
0211: */
0212: protected static Log log = LogFactory.getLog(ActionServlet.class);
0213:
0214: // ----------------------------------------------------- Instance Variables
0215:
0216: /**
0217: * <p>Comma-separated list of context-relative path(s) to our
0218: * configuration resource(s) for the default module.</p>
0219: */
0220: protected String config = "/WEB-INF/struts-config.xml";
0221:
0222: /**
0223: * <p>Comma-separated list of context or classloader-relative path(s) that
0224: * contain the configuration for the default commons-chain
0225: * catalog(s).</p>
0226: */
0227: protected String chainConfig = "org/apache/struts/chain/chain-config.xml";
0228:
0229: /**
0230: * <p>The Digester used to produce ModuleConfig objects from a Struts
0231: * configuration file.</p>
0232: *
0233: * @since Struts 1.1
0234: */
0235: protected Digester configDigester = null;
0236:
0237: /**
0238: * <p>The flag to request backwards-compatible conversions for form bean
0239: * properties of the Java wrapper class types.</p>
0240: *
0241: * @since Struts 1.1
0242: */
0243: protected boolean convertNull = false;
0244:
0245: /**
0246: * <p>The resources object for our internal resources.</p>
0247: */
0248: protected MessageResources internal = null;
0249:
0250: /**
0251: * <p>The Java base name of our internal resources.</p>
0252: *
0253: * @since Struts 1.1
0254: */
0255: protected String internalName = "org.apache.struts.action.ActionResources";
0256:
0257: /**
0258: * <p>The set of public identifiers, and corresponding resource names, for
0259: * the versions of the configuration file DTDs that we know about. There
0260: * <strong>MUST</strong> be an even number of Strings in this list!</p>
0261: */
0262: protected String[] registrations = {
0263: "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",
0264: "/org/apache/struts/resources/struts-config_1_0.dtd",
0265: "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN",
0266: "/org/apache/struts/resources/struts-config_1_1.dtd",
0267: "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",
0268: "/org/apache/struts/resources/struts-config_1_2.dtd",
0269: "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",
0270: "/org/apache/struts/resources/struts-config_1_3.dtd",
0271: "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
0272: "/org/apache/struts/resources/web-app_2_3.dtd" };
0273:
0274: /**
0275: * <p>The URL pattern to which we are mapped in our web application
0276: * deployment descriptor.</p>
0277: */
0278: protected String servletMapping = null; // :FIXME: - multiples?
0279:
0280: /**
0281: * <p>The servlet name under which we are registered in our web
0282: * application deployment descriptor.</p>
0283: */
0284: protected String servletName = null;
0285:
0286: // ---------------------------------------------------- HttpServlet Methods
0287:
0288: /**
0289: * <p>Gracefully shut down this controller servlet, releasing any
0290: * resources that were allocated at initialization.</p>
0291: */
0292: public void destroy() {
0293: if (log.isDebugEnabled()) {
0294: log.debug(internal.getMessage("finalizing"));
0295: }
0296:
0297: destroyModules();
0298: destroyInternal();
0299: getServletContext().removeAttribute(Globals.ACTION_SERVLET_KEY);
0300:
0301: // Release our LogFactory and Log instances (if any)
0302: ClassLoader classLoader = Thread.currentThread()
0303: .getContextClassLoader();
0304:
0305: if (classLoader == null) {
0306: classLoader = ActionServlet.class.getClassLoader();
0307: }
0308:
0309: try {
0310: LogFactory.release(classLoader);
0311: } catch (Throwable t) {
0312: ; // Servlet container doesn't have the latest version
0313:
0314: // of commons-logging-api.jar installed
0315: // :FIXME: Why is this dependent on the container's version of
0316: // commons-logging? Shouldn't this depend on the version packaged
0317: // with Struts?
0318:
0319: /*
0320: Reason: LogFactory.release(classLoader); was added as
0321: an attempt to investigate the OutOfMemory error reported on
0322: Bugzilla #14042. It was committed for version 1.136 by craigmcc
0323: */
0324: }
0325:
0326: CatalogFactory.clear();
0327: PropertyUtils.clearDescriptors();
0328: }
0329:
0330: /**
0331: * <p>Initialize this servlet. Most of the processing has been factored
0332: * into support methods so that you can override particular functionality
0333: * at a fairly granular level.</p>
0334: *
0335: * @throws ServletException if we cannot configure ourselves correctly
0336: */
0337: public void init() throws ServletException {
0338: final String configPrefix = "config/";
0339: final int configPrefixLength = configPrefix.length() - 1;
0340:
0341: // Wraps the entire initialization in a try/catch to better handle
0342: // unexpected exceptions and errors to provide better feedback
0343: // to the developer
0344: try {
0345: initInternal();
0346: initOther();
0347: initServlet();
0348: initChain();
0349:
0350: getServletContext().setAttribute(
0351: Globals.ACTION_SERVLET_KEY, this );
0352: initModuleConfigFactory();
0353:
0354: // Initialize modules as needed
0355: ModuleConfig moduleConfig = initModuleConfig("", config);
0356:
0357: initModuleMessageResources(moduleConfig);
0358: initModulePlugIns(moduleConfig);
0359: initModuleFormBeans(moduleConfig);
0360: initModuleForwards(moduleConfig);
0361: initModuleExceptionConfigs(moduleConfig);
0362: initModuleActions(moduleConfig);
0363: moduleConfig.freeze();
0364:
0365: Enumeration names = getServletConfig()
0366: .getInitParameterNames();
0367:
0368: while (names.hasMoreElements()) {
0369: String name = (String) names.nextElement();
0370:
0371: if (!name.startsWith(configPrefix)) {
0372: continue;
0373: }
0374:
0375: String prefix = name.substring(configPrefixLength);
0376:
0377: moduleConfig = initModuleConfig(prefix,
0378: getServletConfig().getInitParameter(name));
0379: initModuleMessageResources(moduleConfig);
0380: initModulePlugIns(moduleConfig);
0381: initModuleFormBeans(moduleConfig);
0382: initModuleForwards(moduleConfig);
0383: initModuleExceptionConfigs(moduleConfig);
0384: initModuleActions(moduleConfig);
0385: moduleConfig.freeze();
0386: }
0387:
0388: this .initModulePrefixes(this .getServletContext());
0389:
0390: this .destroyConfigDigester();
0391: } catch (UnavailableException ex) {
0392: throw ex;
0393: } catch (Throwable t) {
0394: // The follow error message is not retrieved from internal message
0395: // resources as they may not have been able to have been
0396: // initialized
0397: log
0398: .error(
0399: "Unable to initialize Struts ActionServlet due to an "
0400: + "unexpected exception or error thrown, so marking the "
0401: + "servlet as unavailable. Most likely, this is due to an "
0402: + "incorrect or missing library dependency.",
0403: t);
0404: throw new UnavailableException(t.getMessage());
0405: }
0406: }
0407:
0408: /**
0409: * <p>Saves a String[] of module prefixes in the ServletContext under
0410: * Globals.MODULE_PREFIXES_KEY. <strong>NOTE</strong> - the "" prefix for
0411: * the default module is not included in this list.</p>
0412: *
0413: * @param context The servlet context.
0414: * @since Struts 1.2
0415: */
0416: protected void initModulePrefixes(ServletContext context) {
0417: ArrayList prefixList = new ArrayList();
0418:
0419: Enumeration names = context.getAttributeNames();
0420:
0421: while (names.hasMoreElements()) {
0422: String name = (String) names.nextElement();
0423:
0424: if (!name.startsWith(Globals.MODULE_KEY)) {
0425: continue;
0426: }
0427:
0428: String prefix = name.substring(Globals.MODULE_KEY.length());
0429:
0430: if (prefix.length() > 0) {
0431: prefixList.add(prefix);
0432: }
0433: }
0434:
0435: String[] prefixes = (String[]) prefixList
0436: .toArray(new String[prefixList.size()]);
0437:
0438: context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes);
0439: }
0440:
0441: /**
0442: * <p>Process an HTTP "GET" request.</p>
0443: *
0444: * @param request The servlet request we are processing
0445: * @param response The servlet response we are creating
0446: * @throws IOException if an input/output error occurs
0447: * @throws ServletException if a servlet exception occurs
0448: */
0449: public void doGet(HttpServletRequest request,
0450: HttpServletResponse response) throws IOException,
0451: ServletException {
0452: process(request, response);
0453: }
0454:
0455: /**
0456: * <p>Process an HTTP "POST" request.</p>
0457: *
0458: * @param request The servlet request we are processing
0459: * @param response The servlet response we are creating
0460: * @throws IOException if an input/output error occurs
0461: * @throws ServletException if a servlet exception occurs
0462: */
0463: public void doPost(HttpServletRequest request,
0464: HttpServletResponse response) throws IOException,
0465: ServletException {
0466: process(request, response);
0467: }
0468:
0469: // --------------------------------------------------------- Public Methods
0470:
0471: /**
0472: * <p>Remember a servlet mapping from our web application deployment
0473: * descriptor, if it is for this servlet.</p>
0474: *
0475: * @param servletName The name of the servlet being mapped
0476: * @param urlPattern The URL pattern to which this servlet is mapped
0477: */
0478: public void addServletMapping(String servletName, String urlPattern) {
0479: if (servletName == null) {
0480: return;
0481: }
0482:
0483: if (servletName.equals(this .servletName)) {
0484: if (log.isDebugEnabled()) {
0485: log.debug("Process servletName=" + servletName
0486: + ", urlPattern=" + urlPattern);
0487: }
0488:
0489: this .servletMapping = urlPattern;
0490: }
0491: }
0492:
0493: /**
0494: * <p>Return the <code>MessageResources</code> instance containing our
0495: * internal message strings.</p>
0496: *
0497: * @return the <code>MessageResources</code> instance containing our
0498: * internal message strings.
0499: * @since Struts 1.1
0500: */
0501: public MessageResources getInternal() {
0502: return (this .internal);
0503: }
0504:
0505: // ------------------------------------------------------ Protected Methods
0506:
0507: /**
0508: * <p>Gracefully terminate use of any modules associated with this
0509: * application (if any).</p>
0510: *
0511: * @since Struts 1.1
0512: */
0513: protected void destroyModules() {
0514: ArrayList values = new ArrayList();
0515: Enumeration names = getServletContext().getAttributeNames();
0516:
0517: while (names.hasMoreElements()) {
0518: values.add(names.nextElement());
0519: }
0520:
0521: Iterator keys = values.iterator();
0522:
0523: while (keys.hasNext()) {
0524: String name = (String) keys.next();
0525: Object value = getServletContext().getAttribute(name);
0526:
0527: if (!(value instanceof ModuleConfig)) {
0528: continue;
0529: }
0530:
0531: ModuleConfig config = (ModuleConfig) value;
0532:
0533: if (this .getProcessorForModule(config) != null) {
0534: this .getProcessorForModule(config).destroy();
0535: }
0536:
0537: getServletContext().removeAttribute(name);
0538:
0539: PlugIn[] plugIns = (PlugIn[]) getServletContext()
0540: .getAttribute(
0541: Globals.PLUG_INS_KEY + config.getPrefix());
0542:
0543: if (plugIns != null) {
0544: for (int i = 0; i < plugIns.length; i++) {
0545: int j = plugIns.length - (i + 1);
0546:
0547: plugIns[j].destroy();
0548: }
0549:
0550: getServletContext().removeAttribute(
0551: Globals.PLUG_INS_KEY + config.getPrefix());
0552: }
0553: }
0554: }
0555:
0556: /**
0557: * <p>Gracefully release any configDigester instance that we have created.
0558: * </p>
0559: *
0560: * @since Struts 1.1
0561: */
0562: protected void destroyConfigDigester() {
0563: configDigester = null;
0564: }
0565:
0566: /**
0567: * <p>Gracefully terminate use of the internal MessageResources.</p>
0568: */
0569: protected void destroyInternal() {
0570: internal = null;
0571: }
0572:
0573: /**
0574: * <p>Return the module configuration object for the currently selected
0575: * module.</p>
0576: *
0577: * @param request The servlet request we are processing
0578: * @return The module configuration object for the currently selected
0579: * module.
0580: * @since Struts 1.1
0581: */
0582: protected ModuleConfig getModuleConfig(HttpServletRequest request) {
0583: ModuleConfig config = (ModuleConfig) request
0584: .getAttribute(Globals.MODULE_KEY);
0585:
0586: if (config == null) {
0587: config = (ModuleConfig) getServletContext().getAttribute(
0588: Globals.MODULE_KEY);
0589: }
0590:
0591: return (config);
0592: }
0593:
0594: /**
0595: * <p>Look up and return the {@link RequestProcessor} responsible for the
0596: * specified module, creating a new one if necessary.</p>
0597: *
0598: * @param config The module configuration for which to acquire and return
0599: * a RequestProcessor.
0600: * @return The {@link RequestProcessor} responsible for the specified
0601: * module,
0602: * @throws ServletException If we cannot instantiate a RequestProcessor
0603: * instance a {@link UnavailableException} is
0604: * thrown, meaning your application is not loaded
0605: * and will not be available.
0606: * @since Struts 1.1
0607: */
0608: protected synchronized RequestProcessor getRequestProcessor(
0609: ModuleConfig config) throws ServletException {
0610: RequestProcessor processor = this .getProcessorForModule(config);
0611:
0612: if (processor == null) {
0613: try {
0614: processor = (RequestProcessor) RequestUtils
0615: .applicationInstance(config
0616: .getControllerConfig()
0617: .getProcessorClass());
0618: } catch (Exception e) {
0619: throw new UnavailableException(
0620: "Cannot initialize RequestProcessor of class "
0621: + config.getControllerConfig()
0622: .getProcessorClass() + ": " + e);
0623: }
0624:
0625: processor.init(this , config);
0626:
0627: String key = Globals.REQUEST_PROCESSOR_KEY
0628: + config.getPrefix();
0629:
0630: getServletContext().setAttribute(key, processor);
0631: }
0632:
0633: return (processor);
0634: }
0635:
0636: /**
0637: * <p>Returns the RequestProcessor for the given module or null if one
0638: * does not exist. This method will not create a RequestProcessor.</p>
0639: *
0640: * @param config The ModuleConfig.
0641: * @return The <code>RequestProcessor</code> for the given module, or
0642: * <code>null</code> if one does not exist.
0643: */
0644: private RequestProcessor getProcessorForModule(ModuleConfig config) {
0645: String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix();
0646:
0647: return (RequestProcessor) getServletContext().getAttribute(key);
0648: }
0649:
0650: /**
0651: * <p>Initialize the factory used to create the module configuration.</p>
0652: *
0653: * @since Struts 1.2
0654: */
0655: protected void initModuleConfigFactory() {
0656: String configFactory = getServletConfig().getInitParameter(
0657: "configFactory");
0658:
0659: if (configFactory != null) {
0660: ModuleConfigFactory.setFactoryClass(configFactory);
0661: }
0662: }
0663:
0664: /**
0665: * <p>Initialize the module configuration information for the specified
0666: * module.</p>
0667: *
0668: * @param prefix Module prefix for this module
0669: * @param paths Comma-separated list of context-relative resource path(s)
0670: * for this modules's configuration resource(s)
0671: * @return The new module configuration instance.
0672: * @throws ServletException if initialization cannot be performed
0673: * @since Struts 1.1
0674: */
0675: protected ModuleConfig initModuleConfig(String prefix, String paths)
0676: throws ServletException {
0677: if (log.isDebugEnabled()) {
0678: log.debug("Initializing module path '" + prefix
0679: + "' configuration from '" + paths + "'");
0680: }
0681:
0682: // Parse the configuration for this module
0683: ModuleConfigFactory factoryObject = ModuleConfigFactory
0684: .createFactory();
0685: ModuleConfig config = factoryObject.createModuleConfig(prefix);
0686:
0687: // Configure the Digester instance we will use
0688: Digester digester = initConfigDigester();
0689:
0690: List urls = splitAndResolvePaths(paths);
0691: URL url;
0692:
0693: for (Iterator i = urls.iterator(); i.hasNext();) {
0694: url = (URL) i.next();
0695: digester.push(config);
0696: this .parseModuleConfigFile(digester, url);
0697: }
0698:
0699: getServletContext().setAttribute(
0700: Globals.MODULE_KEY + config.getPrefix(), config);
0701:
0702: return config;
0703: }
0704:
0705: /**
0706: * <p>Parses one module config file.</p>
0707: *
0708: * @param digester Digester instance that does the parsing
0709: * @param path The path to the config file to parse.
0710: * @throws UnavailableException if file cannot be read or parsed
0711: * @since Struts 1.2
0712: * @deprecated use parseModuleConfigFile(Digester digester, URL url)
0713: * instead
0714: */
0715: protected void parseModuleConfigFile(Digester digester, String path)
0716: throws UnavailableException {
0717: try {
0718: List paths = splitAndResolvePaths(path);
0719:
0720: if (paths.size() > 0) {
0721: // Get first path as was the old behavior
0722: URL url = (URL) paths.get(0);
0723:
0724: parseModuleConfigFile(digester, url);
0725: } else {
0726: throw new UnavailableException("Cannot locate path "
0727: + path);
0728: }
0729: } catch (UnavailableException ex) {
0730: throw ex;
0731: } catch (ServletException ex) {
0732: handleConfigException(path, ex);
0733: }
0734: }
0735:
0736: /**
0737: * <p>Parses one module config file.</p>
0738: *
0739: * @param digester Digester instance that does the parsing
0740: * @param url The url to the config file to parse.
0741: * @throws UnavailableException if file cannot be read or parsed
0742: * @since Struts 1.3
0743: */
0744: protected void parseModuleConfigFile(Digester digester, URL url)
0745: throws UnavailableException {
0746:
0747: try {
0748: digester.parse(url);
0749: } catch (IOException e) {
0750: handleConfigException(url.toString(), e);
0751: } catch (SAXException e) {
0752: handleConfigException(url.toString(), e);
0753: }
0754: }
0755:
0756: /**
0757: * <p>Simplifies exception handling in the parseModuleConfigFile
0758: * method.<p>
0759: *
0760: * @param path The path to which the exception relates.
0761: * @param e The exception to be wrapped and thrown.
0762: * @throws UnavailableException as a wrapper around Exception
0763: */
0764: private void handleConfigException(String path, Exception e)
0765: throws UnavailableException {
0766: String msg = internal.getMessage("configParse", path);
0767:
0768: log.error(msg, e);
0769: throw new UnavailableException(msg);
0770: }
0771:
0772: /**
0773: * <p>Handle errors related to creating an instance of the specified
0774: * class.</p>
0775: *
0776: * @param className The className that could not be instantiated.
0777: * @param e The exception that was caught.
0778: * @throws ServletException to communicate the error.
0779: */
0780: private void handleCreationException(String className, Exception e)
0781: throws ServletException {
0782: String errorMessage = internal.getMessage(
0783: "configExtends.creation", className);
0784:
0785: log.error(errorMessage, e);
0786: throw new UnavailableException(errorMessage);
0787: }
0788:
0789: /**
0790: * <p>General handling for exceptions caught while inheriting config
0791: * information.</p>
0792: *
0793: * @param configType The type of configuration object of configName.
0794: * @param configName The name of the config that could not be extended.
0795: * @param e The exception that was caught.
0796: * @throws ServletException to communicate the error.
0797: */
0798: private void handleGeneralExtensionException(String configType,
0799: String configName, Exception e) throws ServletException {
0800: String errorMessage = internal.getMessage("configExtends",
0801: configType, configName);
0802:
0803: log.error(errorMessage, e);
0804: throw new UnavailableException(errorMessage);
0805: }
0806:
0807: /**
0808: * <p>Handle errors caused by required fields that were not
0809: * specified.</p>
0810: *
0811: * @param field The name of the required field that was not found.
0812: * @param configType The type of configuration object of configName.
0813: * @param configName The name of the config that's missing the required
0814: * value.
0815: * @throws ServletException to communicate the error.
0816: */
0817: private void handleValueRequiredException(String field,
0818: String configType, String configName)
0819: throws ServletException {
0820: String errorMessage = internal.getMessage(
0821: "configFieldRequired", field, configType, configName);
0822:
0823: log.error(errorMessage);
0824: throw new UnavailableException(errorMessage);
0825: }
0826:
0827: /**
0828: * <p>Initialize the plug ins for the specified module.</p>
0829: *
0830: * @param config ModuleConfig information for this module
0831: * @throws ServletException if initialization cannot be performed
0832: * @since Struts 1.1
0833: */
0834: protected void initModulePlugIns(ModuleConfig config)
0835: throws ServletException {
0836: if (log.isDebugEnabled()) {
0837: log.debug("Initializing module path '" + config.getPrefix()
0838: + "' plug ins");
0839: }
0840:
0841: PlugInConfig[] plugInConfigs = config.findPlugInConfigs();
0842: PlugIn[] plugIns = new PlugIn[plugInConfigs.length];
0843:
0844: getServletContext().setAttribute(
0845: Globals.PLUG_INS_KEY + config.getPrefix(), plugIns);
0846:
0847: for (int i = 0; i < plugIns.length; i++) {
0848: try {
0849: plugIns[i] = (PlugIn) RequestUtils
0850: .applicationInstance(plugInConfigs[i]
0851: .getClassName());
0852: BeanUtils.populate(plugIns[i], plugInConfigs[i]
0853: .getProperties());
0854:
0855: // Pass the current plugIn config object to the PlugIn.
0856: // The property is set only if the plugin declares it.
0857: // This plugin config object is needed by Tiles
0858: try {
0859: PropertyUtils.setProperty(plugIns[i],
0860: "currentPlugInConfigObject",
0861: plugInConfigs[i]);
0862: } catch (Exception e) {
0863: ;
0864:
0865: // FIXME Whenever we fail silently, we must document a valid
0866: // reason for doing so. Why should we fail silently if a
0867: // property can't be set on the plugin?
0868:
0869: /**
0870: * Between version 1.138-1.140 cedric made these changes.
0871: * The exceptions are caught to deal with containers
0872: * applying strict security. This was in response to bug
0873: * #15736
0874: *
0875: * Recommend that we make the currentPlugInConfigObject part
0876: * of the PlugIn Interface if we can, Rob
0877: */
0878: }
0879:
0880: plugIns[i].init(this , config);
0881: } catch (ServletException e) {
0882: throw e;
0883: } catch (Exception e) {
0884: String errMsg = internal.getMessage("plugIn.init",
0885: plugInConfigs[i].getClassName());
0886:
0887: log(errMsg, e);
0888: throw new UnavailableException(errMsg);
0889: }
0890: }
0891: }
0892:
0893: /**
0894: * <p>Initialize the form beans for the specified module.</p>
0895: *
0896: * @param config ModuleConfig information for this module
0897: * @throws ServletException if initialization cannot be performed
0898: * @since Struts 1.3
0899: */
0900: protected void initModuleFormBeans(ModuleConfig config)
0901: throws ServletException {
0902: if (log.isDebugEnabled()) {
0903: log.debug("Initializing module path '" + config.getPrefix()
0904: + "' form beans");
0905: }
0906:
0907: // Process form bean extensions.
0908: FormBeanConfig[] formBeans = config.findFormBeanConfigs();
0909:
0910: for (int i = 0; i < formBeans.length; i++) {
0911: FormBeanConfig beanConfig = formBeans[i];
0912:
0913: processFormBeanExtension(beanConfig, config);
0914: }
0915:
0916: for (int i = 0; i < formBeans.length; i++) {
0917: FormBeanConfig formBean = formBeans[i];
0918:
0919: // Verify that required fields are all present for the form config
0920: if (formBean.getType() == null) {
0921: handleValueRequiredException("type",
0922: formBean.getName(), "form bean");
0923: }
0924:
0925: // ... and the property configs
0926: FormPropertyConfig[] fpcs = formBean
0927: .findFormPropertyConfigs();
0928:
0929: for (int j = 0; j < fpcs.length; j++) {
0930: FormPropertyConfig property = fpcs[j];
0931:
0932: if (property.getType() == null) {
0933: handleValueRequiredException("type", property
0934: .getName(), "form property");
0935: }
0936: }
0937:
0938: // Force creation and registration of DynaActionFormClass instances
0939: // for all dynamic form beans
0940: if (formBean.getDynamic()) {
0941: formBean.getDynaActionFormClass();
0942: }
0943: }
0944: }
0945:
0946: /**
0947: * <p>Extend the form bean's configuration as necessary.</p>
0948: *
0949: * @param beanConfig the configuration to process.
0950: * @param moduleConfig the module configuration for this module.
0951: * @throws ServletException if initialization cannot be performed.
0952: */
0953: protected void processFormBeanExtension(FormBeanConfig beanConfig,
0954: ModuleConfig moduleConfig) throws ServletException {
0955: try {
0956: if (!beanConfig.isExtensionProcessed()) {
0957: if (log.isDebugEnabled()) {
0958: log.debug("Processing extensions for '"
0959: + beanConfig.getName() + "'");
0960: }
0961:
0962: beanConfig = processFormBeanConfigClass(beanConfig,
0963: moduleConfig);
0964:
0965: beanConfig.processExtends(moduleConfig);
0966: }
0967: } catch (ServletException e) {
0968: throw e;
0969: } catch (Exception e) {
0970: handleGeneralExtensionException("FormBeanConfig",
0971: beanConfig.getName(), e);
0972: }
0973: }
0974:
0975: /**
0976: * <p>Checks if the current beanConfig is using the correct class based on
0977: * the class of its ancestor form bean config.</p>
0978: *
0979: * @param beanConfig The form bean to check.
0980: * @param moduleConfig The config for the current module.
0981: * @return The form bean config using the correct class as determined by
0982: * the config's ancestor and its own overridden value.
0983: * @throws UnavailableException if an instance of the form bean config
0984: * class cannot be created.
0985: * @throws ServletException on class creation error
0986: */
0987: protected FormBeanConfig processFormBeanConfigClass(
0988: FormBeanConfig beanConfig, ModuleConfig moduleConfig)
0989: throws ServletException {
0990: String ancestor = beanConfig.getExtends();
0991:
0992: if (ancestor == null) {
0993: // Nothing to do, then
0994: return beanConfig;
0995: }
0996:
0997: // Make sure that this bean is of the right class
0998: FormBeanConfig baseConfig = moduleConfig
0999: .findFormBeanConfig(ancestor);
1000:
1001: if (baseConfig == null) {
1002: throw new UnavailableException("Unable to find "
1003: + "form bean '" + ancestor + "' to extend.");
1004: }
1005:
1006: // Was our bean's class overridden already?
1007: if (beanConfig.getClass().equals(FormBeanConfig.class)) {
1008: // Ensure that our bean is using the correct class
1009: if (!baseConfig.getClass().equals(beanConfig.getClass())) {
1010: // Replace the bean with an instance of the correct class
1011: FormBeanConfig newBeanConfig = null;
1012: String baseConfigClassName = baseConfig.getClass()
1013: .getName();
1014:
1015: try {
1016: newBeanConfig = (FormBeanConfig) RequestUtils
1017: .applicationInstance(baseConfigClassName);
1018:
1019: // copy the values
1020: BeanUtils.copyProperties(newBeanConfig, beanConfig);
1021:
1022: FormPropertyConfig[] fpc = beanConfig
1023: .findFormPropertyConfigs();
1024:
1025: for (int i = 0; i < fpc.length; i++) {
1026: newBeanConfig.addFormPropertyConfig(fpc[i]);
1027: }
1028: } catch (Exception e) {
1029: handleCreationException(baseConfigClassName, e);
1030: }
1031:
1032: // replace beanConfig with newBeanConfig
1033: moduleConfig.removeFormBeanConfig(beanConfig);
1034: moduleConfig.addFormBeanConfig(newBeanConfig);
1035: beanConfig = newBeanConfig;
1036: }
1037: }
1038:
1039: return beanConfig;
1040: }
1041:
1042: /**
1043: * <p>Initialize the forwards for the specified module.</p>
1044: *
1045: * @param config ModuleConfig information for this module
1046: * @throws ServletException if initialization cannot be performed
1047: */
1048: protected void initModuleForwards(ModuleConfig config)
1049: throws ServletException {
1050: if (log.isDebugEnabled()) {
1051: log.debug("Initializing module path '" + config.getPrefix()
1052: + "' forwards");
1053: }
1054:
1055: // Process forwards extensions.
1056: ForwardConfig[] forwards = config.findForwardConfigs();
1057:
1058: for (int i = 0; i < forwards.length; i++) {
1059: ForwardConfig forward = forwards[i];
1060:
1061: processForwardExtension(forward, config, null);
1062: }
1063:
1064: for (int i = 0; i < forwards.length; i++) {
1065: ForwardConfig forward = forwards[i];
1066:
1067: // Verify that required fields are all present for the forward
1068: if (forward.getPath() == null) {
1069: handleValueRequiredException("path", forward.getName(),
1070: "global forward");
1071: }
1072: }
1073: }
1074:
1075: /**
1076: * <p>Extend the forward's configuration as necessary. If actionConfig is
1077: * provided, then this method will process the forwardConfig as part
1078: * of that actionConfig. If actionConfig is null, the forwardConfig
1079: * will be processed as a global forward.</p>
1080: *
1081: * @param forwardConfig the configuration to process.
1082: * @param moduleConfig the module configuration for this module.
1083: * @param actionConfig If applicable, the config for the current action.
1084: * @throws ServletException if initialization cannot be performed.
1085: */
1086: protected void processForwardExtension(ForwardConfig forwardConfig,
1087: ModuleConfig moduleConfig, ActionConfig actionConfig)
1088: throws ServletException {
1089: try {
1090: if (!forwardConfig.isExtensionProcessed()) {
1091: if (log.isDebugEnabled()) {
1092: log.debug("Processing extensions for '"
1093: + forwardConfig.getName() + "'");
1094: }
1095:
1096: forwardConfig = processForwardConfigClass(
1097: forwardConfig, moduleConfig, actionConfig);
1098:
1099: forwardConfig
1100: .processExtends(moduleConfig, actionConfig);
1101: }
1102: } catch (ServletException e) {
1103: throw e;
1104: } catch (Exception e) {
1105: handleGeneralExtensionException("Forward", forwardConfig
1106: .getName(), e);
1107: }
1108: }
1109:
1110: /**
1111: * <p>Checks if the current forwardConfig is using the correct class based
1112: * on the class of its configuration ancestor. If actionConfig is
1113: * provided, then this method will process the forwardConfig as part
1114: * of that actionConfig. If actionConfig is null, the forwardConfig
1115: * will be processed as a global forward.</p>
1116: *
1117: * @param forwardConfig The forward to check.
1118: * @param moduleConfig The config for the current module.
1119: * @param actionConfig If applicable, the config for the current action.
1120: * @return The forward config using the correct class as determined by the
1121: * config's ancestor and its own overridden value.
1122: * @throws UnavailableException if an instance of the forward config class
1123: * cannot be created.
1124: * @throws ServletException on class creation error
1125: */
1126: protected ForwardConfig processForwardConfigClass(
1127: ForwardConfig forwardConfig, ModuleConfig moduleConfig,
1128: ActionConfig actionConfig) throws ServletException {
1129: String ancestor = forwardConfig.getExtends();
1130:
1131: if (ancestor == null) {
1132: // Nothing to do, then
1133: return forwardConfig;
1134: }
1135:
1136: // Make sure that this config is of the right class
1137: ForwardConfig baseConfig = null;
1138: if (actionConfig != null) {
1139: // Look for this in the actionConfig
1140: baseConfig = actionConfig.findForwardConfig(ancestor);
1141: }
1142:
1143: if (baseConfig == null) {
1144: // Either this is a forwardConfig that inherits a global config,
1145: // or actionConfig is null
1146: baseConfig = moduleConfig.findForwardConfig(ancestor);
1147: }
1148:
1149: if (baseConfig == null) {
1150: throw new UnavailableException("Unable to find "
1151: + "forward '" + ancestor + "' to extend.");
1152: }
1153:
1154: // Was our forwards's class overridden already?
1155: if (forwardConfig.getClass().equals(ActionForward.class)) {
1156: // Ensure that our forward is using the correct class
1157: if (!baseConfig.getClass().equals(forwardConfig.getClass())) {
1158: // Replace the config with an instance of the correct class
1159: ForwardConfig newForwardConfig = null;
1160: String baseConfigClassName = baseConfig.getClass()
1161: .getName();
1162:
1163: try {
1164: newForwardConfig = (ForwardConfig) RequestUtils
1165: .applicationInstance(baseConfigClassName);
1166:
1167: // copy the values
1168: BeanUtils.copyProperties(newForwardConfig,
1169: forwardConfig);
1170: } catch (Exception e) {
1171: handleCreationException(baseConfigClassName, e);
1172: }
1173:
1174: // replace forwardConfig with newForwardConfig
1175: if (actionConfig != null) {
1176: actionConfig.removeForwardConfig(forwardConfig);
1177: actionConfig.addForwardConfig(newForwardConfig);
1178: } else {
1179: // this is a global forward
1180: moduleConfig.removeForwardConfig(forwardConfig);
1181: moduleConfig.addForwardConfig(newForwardConfig);
1182: }
1183: forwardConfig = newForwardConfig;
1184: }
1185: }
1186:
1187: return forwardConfig;
1188: }
1189:
1190: /**
1191: * <p>Initialize the exception handlers for the specified module.</p>
1192: *
1193: * @param config ModuleConfig information for this module
1194: * @throws ServletException if initialization cannot be performed
1195: * @since Struts 1.3
1196: */
1197: protected void initModuleExceptionConfigs(ModuleConfig config)
1198: throws ServletException {
1199: if (log.isDebugEnabled()) {
1200: log.debug("Initializing module path '" + config.getPrefix()
1201: + "' forwards");
1202: }
1203:
1204: // Process exception config extensions.
1205: ExceptionConfig[] exceptions = config.findExceptionConfigs();
1206:
1207: for (int i = 0; i < exceptions.length; i++) {
1208: ExceptionConfig exception = exceptions[i];
1209:
1210: processExceptionExtension(exception, config, null);
1211: }
1212:
1213: for (int i = 0; i < exceptions.length; i++) {
1214: ExceptionConfig exception = exceptions[i];
1215:
1216: // Verify that required fields are all present for the config
1217: if (exception.getKey() == null) {
1218: handleValueRequiredException("key",
1219: exception.getType(), "global exception config");
1220: }
1221: }
1222: }
1223:
1224: /**
1225: * <p>Extend the exception's configuration as necessary. If actionConfig is
1226: * provided, then this method will process the exceptionConfig as part
1227: * of that actionConfig. If actionConfig is null, the exceptionConfig
1228: * will be processed as a global forward.</p>
1229: *
1230: * @param exceptionConfig the configuration to process.
1231: * @param moduleConfig the module configuration for this module.
1232: * @param actionConfig If applicable, the config for the current action.
1233: * @throws ServletException if initialization cannot be performed.
1234: */
1235: protected void processExceptionExtension(
1236: ExceptionConfig exceptionConfig, ModuleConfig moduleConfig,
1237: ActionConfig actionConfig) throws ServletException {
1238: try {
1239: if (!exceptionConfig.isExtensionProcessed()) {
1240: if (log.isDebugEnabled()) {
1241: log.debug("Processing extensions for '"
1242: + exceptionConfig.getType() + "'");
1243: }
1244:
1245: exceptionConfig = processExceptionConfigClass(
1246: exceptionConfig, moduleConfig, actionConfig);
1247:
1248: exceptionConfig.processExtends(moduleConfig,
1249: actionConfig);
1250: }
1251: } catch (ServletException e) {
1252: throw e;
1253: } catch (Exception e) {
1254: handleGeneralExtensionException("Exception",
1255: exceptionConfig.getType(), e);
1256: }
1257: }
1258:
1259: /**
1260: * <p>Checks if the current exceptionConfig is using the correct class
1261: * based on the class of its configuration ancestor. If actionConfig is
1262: * provided, then this method will process the exceptionConfig as part
1263: * of that actionConfig. If actionConfig is null, the exceptionConfig
1264: * will be processed as a global forward.</p>
1265: *
1266: * @param exceptionConfig The config to check.
1267: * @param moduleConfig The config for the current module.
1268: * @param actionConfig If applicable, the config for the current action.
1269: * @return The exception config using the correct class as determined by
1270: * the config's ancestor and its own overridden value.
1271: * @throws ServletException if an instance of the exception config class
1272: * cannot be created.
1273: */
1274: protected ExceptionConfig processExceptionConfigClass(
1275: ExceptionConfig exceptionConfig, ModuleConfig moduleConfig,
1276: ActionConfig actionConfig) throws ServletException {
1277: String ancestor = exceptionConfig.getExtends();
1278:
1279: if (ancestor == null) {
1280: // Nothing to do, then
1281: return exceptionConfig;
1282: }
1283:
1284: // Make sure that this config is of the right class
1285: ExceptionConfig baseConfig = null;
1286: if (actionConfig != null) {
1287: baseConfig = actionConfig.findExceptionConfig(ancestor);
1288: }
1289:
1290: if (baseConfig == null) {
1291: // This means either there's no actionConfig anyway, or the
1292: // ancestor is not defined within the action.
1293: baseConfig = moduleConfig.findExceptionConfig(ancestor);
1294: }
1295:
1296: if (baseConfig == null) {
1297: throw new UnavailableException("Unable to find "
1298: + "exception config '" + ancestor + "' to extend.");
1299: }
1300:
1301: // Was our config's class overridden already?
1302: if (exceptionConfig.getClass().equals(ExceptionConfig.class)) {
1303: // Ensure that our config is using the correct class
1304: if (!baseConfig.getClass().equals(
1305: exceptionConfig.getClass())) {
1306: // Replace the config with an instance of the correct class
1307: ExceptionConfig newExceptionConfig = null;
1308: String baseConfigClassName = baseConfig.getClass()
1309: .getName();
1310:
1311: try {
1312: newExceptionConfig = (ExceptionConfig) RequestUtils
1313: .applicationInstance(baseConfigClassName);
1314:
1315: // copy the values
1316: BeanUtils.copyProperties(newExceptionConfig,
1317: exceptionConfig);
1318: } catch (Exception e) {
1319: handleCreationException(baseConfigClassName, e);
1320: }
1321:
1322: // replace exceptionConfig with newExceptionConfig
1323: if (actionConfig != null) {
1324: actionConfig.removeExceptionConfig(exceptionConfig);
1325: actionConfig.addExceptionConfig(newExceptionConfig);
1326: } else {
1327: moduleConfig.removeExceptionConfig(exceptionConfig);
1328: moduleConfig.addExceptionConfig(newExceptionConfig);
1329: }
1330: exceptionConfig = newExceptionConfig;
1331: }
1332: }
1333:
1334: return exceptionConfig;
1335: }
1336:
1337: /**
1338: * <p>Initialize the action configs for the specified module.</p>
1339: *
1340: * @param config ModuleConfig information for this module
1341: * @throws ServletException if initialization cannot be performed
1342: * @since Struts 1.3
1343: */
1344: protected void initModuleActions(ModuleConfig config)
1345: throws ServletException {
1346: if (log.isDebugEnabled()) {
1347: log.debug("Initializing module path '" + config.getPrefix()
1348: + "' action configs");
1349: }
1350:
1351: // Process ActionConfig extensions.
1352: ActionConfig[] actionConfigs = config.findActionConfigs();
1353:
1354: for (int i = 0; i < actionConfigs.length; i++) {
1355: ActionConfig actionConfig = actionConfigs[i];
1356:
1357: processActionConfigExtension(actionConfig, config);
1358: }
1359:
1360: for (int i = 0; i < actionConfigs.length; i++) {
1361: ActionConfig actionConfig = actionConfigs[i];
1362:
1363: // Verify that required fields are all present for the forward
1364: // configs
1365: ForwardConfig[] forwards = actionConfig
1366: .findForwardConfigs();
1367:
1368: for (int j = 0; j < forwards.length; j++) {
1369: ForwardConfig forward = forwards[j];
1370:
1371: if (forward.getPath() == null) {
1372: handleValueRequiredException("path", forward
1373: .getName(), "action forward");
1374: }
1375: }
1376:
1377: // ... and the exception configs
1378: ExceptionConfig[] exceptions = actionConfig
1379: .findExceptionConfigs();
1380:
1381: for (int j = 0; j < exceptions.length; j++) {
1382: ExceptionConfig exception = exceptions[j];
1383:
1384: if (exception.getKey() == null) {
1385: handleValueRequiredException("key", exception
1386: .getType(), "action exception config");
1387: }
1388: }
1389: }
1390: }
1391:
1392: /**
1393: * <p>Extend the action's configuration as necessary.</p>
1394: *
1395: * @param actionConfig the configuration to process.
1396: * @param moduleConfig the module configuration for this module.
1397: * @throws ServletException if initialization cannot be performed.
1398: */
1399: protected void processActionConfigExtension(
1400: ActionConfig actionConfig, ModuleConfig moduleConfig)
1401: throws ServletException {
1402: try {
1403: if (!actionConfig.isExtensionProcessed()) {
1404: if (log.isDebugEnabled()) {
1405: log.debug("Processing extensions for '"
1406: + actionConfig.getPath() + "'");
1407: }
1408:
1409: actionConfig = processActionConfigClass(actionConfig,
1410: moduleConfig);
1411:
1412: actionConfig.processExtends(moduleConfig);
1413: }
1414:
1415: // Process forwards extensions.
1416: ForwardConfig[] forwards = actionConfig
1417: .findForwardConfigs();
1418: for (int i = 0; i < forwards.length; i++) {
1419: ForwardConfig forward = forwards[i];
1420: processForwardExtension(forward, moduleConfig,
1421: actionConfig);
1422: }
1423:
1424: // Process exception extensions.
1425: ExceptionConfig[] exceptions = actionConfig
1426: .findExceptionConfigs();
1427: for (int i = 0; i < exceptions.length; i++) {
1428: ExceptionConfig exception = exceptions[i];
1429: processExceptionExtension(exception, moduleConfig,
1430: actionConfig);
1431: }
1432: } catch (ServletException e) {
1433: throw e;
1434: } catch (Exception e) {
1435: handleGeneralExtensionException("Action", actionConfig
1436: .getPath(), e);
1437: }
1438: }
1439:
1440: /**
1441: * <p>Checks if the current actionConfig is using the correct class based
1442: * on the class of its ancestor ActionConfig.</p>
1443: *
1444: * @param actionConfig The action config to check.
1445: * @param moduleConfig The config for the current module.
1446: * @return The config object using the correct class as determined by the
1447: * config's ancestor and its own overridden value.
1448: * @throws ServletException if an instance of the action config class
1449: * cannot be created.
1450: */
1451: protected ActionConfig processActionConfigClass(
1452: ActionConfig actionConfig, ModuleConfig moduleConfig)
1453: throws ServletException {
1454: String ancestor = actionConfig.getExtends();
1455:
1456: if (ancestor == null) {
1457: // Nothing to do, then
1458: return actionConfig;
1459: }
1460:
1461: // Make sure that this config is of the right class
1462: ActionConfig baseConfig = moduleConfig
1463: .findActionConfig(ancestor);
1464:
1465: if (baseConfig == null) {
1466: throw new UnavailableException("Unable to find "
1467: + "action config for '" + ancestor + "' to extend.");
1468: }
1469:
1470: // Was our actionConfig's class overridden already?
1471: if (actionConfig.getClass().equals(ActionMapping.class)) {
1472: // Ensure that our config is using the correct class
1473: if (!baseConfig.getClass().equals(actionConfig.getClass())) {
1474: // Replace the config with an instance of the correct class
1475: ActionConfig newActionConfig = null;
1476: String baseConfigClassName = baseConfig.getClass()
1477: .getName();
1478:
1479: try {
1480: newActionConfig = (ActionConfig) RequestUtils
1481: .applicationInstance(baseConfigClassName);
1482:
1483: // copy the values
1484: BeanUtils.copyProperties(newActionConfig,
1485: actionConfig);
1486:
1487: // copy the forward and exception configs, too
1488: ForwardConfig[] forwards = actionConfig
1489: .findForwardConfigs();
1490:
1491: for (int i = 0; i < forwards.length; i++) {
1492: newActionConfig.addForwardConfig(forwards[i]);
1493: }
1494:
1495: ExceptionConfig[] exceptions = actionConfig
1496: .findExceptionConfigs();
1497:
1498: for (int i = 0; i < exceptions.length; i++) {
1499: newActionConfig
1500: .addExceptionConfig(exceptions[i]);
1501: }
1502: } catch (Exception e) {
1503: handleCreationException(baseConfigClassName, e);
1504: }
1505:
1506: // replace actionConfig with newActionConfig
1507: moduleConfig.removeActionConfig(actionConfig);
1508: moduleConfig.addActionConfig(newActionConfig);
1509: actionConfig = newActionConfig;
1510: }
1511: }
1512:
1513: return actionConfig;
1514: }
1515:
1516: /**
1517: * <p>Initialize the application <code>MessageResources</code> for the
1518: * specified module.</p>
1519: *
1520: * @param config ModuleConfig information for this module
1521: * @throws ServletException if initialization cannot be performed
1522: * @since Struts 1.1
1523: */
1524: protected void initModuleMessageResources(ModuleConfig config)
1525: throws ServletException {
1526: MessageResourcesConfig[] mrcs = config
1527: .findMessageResourcesConfigs();
1528:
1529: for (int i = 0; i < mrcs.length; i++) {
1530: if ((mrcs[i].getFactory() == null)
1531: || (mrcs[i].getParameter() == null)) {
1532: continue;
1533: }
1534:
1535: if (log.isDebugEnabled()) {
1536: log.debug("Initializing module path '"
1537: + config.getPrefix()
1538: + "' message resources from '"
1539: + mrcs[i].getParameter() + "'");
1540: }
1541:
1542: String factory = mrcs[i].getFactory();
1543:
1544: MessageResourcesFactory.setFactoryClass(factory);
1545:
1546: MessageResourcesFactory factoryObject = MessageResourcesFactory
1547: .createFactory();
1548:
1549: factoryObject.setConfig(mrcs[i]);
1550:
1551: MessageResources resources = factoryObject
1552: .createResources(mrcs[i].getParameter());
1553:
1554: resources.setReturnNull(mrcs[i].getNull());
1555: resources.setEscape(mrcs[i].isEscape());
1556: getServletContext().setAttribute(
1557: mrcs[i].getKey() + config.getPrefix(), resources);
1558: }
1559: }
1560:
1561: /**
1562: * <p>Create (if needed) and return a new <code>Digester</code> instance
1563: * that has been initialized to process Struts module configuration files
1564: * and configure a corresponding <code>ModuleConfig</code> object (which
1565: * must be pushed on to the evaluation stack before parsing begins).</p>
1566: *
1567: * @return A new configured <code>Digester</code> instance.
1568: * @throws ServletException if a Digester cannot be configured
1569: * @since Struts 1.1
1570: */
1571: protected Digester initConfigDigester() throws ServletException {
1572: // :FIXME: Where can ServletException be thrown?
1573: // Do we have an existing instance?
1574: if (configDigester != null) {
1575: return (configDigester);
1576: }
1577:
1578: // Create a new Digester instance with standard capabilities
1579: configDigester = new Digester();
1580: configDigester.setNamespaceAware(true);
1581: configDigester.setValidating(this .isValidating());
1582: configDigester.setUseContextClassLoader(true);
1583: configDigester.addRuleSet(new ConfigRuleSet());
1584:
1585: for (int i = 0; i < registrations.length; i += 2) {
1586: URL url = this .getClass().getResource(registrations[i + 1]);
1587:
1588: if (url != null) {
1589: configDigester.register(registrations[i], url
1590: .toString());
1591: }
1592: }
1593:
1594: this .addRuleSets();
1595:
1596: // Return the completely configured Digester instance
1597: return (configDigester);
1598: }
1599:
1600: /**
1601: * <p>Add any custom RuleSet instances to configDigester that have been
1602: * specified in the <code>rulesets</code> init parameter.</p>
1603: *
1604: * @throws ServletException if an error occurs
1605: */
1606: private void addRuleSets() throws ServletException {
1607: String rulesets = getServletConfig().getInitParameter(
1608: "rulesets");
1609:
1610: if (rulesets == null) {
1611: rulesets = "";
1612: }
1613:
1614: rulesets = rulesets.trim();
1615:
1616: String ruleset;
1617:
1618: while (rulesets.length() > 0) {
1619: int comma = rulesets.indexOf(",");
1620:
1621: if (comma < 0) {
1622: ruleset = rulesets.trim();
1623: rulesets = "";
1624: } else {
1625: ruleset = rulesets.substring(0, comma).trim();
1626: rulesets = rulesets.substring(comma + 1).trim();
1627: }
1628:
1629: if (log.isDebugEnabled()) {
1630: log
1631: .debug("Configuring custom Digester Ruleset of type "
1632: + ruleset);
1633: }
1634:
1635: try {
1636: RuleSet instance = (RuleSet) RequestUtils
1637: .applicationInstance(ruleset);
1638:
1639: this .configDigester.addRuleSet(instance);
1640: } catch (Exception e) {
1641: log
1642: .error(
1643: "Exception configuring custom Digester RuleSet",
1644: e);
1645: throw new ServletException(e);
1646: }
1647: }
1648: }
1649:
1650: /**
1651: * <p>Check the status of the <code>validating</code> initialization
1652: * parameter.</p>
1653: *
1654: * @return true if the module Digester should validate.
1655: */
1656: private boolean isValidating() {
1657: boolean validating = true;
1658: String value = getServletConfig()
1659: .getInitParameter("validating");
1660:
1661: if ("false".equalsIgnoreCase(value)
1662: || "no".equalsIgnoreCase(value)
1663: || "n".equalsIgnoreCase(value)
1664: || "0".equalsIgnoreCase(value)) {
1665: validating = false;
1666: }
1667:
1668: return validating;
1669: }
1670:
1671: /**
1672: * <p>Initialize our internal MessageResources bundle.</p>
1673: *
1674: * @throws ServletException if we cannot initialize these resources
1675: * @throws UnavailableException if we cannot load resources
1676: */
1677: protected void initInternal() throws ServletException {
1678: try {
1679: internal = MessageResources
1680: .getMessageResources(internalName);
1681: } catch (MissingResourceException e) {
1682: log.error("Cannot load internal resources from '"
1683: + internalName + "'", e);
1684: throw new UnavailableException(
1685: "Cannot load internal resources from '"
1686: + internalName + "'");
1687: }
1688: }
1689:
1690: /**
1691: * <p>Parse the configuration documents specified by the
1692: * <code>chainConfig</code> init-param to configure the default {@link
1693: * org.apache.commons.chain.Catalog} that is registered in the {@link
1694: * CatalogFactory} instance for this application.</p>
1695: *
1696: * @throws ServletException if an error occurs.
1697: */
1698: protected void initChain() throws ServletException {
1699: // Parse the configuration file specified by path or resource
1700: try {
1701: String value;
1702:
1703: value = getServletConfig().getInitParameter("chainConfig");
1704:
1705: if (value != null) {
1706: chainConfig = value;
1707: }
1708:
1709: ConfigParser parser = new ConfigParser();
1710: List urls = splitAndResolvePaths(chainConfig);
1711: URL resource;
1712:
1713: for (Iterator i = urls.iterator(); i.hasNext();) {
1714: resource = (URL) i.next();
1715: log.info("Loading chain catalog from " + resource);
1716: parser.parse(resource);
1717: }
1718: } catch (Exception e) {
1719: log.error("Exception loading resources", e);
1720: throw new ServletException(e);
1721: }
1722: }
1723:
1724: /**
1725: * <p>Initialize other global characteristics of the controller
1726: * servlet.</p>
1727: *
1728: * @throws ServletException if we cannot initialize these resources
1729: */
1730: protected void initOther() throws ServletException {
1731: String value;
1732:
1733: value = getServletConfig().getInitParameter("config");
1734:
1735: if (value != null) {
1736: config = value;
1737: }
1738:
1739: // Backwards compatibility for form beans of Java wrapper classes
1740: // Set to true for strict Struts 1.0 compatibility
1741: value = getServletConfig().getInitParameter("convertNull");
1742:
1743: if ("true".equalsIgnoreCase(value)
1744: || "yes".equalsIgnoreCase(value)
1745: || "on".equalsIgnoreCase(value)
1746: || "y".equalsIgnoreCase(value)
1747: || "1".equalsIgnoreCase(value)) {
1748: convertNull = true;
1749: }
1750:
1751: if (convertNull) {
1752: ConvertUtils.deregister();
1753: ConvertUtils.register(new BigDecimalConverter(null),
1754: BigDecimal.class);
1755: ConvertUtils.register(new BigIntegerConverter(null),
1756: BigInteger.class);
1757: ConvertUtils.register(new BooleanConverter(null),
1758: Boolean.class);
1759: ConvertUtils.register(new ByteConverter(null), Byte.class);
1760: ConvertUtils.register(new CharacterConverter(null),
1761: Character.class);
1762: ConvertUtils.register(new DoubleConverter(null),
1763: Double.class);
1764: ConvertUtils
1765: .register(new FloatConverter(null), Float.class);
1766: ConvertUtils.register(new IntegerConverter(null),
1767: Integer.class);
1768: ConvertUtils.register(new LongConverter(null), Long.class);
1769: ConvertUtils
1770: .register(new ShortConverter(null), Short.class);
1771: }
1772: }
1773:
1774: /**
1775: * <p>Initialize the servlet mapping under which our controller servlet is
1776: * being accessed. This will be used in the <code>&html:form></code>
1777: * tag to generate correct destination URLs for form submissions.</p>
1778: *
1779: * @throws ServletException if error happens while scanning web.xml
1780: */
1781: protected void initServlet() throws ServletException {
1782: // Remember our servlet name
1783: this .servletName = getServletConfig().getServletName();
1784:
1785: // Prepare a Digester to scan the web application deployment descriptor
1786: Digester digester = new Digester();
1787:
1788: digester.push(this );
1789: digester.setNamespaceAware(true);
1790: digester.setValidating(false);
1791:
1792: // Register our local copy of the DTDs that we can find
1793: for (int i = 0; i < registrations.length; i += 2) {
1794: URL url = this .getClass().getResource(registrations[i + 1]);
1795:
1796: if (url != null) {
1797: digester.register(registrations[i], url.toString());
1798: }
1799: }
1800:
1801: // Configure the processing rules that we need
1802: digester.addCallMethod("web-app/servlet-mapping",
1803: "addServletMapping", 2);
1804: digester
1805: .addCallParam("web-app/servlet-mapping/servlet-name", 0);
1806: digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
1807:
1808: // Process the web application deployment descriptor
1809: if (log.isDebugEnabled()) {
1810: log
1811: .debug("Scanning web.xml for controller servlet mapping");
1812: }
1813:
1814: InputStream input = getServletContext().getResourceAsStream(
1815: "/WEB-INF/web.xml");
1816:
1817: if (input == null) {
1818: log.error(internal.getMessage("configWebXml"));
1819: throw new ServletException(internal
1820: .getMessage("configWebXml"));
1821: }
1822:
1823: try {
1824: digester.parse(input);
1825: } catch (IOException e) {
1826: log.error(internal.getMessage("configWebXml"), e);
1827: throw new ServletException(e);
1828: } catch (SAXException e) {
1829: log.error(internal.getMessage("configWebXml"), e);
1830: throw new ServletException(e);
1831: } finally {
1832: try {
1833: input.close();
1834: } catch (IOException e) {
1835: log.error(internal.getMessage("configWebXml"), e);
1836: throw new ServletException(e);
1837: }
1838: }
1839:
1840: // Record a servlet context attribute (if appropriate)
1841: if (log.isDebugEnabled()) {
1842: log.debug("Mapping for servlet '" + servletName + "' = '"
1843: + servletMapping + "'");
1844: }
1845:
1846: if (servletMapping != null) {
1847: getServletContext().setAttribute(Globals.SERVLET_KEY,
1848: servletMapping);
1849: }
1850: }
1851:
1852: /**
1853: * <p>Takes a comma-delimited string and splits it into paths, then
1854: * resolves those paths using the ServletContext and appropriate
1855: * ClassLoader. When loading from the classloader, multiple resources per
1856: * path are supported to support, for example, multiple jars containing
1857: * the same named config file.</p>
1858: *
1859: * @param paths A comma-delimited string of paths
1860: * @return A list of resolved URL's for all found resources
1861: * @throws ServletException if a servlet exception is thrown
1862: */
1863: protected List splitAndResolvePaths(String paths)
1864: throws ServletException {
1865: ClassLoader loader = Thread.currentThread()
1866: .getContextClassLoader();
1867:
1868: if (loader == null) {
1869: loader = this .getClass().getClassLoader();
1870: }
1871:
1872: ArrayList resolvedUrls = new ArrayList();
1873:
1874: URL resource;
1875: String path = null;
1876:
1877: try {
1878: // Process each specified resource path
1879: while (paths.length() > 0) {
1880: resource = null;
1881:
1882: int comma = paths.indexOf(',');
1883:
1884: if (comma >= 0) {
1885: path = paths.substring(0, comma).trim();
1886: paths = paths.substring(comma + 1);
1887: } else {
1888: path = paths.trim();
1889: paths = "";
1890: }
1891:
1892: if (path.length() < 1) {
1893: break;
1894: }
1895:
1896: if (path.charAt(0) == '/') {
1897: resource = getServletContext().getResource(path);
1898: }
1899:
1900: if (resource == null) {
1901: if (log.isDebugEnabled()) {
1902: log.debug("Unable to locate " + path
1903: + " in the servlet context, "
1904: + "trying classloader.");
1905: }
1906:
1907: Enumeration e = loader.getResources(path);
1908:
1909: if (!e.hasMoreElements()) {
1910: String msg = internal.getMessage(
1911: "configMissing", path);
1912:
1913: log.error(msg);
1914: throw new UnavailableException(msg);
1915: } else {
1916: while (e.hasMoreElements()) {
1917: resolvedUrls.add(e.nextElement());
1918: }
1919: }
1920: } else {
1921: resolvedUrls.add(resource);
1922: }
1923: }
1924: } catch (MalformedURLException e) {
1925: handleConfigException(path, e);
1926: } catch (IOException e) {
1927: handleConfigException(path, e);
1928: }
1929:
1930: return resolvedUrls;
1931: }
1932:
1933: /**
1934: * <p>Perform the standard request processing for this request, and create
1935: * the corresponding response.</p>
1936: *
1937: * @param request The servlet request we are processing
1938: * @param response The servlet response we are creating
1939: * @throws IOException if an input/output error occurs
1940: * @throws ServletException if a servlet exception is thrown
1941: */
1942: protected void process(HttpServletRequest request,
1943: HttpServletResponse response) throws IOException,
1944: ServletException {
1945: ModuleUtils.getInstance().selectModule(request,
1946: getServletContext());
1947:
1948: ModuleConfig config = getModuleConfig(request);
1949:
1950: RequestProcessor processor = getProcessorForModule(config);
1951:
1952: if (processor == null) {
1953: processor = getRequestProcessor(config);
1954: }
1955:
1956: processor.process(request, response);
1957: }
1958: }
|