码迷,mamicode.com
首页 > 其他好文 > 详细

StandardContext

时间:2018-12-13 23:28:12      阅读:172      评论:0      收藏:0      [点我收藏+]

标签:def   oba   hat   针对   cal   startup   static   生命周期   OLE   

StandardContext

Context实例表示一个具体的Web应用程序,其中包含一个或者多个Wrapper实例,每个Wrapper表示一个具体的Servlet定义,Context还需要其他组件的支持,典型的如加载器 和 Session管理器,下面就对org.apache.catlainia.core.StandardContext类的工作机制进行详细记录,该类是Context的标准实现,先来简单回顾一下StandardContext类的实例化 和 配置,然后 在了解下与StandardContext类相关的StandardContextMapper类(存在Tomcat4中),和ContextConfig类,然后呢学习一下对于每一个引入的HTTP请求的方法的方法的调用序列,然后在了解下StandardContext类的几个重要属性

StandardContext的配置

在创建了StandardContext实例之后,必须调用其start()方法来为引入的每个http请求提供服务,但是可能会因为某种原因,StandardContext对象可能会启动失败,这时StandardContext类的available属性会被设置为false,available属性表明属性StandardContext对象是否可用,展示一下其生命变量 以及其设置 与获取的方法

   /**
     * 该 {@link Context} 的应用程序可用标志
     */
    private boolean available = false;        
 1 /**
 2      * 返回这个{@link Context} 应用程序的可用标志
 3      */
 4     public boolean getAvailable() {
 5 
 6         return (this.available);
 7 
 8     }
 9 
10     /**
11      * 
12      * <dd>设置这个{@link Context} 应用程序的可用标志</dd>
13      *
14      * @param available
15      *            应用程序新的可用标志
16      */
17     public void setAvailable(boolean available) {
18 
19         boolean oldAvailable = this.available;
20         this.available = available;
21         // 触发属性改变的监听事件
22         support.firePropertyChange("available", new Boolean(oldAvailable), new Boolean(this.available));
23 
24     }

  若是start()方法正确执行,则表明StandardContext对象配置正确,在Tomcat的实际部署中,配置StandardContext对象需要一系列的操作,正确的设置后,StandardContext对象才能读取并解析默认的web.xml文件,该文件位于%CATALINA_HOME%/conf目录下,该文件的内容会应用到所有部署到Tomcat中的应用程序中,这也保证了StandardContext实例可以处理应用程序级的web.xml文件,此外还会配置验证器阀和许可阀。

StandardContext类的configured属性是一个布尔变量,表明 StandardContext实例是否正确设置,StandardContext类使用一个事件监听器作为其配置器,当调用StandardContext实例的start方法时,其中要做的第一件事就是触发一个声明周期事件,该事件调用监听器,对StandardContext实例进行配置,若配置成功,监听器会将configured属性设置为true,否则StandardContext实例会拒绝启动,也就无法为http请求提供服务了。

下面我们从StandardContext的类构造器来开始了解它的工作原理

 1   /**
 2      *
 3      * 用默认的基本阀 创建一个新的标准Context组件
 4      */
 5     public StandardContext() {
 6 
 7         super();
 8         pipeline.setBasic(new StandardContextValve());
 9         namingResources.setContainer(this);
10 
11     }

构造函数中最重要的事情是为StandardContext实例的管道对象设置基础阀,其类型是 org.apache.catalina.core.StandardContextValue类,该基础阀会处理从连接器中接收到的每个Http请求。

启动 StandardContext实例

  start方法会初始化StandardContext对象,用生命周期监听器配置StandardContext实例,当配置成功后,监听器会将其configured属性设置为true,最后,start方法会将available属性设置为true或者false,true表明StandardContext对象设置正确,与其相关联的子容器和组件都正确启动,因此,StandardContext实例可以准备为引入的Http请求提供服务了,若期间 发生任何错误,可用属性 available会被设置为false。在Tomcat实际部署中,负责配置StandardContext实例的生命周期监听器是org.apache.catalina.core.startup.ContextConfig类。这个类我会在后续的随笔中详细讨论。

  StandardContext使用一个初始化为false的布尔类型变量configured来表明StandardContext对象是否正确配置,如果生命周期监听器成功了执行其配置StandardContext实例的任务,生命周期监听器就会将StandardContext对象的configured属性设置为true,在StandardContext类的

start方法末尾,会检查StandardContext对象的configured属性的值,若configured属性的值为true,则StandardContext实例启动成功,否则将调用stop方法,关闭在start方法中启动的所有组件。

  1 /**
  2      * 启动这个 {@link Context} 组件
  3      *
  4      * @exception LifecycleException
  5      *                如果在启动时发生错误
  6      */
  7     public synchronized void start() throws LifecycleException {
  8         if (started)
  9             throw new LifecycleException(sm.getString("containerBase.alreadyStarted", logName()));
 10 
 11         if (debug >= 1)
 12             log("Starting");
 13 
 14         // 第一步:触发生命监听器的 BEFORE_START_EVENT 事件
 15         lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
 16 
 17         if (debug >= 1)
 18             log("Processing start(), current available=" + getAvailable());
 19         // 先将Context 的available 和 configured 属性都设置为false
 20         // 表示当前的StandardContext实例不可以用 且 没有配置成功
 21         setAvailable(false);
 22         setConfigured(false);
 23         // start方法中 正确执行的状态值 由于下面的代码可能会出现某些意外情况导致 启动失败 ok为 下面代码是否可以正确执行的标志
 24         boolean ok = true;
 25         // --------------配置资源
 26         if (getResources() == null) { // (1) Required by Loader
 27             if (debug >= 1)
 28                 log("Configuring default Resources");
 29             try {
 30                 if ((docBase != null) && (docBase.endsWith(".war")))
 31                     setResources(new WARDirContext());
 32                 else
 33                     setResources(new FileDirContext());
 34             } catch (IllegalArgumentException e) {
 35                 log("Error initializing resources: " + e.getMessage());
 36                 ok = false;
 37             }
 38         }
 39         if (ok && (resources instanceof ProxyDirContext)) {
 40             DirContext dirContext = ((ProxyDirContext) resources).getDirContext();
 41             if ((dirContext != null) && (dirContext instanceof BaseDirContext)) {
 42                 ((BaseDirContext) dirContext).setDocBase(getBasePath());
 43                 ((BaseDirContext) dirContext).allocate();
 44             }
 45         }
 46         // ------------------设置载入器
 47         if (getLoader() == null) { // 若没有配置好的加载器
 48             if (getPrivileged()) {
 49                 if (debug >= 1)
 50                     log("Configuring privileged default Loader");
 51                 // 使用默认的加载器
 52                 setLoader(new WebappLoader(this.getClass().getClassLoader()));
 53             } else {
 54                 if (debug >= 1)
 55                     log("Configuring non-privileged default Loader");
 56                 setLoader(new WebappLoader(getParentClassLoader()));
 57             }
 58         }
 59         // ----------------设置Session管理器
 60         if (getManager() == null) { // 若没有配置好的Session管理器 设置默认的管理器
 61             if (debug >= 1)
 62                 log("Configuring default Manager");
 63             setManager(new StandardManager());
 64         }
 65 
 66         // 初始化字符集映射器
 67         getCharsetMapper();
 68 
 69         // Post work directory
 70         postWorkDirectory();
 71 
 72         // Reading the "catalina.useNaming" environment variable
 73         String useNamingProperty = System.getProperty("catalina.useNaming");
 74         if ((useNamingProperty != null) && (useNamingProperty.equals("false"))) {
 75             useNaming = false;
 76         }
 77 
 78         if (ok && isUseNaming()) {
 79             if (namingContextListener == null) {
 80                 namingContextListener = new NamingContextListener();
 81                 namingContextListener.setDebug(getDebug());
 82                 namingContextListener.setName(getNamingContextName());
 83                 addLifecycleListener(namingContextListener);
 84             }
 85         }
 86 
 87         // Binding thread
 88         ClassLoader oldCCL = bindThread();
 89 
 90         // Standard container startup
 91         if (debug >= 1)
 92             log("Processing standard container startup");
 93 
 94         if (ok) {
 95 
 96             try {
 97 
 98                 addDefaultMapper(this.mapperClass);
 99                 started = true;
100 
101                 // ------------ 启动我们的附属组件,如果有的话
102                 if ((loader != null) && (loader instanceof Lifecycle))
103                     ((Lifecycle) loader).start();
104                 if ((logger != null) && (logger instanceof Lifecycle))
105                     ((Lifecycle) logger).start();
106 
107                 // Unbinding thread
108                 unbindThread(oldCCL);
109 
110                 // Binding thread
111                 oldCCL = bindThread();
112 
113                 if ((cluster != null) && (cluster instanceof Lifecycle))
114                     ((Lifecycle) cluster).start();
115                 if ((realm != null) && (realm instanceof Lifecycle))
116                     ((Lifecycle) realm).start();
117                 if ((resources != null) && (resources instanceof Lifecycle))
118                     ((Lifecycle) resources).start();
119 
120                 // 启动该StandardContext关联的映射器
121                 Mapper mappers[] = findMappers();
122                 for (int i = 0; i < mappers.length; i++) {
123                     if (mappers[i] instanceof Lifecycle)
124                         ((Lifecycle) mappers[i]).start();
125                 }
126 
127                 // 启动该StandardContext拥有的所有生命周期子容器
128                 Container children[] = findChildren();
129                 for (int i = 0; i < children.length; i++) {
130                     if (children[i] instanceof Lifecycle)
131                         ((Lifecycle) children[i]).start();
132                 }
133 
134                 // 启动管道对象
135                 if (pipeline instanceof Lifecycle)
136                     ((Lifecycle) pipeline).start();
137 
138                 // 触发Start监听事件,在这里(ContextConfig)监听器
139                 // 会执行一些配置操作,若设置成功,ContextConfig实例就会将StandardContext实例的configured
140                 // 设置为true。
141                 lifecycle.fireLifecycleEvent(START_EVENT, null);
142 
143                 // 启动设置好的Session管理器
144                 if ((manager != null) && (manager instanceof Lifecycle))
145                     ((Lifecycle) manager).start();
146 
147             } finally {
148                 // Unbinding thread
149                 unbindThread(oldCCL);
150             }
151 
152         }
153         // 检查Configured的值 若为false(也就是在上面的Start监听事件中 ContextConfig实例没有正确配置
154         // StandContext)所以会将 ok 标志位赋值为false 表示启动失败
155         if (!getConfigured())
156             ok = false;
157 
158         // We put the resources into the servlet context
159         if (ok)
160             getServletContext().setAttribute(Globals.RESOURCES_ATTR, getResources());
161 
162         // Binding thread
163         oldCCL = bindThread();
164 
165         // Create context attributes that will be required
166         if (ok) {
167             if (debug >= 1)
168                 log("Posting standard context attributes");
169             postWelcomeFiles();
170         }
171 
172         // Configure and call application event listeners and filters
173         if (ok) {
174             if (!listenerStart())
175                 ok = false;
176         }
177         if (ok) {
178             if (!filterStart())
179                 ok = false;
180         }
181 
182         // Load and initialize all "load on startup" servlets
183         if (ok)
184             loadOnStartup(findChildren());
185 
186         // Unbinding thread
187         unbindThread(oldCCL);
188 
189         // Set available status depending upon startup success
190         if (ok) {
191             if (debug >= 1)
192                 log("Starting completed");
193             setAvailable(true);
194         } else {
195             log(sm.getString("standardContext.startFailed"));
196             try {
197                 stop();
198             } catch (Throwable t) {
199                 log(sm.getString("standardContext.startCleanup"), t);
200             }
201             setAvailable(false);
202         }
203 
204         // 触发 AFTER——START事件
205         lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
206 
207     }

start方法主要做了以下工作

  • 触发生命周期监听器的 BEFORE_START_EVEN事件
  • 将代表StandardContext实例是否可用的标志属性available 赋值为false 代表还没有启动成功 在方法最后会对该值进行重新赋值;
  • 将代表StandardContext实例是否正确得到配置的标志属性configured 设置false;代表还没有成功配置。在方法中触发生命周期事件的START事件时 ContextConfig实例会对configured属性重新赋值;
  • 获取并配置资源
  • 获取配置的载入器并设置载入器 
  • 获取配置的Session管理器并设置Session管理器
  • 初始化默认字符映射集 插入点细致的分析这一步 因为看了 所以就记录下来吧 要不就白看了 

     先看看获取字符映射集

    // 初始化字符集映射器
        getCharsetMapper();
/**
     * 
     * 返回此Context的字符集映射器的区域设置。
     */
    public CharsetMapper getCharsetMapper() {

        // 在第一次调用该方法时 创建映射器
        if (this.charsetMapper == null) {
            try {
                Class clazz = Class.forName(charsetMapperClass);
                this.charsetMapper = (CharsetMapper) clazz.newInstance();
            } catch (Throwable t) {
                this.charsetMapper = new CharsetMapper();
            }
        }

        return (this.charsetMapper);

    }

StandardContext类 用一个名为 charsetMapperClass 的String类型 成员变量表示 默认字符映射集类的 的完全限定名

/**
     * 要创建的字符集类的Java类名.
     */
    private String charsetMapperClass = "org.apache.catalina.util.CharsetMapper";

有对应的get与set方法 但是set方法最好在 StandardContext类的START方法执行前 设置 看上面可以了解到 字符映射集类 是在start方法中被创建的,又或者可以写一个属性监听器来做这件事情 因为StandardContext中大多数set方法都会触发

属性更改监听事件,可以在监听器中 将StandardContext的 charsetMapper属性重置。

public void setCharsetMapper(CharsetMapper mapper) {

        CharsetMapper oldCharsetMapper = this.charsetMapper;
        this.charsetMapper = mapper;
        support.firePropertyChange("charsetMapper", oldCharsetMapper, this.charsetMapper);

    }

那么下面看下默认的字符集映射的默认实现类

package org.apache.catalina.util;

import java.io.InputStream;
import java.util.Locale;
import java.util.Properties;

/**
 * 
 * <p>
 * <b>Title:CharsetMapper.java</b>
 * </p>
 * <p>
 * Copyright:ChenDong 2018
 * </p>
 * <p>
 * Company:仅学习时使用
 * </p>
 * <p>
 * 类功能描述: 实用程序类,当请求头集合中不包括Content-Type时,尝试从Locale映射到用于解释输入文本(或生成输出文本)的相应字符集。
 * 您可以通过修改它加载的映射数据,或者通过子类化(以改变方法法),然后为特定的Web应用程序使用您自己的版本,来定制这个类的行为
 * </p>
 * 
 * @author 陈东
 * @date 2018年12月3日 下午8:35:28
 * @version 1.0
 */
public class CharsetMapper {

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

    /**
     * 默认属性资源名称。
     */
    public static final String DEFAULT_RESOURCE = "/org/apache/catalina/util/CharsetMapperDefault.properties";

    // ---------------------------------------------------------- Constructors

    /**
     * 使用默认属性资源构造一个新的字符集
     */
    public CharsetMapper() {

        this(DEFAULT_RESOURCE);

    }

    /**
     * 
     * 使用指定的属性资源来构造一个新的字符集
     *
     * @param name
     *            要被加载属性资源的完全限定名
     *
     * @exception IllegalArgumentException
     *                如果这个指定的属性资源因为任何原因不能被加载的话
     */
    public CharsetMapper(String name) {

        try {
            InputStream stream = this.getClass().getResourceAsStream(name);
            map.load(stream);
            stream.close();
        } catch (Throwable t) {
            throw new IllegalArgumentException(t.toString());
        }

    }

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

    /**
     * 已经从默认的资源属性或者指定资源 加载了资源并且初始化了的字符集合
     */
    private Properties map = new Properties();

    // ------------------------------------------------------- Public Methods

    /**
     * 根据指定的区域设置获取字符集编码
     *
     * @param locale
     *            用于计算字符集的区域设置
     */
    public String getCharset(Locale locale) {

        String charset = null;

        // 首先, 尝试全名匹配(语言和国家)
        charset = map.getProperty(locale.toString());
        if (charset != null)
            return (charset);

        // 其次, 尝试语言匹配
        charset = map.getProperty(locale.getLanguage());
        return (charset);

    }

}

看下 CharsetMapperDefault.properties的内容

ar=ISO-8859-6
be=ISO-8859-5
bg=ISO-8859-5
ca=ISO-8859-1
cs=ISO-8859-2
da=ISO-8859-1
de=ISO-8859-1
el=ISO-8859-7
en=ISO-8859-1
es=ISO-8859-1
et=ISO-8859-1
fi=ISO-8859-1
fr=ISO-8859-1
hr=ISO-8859-2
hu=ISO-8859-2
is=ISO-8859-1
it=ISO-8859-1
iw=ISO-8859-8
ja=Shift_JIS
ko=EUC-KR
lt=ISO-8859-2
lv=ISO-8859-2
mk=ISO-8859-5
nl=ISO-8859-1
no=ISO-8859-1
pl=ISO-8859-2
pt=ISO-8859-1
ro=ISO-8859-2
ru=ISO-8859-5
sh=ISO-8859-5
sk=ISO-8859-2
sl=ISO-8859-2
sq=ISO-8859-2
sr=ISO-8859-5
sv=ISO-8859-1
tr=ISO-8859-9
uk=ISO-8859-5
zh=GB2312
zh_TW=Big5
  • 启动StandContext的相互关联的组件
  • 启动该StandardContext拥有的所有生命周期子容器
  • 启动管道对象
  • 启动生命周期STRART事件ContextConfig对象就是在这里为StandardContext对象进行配置
  • 启动设置好的Session管理器
  • 检查configured标志是否为true也就是检查ContextConfig配置StandardContext对象是否成功若不成功 会导致 不会讲available标志重新赋值为true导致启动失败 该StandardContext不可用
  • 触发AFTERSTART事件

invoke方法

  在Tomcat4中,StandardContext类的invoke方法由其相关联的连接器调用,或者当StandardContext类实例是Host容器的一个子容器时,由HOST实例的invoke方法调用,StandardContext类的invoke方法首先会检查应用程序是否正在重载过程中,若是

则等待应用程序重载完成,然后,它将调用其父类ContainBase的invoke方法

    /**
     * 
     * 根据特定容器的设计,处理指定的请求,并生成相应的响应。
     * 
     * @param request
     *            将被处理的请求
     * @param response
     *            生产的响应
     *
     * @exception IOException
     *                if an input/output error occurred while processing
     * @exception ServletException
     *                if a ServletException was thrown while processing this
     *                request
     */
    public void invoke(Request request, Response response) throws IOException, ServletException {

        // 检查当前StandardContext实例是否在重载 若是 则等待一段之后重新检查 一直到 重载之后 才会进行以后的操作
        while (getPaused()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                ;
            }
        }

        // Normal request processing
        if (swallowOutput) {
            try {
                SystemLogHandler.startCapture();
                super.invoke(request, response);
            } finally {
                String log = SystemLogHandler.stopCapture();
                if (log != null && log.length() > 0) {
                    log(log);
                }
            }
        } else {
            // 调用其父类的invoke方法 其实就是开始调用管道的的invoke方法了
            super.invoke(request, response);
        }

    }

在Tomcat5中,StandardContext类并没有提供invoke方法的实现 。因此会执行其父类ContainerBase类的invoke方法,检查应用程序是否在重载的工作就移动到了 ContainerBase的invoke方法中

StandardContextMapper类

  对于每一个引入的HTTP请求,都会调用StandardContext实例的管道对象的基础阀的invoke方法来处理,StandardContex实例(就是StandardContext实例的管道对象)的基础阀 是org.apache.catalina.core.StandardContextValue类的实例,

StandardContextValue类的invoke方法要做的第一件事就是获取一个要处理HTTP请求的Wrapper实例。

  在Tomcat4中,StandardContextValue实例在它包含的属性StandardContext中查找,StandardContextValue实例会使用StandardContext实例的映射器找到一个合适的Wrapper实例。获得了对应的Wrapper实例之后,他就会调用Wrapper实例的invoke方法,在深入无挖掘StandardContextValue类的工作原理之前,先介绍一些映射器组件。

  ContainerBase类是StandardContext类的父类,前者定义了一个addDefaultMapper()方法用来添加一个默认的映射器。

    /**
     * 如果没有显式配置,则添加默认Mapper实现
     *
     * @param mapperClass
     *            Mapper实现的java完全限定类名
     */
    protected void addDefaultMapper(String mapperClass) {

        //  若限定名为null 则证明我们不需要映射器 直接返回
        if (mapperClass == null)
            return;
        //如果已经存在了mapper 则也直接返回
        if (mappers.size() >= 1)
            return;

        // 根据指定的限定名 初始化并添加一个 映射器默
        try {
            Class clazz = Class.forName(mapperClass);
            Mapper mapper = (Mapper) clazz.newInstance();
            //固定http协议
            mapper.setProtocol("http");
            addMapper(mapper);
        } catch (Exception e) {
            log(sm.getString("containerBase.addDefaultMapper", mapperClass), e);
        }

    }

StandardContext类在其start方法中调用 addDefaultMapper方法,并传入变量mapperClass的值;

public synchronized void start() throws LifecycleException {
/***/
if (ok) {

            try {

                addDefaultMapper(this.mapperClass);
}

所以通过设置StandardContext的mapperClass的值就可以控制 使用自定义的映射器。

那么StandardContext 的mapperClass的默认值如下;

    /**
     * 
     * 与该容器相关联的默认映射器对象的完全限定名
     */
    private String mapperClass = "org.apache.catalina.core.StandardContextMapper";

  必须要调用映射器的setContainer方法,通过传入一个容器的实例,将映射器和容器相关联,在Catalina中,org.apache.catalina.Mapper接口的实现类是org,apache.catalina.core.StandardContextMapper类。StandardContextMapper实例只能与Context级别容器相互关联,看下其setContaliner方法

/**
     *
     * 设置一个与该映射器相关联的容器
     *
     * @param container
     *            关联的新容器
     *
     * @exception IllegalArgumentException
     *                如果这个容器不是一个StandardContext实现则抛出错误
     */
    public void setContainer(Container container) {

        if (!(container instanceof StandardContext))
            throw new IllegalArgumentException(sm.getString("httpContextMapper.container"));
        context = (StandardContext) container;

    }

映射器中最重要的就是map方法,该方法返回用来处理HTTP请求的子容器。方法签名如下

 public Container map(Request request, boolean update);

在StandardContextMapper类中,map方法返回一个Wrapper实例,用于处理请求,若找不到适合的Wrapper实例,则返回null

  对于引入的每一个http请求,StandardContextValue实例调用Context容器的map方法,并传入一个org.apache.catalina.Request对象,map方法(实际上是定义在其父类ContainerBase类中的)会针对某个特定的协议调用findMapper方法返回一个映射器对象,然后调用映射器对象的map方法获取Wrapper实例。

/**
     * 
     * 根据请求的特性,返回应该用于处理该请求的子容器。如果无法标识此类子容器,则返回<code>null</code>。
     * 
     * @param request
     *            Request being processed
     * @param update
     *            Update the Request to reflect the mapping selection?
     */
    public Container map(Request request, boolean update) {

        // 根据指定请求的协议返回一个Mapper 
        Mapper mapper = findMapper(request.getRequest().getProtocol());
        if (mapper == null)
            return (null);

        // 使用这个Mapper获取一个处理该请求的Wrapper实例
        return (mapper.map(request, update));

    }

先写到这

 

StandardContext

标签:def   oba   hat   针对   cal   startup   static   生命周期   OLE   

原文地址:https://www.cnblogs.com/ChenD/p/10061008.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!