package org.apache.struts.action; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.math.BigDecimal; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.sql.SQLException; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.MissingResourceException; import javax.servlet.ServletException; import javax.servlet.UnavailableException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.sql.DataSource; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.Converter; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.beanutils.converters.BigDecimalConverter; import org.apache.commons.beanutils.converters.BigIntegerConverter; import org.apache.commons.beanutils.converters.BooleanConverter; import org.apache.commons.beanutils.converters.ByteConverter; import org.apache.commons.beanutils.converters.CharacterConverter; import org.apache.commons.beanutils.converters.DoubleConverter; import org.apache.commons.beanutils.converters.FloatConverter; import org.apache.commons.beanutils.converters.IntegerConverter; import org.apache.commons.beanutils.converters.LongConverter; import org.apache.commons.beanutils.converters.ShortConverter; import org.apache.commons.collections.FastHashMap; import org.apache.commons.digester.Digester; import org.apache.commons.digester.RuleSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.config.ActionConfig; import org.apache.struts.config.ApplicationConfig; import org.apache.struts.config.ConfigRuleSet; import org.apache.struts.config.ControllerConfig; import org.apache.struts.config.DataSourceConfig; import org.apache.struts.config.FormBeanConfig; import org.apache.struts.config.ForwardConfig; import org.apache.struts.config.MessageResourcesConfig; import org.apache.struts.config.ModuleConfig; import org.apache.struts.config.ModuleConfigFactory; import org.apache.struts.config.PlugInConfig; import org.apache.struts.config.impl.ModuleConfigImpl; import org.apache.struts.util.GenericDataSource; import org.apache.struts.util.MessageResources; import org.apache.struts.util.MessageResourcesFactory; import org.apache.struts.util.RequestUtils; import org.apache.struts.util.ServletContextWriter; import org.xml.sax.InputSource; import org.xml.sax.SAXException; public class ActionServlet extends HttpServlet { protected String config = "/WEB-INF/struts-config.xml"; protected Digester configDigester = null; protected boolean convertNull = false; protected FastHashMap dataSources = new FastHashMap(); protected int debug = 0; protected MessageResources internal = null; protected String internalName = "org.apache.struts.action.ActionResources"; protected static Log log = LogFactory.getLog(ActionServlet.class); protected RequestProcessor processor = null; protected String[] registrations = new String[] { "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN", "/org/apache/struts/resources/struts-config_1_0.dtd", "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN", "/org/apache/struts/resources/struts-config_1_1.dtd", "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN", "/org/apache/struts/resources/web-app_2_2.dtd", "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN", "/org/apache/struts/resources/web-app_2_3.dtd" }; protected String servletMapping = null; protected String servletName = null; public void destroy() { if (log.isDebugEnabled()) log.debug(this.internal.getMessage("finalizing")); destroyModules(); destroyDataSources(); destroyInternal(); getServletContext().removeAttribute("org.apache.struts.action.ACTION_SERVLET"); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) classLoader = ActionServlet.class.getClassLoader(); try { LogFactory.release(classLoader); } catch (Throwable t) {} } public void init() throws ServletException { initInternal(); initOther(); initServlet(); getServletContext().setAttribute("org.apache.struts.action.ACTION_SERVLET", this); ModuleConfig moduleConfig = initModuleConfig("", this.config); initModuleMessageResources(moduleConfig); initModuleDataSources(moduleConfig); initModulePlugIns(moduleConfig); moduleConfig.freeze(); Enumeration names = getServletConfig().getInitParameterNames(); while (names.hasMoreElements()) { String name = names.nextElement(); if (!name.startsWith("config/")) continue; String prefix = name.substring(6); moduleConfig = initModuleConfig(prefix, getServletConfig().getInitParameter(name)); initModuleMessageResources(moduleConfig); initModuleDataSources(moduleConfig); initModulePlugIns(moduleConfig); moduleConfig.freeze(); } destroyConfigDigester(); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } public void addServletMapping(String servletName, String urlPattern) { if (log.isDebugEnabled()) log.debug("Process servletName=" + servletName + ", urlPattern=" + urlPattern); if (servletName == null) return; if (servletName.equals(this.servletName)) this.servletMapping = urlPattern; } public DataSource findDataSource(String key) { if (key == null) return (DataSource)this.dataSources.get("org.apache.struts.action.DATA_SOURCE"); return (DataSource)this.dataSources.get(key); } public ActionFormBean findFormBean(String name) { ActionFormBeans afb = (ActionFormBeans)getServletContext().getAttribute("org.apache.struts.action.FORM_BEANS"); if (afb == null) return null; return afb.findFormBean(name); } public ActionForward findForward(String name) { ActionForwards af = (ActionForwards)getServletContext().getAttribute("org.apache.struts.action.FORWARDS"); if (af == null) return null; return af.findForward(name); } public ActionMapping findMapping(String path) { ActionMappings am = (ActionMappings)getServletContext().getAttribute("org.apache.struts.action.MAPPINGS"); if (am == null) return null; return am.findMapping(path); } public int getDebug() { return this.debug; } public MessageResources getInternal() { return this.internal; } public MessageResources getResources() { return (MessageResources)getServletContext().getAttribute("org.apache.struts.action.MESSAGE"); } public void log(String message, int level) { if (this.debug >= level) log(message); } protected void destroyApplications() { destroyModules(); } protected void destroyModules() { ArrayList values = new ArrayList(); Enumeration names = getServletContext().getAttributeNames(); while (names.hasMoreElements()) values.add(names.nextElement()); Iterator keys = values.iterator(); while (keys.hasNext()) { String name = keys.next(); Object value = getServletContext().getAttribute(name); if (value instanceof ModuleConfig) { ModuleConfig config = (ModuleConfig)value; try { getRequestProcessor(config).destroy(); } catch (ServletException e) { log.error(e); } getServletContext().removeAttribute(name); PlugIn[] plugIns = (PlugIn[])getServletContext().getAttribute("org.apache.struts.action.PLUG_INS" + config.getPrefix()); if (plugIns != null) { for (int i = 0; i < plugIns.length; i++) { int j = plugIns.length - i + 1; plugIns[j].destroy(); } getServletContext().removeAttribute("org.apache.struts.action.PLUG_INS" + config.getPrefix()); } } } } protected void destroyConfigDigester() { this.configDigester = null; } protected void destroyDataSources() { synchronized (this.dataSources) { Iterator keys = this.dataSources.keySet().iterator(); while (keys.hasNext()) { String key = keys.next(); getServletContext().removeAttribute(key); DataSource dataSource = findDataSource(key); if (dataSource instanceof GenericDataSource) { if (log.isDebugEnabled()) log.debug(this.internal.getMessage("dataSource.destroy", key)); try { ((GenericDataSource)dataSource).close(); } catch (SQLException e) { log.error(this.internal.getMessage("destroyDataSource", key), e); } } } this.dataSources.clear(); } } protected void destroyInternal() { this.internal = null; } protected ApplicationConfig getApplicationConfig(HttpServletRequest request) { return new ApplicationConfig((ModuleConfigImpl)getModuleConfig(request)); } protected ModuleConfig getModuleConfig(HttpServletRequest request) { ModuleConfig config = (ModuleConfig)request.getAttribute("org.apache.struts.action.MODULE"); if (config == null) config = (ModuleConfig)getServletContext().getAttribute("org.apache.struts.action.MODULE"); return config; } protected synchronized RequestProcessor getRequestProcessor(ModuleConfig config) throws ServletException { String key = "org.apache.struts.action.REQUEST_PROCESSOR" + config.getPrefix(); RequestProcessor processor = (RequestProcessor)getServletContext().getAttribute(key); if (processor == null) { try { processor = (RequestProcessor)RequestUtils.applicationInstance(config.getControllerConfig().getProcessorClass()); } catch (Exception e) { throw new UnavailableException("Cannot initialize RequestProcessor of class " + config.getControllerConfig().getProcessorClass() + ": " + e); } processor.init(this, config); getServletContext().setAttribute(key, processor); } return processor; } protected ApplicationConfig initApplicationConfig(String prefix, String path) throws ServletException { return new ApplicationConfig((ModuleConfigImpl)initModuleConfig(prefix, path)); } protected ModuleConfig initModuleConfig(String prefix, String paths) throws ServletException { if (log.isDebugEnabled()) log.debug("Initializing module path '" + prefix + "' configuration from '" + paths + "'"); ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory(); ModuleConfig config = factoryObject.createModuleConfig(prefix); String mapping = getServletConfig().getInitParameter("mapping"); if (mapping != null) config.setActionMappingClass(mapping); Digester digester = initConfigDigester(); while (paths.length() > 0) { digester.push(config); String path = null; int comma = paths.indexOf(','); if (comma >= 0) { path = paths.substring(0, comma).trim(); paths = paths.substring(comma + 1); } else { path = paths.trim(); paths = ""; } if (path.length() < 1) break; parseModuleConfigFile(prefix, paths, config, digester, path); } FormBeanConfig[] fbs = config.findFormBeanConfigs(); for (int i = 0; i < fbs.length; i++) { if (fbs[i].getDynamic()) DynaActionFormClass.createDynaActionFormClass(fbs[i]); } if (prefix.length() < 1) { defaultControllerConfig(config); defaultMessageResourcesConfig(config); defaultFormBeansConfig(config); defaultForwardsConfig(config); defaultMappingsConfig(config); } return config; } private void parseModuleConfigFile(String prefix, String paths, ModuleConfig config, Digester digester, String path) throws UnavailableException { InputStream input = null; try { URL url = getServletContext().getResource(path); InputSource is = new InputSource(url.toExternalForm()); input = getServletContext().getResourceAsStream(path); is.setByteStream(input); digester.parse(is); getServletContext().setAttribute("org.apache.struts.action.MODULE" + prefix, config); } catch (MalformedURLException e) { handleConfigException(paths, e); } catch (IOException e) { handleConfigException(paths, e); } catch (SAXException e) { handleConfigException(paths, e); } finally { if (input != null) try { input.close(); } catch (IOException e) { throw new UnavailableException(e.getMessage()); } } } private void handleConfigException(String paths, Exception e) throws UnavailableException { log.error(this.internal.getMessage("configParse", paths), e); throw new UnavailableException(this.internal.getMessage("configParse", paths)); } protected void initApplicationDataSources(ModuleConfig config) throws ServletException { initModuleDataSources(config); } protected void initModuleDataSources(ModuleConfig config) throws ServletException { if (log.isDebugEnabled()) log.debug("Initializing module path '" + config.getPrefix() + "' data sources"); ServletContextWriter scw = new ServletContextWriter(getServletContext()); DataSourceConfig[] dscs = config.findDataSourceConfigs(); if (dscs == null) dscs = new DataSourceConfig[0]; this.dataSources.setFast(false); for (int i = 0; i < dscs.length; i++) { if (log.isDebugEnabled()) log.debug("Initializing module path '" + config.getPrefix() + "' data source '" + dscs[i].getKey() + "'"); DataSource ds = null; try { ds = (DataSource)RequestUtils.applicationInstance(dscs[i].getType()); BeanUtils.populate(ds, dscs[i].getProperties()); if (ds instanceof GenericDataSource) ((GenericDataSource)ds).open(); ds.setLogWriter((PrintWriter)scw); } catch (Exception e) { log.error(this.internal.getMessage("dataSource.init", dscs[i].getKey()), e); throw new UnavailableException(this.internal.getMessage("dataSource.init", dscs[i].getKey())); } getServletContext().setAttribute(dscs[i].getKey() + config.getPrefix(), ds); this.dataSources.put(dscs[i].getKey(), ds); } this.dataSources.setFast(true); if ("".equals(config.getPrefix())) initDataSources(); } protected void initApplicationPlugIns(ModuleConfig config) throws ServletException { initModulePlugIns(config); } protected void initModulePlugIns(ModuleConfig config) throws ServletException { if (log.isDebugEnabled()) log.debug("Initializing module path '" + config.getPrefix() + "' plug ins"); PlugInConfig[] plugInConfigs = config.findPlugInConfigs(); PlugIn[] plugIns = new PlugIn[plugInConfigs.length]; getServletContext().setAttribute("org.apache.struts.action.PLUG_INS" + config.getPrefix(), plugIns); for (int i = 0; i < plugIns.length; i++) { try { plugIns[i] = (PlugIn)RequestUtils.applicationInstance(plugInConfigs[i].getClassName()); BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties()); try { PropertyUtils.setProperty(plugIns[i], "currentPlugInConfigObject", plugInConfigs[i]); } catch (Exception e) {} plugIns[i].init(this, config); } catch (ServletException e) { throw e; } catch (Exception e) { String errMsg = this.internal.getMessage("plugIn.init", plugInConfigs[i].getClassName()); log(errMsg, e); throw new UnavailableException(errMsg); } } } protected void initApplicationMessageResources(ModuleConfig config) throws ServletException { initModuleMessageResources(config); } protected void initModuleMessageResources(ModuleConfig config) throws ServletException { MessageResourcesConfig[] mrcs = config.findMessageResourcesConfigs(); for (int i = 0; i < mrcs.length; i++) { if (mrcs[i].getFactory() != null && mrcs[i].getParameter() != null) { if (log.isDebugEnabled()) log.debug("Initializing module path '" + config.getPrefix() + "' message resources from '" + mrcs[i].getParameter() + "'"); String factory = mrcs[i].getFactory(); MessageResourcesFactory.setFactoryClass(factory); MessageResourcesFactory factoryObject = MessageResourcesFactory.createFactory(); MessageResources resources = factoryObject.createResources(mrcs[i].getParameter()); resources.setReturnNull(mrcs[i].getNull()); getServletContext().setAttribute(mrcs[i].getKey() + config.getPrefix(), resources); } } } protected Digester initConfigDigester() throws ServletException { if (this.configDigester != null) return this.configDigester; boolean validating = true; String value = getServletConfig().getInitParameter("validating"); if ("false".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value) || "n".equalsIgnoreCase(value) || "0".equalsIgnoreCase(value)) validating = false; this.configDigester = new Digester(); this.configDigester.setNamespaceAware(true); this.configDigester.setValidating(validating); this.configDigester.setUseContextClassLoader(true); this.configDigester.addRuleSet((RuleSet)new ConfigRuleSet()); for (int i = 0; i < this.registrations.length; i += 2) { URL url = getClass().getResource(this.registrations[i + 1]); if (url != null) this.configDigester.register(this.registrations[i], url.toString()); } String rulesets = getServletConfig().getInitParameter("rulesets"); if (rulesets == null) rulesets = ""; rulesets = rulesets.trim(); String ruleset = null; while (rulesets.length() > 0) { int comma = rulesets.indexOf(","); if (comma < 0) { ruleset = rulesets.trim(); rulesets = ""; } else { ruleset = rulesets.substring(0, comma).trim(); rulesets = rulesets.substring(comma + 1).trim(); } if (log.isDebugEnabled()) log.debug("Configuring custom Digester Ruleset of type " + ruleset); try { RuleSet instance = (RuleSet)RequestUtils.applicationInstance(ruleset); this.configDigester.addRuleSet(instance); } catch (Exception e) { log.error("Exception configuring custom Digester RuleSet", e); throw new ServletException(e); } } return this.configDigester; } protected void initDataSources() throws ServletException {} protected void initInternal() throws ServletException { try { this.internal = MessageResources.getMessageResources(this.internalName); } catch (MissingResourceException e) { log.error("Cannot load internal resources from '" + this.internalName + "'", e); throw new UnavailableException("Cannot load internal resources from '" + this.internalName + "'"); } } protected void initOther() throws ServletException { String value = null; value = getServletConfig().getInitParameter("config"); if (value != null) this.config = value; value = getServletConfig().getInitParameter("debug"); if (value != null) try { this.debug = Integer.parseInt(value); } catch (NumberFormatException e) { this.debug = 0; } value = getServletConfig().getInitParameter("convertNull"); if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value) || "y".equalsIgnoreCase(value) || "1".equalsIgnoreCase(value)) this.convertNull = true; if (this.convertNull) { ConvertUtils.deregister(); ConvertUtils.register((Converter)new BigDecimalConverter(null), BigDecimal.class); ConvertUtils.register((Converter)new BigIntegerConverter(null), BigInteger.class); ConvertUtils.register((Converter)new BooleanConverter(null), Boolean.class); ConvertUtils.register((Converter)new ByteConverter(null), Byte.class); ConvertUtils.register((Converter)new CharacterConverter(null), Character.class); ConvertUtils.register((Converter)new DoubleConverter(null), Double.class); ConvertUtils.register((Converter)new FloatConverter(null), Float.class); ConvertUtils.register((Converter)new IntegerConverter(null), Integer.class); ConvertUtils.register((Converter)new LongConverter(null), Long.class); ConvertUtils.register((Converter)new ShortConverter(null), Short.class); } } protected void initServlet() throws ServletException { this.servletName = getServletConfig().getServletName(); Digester digester = new Digester(); digester.push(this); digester.setNamespaceAware(true); digester.setValidating(false); for (int i = 0; i < this.registrations.length; i += 2) { URL url = getClass().getResource(this.registrations[i + 1]); if (url != null) digester.register(this.registrations[i], url.toString()); } digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2); digester.addCallParam("web-app/servlet-mapping/servlet-name", 0); digester.addCallParam("web-app/servlet-mapping/url-pattern", 1); if (log.isDebugEnabled()) log.debug("Scanning web.xml for controller servlet mapping"); InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml"); try { digester.parse(input); } catch (IOException e) { log.error(this.internal.getMessage("configWebXml"), e); throw new ServletException(e); } catch (SAXException e) { log.error(this.internal.getMessage("configWebXml"), e); throw new ServletException(e); } finally { if (input != null) try { input.close(); } catch (IOException e) { log.error(this.internal.getMessage("configWebXml"), e); throw new ServletException(e); } } if (log.isDebugEnabled()) log.debug("Mapping for servlet '" + this.servletName + "' = '" + this.servletMapping + "'"); if (this.servletMapping != null) getServletContext().setAttribute("org.apache.struts.action.SERVLET_MAPPING", this.servletMapping); } protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { RequestUtils.selectModule(request, getServletContext()); getRequestProcessor(getModuleConfig(request)).process(request, response); } private void defaultControllerConfig(ModuleConfig config) { String value = null; ControllerConfig cc = config.getControllerConfig(); value = getServletConfig().getInitParameter("bufferSize"); if (value != null) cc.setBufferSize(Integer.parseInt(value)); value = getServletConfig().getInitParameter("content"); if (value != null) cc.setContentType(value); value = getServletConfig().getInitParameter("locale"); if (value != null) if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value)) { cc.setLocale(true); } else { cc.setLocale(false); } value = getServletConfig().getInitParameter("maxFileSize"); if (value != null) cc.setMaxFileSize(value); value = getServletConfig().getInitParameter("nocache"); if (value != null) if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value)) { cc.setNocache(true); } else { cc.setNocache(false); } value = getServletConfig().getInitParameter("multipartClass"); if (value != null) cc.setMultipartClass(value); value = getServletConfig().getInitParameter("tempDir"); if (value != null) cc.setTempDir(value); } private void defaultFormBeansConfig(ModuleConfig config) { FormBeanConfig[] fbcs = config.findFormBeanConfigs(); ActionFormBeans afb = new ActionFormBeans(); afb.setFast(false); for (int i = 0; i < fbcs.length; i++) afb.addFormBean((ActionFormBean)fbcs[i]); afb.setFast(true); getServletContext().setAttribute("org.apache.struts.action.FORM_BEANS", afb); } private void defaultForwardsConfig(ModuleConfig config) { ForwardConfig[] fcs = config.findForwardConfigs(); ActionForwards af = new ActionForwards(); af.setFast(false); for (int i = 0; i < fcs.length; i++) af.addForward((ActionForward)fcs[i]); af.setFast(true); getServletContext().setAttribute("org.apache.struts.action.FORWARDS", af); } private void defaultMappingsConfig(ModuleConfig config) { ActionConfig[] acs = config.findActionConfigs(); ActionMappings am = new ActionMappings(); am.setServlet(this); am.setFast(false); for (int i = 0; i < acs.length; i++) am.addMapping((ActionMapping)acs[i]); am.setFast(true); getServletContext().setAttribute("org.apache.struts.action.MAPPINGS", am); } private void defaultMessageResourcesConfig(ModuleConfig config) { String value = null; MessageResourcesConfig mrc = config.findMessageResourcesConfig("org.apache.struts.action.MESSAGE"); if (mrc == null) { mrc = new MessageResourcesConfig(); mrc.setKey("org.apache.struts.action.MESSAGE"); config.addMessageResourcesConfig(mrc); } value = getServletConfig().getInitParameter("application"); if (value != null) mrc.setParameter(value); value = getServletConfig().getInitParameter("factory"); if (value != null) mrc.setFactory(value); value = getServletConfig().getInitParameter("null"); if (value != null) if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes")) { mrc.setNull(true); } else { mrc.setNull(false); } } }