实现有价值的IT服务
全部博文(709)
分类: Java
2006-07-21 15:26:06
第一篇 struts的初始化
struts 的核心类是org.apache.struts.action.ActionServlet,这个类将会在struts第一次使用时,
作为servlet初始化并存入servlet容器。很显然的,初始化将会调用init方法初始化相应的数据。
1.Init()流程:
1) 调用initInternal()方法,初始化Struts框架内在的消息资源,如系统日志相关的通知、警告和错误消息
initInternal()方法: 通过调用MessageResources.getMessageResources(internalName)方法生成一个 MessageResources类,getMessageResources是通过调用MessageResourcesFactory. createResources(config)来实现的。至于MessageResourcesFactory是一个abstract类,任何 继承自它的类都要实现createResources方法,生成MessageResources对象。整个程序生成 MessageResourcesFactory使用了如下技巧: MessageResourcesFactory.factoryClass = factoryClass; MessageResourcesFactory.clazz = null; 首先会通过factoryClass来定义一个类全名,然后通过ClassLoader.loadClass (factoryClass)方法来生成这个类,并赋给clazz,然后通过newInstance来生成一个对象。 在本程序中,生成MessageResources对象实际就是对如下属性进行了初始化: this.factory = factory;("org.apache.struts.util.PropertyMessageResourcesFactory") this.config = config;("org.apache.struts.action.ActionResources") this.returnNull = returnNull;(true/false) 对于MessageResources类的作用是根据不同的Locate来格式化相应的string。或者把你需要改变 的string存放到数组中,然后通过getMessage(Locale locale, String key, Object args[]) 方法来格式化。然后把格式好的string存放到HashMap里,这样就可以为以后重用。这里的key是 使用的locale.toString() + "." + key 在PropertyMessageResources中的loadLocale方法用来读取resource的初始化信息。首先它会 通过一个HashMap检测这个localKey相关的message是否已经被初始化了,如果被初始化过就跳 出,检测的方法是locales.get(localeKey) != null。 然后会读取如下一个文件: org/apache/struts/action/ActionResources_(localKey).properties,然后进行如下操作: Properties props = new Properties(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); is = classLoader.getResourceAsStream(name); props.load(is); Iterator names = props.keySet().iterator(); while (names.hasNext()) { String key = (String) names.next(); if (log.isTraceEnabled()) { log.trace(" Saving message key '" + messageKey(localeKey, key)); } messages.put(messageKey(localeKey, key), props.getProperty(key)); } PropertyMessageResources 就是通过上面的loadLocale方法查找与Locale locale, String key 相对对应的Message.查找的次序如下locale.toString(),然后是 localeKey = localeKey.substring(0, underscore),然后是defaultLocale,然后是key。 最后,resource类的结构如下: PropertyMessageResources extends MessageResources PropertyMessageResourcesFactory extends MessageResourcesFactory |
2) 调用initOther()方法,从web.xml中加载ActionServlet的初始化参数,如config等
initOther()方法: 从servlet中获取config和debug两个参数,然后初始化ConvertUtils对象。由于 ConvertUtils.deregister()的初始化,所有的Converter都是有初始值的,所以这里Struts自己 把这些初始值设置为null,即转换出错的时候返回null,而不是初始值。使用ConvertUtils类的 原因是由于从form传输过来的都是String类型的值,所以我们要把它们转换成相应的类型。 提到几个技巧: *public boolean isIndexed() { if (type == null) { return (false); //技巧一:判断是否是一个Array类的方法 } else if (type.isArray()) { return (true); //技巧二:判断type是否是List的一个父类或者父接口,或者与List为同一个类 //要注意如果List是另一个primitive的TYPE类,那么type必须也是这个类才会 //返回true,否则都是false。注意long.TYPE与Long.class是不同的 } else if (List.class.isAssignableFrom(type)) { return (true); } else { return (false); } } *//componentType为Array类所存储的元素的类别 Class componentType = indexedProperty.getClass().getComponentType(); //生成一个新的Array Object newArray = Array.newInstance(componentType, (index + 1)); System.arraycopy(indexedProperty, 0, newArray, 0, length); indexedProperty = newArray; set(name, indexedProperty); int newLength = Array.getLength(indexedProperty); for (int i = length; i < newLength; i++) { Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType)); } |
3) 调用initServlet()方法,从web.xml文件中加载ActionServlet的Url映射信息。此外还会注册web.xml和struts配置文件所使用的DTD文件,这些DTD文件用来验证web.xml和strutc配置文件的语法
initServlet()方法: 这个方法主要是通过digester类解析web.xml,对String servletMapping属性进行初始化。对于 digester说明如下:这是一个基于DOM的SAX实现的类,它是事件触发的,根据xml文件的结构, 每次读到一个节点元素就会触发一个事件。 InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml"); 这是一个比较少见的方法。首先通过this.servletName = getServletConfig(). getServletName()获取servlet的名称,然后根据 if (servletName.equals(this.servletName)) { this.servletMapping = urlPattern; } 来判断当前读到的servlet名称是否是我们运行的servlet的名称,如果是,就把url-pattern作为 我们的servletMapping。 |
4) 调用initModuleConfig()方法,加载并解释默认子应用模块的struts配置文件;创建ModuleConfig对象,把它存储在ServletContext中
ModuleConfig moduleConfig = initModuleConfig("", config) 这个方法使用由initOther()方法获取的config值为要解析的xml路径,用来初始化ModuleConfig。 它首先采用与生成MessageResourcesFactory同样的方法产生一个MessageResourcesFactory对象: MessageResourcesFactory为一个抽象类,每一个继承它的类都要实现 createModuleConfig(String prefix)方法。本程序使用的缺省的MessageResourcesFactory类为 org.apache.struts.config.impl.DefaultModuleConfigFactory,它 的createModuleConfig(String prefix)方法会生成一个ModuleConfigImpl类。 ModuleConfigImpl类相当于一个JavaBean,用来存放一个web模块运行时所需要的配置信息。当 然,一个web模块可以拥有多个ModuleConfig,但是缺省的是prefix长度为0的ModuleConifg。它 的每个属性几乎都是由HashMap组成的,它通过一个configured布尔值来描述当前的ModuleConfig 是否已经被初始化完毕,在每存放一个属性的时候都会监测这个值。如果初始化完毕而还要改变 里面的属性值,则会报出IllegalStateException("Configuration is frozen")异常,现在对它 的属性简单说明如下: * protected HashMap actionConfigs: 这个HashMap用来存储ActionConfig对象。 * protected HashMap dataSources 这个HashMap用来存储DataSourceConfig对象。 * protected HashMap exceptions 这个HashMap用来存储ExceptionConfig对象。 * protected HashMap formBeans 这个HashMap用来存储FormBeanConfig对象。 * protected HashMap forwards 这个HashMap用来存储ForwardConfig对象。 * protected HashMap messageResources 这个HashMap用来存储MessageResourcesConfig对象。 * protected ArrayList plugIns 这个HashMap用来存储PlugInConfig对象。 * protected ControllerConfig controllerConfig ControllerConfig类 * protected boolean configured 标志这个ModuleConfig是(true)否(false)配置完成。 * protected String prefix 用来标志和区分ModuleConfig类,同时在使用上面的config类初始化相应的资源以后,也是通 过这个prefix来区分所属的不同的web模块。 * protected String actionMappingClass = "org.apache.struts.action.ActionMapping" ActionMapping类名,缺省为org.apache.struts.action.ActionMapping。 初始化ModuleConfig的方法如下: 首先是使用getServletConfig().getInitParameter("mapping")来获取设定的ActionMapping类 名,然后通过initConfigDigester()方法来生成一个digester。最后用","分隔config,对每一 块调用parseModuleConfigFile(prefix, paths, config, digester, path)方法解析。注意,这 个方法实际上只有两个参数是有意义的:path为我们要解析的xml文件,config用来初始化完成 后保存到servletContext中。 如果ModuleConfig中存放的FormBeanConfig为Dydamic类型,那么就调用 DynaActionFormClass.createDynaActionFormClass(FormBeanConfig)初始化 DynaActionFormClass,并存放到DynaActionFormClass.dynaClasses 的 static HashMap中。这 里的key为FormBeanConfig.getName() + moduleConfig.getPrefix()。 如果当前的ModuleConfig为缺省的ModuleConfig,那么将会调用如下几个方法: defaultControllerConfig(config) defaultMessageResourcesConfig(config) defaultFormBeansConfig(config) defaultForwardsConfig(config) defaultMappingsConfig(config) 在struts1.1以后,这个特例将会被废弃: defaultControllerConfig(config)为ControllerConfig通过getInitParameter(s)方法初始化如 下几个属性:bufferSize,content,locale(true/false),maxFileSize,nocache(true/false) ,multipartClass,tempDir。 defaultMessageResourcesConfig(config)为MessageResourcesConfig通过getInitParameter(s) 方法初始化如下几个属性:application,factory,null(true/false)。 其它的几个方法就是获取不同的对象,然后把它们相应的存储到servlet中。关心如下: ActionFormBeans=>FormBeanConfig,ActionForwards=>ForwardConfig, ActionMappings=>ActionConfig。 |
5) 调用initModuleMessageResources()方法,加载并初始化默认子应用模块的消息资源;创建MessageResources对象,把它存储在ServletContext中
initModuleMessageResources(ModuleConfig config) 通过存储在ModuleConfig中的MessageResourcesConfig对象,逐个初始化MessageResource, 然后再把初始化好的MessageResources存放到ServletContext中,attributeName为 MessageResourcesConfig.getKey() + ModuleConfig.getPrefix()。 |
6) 调用initModuleDataSources方法,加载并存储默认子应用模块的数据源。如果在struts配置文件中没有定义
initModuleDataSources(ModuleConfig config) 通过存储在ModuleConfig中的DataSourceConfig对象,逐个初始化DataSource。然后对于每一个 DateSource通过BeanUtils.populate(ds, dscs[i].getProperties())方法初始化其属性。再把初 始化好的DateSource存放到ServletContext中,attributeName为 DataSourceConfig.getKey() + ModuleConfig.getPrefix()。同时也存放到名位dataSources的 FastHashMap中,key为DataSourceConfig.getKey()。 这里还会根据生成的DateSource对象是否是GenericDataSource类型,如果是则调用 GenericDataSource.open()方法。GenericDataSource是一个非常简单的数据库连接池,它的 open()方法用来初始化连接池,生成最小数目的GenericConnection,这里的open()方法根据 String driver变量是否为null来判断是否已经被初始化过。需要仔细说明的是getConnection() 方法,它首先从连接池中取出GenericConnection对象,然后检查其是否是可链接的,如果是就 返回,否则继续取出,同时activeCount-1。如果没有取到,则会检查当前可使用的 GenericConnection是否达到最大值(activeCount < maxCount),如果没有,调用 createConnection()方法声成一个新的GenericConnection,然后检查其是否是可链接,如果可以 则返回。returnConnection(GenericConnection conn)方法则是通过把GenericConnection放回到 连接池,然后activeCount-1。 这个方法中使用到了ServletContextWriter类,DateSource的log信息就通过这个类写入。对这个 类说明如下: 它继承自PrintWriter,而PrintWriter又继承自Writer。Writer类所作的事情就是在同步的情况下 调用abstract方法:abstract public void write(char cbuf[], int off, int len),这个方法 将会根据调用者的需要由调用者实现。 PrintWriter则首先通过ensureOpen()方法检验这个类中是否有写入的对象(Writer类或其子类), 如果有则根据不同的情况调用这个写入对象的write方法(out.write(....))。这个类的print(...) 方法就是据不同的情况调用相应的write(...)方法。而println(...)与之的区别就是每次多写入一 个换行字符串。还有一个区别是println(...)会根据是否需要autoflush进行flush,而write(...) 方法不会。 ServletContextWriter类的作用是把字符写入ServletContext中。ServletContextWriter类方法中 真正实现了write方法: public void write(char c) { if (c == '\n') flush(); else if (c != '\r') buffer.append(c); } public void flush() { if (buffer.length() > 0) { context.log(buffer.toString()); buffer.setLength(0); } } |
7) 调用initModulePlugins()方法,加载并初始话子应用模块的所有插件
1
initModulePlugIns(moduleConfig) 通过存储在ModuleConfig中的PlugInConfig对象,逐个初始化PlugIn对象,存放到一个数组中, 然后再把这个数组存放到ServletContext中,attributeName为 Globals.PLUG_INS_KEY + ModuleConfig.getPrefix()。 对每一个生成的PlugIn对象通过 BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties())方法初始化其属性。然后 再把PlugInConfig对象存放到由其生成的PlugIn对象中。 最后,通过plugIns[i].init(this, (ModuleConfig) config)初始化这个plugIn对象。 |
8) 当默认子应用模块被成功的初始化后,如果还包括其他子应用模块,将重复流程4-7,分别对其他子应用模块进行初始化
初始化结束 完成了这个初始化以后,会调用ModuleConfig.freeze()令这个ModuleConfig变得不可改变。然后 会遍历ServletConfig中的initParameterNames,如果有以"config/"开头的,则通过这个parameter 的值继续初始化其它的ModuleConfig,且这个ModuleConfig的prefix为"config/"后的字符串。 同样调用如下方法: initModuleMessageResources(moduleConfig); initModuleDataSources(moduleConfig); initModulePlugIns(moduleConfig); moduleConfig.freeze(); 最后调用destroyConfigDigester()释放内存。 |
/* * $Header: /home/cvspublic/jakarta-struts/src/share/org/apache/struts/action/ActionServlet.java,v 1.176 2004/04/24 * $Revision: 1.176 $ * $Date: 2004/04/24 * * Copyright 2000-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.struts.action; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.MissingResourceException; import javax.servlet.ServletContext; 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.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.Globals; import org.apache.struts.config.ConfigRuleSet; import org.apache.struts.config.DataSourceConfig; import org.apache.struts.config.FormBeanConfig; 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.util.MessageResources; import org.apache.struts.util.MessageResourcesFactory; import org.apache.struts.util.ModuleUtils; import org.apache.struts.util.RequestUtils; import org.apache.struts.util.ServletContextWriter; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * ActionServlet provides the "controller" in the * Model-View-Controller (MVC) design pattern for web applications that is * commonly known as "Model 2". This nomenclature originated with a * description in the JavaServerPages Specification, version 0.92, and has * persisted ever since (in the absence of a better name). * * Generally, a "Model 2" application is architected as follows: * * * will not themselves contain any business logic. These pages represent * the "view" component of an MVC architecture. * * to be executed will be submitted to a request URI that is mapped to this * servlet. * * which receives and processes all requests that change the state of * a user's interaction with the application. The servlet delegates the * handling of a request to a @link(RequestProcessor) object. This component * represents the "controller" component of an MVC architecture. * * the requested business logic, or delegates the response to another resource. * * interaction with the user, typically by creating or modifying JavaBeans * that are stored as request or session attributes (depending on how long * they need to be available). Such JavaBeans represent the "model" * component of an MVC architecture. * * * which resource should handle the response. If the * does not return null, the * redirects to the specified resource (by utilizing * * so as to produce the next page of the user interface. * * * The standard version of * following logic for each incoming HTTP request. You can override * some or all of this functionality by subclassing this object and * implementing your own version of the processing. * * * used to select an action procedure. * * action class (an implementation of the * * * instantiate an instance of that class and cache it for future use. * * associated with this mapping. * * on a reference to the mapping that was used, the relevant form-bean * (if any), and the request and the response that were passed to the * controller by the servlet container (thereby providing access to any * specialized properties of the mapping itself as well as to the * ServletContext). * * * * The standard version of * on the following servlet initialization parameters, which you will specify * in the web application deployment descriptor ( * for your application. Subclasses that specialize this servlet are free to * define additional initialization parameters. * * * path(s) to the XML resource(s) containing the configuration information * for the default module. (Multiple files support since Struts 1.1) * [/WEB-INF/struts-config.xml]. * * Context-relative path(s) to the XML resource(s) * containing the configuration information for the module that * will use the specified prefix (/${module}). This can be repeated as many * times as required for multiple modules. (Since Struts 1.1) * * * * [org.apache.struts.config.impl.DefaultModuleConfigFactory] * * * when populating forms. If set to true, the numeric Java wrapper class types * (like * (Since Struts 1.1) [false] * * classnames of additional * instances that should be added to the * be processing * the * loaded. (Since Struts 1.1) * * process the configuration file (strongly recommended)? [true] * * * @version $Revision: 1.176 $ $Date: 2004/04/24 */ public class ActionServlet extends HttpServlet { // ----------------------------------------------------- Instance Variables /** * Comma-separated list of context-relative path(s) to our configuration * resource(s) for the default module. */ protected String config = "/WEB-INF/struts-config.xml"; /** * The Digester used to produce ModuleConfig objects from a * Struts configuration file. * * @since Struts 1.1 */ protected Digester configDigester = null; /** * The flag to request backwards-compatible conversions for form bean * properties of the Java wrapper class types. * * @since Struts 1.1 */ protected boolean convertNull = false; /** * The JDBC data sources that has been configured for this module, * if any, keyed by the servlet context attribute under which they are * stored. */ protected FastHashMap dataSources = new FastHashMap(); /** * The resources object for our internal resources. */ protected MessageResources internal = null; /** * The Java base name of our internal resources. * @since Struts 1.1 */ protected String internalName = "org.apache.struts.action.ActionResources"; /** * Commons Logging instance. * * @since Struts 1.1 */ protected static Log log = LogFactory.getLog(ActionServlet.class); /** * The * all incoming requests. * * @since Struts 1.1 */ protected RequestProcessor processor = null; /** * The set of public identifiers, and corresponding resource names, for * the versions of the configuration file DTDs that we know about. There * MUST be an even number of Strings in this list! */ protected String registrations[] = { "-//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", "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN", "/org/apache/struts/resources/struts-config_1_2.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" }; /** * The URL pattern to which we are mapped in our web application * deployment descriptor. */ protected String servletMapping = null; // :FIXME: - multiples? /** * The servlet name under which we are registered in our web application * deployment descriptor. */ protected String servletName = null; // ---------------------------------------------------- HttpServlet Methods /** * Gracefully shut down this controller servlet, releasing any resources * that were allocated at initialization. */ public void destroy() { if (log.isDebugEnabled()) { log.debug(internal.getMessage("finalizing")); } destroyModules(); destroyInternal(); getServletContext().removeAttribute(Globals.ACTION_SERVLET_KEY); // Release our LogFactory and Log instances (if any) ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) { classLoader = ActionServlet.class.getClassLoader(); } try { LogFactory.release(classLoader); } catch (Throwable t) { ; // Servlet container doesn't have the latest version ; // of commons-logging-api.jar installed // :FIXME: Why is this dependent on the container's version of commons-logging? // Shouldn't this depend on the version packaged with Struts? /* Reason: LogFactory.release(classLoader); was added as an attempt to investigate the OutOfMemory error reported on Bugzilla #14042. It was committed for version 1.136 by craigmcc */ } } /** * Initialize this servlet. Most of the processing has been factored into * support methods so that you can override particular functionality at a * fairly granular level. * * @exception ServletException if we cannot configure ourselves correctly */ public void init() throws ServletException { initInternal(); initOther(); initServlet(); getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this); initModuleConfigFactory(); // Initialize modules as needed ModuleConfig moduleConfig = initModuleConfig("", config); initModuleMessageResources(moduleConfig); initModuleDataSources(moduleConfig); initModulePlugIns(moduleConfig); moduleConfig.freeze(); Enumeration names = getServletConfig().getInitParameterNames(); while (names.hasMoreElements()) { String name = (String) 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(); } this.initModulePrefixes(this.getServletContext()); this.destroyConfigDigester(); } /** * Saves a String[] of module prefixes in the ServletContext under * Globals.MODULE_PREFIXES_KEY. NOTE - * the "" prefix for the default module is not included in this list. * * @param context The servlet context. * @since Struts 1.2 */ protected void initModulePrefixes(ServletContext context) { ArrayList prefixList = new ArrayList(); Enumeration names = context.getAttributeNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); if (!name.startsWith(Globals.MODULE_KEY)) { continue; } String prefix = name.substring(Globals.MODULE_KEY.length()); if (prefix.length() > 0) { prefixList.add(prefix); } } String[] prefixes = (String[]) prefixList.toArray(new String[prefixList.size()]); context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes); } /** * Process an HTTP "GET" request. * * @param request The servlet request we are processing * @param response The servlet response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } /** * Process an HTTP "POST" request. * * @param request The servlet request we are processing * @param response The servlet response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } // --------------------------------------------------------- Public Methods /** * Remember a servlet mapping from our web application deployment * descriptor, if it is for this servlet. * * @param servletName The name of the servlet being mapped * @param urlPattern The URL pattern to which this servlet is mapped */ 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; } } /** * Return the * internal message strings. * * @since Struts 1.1 */ public MessageResources getInternal() { return (this.internal); } // ------------------------------------------------------ Protected Methods /** * Gracefully terminate use of any modules associated with this * application (if any). * * @since Struts 1.1 */ 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 = (String) keys.next(); Object value = getServletContext().getAttribute(name); if (!(value instanceof ModuleConfig)) { continue; } ModuleConfig config = (ModuleConfig) value; if (this.getProcessorForModule(config) != null) { this.getProcessorForModule(config).destroy(); } getServletContext().removeAttribute(name); PlugIn plugIns[] = (PlugIn[]) getServletContext().getAttribute( Globals.PLUG_INS_KEY + config.getPrefix()); if (plugIns != null) { for (int i = 0; i < plugIns.length; i++) { int j = plugIns.length - (i + 1); plugIns[j].destroy(); } getServletContext().removeAttribute( Globals.PLUG_INS_KEY + config.getPrefix()); } } } /** * Gracefully release any configDigester instance that we have created. * * @since Struts 1.1 */ protected void destroyConfigDigester() { configDigester = null; } /** * Gracefully terminate use of the internal MessageResources. */ protected void destroyInternal() { internal = null; } /** * Return the module configuration object for the currently selected * module. * * @param request The servlet request we are processing * @since Struts 1.1 */ protected ModuleConfig getModuleConfig (HttpServletRequest request) { ModuleConfig config = (ModuleConfig) request.getAttribute(Globals.MODULE_KEY); if (config == null) { config = (ModuleConfig) getServletContext().getAttribute(Globals.MODULE_KEY); } return (config); } /** * Look up and return the {@link RequestProcessor} responsible for the * specified module, creating a new one if necessary. * * @param config The module configuration for which to * acquire and return a RequestProcessor. * * @exception ServletException if we cannot instantiate a RequestProcessor * instance * @since Struts 1.1 */ protected synchronized RequestProcessor getRequestProcessor(ModuleConfig config) throws ServletException { // :FIXME: Document UnavailableException? RequestProcessor processor = this.getProcessorForModule(config); 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); String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix(); getServletContext().setAttribute(key, processor); } return (processor); } /** * Returns the RequestProcessor for the given module or null if one does not * exist. This method will not create a RequestProcessor. * * @param config The ModuleConfig. */ private RequestProcessor getProcessorForModule(ModuleConfig config) { String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix(); return (RequestProcessor) getServletContext().getAttribute(key); } /** * Initialize the factory used to create the module configuration. * @since Struts 1.2 */ protected void initModuleConfigFactory(){ String configFactory = getServletConfig().getInitParameter("configFactory"); if (configFactory != null) { ModuleConfigFactory.setFactoryClass(configFactory); } } /** * Initialize the module configuration information for the * specified module. * * @param prefix Module prefix for this module * @param paths Comma-separated list of context-relative resource path(s) * for this modules's configuration resource(s) * * @exception ServletException if initialization cannot be performed * @since Struts 1.1 */ protected ModuleConfig initModuleConfig(String prefix, String paths) throws ServletException { // :FIXME: Document UnavailableException? (Doesn't actually throw anything) if (log.isDebugEnabled()) { log.debug( "Initializing module path '" + prefix + "' configuration from '" + paths + "'"); } // Parse the configuration for this module ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory(); ModuleConfig config = factoryObject.createModuleConfig(prefix); // Configure the Digester instance we will use Digester digester = initConfigDigester(); // Process each specified resource path 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; } this.parseModuleConfigFile(digester, path); } getServletContext().setAttribute( Globals.MODULE_KEY + config.getPrefix(), config); // Force creation and registration of DynaActionFormClass instances // for all dynamic form beans we wil be using FormBeanConfig fbs[] = config.findFormBeanConfigs(); for (int i = 0; i < fbs.length; i++) { if (fbs[i].getDynamic()) { DynaActionFormClass.createDynaActionFormClass(fbs[i], config); } } return config; } /** * Parses one module config file. * * @param digester Digester instance that does the parsing * @param path The path to the config file to parse. * * @throws UnavailableException if file cannot be read or parsed * @since Struts 1.2 */ protected void parseModuleConfigFile(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); } catch (MalformedURLException e) { handleConfigException(path, e); } catch (IOException e) { handleConfigException(path, e); } catch (SAXException e) { handleConfigException(path, e); } finally { if (input != null) { try { input.close(); } catch (IOException e) { throw new UnavailableException(e.getMessage()); } } } } /** * Simplifies exception handling in the * @param path * @param e * @throws UnavailableException as a wrapper around Exception */ private void handleConfigException(String path, Exception e) throws UnavailableException { String msg = internal.getMessage("configParse", path); log.error(msg, e); throw new UnavailableException(msg); } /** * Initialize the data sources for the specified module. * * @param config ModuleConfig information for this module * * @exception ServletException if initialization cannot be performed * @since Struts 1.1 */ protected void initModuleDataSources(ModuleConfig config) throws ServletException { // :FIXME: Document UnavailableException? 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]; } 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()); ds.setLogWriter(scw); } catch (Exception e) { log.error(internal.getMessage("dataSource.init", dscs[i].getKey()), e); throw new UnavailableException (internal.getMessage("dataSource.init", dscs[i].getKey())); } getServletContext().setAttribute (dscs[i].getKey() + config.getPrefix(), ds); dataSources.put(dscs[i].getKey(), ds); } dataSources.setFast(true); } /** * Initialize the plug ins for the specified module. * * @param config ModuleConfig information for this module * * @exception ServletException if initialization cannot be performed * @since Struts 1.1 */ 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(Globals.PLUG_INS_KEY + 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()); // Pass the current plugIn config object to the PlugIn. // The property is set only if the plugin declares it. // This plugin config object is needed by Tiles try { PropertyUtils.setProperty( plugIns[i], "currentPlugInConfigObject", plugInConfigs[i]); } catch (Exception e) { // FIXME Whenever we fail silently, we must document a valid reason // for doing so. Why should we fail silently if a property can't be set on // the plugin? /** * Between version 1.138-1.140 cedric made these changes. * The exceptions are caught to deal with containers applying strict security. * This was in response to bug #15736 * * Recommend that we make the currentPlugInConfigObject part of the PlugIn Interface if we can, Rob */ } plugIns[i].init(this, config); } catch (ServletException e) { throw e; } catch (Exception e) { String errMsg = internal.getMessage( "plugIn.init", plugInConfigs[i].getClassName()); log(errMsg, e); throw new UnavailableException(errMsg); } } } /** * Initialize the application * module. * * @param config ModuleConfig information for this module * * @exception ServletException if initialization cannot be performed * @since Struts 1.1 */ 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)) { continue; } 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); } } /** * Create (if needed) and return a new * instance that has been initialized to process Struts module * configuration files and configure a corresponding * object (which must be pushed on to the evaluation stack before parsing * begins). * * @exception ServletException if a Digester cannot be configured * @since Struts 1.1 */ protected Digester initConfigDigester() throws ServletException { // :FIXME: Where can ServletException be thrown? // Do we have an existing instance? if (configDigester != null) { return (configDigester); } // Create a new Digester instance with standard capabilities configDigester = new Digester(); configDigester.setNamespaceAware(true); configDigester.setValidating(this.isValidating()); configDigester.setUseContextClassLoader(true); configDigester.addRuleSet(new ConfigRuleSet()); for (int i = 0; i < registrations.length; i += 2) { URL url = this.getClass().getResource(registrations[i+1]); if (url != null) { configDigester.register(registrations[i], url.toString()); } } this.addRuleSets(); // Return the completely configured Digester instance return (configDigester); } /** * Add any custom RuleSet instances to configDigester that have * been specified in the * * @throws ServletException */ private void addRuleSets() throws ServletException { 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); } } } /** * Check the status of the * * @return true if the module Digester should validate. */ private boolean isValidating() { boolean validating = true; String value = getServletConfig().getInitParameter("validating"); if ("false".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value) || "n".equalsIgnoreCase(value) || "0".equalsIgnoreCase(value)) { validating = false; } return validating; } /** * Initialize our internal MessageResources bundle. * * @exception ServletException if we cannot initialize these resources * :222222222222222222222222222222222222222222222222222222222 */ protected void initInternal() throws ServletException { // :FIXME: Document UnavailableException try { internal = MessageResources.getMessageResources(internalName); } catch (MissingResourceException e) { log.error("Cannot load internal resources from '" + internalName + "'", e); throw new UnavailableException ("Cannot load internal resources from '" + internalName + "'"); } } /** * Initialize other global characteristics of the controller servlet. * * @exception ServletException if we cannot initialize these resources */ protected void initOther() throws ServletException { String value = null; value = getServletConfig().getInitParameter("config"); if (value != null) { config = value; } // Backwards compatibility for form beans of Java wrapper classes // Set to true for strict Struts 1.0 compatibility value = getServletConfig().getInitParameter("convertNull"); if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value) || "y".equalsIgnoreCase(value) || "1".equalsIgnoreCase(value)) { convertNull = true; } if (convertNull) { ConvertUtils.deregister(); ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class); ConvertUtils.register(new BigIntegerConverter(null), BigInteger.class); ConvertUtils.register(new BooleanConverter(null), Boolean.class); ConvertUtils.register(new ByteConverter(null), Byte.class); ConvertUtils.register(new CharacterConverter(null), Character.class); ConvertUtils.register(new DoubleConverter(null), Double.class); ConvertUtils.register(new FloatConverter(null), Float.class); ConvertUtils.register(new IntegerConverter(null), Integer.class); ConvertUtils.register(new LongConverter(null), Long.class); ConvertUtils.register(new ShortConverter(null), Short.class); } } /** * Initialize the servlet mapping under which our controller servlet * is being accessed. This will be used in the * tag to generate correct destination URLs for form submissions. * * @throws ServletException if error happens while scanning web.xml */ protected void initServlet() throws ServletException { // Remember our servlet name this.servletName = getServletConfig().getServletName(); // Prepare a Digester to scan the web application deployment descriptor Digester digester = new Digester(); digester.push(this); digester.setNamespaceAware(true); digester.setValidating(false); // Register our local copy of the DTDs that we can find for (int i = 0; i < registrations.length; i += 2) { URL url = this.getClass().getResource(registrations[i+1]); if (url != null) { digester.register(registrations[i], url.toString()); } } // Configure the processing rules that we need 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); // Process the web application deployment descriptor if (log.isDebugEnabled()) { log.debug("Scanning web.xml for controller servlet mapping"); } InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml"); if (input == null) { log.error(internal.getMessage("configWebXml")); throw new ServletException(internal.getMessage("configWebXml")); } try { digester.parse(input); } catch (IOException e) { log.error(internal.getMessage("configWebXml"), e); throw new ServletException(e); } catch (SAXException e) { log.error(internal.getMessage("configWebXml"), e); throw new ServletException(e); } finally { try { input.close(); } catch (IOException e) { log.error(internal.getMessage("configWebXml"), e); throw new ServletException(e); } } // Record a servlet context attribute (if appropriate) if (log.isDebugEnabled()) { log.debug("Mapping for servlet '" + servletName + "' = '" + servletMapping + "'"); } if (servletMapping != null) { getServletContext().setAttribute(Globals.SERVLET_KEY, servletMapping); } } /** * Perform the standard request processing for this request, and create * the corresponding response. * * @param request The servlet request we are processing * @param response The servlet response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception is thrown */ protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { ModuleUtils.getInstance().selectModule(request, getServletContext()); getRequestProcessor(getModuleConfig(request)).process(request, response); } } |
2.ActionServlet实例收到http请求后,在doGet或者doPost方法中都会调用process()来处理请求。
process()方法中首先调用org.apache.struts,util.ModuleUtils类中的selectModule()方法,以选择负责处理请求的子应用模块,然后把子应用模块相关的ModuleConfig和MessageResource对象存储到request范围中,使得框架的其他组件可以方便的从request范围中读取这些对象,获取应用配置信息和消息资源。
struts在初始化之后是如何处理一个请求,并返回数据的。这里最核心的类是RequestProcessor以及RequestUtils。RequestProcessor类通过RequestDispatcher实现页面的跳转,而RequestProcessor负责处理request中传来的请求信息,存放到FormBeanConfig中,以及对要跳转的url进行处理。
struts 在初始化完成之后,会根据请求调用doGet(...)或者doPost(...)方法,这两个方法直接
调用process(request, response)方法。process(...)方法首先判断当前的request属于哪一个ModuleConfig,然后生成与这个ModuleConifg相对应的RequestProcessor,最后调用这个RequestProcessor的process(...)方法,执行request的请求。
一、RequestUtils.selectModule(String prefix, HttpServletRequest,ServletContext)方法:
这个方法,根据prefix,从ServletContext中选择相应的ModuleConfig,然后把这个ModuleConfig保存到request中。ServletContext对应的key值为Globals.MODULE_KEY + prefix,保存到request中使用的key值为Globals.MODULE_KEY。如果在ServletContext中不存在这样的一个ModuleConfig,那么调用request.removeAttribute(Globals.MODULE_KEY)方法。然后以同样的方法查找、保存MessageResources对象。当prefix为空时,会调用下面的方法选择ModuleConfig。
二、RequestUtils.selectModule(HttpServletRequest, ServletContext)
这个方发首先使用getModuleName(HttpServletRequest, ServletContext)获取相应的path,然后
通过调用getModuleName(String matchPath, ServletContext)获取相应的prefix。以这prefix为
参数调用(一)中的selectModule(...)选择ModuleConfig。
获取path的过程为:首先从request中查找名称为
INCLUDE_SERVLET_PATH(javax.servlet.include.servlet_path)的属性,如果为空,就调用
request.getServletPath()方法获取servletPath。
获取prefix的过程为:它首先调用getModulePrefixes(ServletContext),获取所有已存在的
module前缀。 然后通过分析上面获取的path来判断当前的url属于哪一个module,方法是截取path中最后一个"/"字符前面的字符串,然后与有上面方法中获取的prefixes[]逐个对比,如果prefixes[]中存在这样的一个值,则返回这个截取的字符串,否则继续截取path最后面的"/"前面的字符串,然后对比。如果始终找不到,则返回""。
getModulePrefixes(ServletContext)的执行过程是:首先通过
context.getAttribute(PREFIXES_KEY)查找是否存在这样的一个保存所有的prefix的string array,如果存在,就说明已经解析过一次了,就直接返回这个string array,否则遍历ServletContext中所有的attribute name,查找以MODULE_KEY(org.apache.struts.action.MODULE)开头的atrribute name,然后截取这个atrribute name中MODULE_KEY后面的字符串,作为一个prefix. 最后把通过这个这个方式获取的所有的prefix作为一个ArrayList存储到ServletContext中,属性key值为PREFIXES_KEY(org.apache.struts.util.PREFIXES),这样下次再次查找的时候就可以直接从这
个attribute中获取了。
三、getModuleConfig(HttpServletRequest)
这个方法就是获取上面的selectModule(...)方法所得到的ModuleConfig。如果找不到这样的
ModuleConfig,那么就把ServletContext中缺省的ModuleConfig返回(调用getServletContext().getAttribute(Globals.MODULE_KEY))
四、getRequestProcessor(ModuleConfig config)
这个方法从根据ModuleConfig的prefix作为key,从ServletContext中获取RequestProcessor。这个key值为Globals.REQUEST_PROCESSOR_KEY + config.getPrefix()。
如果ServletContext中不存在这样的一个RequestProcessor,那么就生成一个新的
RequestProcessor的实例,完成初始化(保存ActionServlet以及ModuleConfig到这个新的实例中)后将其保存到ServletContext中。
五、RequestProcessor.process(HttpServletRequest, HttpServletResponse)
这是真正执行HttpServletRequst请求的方法。
这个方法首先判断但前的HttpServletRequest是否是Multipart类型的request,也就是说当前
的request是否有字段是"file"类型。这样做的原因是这种类型的request中getParameter()等
相类似的方法都是无法执行的,所以要区分对待。如果是Multipart类型的,那么把这个request包装成MultipartRequestWrapper类。
MultipartRequestWrapper 与 HttpServletRequest不同的就是重新实现了
setParameter(String name, String value),getParameter(String name),getParameterNames()
以及getParameterValues(String name)方法。
这些方法的思想是所有的变量名、值对都保存到一个HashMap里:key为变量名,value为变量值,但是注意value都是String[]格式的。当有一个新的值加入的时候,通过setParameter(...)方法,把值加到数组的最后。在setParameter(...)中有一个比较少见的保存值的方法,记录如下:
public void setParameter(String name, String value) {
String[] mValue = (String[]) parameters.get(name);
if (mValue == null) {
mValue = new String[0];
}
String[] newValue = new String[mValue.length + 1];
System.arraycopy(mValue, 0, newValue, 0, mValue.length);
newValue[mValue.length] = value;
parameters.put(name, newValue);
}
然后是调用processPath(HttpServletRequest, HttpServletResponse)获取地址,以这个地址作为
从ModuleConfig获取ActionMapping的Key。这里首先查询
request.getAttribute(INCLUDE_PATH_INFO)中是否有这样的一个值,如果为空就调用
request.getPathInfo()方法获取path。如果这样还是获取不了,就从
request.getAttribute(INCLUDE_SERVLET_PATH)方法中获取path,找不到就使用
request.getServletPath()得到的path进行分析。分析过程如下:
然后如果这个path不是属于当前的ModuleConfig的话,直接返回null。截取prefix后面的字符串,如果这个字符串以.XX结尾,那么就截取"."前面的字符,比如
http://localhost:8080/servlet/where/go.do。where为module的名称(prefix),那么我们获取的
path值为/go。
然后是通过processLocale(HttpServletRequest ,HttpServletResponse)为当前用户设定一个Local对象,它查找的顺序是:moduleConfig.getControllerConfig().getLocale(),
session.getAttribute(Globals.LOCALE_KEY),request.getLocale()。你可以在config XML文件
中通过
调用processContent(HttpServletRequest, HttpServletResponse)为response设定contentType。
这个contentType是从moduleConfig.getControllerConfig().getContentType()获取的。你可以
在config XML文件中通过
通过processNoCache(HttpServletRequest, HttpServletResponse)方法设置response的缓存。
你可以在config XML文件中通过
processPreprocess(HttpServletRequest, HttpServletResponse)预处理request,这个方法是预
留的,用户可以根据自己的需要加一些预处理的程序。
通过processMapping(HttpServletRequest, HttpServletResponse, String path)以
processPath(...)的返回值为key从ModuleConfig中查找ActionMapping 对象。如果找到了,那么保存这个Mapping到request中,key值为Globals.MAPPING_KEY。如果不存在,那么遍历ModuleConfig中所有的ActionMapping,查找哪一个是缺省的ActionMapping。然后把它保存到request中,key值为Globals.MAPPING_KEY。
通过processRoles(HttpServletRequest,HttpServletResponse,ActionMapping)检查当前用户是
否有权限执行这个请求。如果request.isUserInRole(roles[i])返回true,则代表有。
通过processActionForm(HttpServletRequest, HttpServletResponse, ActionMapping)生成一个
ActionForm,然后根据ActionMapping所属的scope,保存到request或者session中。key值为这个ActionMapping中的attribute属性。ActionForm是通过RequestUtils.createActionForm(...)方法获取的,在下一篇将会对这个方法进行详细的说明。这里只说明ActionForm与FormBeanConfig以及FormPropertyConfig之间的区别。每个FormPropertyConfig代表Form表单的一个字段,表单中的所有FormPropertyConfig以一个HashMap保存到FormBeanConfig中。而FormBeanConfig是在
Actionservet初始化的时候生成的:
名称用来在MoudleConfig中的formBeans HashMap中查找相应的FormBeanConfig,而FormBeanConfig
中有一个type,它保存了上面XML文件中定义的值,用来生成ActionForm。
通过processPopulate(HttpServletRequest, HttpServletResponse, ActionForm,
ActionMapping)方法初始化ActionForm 以及 FormBean,这个方法首先会设定这个ActionForm所属于的ActionServlet,然后对这个ActionForm进行初始化。判断当前的reqest是否Multipart类型,如果是就把相应的MultipartClass类全名保存到request中,key值为Globals.MULTIPART_KEY。调用RequestUtils.populate(...)对request中的参数进行处理,并保存到相应的FormBean中。在下一篇将会对这个方法进行详细的说明。最后根据request的parameter判断当前的请求是否是被取消,然后把相关信息保存到request中:
if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) ||
(request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) {
request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
}
六、下面说明这个request是如何真正被处理的,下面的方法都是在RequestProcessor中定义。
通过processValidate(HttpServletRequest, HttpServletResponse, ActionForm,
ActionMapping)判断request中的parameter是否合法。如果合法就返回true,否则取消这次请求,并且返回到指定的输入页面。它判断合法的过程是:如果这次的请求被取消,那么直接返回true;如果没有要求对request中的parameter进行合法性检验,也直接返回true;最后通过调用form.validate(mapping, request)执行检验,如果返回的ActionErrors为null,那么代表通过检验,返回true,否则取消这次request。取消的过程是:如果这个请求是Multipart类型的,那么要对这个MultipartRequestHandler进行回滚;而后,获取当前的ActionMapping的input值,即在config XML 中
processForward(HttpServletRequest, HttpServletResponse, ActionMapping)以及
processInclude(HttpServletRequest, HttpServletResponse, ActionMapping)分别通过执行
RequestDispatcher.forward(request, response) 以及 RequestDispatcher.include(request,
response)实现对页面的跳转。
但是如果当前的请求还有其它操作要执行,那么ActionMapping中的include或者forward属性值就会为空。这时需要通过调用processActionCreate(HttpServletRequest, HttpServletResponse,ActionMapping)方法根据config XML文件的配置信息生成一个Action类,然后通过processActionPerform(...)调用生成的Action中的execute(...)方法。最后根据execute(...)方法返回的ActionForword类,执行跳转。在整个过程中有一个url的计算方法,这个将在下一篇中说明。
七、对ActionMaping结构的说明:
在ActionConfig为保存Moudle Action属性的一个javabean,它有以下属性:
* boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中任何属性都会抛出IllegalStateException("Configuration is frozen")
* ModuleConfig moduleConfig 本ActionConfig类所属于的ModuleConfig。
* String attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来
获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方法:
public String getAttribute() {
if (this.attribute == null) {
return (this.name);
} else {
return (this.attribute);
}
}
* String forward 调用RequestDispatcher.forward()方法时所需要的上下文相关的地址。
* String include 调用RequestDispatcher.include()方法时所需要的上下文相关的地址。
* String type Action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理
request。
* String input 如果输入数据没有通过验证,将会以这个值为地址,跳转到相应的页面。
* String multipartClass MultipartRequestHandler实现类的全名
* String name 与本类相关的 form bean 的名称。
* String parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。
* String path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/"
开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。
* String roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。
* String scope 缺省为session。
* String prefix,String suffix后缀和前缀。
* boolean unknown 标志当前的ActionConfig类是否是为了未知的request path而准备的,
ActionMappings将会根据这个标志返回这个ActionConfig。
* boolean validate 是否调用validate()方法进行数据校验。
ActionMapping 继承自 ActionConfig,它增加了从ModuleConfig查找ActionForword的方法。同时它还提供了从ModuleConfig查找ExceptionConfig的方法,如果找不到,会通过
type.getSuperclass()方法,根据父类的类名称继续查找,直到最后。
八、关于request.getServletPath()的解释:
request.getServletPath() 的返回值就是在下面url中定义的值,比如如果按照下面的定义
*.do
那么:
http://localhost:8080/servlet/go.do ---------->/go.do
http://localhost:8080/servlet/where/go.do ---------->/where/go.do
这里有一个小常识,如果
/where/*.do
那么地址中只有http://localhost:8080/servlet/where/*.do有效(tomcat 4.0)
在ActionConfig为保存Moudle Action属性的一个javabean,它有以下属性:
* boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中任何属性都会抛出IllegalStateException("Configuration is frozen")
* ModuleConfig moduleConfig 本ActionConfig类所属于的ModuleConfig。
* String attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方法:
public String getAttribute() {
if (this.attribute == null) {
return (this.name);
} else {
return (this.attribute);
}
}
* String forward 调用RequestDispatcher.forward()方法时所需要的上下文相关的地址。
* String include 调用RequestDispatcher.include()方法时所需要的上下文相关的地址。
* String type Action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理
request。
* String input
* String multipartClass MultipartRequestHandler实现类的全名
* String name 与本类相关的 form bean 的名称。
* String parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。
* String path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/"
开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。
* String roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。
* String scope 缺省为session。
* String prefix,String suffix后缀和前缀。
* boolean unknown 标志当前的ActionConfig类是否是为了未知的request path而准备的,
ActionMappings将会根据这个标志返回这个ActionConfig。
* boolean validate 是否调用validate()方法进行数据校验。
ActionMapping 继承自 ActionConfig,它增加了从ModuleConfig查找ActionForword的方法。同时它还提供了从ModuleConfig查找ExceptionConfig的方法,如果找不到,会通过
type.getSuperclass()方法,根据父类的类名称继续查找,直到最后。
/* * $Header: /home/cvspublic/jakarta-struts/src/share/org/apache/struts/action/RequestProcessor.java,v 1.45 2004/04/25 * $Revision: 1.45 $ * $Date: 2004/04/25 * * Copyright 2000-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.struts.action; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.Globals; import org.apache.struts.config.ActionConfig; import org.apache.struts.config.ExceptionConfig; import org.apache.struts.config.ForwardConfig; import org.apache.struts.config.ModuleConfig; import org.apache.struts.taglib.html.Constants; import org.apache.struts.upload.MultipartRequestWrapper; import org.apache.struts.util.MessageResources; import org.apache.struts.util.RequestUtils; /** * RequestProcessor contains the processing logic that * the @link(ActionServlet) performs as it receives each servlet request * from the container. You can customize the request processing behavior by * subclassing this class and overriding the method(s) whose behavior you are * interested in changing. * * @version $Revision: 1.45 $ $Date: 2004/04/25 * @since Struts 1.1 */ public class RequestProcessor { // ----------------------------------------------------- Manifest Constants /** * The request attribute under which the path information is stored for * processing during a */ public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info"; /** * The request attribute under which the servlet path information is stored * for processing during a */ public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path"; // ----------------------------------------------------- Instance Variables /** * The set of * initialized, keyed by the fully qualified Java class name of the * */ protected HashMap actions = new HashMap(); /** * The */ protected ModuleConfig moduleConfig = null; /** * Commons Logging instance. */ protected static Log log = LogFactory.getLog(RequestProcessor.class); /** * The servlet with which we are associated. */ protected ActionServlet servlet = null; // --------------------------------------------------------- Public Methods /** * Clean up in preparation for a shutdown of this application. */ public void destroy() { synchronized (this.actions) { Iterator actions = this.actions.values().iterator(); while (actions.hasNext()) { Action action = (Action) actions.next(); action.setServlet(null); } this.actions.clear(); } this.servlet = null; } /** * Initialize this request processor instance. * * @param servlet The ActionServlet we are associated with * @param moduleConfig The ModuleConfig we are associated with. * @throws ServletException If an error occor during initialization */ public void init(ActionServlet servlet, ModuleConfig moduleConfig) throws ServletException { synchronized (actions) { actions.clear(); } this.servlet = servlet; this.moduleConfig = moduleConfig; } /** * Process an * corresponding * to another resource. * * @param request The servlet request we are processing * @param response The servlet response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a processing exception occurs */ public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Wrap multipart requests with a special wrapper request = processMultipart(request); // Identify the path component we will use to select a mapping String path = processPath(request, response); if (path == null) { return; } if (log.isDebugEnabled()) { log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'"); } // Select a Locale for the current user if requested processLocale(request, response); // Set the content type and no-caching headers if requested processContent(request, response); processNoCache(request, response); // General purpose preprocessing hook if (!processPreprocess(request, response)) { return; } this.processCachedMessages(request, response); // Identify the mapping for this request ActionMapping mapping = processMapping(request, response, path); if (mapping == null) { return; } // Check for any role required to perform this action if (!processRoles(request, response, mapping)) { return; } // Process any ActionForm bean related to this request ActionForm form = processActionForm(request, response, mapping); processPopulate(request, response, form, mapping); if (!processValidate(request, response, form, mapping)) { return; } // Process a forward or include specified by this mapping if (!processForward(request, response, mapping)) { return; } if (!processInclude(request, response, mapping)) { return; } // Create or acquire the Action instance to process this request Action action = processActionCreate(request, response, mapping); if (action == null) { return; } // Call the Action instance itself ActionForward forward = processActionPerform(request, response, action, form, mapping); // Process the returned ActionForward instance processForwardConfig(request, response, forward); } // ----------------------------------------------------- Processing Methods /** * Return an * the current request, creating a new one if necessary. * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param mapping The mapping we are using * * @exception IOException if an input/output error occurs */ protected Action processActionCreate(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException { // Acquire the Action instance we will be using (if there is one) String className = mapping.getType(); if (log.isDebugEnabled()) { log.debug(" Looking for Action instance for class " + className); } // :TODO: If there were a mapping property indicating whether // an Action were a singleton or not ([true]), // could we just instantiate and return a new instance here? Action instance = null; synchronized (actions) { // Return any existing Action instance of this class instance = (Action) actions.get(className); if (instance != null) { if (log.isTraceEnabled()) { log.trace(" Returning existing Action instance"); } return (instance); } // Create and return a new Action instance if (log.isTraceEnabled()) { log.trace(" Creating new Action instance"); } try { instance = (Action) RequestUtils.applicationInstance(className); // :TODO: Maybe we should propagate this exception // instead of returning null. } catch (Exception e) { log.error( getInternal().getMessage("actionCreate", mapping.getPath()), e); response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("actionCreate", mapping.getPath())); return (null); } instance.setServlet(this.servlet); actions.put(className, instance); } return (instance); } /** * Retrieve and return the * this mapping, creating and retaining one if necessary. If there is no * * * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param mapping The mapping we are using */ protected ActionForm processActionForm(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) { // Create (if necessary) a form bean to use ActionForm instance = RequestUtils.createActionForm (request, mapping, moduleConfig, servlet); if (instance == null) { return (null); } // Store the new instance in the appropriate scope if (log.isDebugEnabled()) { log.debug(" Storing ActionForm bean instance in scope '" + mapping.getScope() + "' under attribute key '" + mapping.getAttribute() + "'"); } if ("request".equals(mapping.getScope())) { request.setAttribute(mapping.getAttribute(), instance); } else { HttpSession session = request.getSession(); session.setAttribute(mapping.getAttribute(), instance); } return (instance); } /** * Forward or redirect to the specified destination, by the specified * mechanism. This method uses a * an * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param forward The ForwardConfig controlling where we go next * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ protected void processForwardConfig(HttpServletRequest request, HttpServletResponse response, ForwardConfig forward) throws IOException, ServletException { if (forward == null) { return; } if (log.isDebugEnabled()) { log.debug("processForwardConfig(" + forward + ")"); } String forwardPath = forward.getPath(); String uri = null; // paths not starting with / should be passed through without any processing // (ie. they're absolute) if (forwardPath.startsWith("/")) { uri = RequestUtils.forwardURL(request, forward, null); // get module relative uri } else { uri = forwardPath; } if (forward.getRedirect()) { // only prepend context path for relative uri if (uri.startsWith("/")) { uri = request.getContextPath() + uri; } response.sendRedirect(response.encodeRedirectURL(uri)); } else { doForward(uri, request, response); } } // :FIXME: if Action.execute throws Exception, and Action.process has been removed, // should the process* methods still throw IOException, ServletException? /** * Ask the specified * request. Return the * returned by the called * * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param action The Action instance to be used * @param form The ActionForm instance to pass to this Action * @param mapping The ActionMapping instance to pass to this Action * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ protected ActionForward processActionPerform(HttpServletRequest request, HttpServletResponse response, Action action, ActionForm form, ActionMapping mapping) throws IOException, ServletException { try { return (action.execute(mapping, form, request, response)); } catch (Exception e) { return (processException(request, response, e, form, mapping)); } } /** * Removes any * * * be stored in the session, display one time, and be released here. * * @param request The servlet request we are processing. * @param response The servlet response we are creating. * * @since Struts 1.2 */ protected void processCachedMessages( HttpServletRequest request, HttpServletResponse response) { HttpSession session = request.getSession(false); if (session == null) { return; } ActionMessages messages = (ActionMessages) session.getAttribute(Globals.MESSAGE_KEY); if (messages == null) { return; } if (messages.isAccessed()) { session.removeAttribute(Globals.MESSAGE_KEY); } } /** * Set the default content type (with optional character encoding) for * all responses if requested. NOTE - This header will * be overridden automatically if a * * ultimately invoked. * * @param request The servlet request we are processing * @param response The servlet response we are creating */ protected void processContent(HttpServletRequest request, HttpServletResponse response) { String contentType = moduleConfig.getControllerConfig().getContentType(); if (contentType != null) { response.setContentType(contentType); } } /** * Ask our exception handler to handle the exception. Return the * * called * * @param request The servlet request we are processing * @param response The servlet response we are processing * @param exception The exception being handled * @param form The ActionForm we are processing * @param mapping The ActionMapping we are using * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ protected ActionForward processException(HttpServletRequest request, HttpServletResponse response, Exception exception, ActionForm form, ActionMapping mapping) throws IOException, ServletException { // Is there a defined handler for this exception? ExceptionConfig config = mapping.findException(exception.getClass()); if (config == null) { log.warn(getInternal().getMessage("unhandledException", exception.getClass())); if (exception instanceof IOException) { throw (IOException) exception; } else if (exception instanceof ServletException) { throw (ServletException) exception; } else { throw new ServletException(exception); } } // Use the configured exception handling try { ExceptionHandler handler = (ExceptionHandler) RequestUtils.applicationInstance(config.getHandler()); return (handler.execute(exception, config, mapping, form, request, response)); } catch (Exception e) { throw new ServletException(e); } } /** * Process a forward requested by this mapping (if any). Return * * * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param mapping The ActionMapping we are using */ protected boolean processForward(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException, ServletException { // Are we going to processing this request? String forward = mapping.getForward(); if (forward == null) { return (true); } internalModuleRelativeForward(forward, request, response); return (false); } /** * Process an include requested by this mapping (if any). Return * * * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param mapping The ActionMapping we are using */ protected boolean processInclude(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException, ServletException { // Are we going to processing this request? String include = mapping.getInclude(); if (include == null) { return (true); } internalModuleRelativeInclude(include, request, response); return (false); } /** * Automatically select a * NOTE - configuring Locale selection will trigger * the creation of a new * * @param request The servlet request we are processing * @param response The servlet response we are creating */ protected void processLocale(HttpServletRequest request, HttpServletResponse response) { // Are we configured to select the Locale automatically? if (!moduleConfig.getControllerConfig().getLocale()) { return; } // Has a Locale already been selected? HttpSession session = request.getSession(); if (session.getAttribute(Globals.LOCALE_KEY) != null) { return; } // Use the Locale returned by the servlet container (if any) Locale locale = request.getLocale(); if (locale != null) { if (log.isDebugEnabled()) { log.debug(" Setting user locale '" + locale + "'"); } session.setAttribute(Globals.LOCALE_KEY, locale); } } /** * Select the mapping used to process the selection path for this request. * If no mapping can be identified, create an error response and return * * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param path The portion of the request URI for selecting a mapping * * @exception IOException if an input/output error occurs */ protected ActionMapping processMapping(HttpServletRequest request, HttpServletResponse response, String path) throws IOException { // Is there a mapping for this path? ActionMapping mapping = (ActionMapping) moduleConfig.findActionConfig(path); // If a mapping is found, put it in the request and return it if (mapping != null) { request.setAttribute(Globals.MAPPING_KEY, mapping); return (mapping); } // Locate the mapping for unknown paths (if any) ActionConfig configs[] = moduleConfig.findActionConfigs(); for (int i = 0; i < configs.length; i++) { if (configs[i].getUnknown()) { mapping = (ActionMapping) configs[i]; request.setAttribute(Globals.MAPPING_KEY, mapping); return (mapping); } } // No mapping can be found to process this request String msg = getInternal().getMessage("processInvalid", path); log.error(msg); response.sendError(HttpServletResponse.SC_NOT_FOUND, msg); return null; } /** * If this is a multipart request, wrap it with a special wrapper. * Otherwise, return the request unchanged. * * @param request The HttpServletRequest we are processing */ protected HttpServletRequest processMultipart(HttpServletRequest request) { if (!"POST".equalsIgnoreCase(request.getMethod())) { return (request); } String contentType = request.getContentType(); if ((contentType != null) && contentType.startsWith("multipart/form-data")) { return (new MultipartRequestWrapper(request)); } else { return (request); } } /** * Set the no-cache headers for all responses, if requested. * NOTE - This header will be overridden * automatically if a * ultimately invoked. * * @param request The servlet request we are processing * @param response The servlet response we are creating */ protected void processNoCache(HttpServletRequest request, HttpServletResponse response) { if (moduleConfig.getControllerConfig().getNocache()) { response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache,no-store,max-age=0"); response.setDateHeader("Expires", 1); } } /** * Identify and return the path component (from the request URI) that * we will use to select an * If no such path can be identified, create an error response and return * * * @param request The servlet request we are processing * @param response The servlet response we are creating * * @exception IOException if an input/output error occurs */ protected String processPath(HttpServletRequest request, HttpServletResponse response) throws IOException { String path = null; // For prefix matching, match on the path info (if any) path = (String) request.getAttribute(INCLUDE_PATH_INFO); if (path == null) { path = request.getPathInfo(); } if ((path != null) && (path.length() > 0)) { return (path); } // For extension matching, strip the module prefix and extension path = (String) request.getAttribute(INCLUDE_SERVLET_PATH); if (path == null) { path = request.getServletPath(); } String prefix = moduleConfig.getPrefix(); if (!path.startsWith(prefix)) { String msg = getInternal().getMessage("processPath", request.getRequestURI()); log.error(msg); response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); return null; } path = path.substring(prefix.length()); int slash = path.lastIndexOf("/"); int period = path.lastIndexOf("."); if ((period >= 0) && (period > slash)) { path = path.substring(0, period); } return (path); } /** * Populate the properties of the specified * the request parameters included with this request. In addition, * request attribute * the request was submitted with a button created by * * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param form The ActionForm instance we are populating * @param mapping The ActionMapping we are using * * @exception ServletException if thrown by RequestUtils.populate() */ protected void processPopulate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws ServletException { if (form == null) { return; } // Populate the bean properties of this ActionForm instance if (log.isDebugEnabled()) { log.debug(" Populating bean properties from this request"); } form.setServlet(this.servlet); form.reset(mapping, request); if (mapping.getMultipartClass() != null) { request.setAttribute(Globals.MULTIPART_KEY, mapping.getMultipartClass()); } RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(), request); // Set the cancellation request attribute if appropriate if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) || (request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) { request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE); } } /** * General-purpose preprocessing hook that can be overridden as required * by subclasses. Return * to continue, or * completed. The default implementation does nothing. * * @param request The servlet request we are processing * @param response The servlet response we are creating */ protected boolean processPreprocess(HttpServletRequest request, HttpServletResponse response) { return (true); } /** * If this action is protected by security roles, make sure that the * current user possesses at least one of them. Return * to continue normal processing, or * response has been created and processing should terminate. * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param mapping The mapping we are using * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ protected boolean processRoles(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException, ServletException { // Is this action protected by role requirements? String roles[] = mapping.getRoleNames(); if ((roles == null) || (roles.length < 1)) { return (true); } // Check the current user against the list of required roles for (int i = 0; i < roles.length; i++) { if (request.isUserInRole(roles[i])) { if (log.isDebugEnabled()) { log.debug(" User '" + request.getRemoteUser() + "' has role '" + roles[i] + "', granting access"); } return (true); } } // The current user is not authorized for this action if (log.isDebugEnabled()) { log.debug(" User '" + request.getRemoteUser() + "' does not have any required role, denying access"); } response.sendError( HttpServletResponse.SC_FORBIDDEN, getInternal().getMessage("notAuthorized", mapping.getPath())); return (false); } /** * If this request was not cancelled, and the request's * {@link ActionMapping} has not disabled validation, call the * * and forward to the input path if there were any errors. * Return * or * to the input form. * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param form The ActionForm instance we are populating * @param mapping The ActionMapping we are using * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ protected boolean processValidate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws IOException, ServletException { if (form == null) { return (true); } // Was this request cancelled? if (request.getAttribute(Globals.CANCEL_KEY) != null) { if (log.isDebugEnabled()) { log.debug(" Cancelled transaction, skipping validation"); } return (true); } // Has validation been turned off for this mapping? if (!mapping.getValidate()) { return (true); } // Call the form bean's validation method if (log.isDebugEnabled()) { log.debug(" Validating input form properties"); } ActionMessages errors = form.validate(mapping, request); if ((errors == null) || errors.isEmpty()) { if (log.isTraceEnabled()) { log.trace(" No errors detected, accepting input"); } return (true); } // Special handling for multipart request if (form.getMultipartRequestHandler() != null) { if (log.isTraceEnabled()) { log.trace(" Rolling back multipart request"); } form.getMultipartRequestHandler().rollback(); } // Was an input path (or forward) specified for this mapping? String input = mapping.getInput(); if (input == null) { if (log.isTraceEnabled()) { log.trace(" Validation failed but no input form available"); } response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("noInput", mapping.getPath())); return (false); } // Save our error messages and return to the input form if possible if (log.isDebugEnabled()) { log.debug(" Validation failed, returning to '" + input + "'"); } request.setAttribute(Globals.ERROR_KEY, errors); if (moduleConfig.getControllerConfig().getInputForward()) { ForwardConfig forward = mapping.findForward(input); processForwardConfig( request, response, forward); } else { internalModuleRelativeForward(input, request, response); } return (false); } /** * Do a module relative forward to specified URI using request dispatcher. * URI is relative to the current module. The real URI is compute by prefixing * the module name. * This method is used internally and is not part of the public API. It is * advised to not use it in subclasses. * * @param uri Module-relative URI to forward to * @param request Current page request * @param response Current page response * * @since Struts 1.1 */ protected void internalModuleRelativeForward( String uri, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Construct a request dispatcher for the specified path uri = moduleConfig.getPrefix() + uri; // Delegate the processing of this request // :FIXME: - exception handling? if (log.isDebugEnabled()) { log.debug(" Delegating via forward to '" + uri + "'"); } doForward(uri, request, response); } /** * Do a module relative include to specified URI using request dispatcher. * URI is relative to the current module. The real URI is compute by prefixing * the module name. * This method is used internally and is not part of the public API. It is * advised to not use it in subclasses. * * @param uri Module-relative URI to include * @param request Current page request * @param response Current page response * * @since Struts 1.1 */ protected void internalModuleRelativeInclude( String uri, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Construct a request dispatcher for the specified path uri = moduleConfig.getPrefix() + uri; // Delegate the processing of this request // FIXME - exception handling? if (log.isDebugEnabled()) { log.debug(" Delegating via include to '" + uri + "'"); } doInclude(uri, request, response); } /** * Do a forward to specified URI using a * This method is used by all internal method needing to do a forward. * * @param uri Context-relative URI to forward to * @param request Current page request * @param response Current page response * @since Struts 1.1 */ protected void doForward( String uri, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Unwrap the multipart request, if there is one. if (request instanceof MultipartRequestWrapper) { request = ((MultipartRequestWrapper) request).getRequest(); } RequestDispatcher rd = getServletContext().getRequestDispatcher(uri); if (rd == null) { response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("requestDispatcher", uri)); return; } rd.forward(request, response); } /** * Do an include of specified URI using a * This method is used by all internal method needing to do an include. * * @param uri Context-relative URI to include * @param request Current page request * @param response Current page response * @since Struts 1.1 */ protected void doInclude( String uri, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Unwrap the multipart request, if there is one. if (request instanceof MultipartRequestWrapper) { request = ((MultipartRequestWrapper) request).getRequest(); } RequestDispatcher rd = getServletContext().getRequestDispatcher(uri); if (rd == null) { response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("requestDispatcher", uri)); return; } rd.include(request, response); } // -------------------------------------------------------- Support Methods /** * Return the * internal message strings. */ protected MessageResources getInternal() { return (servlet.getInternal()); } /** * Return the * we are running. */ protected ServletContext getServletContext() { return (servlet.getServletContext()); } /** * Log the specified message to the servlet context log for this * web application. * * @param message The message to be logged * @deprecated Use commons-logging instead. This will be removed in a release * after Struts 1.2. */ protected void log(String message) { // :TODO: Remove after Struts 1.2 servlet.log(message); } /** * Log the specified message and exception to the servlet context log * for this web application. * * @param message The message to be logged * @param exception The exception to be logged * @deprecated Use commons-logging instead. This will be removed in a release * after Struts 1.2. */ protected void log(String message, Throwable exception) { // :TODO: Remove after Sruts 1.2 servlet.log(message, exception); } } |
3.实例说明:
1)web.xml
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
|
2) struts-config.xml
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
type = "hello.HelloAction" name = "HelloForm" scope = "request" validate = "true" input = "/hello.jsp" >
|
3)流程:
A)用户请求/HelloWorld.do B)Servlet容器在web.xml中寻找
C)servlet容器依据以上
D)servlet容器把请求转发给以上
E)ActionServlet根据 F)由于 HelloForm.validate() G_1)如果HelloForm.validate()返回一个ActionErrors对象。这个对象里面保护一个ActionMessage对象,这个对象中封装了错误消息。ActionServlet把该ActionErrors对象存在request范围内,然后根据 G_2)如果HelloForm.validate()返回的ActionErrors对象中不包含任何ActionMessage对象,表示表单验证成功。 H)ActionServlet查找 I_1)HelloAction的execute方法先进行逻辑验证,如果没有通过逻辑验证,就创建一个ActionMessage对象,这个ActionMessage对象封装了错误消息。Execute()方法把ActionMessage对象保存在ActionMessage对象中,再把ActionMessage对象存放在request范围内。最后返回一个ActionForward对象,该对象包含的请求转发路径为 I_2)HelloAction的execute通过逻辑验证。最后调用ActionMapping.findForward(“SayHello”)。 Return(mapping.findForward(“SayHello”)) ; J)ActionMapping.findForward()方法从 K)HelloAction的execute()方法然后把ActionForward对象返回给ActionServlet,ActionServlet再把客户请求转发给hello.jsp。 |