Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2566844
  • 博文数量: 709
  • 博客积分: 12251
  • 博客等级: 上将
  • 技术积分: 7905
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-17 00:00
个人简介

实现有价值的IT服务

文章存档

2012年(7)

2011年(147)

2009年(3)

2008年(5)

2007年(74)

2006年(431)

2005年(42)

分类: Java

2006-07-21 15:26:06

第一篇 struts的初始化

 

struts 的核心类是org.apache.struts.action.ActionServlet,这个类将会在struts第一次使用时,

作为servlet初始化并存入servlet容器。很显然的,初始化将会调用init方法初始化相应的数据。

1Init()流程:

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中获取configdebug两个参数,然后初始化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是另一个primitiveTYPE类,那么type必须也是这个类才会

//返回true,否则都是false。注意long.TYPELong.class是不同的

} else if (List.class.isAssignableFrom(type)) {

return (true);

} else {

return (false);

}

}

 

*//componentTypeArray类所存储的元素的类别

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文件中加载ActionServletUrl映射信息。此外还会注册web.xmlstruts配置文件所使用的DTD文件,这些DTD文件用来验证web.xmlstrutc配置文件的语法

initServlet()方法:

这个方法主要是通过digester类解析web.xml,对String servletMapping属性进行初始化。对于

digester说明如下:这是一个基于DOMSAX实现的类,它是事件触发的,根据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长度为0ModuleConifg。它

的每个属性几乎都是由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中存放的FormBeanConfigDydamic类型,那么就调用

DynaActionFormClass.createDynaActionFormClass(FormBeanConfig)初始化

DynaActionFormClass,并存放到DynaActionFormClass.dynaClasses static HashMap中。这

里的keyFormBeanConfig.getName() + moduleConfig.getPrefix()

 

如果当前的ModuleConfig为缺省的ModuleConfig,那么将会调用如下几个方法:

defaultControllerConfig(config)

defaultMessageResourcesConfig(config)

defaultFormBeansConfig(config)

defaultForwardsConfig(config)

defaultMappingsConfig(config)

struts1.1以后,这个特例将会被废弃:

 

defaultControllerConfig(config)ControllerConfig通过getInitParameter(s)方法初始化如

下几个属性:bufferSizecontentlocale(true/false)maxFileSizenocache(true/false)

multipartClasstempDir

 

defaultMessageResourcesConfig(config)MessageResourcesConfig通过getInitParameter(s)

方法初始化如下几个属性:applicationfactorynull(true/false)

 

其它的几个方法就是获取不同的对象,然后把它们相应的存储到servlet中。关心如下:

ActionFormBeans=>FormBeanConfigActionForwards=>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中,keyDataSourceConfig.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类,DateSourcelog信息就通过这个类写入。对这个

类说明如下:

它继承自PrintWriter,而PrintWriter又继承自WriterWriter类所作的事情就是在同步的情况下

调用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,且这个ModuleConfigprefix"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 05:13:28 rleland Exp $

 * $Revision: 1.176 $

 * $Date: 2004/04/24 05:13:28 $

 *

 * 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:

 *

     *

  • The user interface will generally be created with server pages, which

     *     will not themselves contain any business logic. These pages represent

     *     the "view" component of an MVC architecture.

  •  *

  • Forms and hyperlinks in the user interface that require business logic

     *     to be executed will be submitted to a request URI that is mapped to this

     *     servlet.

  •  *

  • There can be one instance of this servlet class,

     *     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 RequestProcessor selects and invokes an @link(Action) class to perform

     *     the requested business logic, or delegates the response to another resource.

  •  *

  • The Action classes can manipulate the state of the application's

     *     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.

  •  *

  • Instead of producing the next page of the user interface directly,

     *     Action classes generally return an @link(ActionForward) to indicate

     *     which resource should handle the response. If the Action

     *     does not return null, the RequestProcessor forwards or

     *     redirects to the specified resource (by utilizing

     *     RequestDispatcher.forward or Response.sendRedirect)

     *     so as to produce the next page of the user interface.

  •  *

 *

 *

The standard version of RequestsProcessor implements the

 *    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.

 *

     *

  • Identify, from the incoming request URI, the substring that will be

     *     used to select an action procedure.

  •  *

  • Use this substring to map to the Java class name of the corresponding

     *     action class (an implementation of the Action interface).

     *    

  •  *

  • If this is the first request for a particular Action class,

     *     instantiate an instance of that class and cache it for future use.

  •  *

  • Optionally populate the properties of an ActionForm bean

     *     associated with this mapping.

  •  *

  • Call the execute method of this Action class, passing

     *     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 ActionServlet is configured based

 * on the following servlet initialization parameters, which you will specify

 * in the web application deployment descriptor (/WEB-INF/web.xml)

 * for your application.  Subclasses that specialize this servlet are free to

 * define additional initialization parameters.

 *

     *

  • config - Comma-separated list of context-relative

     *     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].

  •  *

  • config/${module} - Comma-separated list of

     *     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)

  •  *

  • configFactory - The Java class name of the

     *     ModuleConfigFactory used to create the implementation of the

     *     ModuleConfig interface.

     *     [org.apache.struts.config.impl.DefaultModuleConfigFactory]

     *

  •  *

  • convertNull - Force simulation of the Struts 1.0 behavior

     *     when populating forms. If set to true, the numeric Java wrapper class types

     *     (like java.lang.Integer) will default to null (rather than 0).

     *     (Since Struts 1.1) [false]

  •  *

  • rulesets - Comma-delimited list of fully qualified

     *     classnames of additional org.apache.commons.digester.RuleSet

     *     instances that should be added to the Digester that will

     *     be processing struts-config.xml files.  By default, only

     *     the RuleSet for the standard configuration elements is

     *     loaded.  (Since Struts 1.1)

  •  *

  • validating - Should we use a validating XML parser to

     *     process the configuration file (strongly recommended)? [true]

  •  *

 *

 * @version $Revision: 1.176 $ $Date: 2004/04/24 05:13:28 $

 */

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 RequestProcessor instance we will use to process

     * 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 MessageResources instance containing our

     * 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 parseModuleConfigFile method.

     * @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 MessageResources for the specified

     * 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 Digester

     * instance that has been initialized to process Struts module

     * configuration files and configure a corresponding ModuleConfig

     * 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 rulesets init parameter.

     *

     * @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 validating initialization parameter.

     *

     * @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 &html:form>

     * 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);

    }

 

}

 

 

2ActionServlet实例收到http请求后,在doGet或者doPost方法中都会调用process()来处理请求。

process()方法中首先调用org.apache.struts,util.ModuleUtils类中的selectModule()方法,以选择负责处理请求的子应用模块,然后把子应用模块相关的ModuleConfigMessageResource对象存储到request范围中,使得框架的其他组件可以方便的从request范围中读取这些对象,获取应用配置信息和消息资源。

 

struts在初始化之后是如何处理一个请求,并返回数据的。这里最核心的类是RequestProcessor以及RequestUtilsRequestProcessor类通过RequestDispatcher实现页面的跳转,而RequestProcessor负责处理request中传来的请求信息,存放到FormBeanConfig中,以及对要跳转的url进行处理。

 

struts 在初始化完成之后,会根据请求调用doGet(...)或者doPost(...)方法,这两个方法直接

调用process(request, response)方法。process(...)方法首先判断当前的request属于哪一个ModuleConfig,然后生成与这个ModuleConifg相对应的RequestProcessor,最后调用这个RequestProcessorprocess(...)方法,执行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)查找是否存在这样的一个保存所有的prefixstring array,如果存在,就说明已经解析过一次了,就直接返回这个string array,否则遍历ServletContext中所有的attribute name,查找以MODULE_KEY(org.apache.struts.action.MODULE)开头的atrribute name,然后截取这个atrribute nameMODULE_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)

这个方法从根据ModuleConfigprefix作为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"类型。这样做的原因是这种类型的requestgetParameter()

相类似的方法都是无法执行的,所以要区分对待。如果是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获取ActionMappingKey。这里首先查询

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.dowheremodule的名称(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(...)的返回值为keyModuleConfig中查找ActionMapping 对象。如果找到了,那么保存这个Mappingrequest中,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(...)方法获取的,在下一篇将会对这个方法进行详细的说明。这里只说明ActionFormFormBeanConfig以及FormPropertyConfig之间的区别。每个FormPropertyConfig代表Form表单的一个字段,表单中的所有FormPropertyConfig以一个HashMap保存到FormBeanConfig中。而FormBeanConfig是在

Actionservet初始化的时候生成的:

一个名称,作为action选择的key" type="实际对应的ActionForm"/>

名称用来在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中。在下一篇将会对这个方法进行详细的说明。最后根据requestparameter判断当前的请求是否是被取消,然后把相关信息保存到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)执行检验,如果返回的ActionErrorsnull,那么代表通过检验,返回true,否则取消这次request。取消的过程是:如果这个请求是Multipart类型的,那么要对这个MultipartRequestHandler进行回滚;而后,获取当前的ActionMappinginput值,即在config XML 定义的input属性。如果ActionMapping没有这个input值,那么调用response.sendError(...)方法,然后返回false;如果存在,就保存验证出错信息到request中,keyGlobals.ERROR_KEY,跳转到input所指定的地址中。

 

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 prefixString 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 prefixString 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 02:29:41 husted Exp $

 * $Revision: 1.45 $

 * $Date: 2004/04/25 02:29:41 $

 *

 * 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 02:29:41 $

 * @since Struts 1.1

 */

public class RequestProcessor {

 

 

    // ----------------------------------------------------- Manifest Constants

 

 

    /**

     *

The request attribute under which the path information is stored for

     * processing during a RequestDispatcher.include call.

     */

    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 RequestDispatcher.include call.

     */

    public static final String INCLUDE_SERVLET_PATH =

        "javax.servlet.include.servlet_path";

 

 

    // ----------------------------------------------------- Instance Variables

 

 

    /**

     *

The set of Action instances that have been created and

     * initialized, keyed by the fully qualified Java class name of the

     * Action class.

     */

    protected HashMap actions = new HashMap();

 

 

    /**

     *

The ModuleConfiguration with which we are associated.

     */

    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 HttpServletRequest and create the

     * corresponding HttpServletResponse or dispatch

     * 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 Action instance that will be used to process

     * 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 ActionForm associated with

     * this mapping, creating and retaining one if necessary. If there is no

     * ActionForm associated with this mapping, return

     * null.

     *

     * @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 ForwardConfig object instead

     * an ActionForward.

     *

     * @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 Action instance to handle this

     * request. Return the ActionForward instance (if any)

     * returned by the called Action for further processing.

     *

     *

     * @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 ActionMessages object stored in the session under

     * Globals.MESSAGE_KEY if the messages'

     * isAccessed method returns true.  This allows messages to

     * 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

     * RequestDispatcher.forward call is

     * 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

     * ActionForward instance (if any) returned by the

     * called ExceptionHandler.

     *

     * @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

     * true if standard processing should continue, or

     * false if we have already handled this request.

     *

     * @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

     * true if standard processing should continue, or

     * false if we have already handled this request.

     *

     * @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 Locale for the current user, if requested.

     * NOTE - configuring Locale selection will trigger

     * the creation of a new HttpSession if necessary.

     *

     * @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

     * null.

     *

     * @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 RequestDispatcher.forward call is

     * 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 ActionMapping with which to dispatch.

     * If no such path can be identified, create an error response and return

     * null.

     *

     * @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 ActionForm instance from

     * the request parameters included with this request.  In addition,

     * request attribute Globals.CANCEL_KEY will be set if

     * the request was submitted with a button created by

     * CancelTag.

     *

     * @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 true if you want standard processing

     * to continue, or false if the response has already been

     * 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 true

     * to continue normal processing, or false if an appropriate

     * 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

     * validate method of the specified {@link ActionForm},

     * and forward to the input path if there were any errors.

     * Return true if we should continue processing,

     * or false if we have already forwarded control back

     * 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 RequestDispatcher.

     * 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 RequestDispatcher.

     * 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 MessageResources instance containing our

     * internal message strings.

     */

    protected MessageResources getInternal() {

 

        return (servlet.getInternal());

 

    }

 

 

    /**

     *

Return the ServletContext for the web application in which

     * 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.实例说明:

1web.xml

 

  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"

  "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

 

  HelloApp Struts Application

 

 

 

    action

    org.apache.struts.action.ActionServlet

   

      config

      /WEB-INF/struts-config.xml

   

     2

 

 

 

 

    action

    *.do

 

   

 

 

    hello.jsp

 

 

 

 

    /WEB-INF/struts-bean.tld

    /WEB-INF/struts-bean.tld

 

 

 

    /WEB-INF/struts-html.tld

    /WEB-INF/struts-html.tld

 

 

 

    /WEB-INF/struts-logic.tld

    /WEB-INF/struts-logic.tld

 

 

 

2) struts-config.xml

 

          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"

          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

 

 

 

   

   

       

   

 

 

 

   

        path      = "/HelloWorld"

               type      = "hello.HelloAction"

               name      = "HelloForm"

               scope     = "request"

               validate  = "true"

               input     = "/hello.jsp"

     >

       

   

 

 

 

 

 

 

 

 

 

3)流程:

A)用户请求/HelloWorld.do

 

B)Servlet容器在web.xml中寻找属性为“*.do”的元素:

 

    action

    *.do

 

 

C)servlet容器依据以上元素的属性”action”,在web.xml中寻找匹配的元素

 

    action

    org.apache.struts.action.ActionServlet

   

      config

      /WEB-INF/struts-config.xml

   

    2

 

 

 

D)servlet容器把请求转发给以上元素指定的ActionServletActionServlet依据用户请求路径“/HelloWorld.do”,在struts配置文件中搜索path属性为”/HelloWorld”元素:

 

   

        path      = "/HelloWorld"

               type      = "hello.HelloAction"

               name      = "HelloForm"

               scope     = "request"

               validate  = "true"

               input     = "/hello.jsp"

     >

       

   

 

 

E)ActionServlet根据元素的name属性,创建一个HelloForm对象,把客户提交的表单数据传给HelloForm对象,再把HelloForm对象保持在元素的scope属性指定的request范围内。

 

F)由于元素的validate属性为true,ActionServlet调用HelloForm对象的validate()方法执行表单验证:

HelloForm.validate()

 

G_1)如果HelloForm.validate()返回一个ActionErrors对象。这个对象里面保护一个ActionMessage对象,这个对象中封装了错误消息。ActionServlet把该ActionErrors对象存在request范围内,然后根据元素的input属性,把客户请求转发给hello.jsp

 

G_2)如果HelloForm.validate()返回的ActionErrors对象中不包含任何ActionMessage对象,表示表单验证成功。

 

H)ActionServlet查找type对应的Action—HelloAction 实例是否存在,如果不存在就创建一个实例。然后调用HelloActionexecute()方法。

 

I_1)HelloActionexecute方法先进行逻辑验证,如果没有通过逻辑验证,就创建一个ActionMessage对象,这个ActionMessage对象封装了错误消息。Execute()方法把ActionMessage对象保存在ActionMessage对象中,再把ActionMessage对象存放在request范围内。最后返回一个ActionForward对象,该对象包含的请求转发路径为元素的input属性指定的hello.jsp

 

I_2HelloActionexecute通过逻辑验证。最后调用ActionMapping.findForward(“SayHello”)

Return(mapping.findForward(“SayHello”)) ;

 

J)ActionMapping.findForward()方法从元素中寻找name属性为”SayHello”子元素,然后返回与之对应的ActionForward对象,它代表的请求转发路径为“/hello.jsp

 

K)HelloActionexecute()方法然后把ActionForward对象返回给ActionServletActionServlet再把客户请求转发给hello.jsp

 

 

 

 

 

 

 

 

阅读(2371) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~