Tomcat源码分析(三)-Container和Lifecycle

版权声明:原创文章,转载请注明出处,来自https://blog.csdn.net/Jacabe https://blog.csdn.net/Jacabe/article/details/82227153

一、Tomcat生命周期的管理

 Tomcat通过package org.apache.catalina.Lifecycle;接口管理Tomcat的生命周期。


package org.apache.catalina;

public interface Lifecycle {
//定义的String常量,用于LifecycleEvent事件的type属性中,作用是区分组件发出的LifecycleEvent事件时的状态(如初始化前,启动前,启动中等)

    public static final String BEFORE_INIT_EVENT = "before_init";

    public static final String AFTER_INIT_EVENT = "after_init";

    public static final String START_EVENT = "start";

    public static final String BEFORE_START_EVENT = "before_start";

    public static final String AFTER_START_EVENT = "after_start";

    public static final String STOP_EVENT = "stop";

    public static final String BEFORE_STOP_EVENT = "before_stop";

    public static final String AFTER_STOP_EVENT = "after_stop";

    public static final String AFTER_DESTROY_EVENT = "after_destroy";

    public static final String BEFORE_DESTROY_EVENT = "before_destroy";

    public static final String PERIODIC_EVENT = "periodic";

    public static final String CONFIGURE_START_EVENT = "configure_start";

    public static final String CONFIGURE_STOP_EVENT = "configure_stop";

    public void addLifecycleListener(LifecycleListener listener); //添加LifecycleListener
                                                                  //监听器

    public LifecycleListener[] findLifecycleListeners(); 

    public void init() throws LifecycleException;

  
    public void start() throws LifecycleException;

    public void stop() throws LifecycleException;

    public void destroy() throws LifecycleException;

    public LifecycleState getState();

    public String getStateName();

    public interface SingleUse {
    }
}

所有生命周期的组件都要实现Lifecycle接口。

LifecycleBase是Lifecycle的默认实现。Server,Service等生命周期组件直接或间接继承了LifecycleBase;

LifecycleBase里面定义了一个 LifecycleSupport类来管理监听器。

 /**
     * Used to handle firing lifecycle events.
     * TODO: Consider merging LifecycleSupport into this class.
     */
    private final LifecycleSupport lifecycle = new LifecycleSupport(this);

LifecycleSupport代码如下:

/**
 * Support class to assist in firing LifecycleEvent notifications to
 * registered LifecycleListeners.
 *
 * @author Craig R. McClanahan
 */
public final class LifecycleSupport {

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

    /**
     * Construct a new LifecycleSupport object associated with the specified
     * Lifecycle component.
     *
     * @param lifecycle The Lifecycle component that will be the source
     *  of events that we fire
     */
    public LifecycleSupport(Lifecycle lifecycle) {
        super();
        this.lifecycle = lifecycle;
    }


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

    /**
     * The source component for lifecycle events that we will fire.
     */
    private final Lifecycle lifecycle;


    /**
     * The list of registered LifecycleListeners for event notifications.
     */
    private final List<LifecycleListener> listeners = new CopyOnWriteArrayList<>();


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

    /**
     * Add a lifecycle event listener to this component.
     *
     * @param listener The listener to add
     */
    public void addLifecycleListener(LifecycleListener listener) {
        listeners.add(listener);
    }


    /**
     * Get the lifecycle listeners associated with this lifecycle. If this
     * Lifecycle has no listeners registered, a zero-length array is returned.
     */
    public LifecycleListener[] findLifecycleListeners() {
        return listeners.toArray(new LifecycleListener[0]);
    }


    /**
     * Notify all lifecycle event listeners that a particular event has
     * occurred for this Container.  The default implementation performs
     * this notification synchronously using the calling thread.
     *
     * @param type Event type
     * @param data Event data
     */
    public void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        for (LifecycleListener listener : listeners) {
            listener.lifecycleEvent(event);
        }
    }


    /**
     * Remove a lifecycle event listener from this component.
     *
     * @param listener The listener to remove
     */
    public void removeLifecycleListener(LifecycleListener listener) {
        listeners.remove(listener);
    }
}

而在LifecycleSupport类里面定义了一个LifecycleListener集合类型来保存LifecycleListener监听器。

代码如下:

/**
 * Support class to assist in firing LifecycleEvent notifications to
 * registered LifecycleListeners.
 *
 * @author Craig R. McClanahan
 */
public final class LifecycleSupport {

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

    /**
     * Construct a new LifecycleSupport object associated with the specified
     * Lifecycle component.
     *
     * @param lifecycle The Lifecycle component that will be the source
     *  of events that we fire
     */
    public LifecycleSupport(Lifecycle lifecycle) {
        super();
        this.lifecycle = lifecycle;
    }


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

    /**
     * The source component for lifecycle events that we will fire.
     */
    private final Lifecycle lifecycle;


    /**
     * The list of registered LifecycleListeners for event notifications.
     */
    private final List<LifecycleListener> listeners = new CopyOnWriteArrayList<>();


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

    /**
     * Add a lifecycle event listener to this component.
     *
     * @param listener The listener to add
     */
    public void addLifecycleListener(LifecycleListener listener) {
        listeners.add(listener);
    }


    /**
     * Get the lifecycle listeners associated with this lifecycle. If this
     * Lifecycle has no listeners registered, a zero-length array is returned.
     */
    public LifecycleListener[] findLifecycleListeners() {
        return listeners.toArray(new LifecycleListener[0]);
    }


    /**
     * Notify all lifecycle event listeners that a particular event has
     * occurred for this Container.  The default implementation performs
     * this notification synchronously using the calling thread.
     *
     * @param type Event type
     * @param data Event data
     */
    public void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        for (LifecycleListener listener : listeners) {
            listener.lifecycleEvent(event);
        }
    }


    /**
     * Remove a lifecycle event listener from this component.
     *
     * @param listener The listener to remove
     */
    public void removeLifecycleListener(LifecycleListener listener) {
        listeners.remove(listener);
    }
}

LifecycleListener类代码如下:

/**
 * Interface defining a listener for significant events (including "component
 * start" and "component stop" generated by a component that implements the
 * Lifecycle interface. The listener will be fired after the associated state
 * change has taken place.
 *
 * @author Craig R. McClanahan
 */
public interface LifecycleListener {


    /**
     * Acknowledge the occurrence of the specified event.
     *
     * @param event LifecycleEvent that has occurred
     */
    public void lifecycleEvent(LifecycleEvent event);


}

   二、 LifecycleBase类的生命周期方法分析:

           四个生命周期方法分别是 init(),start(),stop(),destroy()方法。都会线判断状态和当前方法是否匹配。

            如在init方法之前调用了start方法。这时会先执行init方法。

             LifecycleBase的状态是通过LifecycleBase类型的state属性来保存的,最开始初始化值为LifecycleState.NEW.
           initInternal()和startInternal()模板方法会调用具体的子类具体来启动。

 LifecycleBase类的init方法代码如下:

  @Override
    public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {//如果状态不是NEW,就
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }

        try { //初始化前将状态设置为LifecycleState.INITIALIZING
            setStateInternal(LifecycleState.INITIALIZING, null, false);
            initInternal(); //具体执行初始化
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.initFail",toString()), t);
        }
    }


    protected abstract void initInternal() throws LifecycleException;
   //invalidTransition 抛出LifecycleException异常  
   //处理不符合要求的状态
   private void invalidTransition(String type) throws LifecycleException {
        String msg = sm.getString("lifecycleBase.invalidTransition", type,
                toString(), state);
        throw new LifecycleException(msg);
    }
   private synchronized void setStateInternal(LifecycleState state,
            Object data, boolean check) throws LifecycleException {

        if (log.isDebugEnabled()) {
            log.debug(sm.getString("lifecycleBase.setState", this, state));
        }

        if (check) {  //check 该段主要是校验state状态,state为null抛出异常,
                      //state不是FAILED 并且state不是STARTING或者this.state不是STARTING_PREP 或state不是STARTING,this.state也不是STARTING_PREP (省略。。)才会抛出异常,无法识别得到state.
                      //state可以是FAILED 
                      //state可以是STARTING,this.state可以是STARTING_PREP 
                      //state可以是STOPPING,this.state可以是STOPPING_PREP 
                      //state可以是STOPPING,this.state可以是FAILED 都不会进入异常
            // Must have been triggered by one of the abstract methods (assume
            // code in this class is correct)
            // null is never a valid state
            if (state == null) {
                invalidTransition("null");
                // Unreachable code - here to stop eclipse complaining about
                // a possible NPE further down the method
                return;
            }

            // Any method can transition to failed
            // startInternal() permits STARTING_PREP to STARTING
            // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
            // STOPPING
            if (!(
                    state == LifecycleState.FAILED ||
                    (this.state == LifecycleState.STARTING_PREP &&
                            state == LifecycleState.STARTING) ||
                    (this.state == LifecycleState.STOPPING_PREP &&
                            state == LifecycleState.STOPPING) ||
                    (this.state == LifecycleState.FAILED &&
                            state == LifecycleState.STOPPING)
                    )) {
                // No other transition permitted
                invalidTransition(state.name());
            }
        }

        this.state = state;
        String lifecycleEvent = state.getLifecycleEvent();
        if (lifecycleEvent != null) {  
            fireLifecycleEvent(lifecycleEvent, data); //处理事件
        }
    }

setStateInternal()方法中通过check参数来判断是否需要检查传入的状态state.如果需要检查则会检查传入的状态是否为空和是否符合正确的状态,最后设置state.

 LifecycleBase类的start方法代码如下:

/**
     * {@inheritDoc}
     */
    @Override
    public final synchronized void start() throws LifecycleException {
        //检查状态是否已经启动,如果已经启动打印log
        if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
                LifecycleState.STARTED.equals(state)) {

            if (log.isDebugEnabled()) {
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
            } else if (log.isInfoEnabled()) {
                log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
            }

            return;
        }

        if (state.equals(LifecycleState.NEW)) {
            init();
        } else if (state.equals(LifecycleState.FAILED)) { //状态失败停止
            stop();
        } else if (!state.equals(LifecycleState.INITIALIZED) &&
                !state.equals(LifecycleState.STOPPED)) {  //如果状态无法识别,抛出异常
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        }

        try {
            setStateInternal(LifecycleState.STARTING_PREP, null, false);
            startInternal();
            if (state.equals(LifecycleState.FAILED)) {
                // This is a 'controlled' failure. The component put itself into the
                // FAILED state so call stop() to complete the clean-up.
                stop();
            } else if (!state.equals(LifecycleState.STARTING)) {//如果启动后状态不是
                                                               //STARTING则抛出异常
                // Shouldn't be necessary but acts as a check that sub-classes are
                // doing what they are supposed to.
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            } else {                                          
                                                          //启动成功后将状态设为STARTED
                setStateInternal(LifecycleState.STARTED, null, false);
            }
        } catch (Throwable t) {
            // This is an 'uncontrolled' failure so put the component into the
            // FAILED state and throw an exception.
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
        }
    }

三、Container源码分析

    Container的作用是具体负责处理Servlet,处理Http请求。

public interface Container extends Lifecycle {

Container 四个子容器 Engine,Host,Context,Wrapper逐层包含的关系。

Engine 引擎,用来管理多个站点。

Host 站点(虚拟主机),整个webapps是一个站点,如:www.test.com域名对应webapps目录所代表的站点,其中ROOT目录 是主应用,直接使用域名就可以访问主应用。而webapps/test是test子应用,访问www.test.com/test

Context 应用程序,默认配置下webapps下的每个目录都市一个应用。其中ROOT目录存放着主应用,其他目录是子应用。

Wrapper 每个Wrapper封装一个Servlet.

配置:

Server.xml文件里面放了Tomcat大部分功能的配置。

Server.xml文件默认配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
  <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
  <Listener className="org.apache.catalina.core.JasperListener"/>
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
  <GlobalNamingResources>
    <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
  </GlobalNamingResources>

  <Service name="Catalina">
    <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
    <Engine defaultHost="localhost" name="Catalina">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

      <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log." suffix=".txt"/>

        <Context docBase="D:\apache-tomcat-7.0.69\wtpwebapps\tp-user" path="/tp-user" reloadable="true" source="org.eclipse.jst.jee.server:tp-user"/>
      </Host>
    </Engine>
  </Service>
</Server>

以上代码定义一个Server,端口是8005,监听命令是"SHUTDOWN",

 Server下定义了listener,如:org.apache.catalina.startup.VersionLoggerListener ,

  Server下定义了一个 GlobalNamingResources 配置了服务器的全局JNDI资源,配置Realm,需要配置GlobalNamingResources 。

            1、可以在GlobalNamingResources中嵌入 <Resource>元素,定义资源参数 。

                 GlobalNamingResources官网介绍:http://tomcat.apache.org/tomcat-7.0-doc/config/globalresources.html

   <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>

                 description-->资源描述,

                 pathname-->加载资源的路径,

                 type->获取该资源的时候返回的类型,

               auth -->用Application登录到资源管理器,或者用Container代表应用程序登录到资源管理器。 此属性的值必须是Application或Container。 如果Web应用程序将在Web应用程序部署描述符中使用<resource-ref>元素,则此属性是必需的,但如果应用程序使用<resource-env-ref>,则该属性是可选的。

             2、 可以在GlobalNamingResources中嵌套<Environment>元素,配置命名的值,这些值作为环境条目资源(Environment Entry Resource),对整个web应用可见 如在GlobalNamingResources下添加:

        <Environment name="maxExemptions" value="10" type="java.lang.Integer" override="false"/>

               value:通过JNDI context请求时,返回给应用的参数值。这个值必须转换成type属性定义的Java类型.

              override: 如果/WEB-INF/web.xml中具有相同名称的<env-entry>覆盖这里指定的值,设为false。缺省值为true.

              name :Environment的名称

              等价于在/WEB-INF/web.xml中的配置:

     <env-entry>

          <env-entry-name>maxExemptions</param-name>

          <env-entry-value>10</env-entry-value>

           <env-entry-type>java.lang.Integer</env-entry-type>

</env-entry>

Server下定义了一个名为"Catalina"的Service ,Service里面定义了两个Connector ,一个是HTTP协议,一个是AJP协议(主要用于集成,与apache集成等),Service下面定义了名为 "Catalina"的Engine,默认站点是localhost。

Engine下定义了一个Host ,Host的appBase的每个文件夹里都代表一个应用,上面定义的站点就是默认是webapps目录,unpackWARs表示十分自动解压war文件,autoDeploy表示是否自动部署,为true,webapps下加入的应用会自动部署并启动。

Engin下定义了一个Realm,用于身份验证等安全机制.如果不指定,则使用Engine的Realm。

             <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm> 对一个UserDatabase Realm的配置,它让默认的web应用程序(manager)加载tomcat-user.xml来进行用户验证.

       Realm可以从这些数据源获取用户验证数据:

  • memory:使用在内存中存放的一个表格进行验证。这个表格时在tomcat启动时从一个xml文件加载到内存中的,在这个表格中的信息格式一般为:用户名/密码/角色。这种方式一般只用于测试和开发阶段. 配置方式如下<Realm className="org.apache.catalina.realm.MemoryRealm"/> 默认是读取 tomcat-users.xml
  • UserDatabase:实现了一个可以修改的、持久的memory Realm,可以向后兼容memory Realm。
  • JDBC:使用一个关系数据库存放用户验证数据
  • DataSource:类似于JDBC Realm,使用JNDI的方式来从关系数据库中拿用户验证数据,内容最终还是在一个关系数据库里。
  • JNDI:使用JNDI来获取Realm数据,这些数据一般存放在LDAP目录下。
  • JAAS:  使用JAAS来获取用户验证信息

     具体的数据源配置可参考博客:https://blog.csdn.net/conquer0715/article/details/78206874?locationNum=6&fps=1

Host下有一个Alias标签,<Host name="www.test.com"><Alias> test.com</Alias></Host> 表示alias别名,www.test.com和test.com访问同一个站点。

   <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log." suffix=".txt"/>访问日志的配置,directory 指的是日志文件放置的目录,在tomcat下面有个logs文件夹,prefix日志文件的名称前缀,suffix后缀,AccessLogValve访问日志类,pattern的匹配如下:

%a      

这是记录访问者的IP,在日志里是127.0.0.1

%A

这是记录本地服务器的IP,在日志里是192.168.254.108

%b

这是发送信息的字节数,不涵括http头,如果字节数为0的话,显示为-

%B

看tomcat的解释,没看出来与b%的区别,但我这里显示为-1,没想明白,望知道者告知,我把官方解释贴出来吧 Bytes sent, excluding HTTP headers

%h

这个就是服务器名称了,如果resolveHosts为false的话,这里就是IP地址了,我的日志里是127.0.0.1

%H

访问者使用的协议,这里是HTTP/1.1

%l

这个也不太清楚,官方也说这个always return '-' 官方解释:Remote logical username from identd (可能这样翻译:记录浏览者进行身份验证时提供的名字)(always returns '-')

%m

访问的方式,是GET还是POST,我这是GET

%p

本地接收访问的端口,呵呵,我这里是80啦

%q

比如你访问的是aaa.jsp?bbb=ccc,那么这里就显示?bbb=ccc,明白了吧,这个q是querystring的意思

%r

官方解释:First line of the request (method and request URI),不是很明白

%s

这个是http的状态,我这里返回的是304,咱们经常看见访问某个网页报错误500什么的,那也会返回500

%S

用户的session ID,这个session ID大家可以另外查一下详细的解释,反正每次都会生成不同的session ID

%t

这就是时间啦,好像有一个Common Log Format可以改,不过我没找到

%u

得到了验证的访问者,否则就是"-"

%U

访问的URL地址,我这里是/rightmainima/leftbott4.swf

%v

服务器名称,可能就是你url里面写的那个吧,我这里是localhost

%D

官方解释:Time taken to process the request, in millis,应该是访问发生的时间,以毫秒记

%T

官方解释:Time taken to process the request, in seconds,应该是访问发生的时间,以秒记

Engin的defaultHost属性,请求的域名在所有的Host的name和Alias都找不到时才使用defaultHost(默认的Host),如果使用IP直接访问会用到defaultHost,删除Engin的defaultHost属性,无法用127.0.0.1访问本机的tomcat.

Host下的定义的Context(应用)配置--》通过文件配置,--》将WAR文件放在Host目录下 ,--》文件夹放在Host目录下。

         文件配置方式:conf/server.xml 中context标签--》配置单独的应用

                              conf/(enginname)/(hostname)/目录下以应用命名的xml文件--》配置单独的应用

                              /META-INF/context.xml文件 --》配置单独的应用

                              conf/context.xml文件 --》Context共享(整个Tomcat共享)

                             conf/(enginname)/(hostname)/context.xml.default文件 --》Context共享(Host共享)

      

ContainerBase类:

   
ContainerBase类:

  
    /**
     * The number of threads available to process start and stop events for any
     * children associated with this container.
     */
    private int startStopThreads = 1;   //启动和停止的线程数
    @Override
    protected void initInternal() throws LifecycleException {
        BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
                                            //ThreadPoolExecutor继承自Executor,管理线程
        startStopExecutor = new ThreadPoolExecutor(          
                getStartStopThreadsInternal(),
                getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
                startStopQueue,
                new StartStopThreadFactory(getName() + "-startStop-"));
        startStopExecutor.allowCoreThreadTimeOut(true);
        super.initInternal();
    }

       /**
     * Handles the special values.
     */
    private int getStartStopThreadsInternal() {
        int result = getStartStopThreads();

        // Positive values are unchanged
        if (result > 0) {     //线程数大于0,直接返回
            return result;
        }

        // Zero == Runtime.getRuntime().availableProcessors()
        // -ve  == Runtime.getRuntime().availableProcessors() + value
        // These two are the same
        result = Runtime.getRuntime().availableProcessors() + result;
        if (result < 1) {   //线程数小于1,也处理为1
            result = 1;
        }
        return result;
    }
 ContainerBase类:

/**
     * Start this component and implement the requirements
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    @Override
    protected synchronized void startInternal() throws LifecycleException {

        // Start our subordinate components, if any
        logger = null;
        getLogger();
        Cluster cluster = getClusterInternal();
        if ((cluster != null) && (cluster instanceof Lifecycle))
            ((Lifecycle) cluster).start();             //启动cluster
        Realm realm = getRealmInternal();
        if ((realm != null) && (realm instanceof Lifecycle))
            ((Lifecycle) realm).start();                //启动realm

        // Start our child containers, if any
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();
        for (int i = 0; i < children.length; i++) {
                              //线程调用子容器的start方法,(children[i].start())
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }

        boolean fail = false;
        for (Future<Void> result : results) {
            try {
                result.get();                            //获取Future返回值
            } catch (Exception e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                fail = true;
            }

        }
        if (fail) {
            throw new LifecycleException(
                    sm.getString("containerBase.threadedStartFailed"));
        }

        // Start the Valves in our pipeline (including the basic), if any
        if (pipeline instanceof Lifecycle)
            ((Lifecycle) pipeline).start();              //启动管道


        setState(LifecycleState.STARTING);

        // Start our thread
        threadStart();                                 //后台线程启动

    }

1、new StartChild(children[i]))分析,实现Callable接口, 调用子容器start()方法。

 // ----------------------------- Inner classes used with start/stop Executor

    private static class StartChild implements Callable<Void> {

        private Container child;

        public StartChild(Container child) {
            this.child = child;
        }

        @Override
        public Void call() throws LifecycleException {
            child.start();
            return null;
        }
    }

2、 threadStart();  方法分析

    

   ContainerBase类:

    /**
     * The processor delay for this component.
     */
    protected int backgroundProcessorDelay = -1;        //后台线程间隔,小于0就不启动

/**
     * Start the background thread that will periodically check for
     * session timeouts.
     */
    protected void threadStart() {

        if (thread != null)
            return;
        if (backgroundProcessorDelay <= 0)
            return;

        threadDone = false;
        String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
        thread = new Thread(new ContainerBackgroundProcessor(), threadName);
        thread.setDaemon(true);
        thread.start();

    }


 /**
     * Private thread class to invoke the backgroundProcess method
     * of this container and its children after a fixed delay.
     */
    protected class ContainerBackgroundProcessor implements Runnable {

        @Override
        public void run() {
            Throwable t = null;
            String unexpectedDeathMessage = sm.getString(
                    "containerBase.backgroundProcess.unexpectedThreadDeath",
                    Thread.currentThread().getName());
            try {
                while (!threadDone) {      //while循环间隔backgroundProcessorDelay * 1000L
                                            //调用backgroundProcess方法
                  try {
                        Thread.sleep(backgroundProcessorDelay * 1000L);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                    if (!threadDone) {
                        processChildren(ContainerBase.this);
                    }
                }
            } catch (RuntimeException|Error e) {
                t = e;
                throw e;
            } finally {
                if (!threadDone) {
                    log.error(unexpectedDeathMessage, t);
                }
            }
        }

        protected void processChildren(Container container) {
            ClassLoader originalClassLoader = null;

            try {
                if (container instanceof Context) {
                    Loader loader = ((Context) container).getLoader();
                    // Loader will be null for FailedContext instances
                    if (loader == null) {
                        return;
                    }

                    // Ensure background processing for Contexts and Wrappers
                    // is performed under the web app's class loader
                    originalClassLoader = ((Context) container).bind(false, null);
                }
                container.backgroundProcess();    //这里调用了container的backgroundProcess
                Container[] children = container.findChildren();
                for (int i = 0; i < children.length; i++) {
                    if (children[i].getBackgroundProcessorDelay() <= 0) {
                        processChildren(children[i]);
                    }
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error("Exception invoking periodic operation: ", t);
            } finally {
                if (container instanceof Context) {
                    ((Context) container).unbind(false, originalClassLoader);
               }
            }
        }
    }

在processChildren()方法里面调用了 container.backgroundProcess();

具体实现类有:StandardContext,StandardWrapper类

    StandardContext类:    

    @Override
    public void backgroundProcess() {

        if (!getState().isAvailable())
            return;

        Loader loader = getLoader();   
        if (loader != null) {
            try {
                loader.backgroundProcess();  //实现类WebappLoader类调用backgroundProcess()
            } catch (Exception e) {
                log.warn(sm.getString(
                        "standardContext.backgroundProcess.loader", loader), e);
            }
        }
        Manager manager = getManager();
        if (manager != null) {
            try {
                manager.backgroundProcess();  //ManageBase类实现,处理Session过期
            } catch (Exception e) {
                log.warn(sm.getString(
                        "standardContext.backgroundProcess.manager", manager),
                        e);
            }
        }
        WebResourceRoot resources = getResources();
        if (resources != null) {
            try {
                resources.backgroundProcess();   //cache和gc()
            } catch (Exception e) {
                log.warn(sm.getString(
                        "standardContext.backgroundProcess.resources",
                        resources), e);
            }
        }
        InstanceManager instanceManager = getInstanceManager();
        if (instanceManager instanceof DefaultInstanceManager) {
            try {    //清除map已经过期死亡的key
                ((DefaultInstanceManager) instanceManager).backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString(
                        "standardContext.backgroundProcess.instanceManager",
                        resources), e);
            }
        }
        super.backgroundProcess();
    }

Engin:

StandardEngin类:
  
   @Override
    protected void initInternal() throws LifecycleException {
        // Ensure that a Realm is present before any attempt is made to start
        // one. This will create the default NullRealm if necessary.
        getRealm();
        super.initInternal();
    }

  @Override
    protected synchronized void startInternal() throws LifecycleException {

        // Log our server identification information
        if(log.isInfoEnabled())
            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());

        // Standard container startup
        super.startInternal();
    }

Host:

    org.apache.catalina.core.StandardHost类:
    private String errorReportValveClass =
        "org.apache.catalina.valves.ErrorReportValve";
   @Override
    protected synchronized void startInternal() throws LifecycleException {

        // Set error report valve
        String errorValve = getErrorReportValveClass();
        if ((errorValve != null) && (!errorValve.equals(""))) {
            try {
                boolean found = false;
                Valve[] valves = getPipeline().getValves();
                for (Valve valve : valves) {
                    if (errorValve.equals(valve.getClass().getName())) {
                        found = true;
                        break;
                    }
                }
                if(!found) {   //检查管道有没有指定的Value,没有则添加
                    Valve valve =
                        (Valve) Class.forName(errorValve).getDeclaredConstructor().newInstance();
                    getPipeline().addValve(valve);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString(
                        "standardHost.invalidErrorReportValveClass",
                        errorValve), t);
            }
        }
        super.startInternal();
    }

HostConfig类:

  package org.apache.catalina.startup;

  public class HostConfig implements LifecycleListener

public interface LifecycleListener {


    /**
     * Acknowledge the occurrence of the specified event.
     *
     * @param event LifecycleEvent that has occurred
     */
    public void lifecycleEvent(LifecycleEvent event);


}

HostConfig的start方法在 ContainerBase类的backgroundProcess()方法有一个

fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);方法调用


  org.apache.catalina.startup.HostConfig

/**
     * Process the START event for an associated Host.
     *
     * @param event The lifecycle event that has occurred
     */
    @Override
    public void lifecycleEvent(LifecycleEvent event) {

        // Identify the host we are associated with
        try {
            host = (Host) event.getLifecycle();
            if (host instanceof StandardHost) {
                setCopyXML(((StandardHost) host).isCopyXML());
                setDeployXML(((StandardHost) host).isDeployXML());
                setUnpackWARs(((StandardHost) host).isUnpackWARs());
                setContextClass(((StandardHost) host).getContextClass());
            }
        } catch (ClassCastException e) {
            log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
            return;
        }

        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
            check();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.START_EVENT)) {
            start();
        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            stop();
        }
    }

HostConfig类的lifecycleEvent方法在接收到Lifecycle.START_EVENT时会调用start方法:

HostConfig的start()方法:

  org.apache.catalina.HostConfig类:
   /**
     * Process a "start" event for this Host.
     */
    public void start() {

        if (log.isDebugEnabled())
            log.debug(sm.getString("hostConfig.start"));

        try {
            ObjectName hostON = host.getObjectName();
            oname = new ObjectName
                (hostON.getDomain() + ":type=Deployer,host=" + host.getName());
            Registry.getRegistry(null, null).registerComponent
                (this, oname, this.getClass().getName());
        } catch (Exception e) {
            log.error(sm.getString("hostConfig.jmx.register", oname), e);
        }

        if (!host.getAppBaseFile().isDirectory()) {
            log.error(sm.getString("hostConfig.appBase", host.getName(),
                    host.getAppBaseFile().getPath()));
            host.setDeployOnStartup(false);
            host.setAutoDeploy(false);
        }

        if (host.getDeployOnStartup())
            deployApps();

    }


    /**
     * Deploy applications for any directories or WAR files that are found
     * in our "application root" directory.
     */
    protected void deployApps() {

        File appBase = host.getAppBaseFile();
        File configBase = host.getConfigBaseFile();
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        // Deploy XML descriptors from configBase   //部署xml描述文件
                                        //xml文件指的是conf/(enginename)/(hostname)/*.xml
        deployDescriptors(configBase, configBase.list());
        // Deploy WARs                               //部署war文件,站点目录下
        deployWARs(appBase, filteredAppPaths);
        // Deploy expanded folders                  //部署文件夹
        deployDirectories(appBase, filteredAppPaths);

    }

 /**
     * Deploy WAR files.
     */
    protected void deployWARs(File appBase, String[] files) {

        if (files == null)
            return;

        ExecutorService es = host.getStartStopExecutor();
        List<Future<?>> results = new ArrayList<>();

        for (int i = 0; i < files.length; i++) {

            if (files[i].equalsIgnoreCase("META-INF"))
                continue;
            if (files[i].equalsIgnoreCase("WEB-INF"))
                continue;
            File war = new File(appBase, files[i]);
            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") &&
                    war.isFile() && !invalidWars.contains(files[i]) ) {

                ContextName cn = new ContextName(files[i], true);

                if (isServiced(cn.getName())) {
                    continue;
                }
                if (deploymentExists(cn.getName())) {
                    DeployedApplication app = deployed.get(cn.getName());
                    boolean unpackWAR = unpackWARs;
                    if (unpackWAR && host.findChild(cn.getName()) instanceof StandardContext) {
                        unpackWAR = ((StandardContext) host.findChild(cn.getName())).getUnpackWAR();
                    }
                    if (!unpackWAR && app != null) {
                        // Need to check for a directory that should not be
                        // there
                        File dir = new File(appBase, cn.getBaseName());
                        if (dir.exists()) {
                            if (!app.loggedDirWarning) {
                                log.warn(sm.getString(
                                        "hostConfig.deployWar.hiddenDir",
                                        dir.getAbsoluteFile(),
                                        war.getAbsoluteFile()));
                                app.loggedDirWarning = true;
                            }
                        } else {
                            app.loggedDirWarning = false;
                        }
                    }
                    continue;
                }

                // Check for WARs with /../ /./ or similar sequences in the name
                if (!validateContextPath(appBase, cn.getBaseName())) {
                    log.error(sm.getString(
                            "hostConfig.illegalWarName", files[i]));
                    invalidWars.add(files[i]);
                    continue;
                }

                results.add(es.submit(new DeployWar(this, cn, war)));
            }
        }

        for (Future<?> result : results) {
            try {
                result.get();
            } catch (Exception e) {
                log.error(sm.getString(
                        "hostConfig.deployWar.threaded.error"), e);
            }
        }
    }

以上代码,HostConfig类start会检查Host站点配置的目录位置是否存在,调用deployApps部署。

Context:

org.apache.catalina.core.standardContext 

 /**
     * Start this component and implement the requirements
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    @Override
    protected synchronized void startInternal() throws LifecycleException {

        if(log.isDebugEnabled())
            log.debug("Starting " + getBaseName());

        // Send j2ee.state.starting notification       //发送j2ee.state.starting的通知
        if (this.getObjectName() != null) {
            Notification notification = new Notification("j2ee.state.starting",
                    this.getObjectName(), sequenceNumber.getAndIncrement());
            broadcaster.sendNotification(notification);
        }

        setConfigured(false);
        boolean ok = true;

        // Currently this is effectively a NO-OP but needs to be called to
        // ensure the NamingResources follows the correct lifecycle
        if (namingResources != null) {
            namingResources.start();
        }

        // Post work directory
        postWorkDirectory();  //设置工作目录等信息到Context中

        // Add missing components as necessary
        if (getResources() == null) {   // (1) Required by Loader
            if (log.isDebugEnabled())
                log.debug("Configuring default Resources");

            try {// //这里的setResources指的是WebResourceRoot resources
                setResources(new StandardRoot(this)); 
            } catch (IllegalArgumentException e) {
                log.error(sm.getString("standardContext.resourcesInit"), e);
                ok = false;
            }
        }
        if (ok) {
            resourcesStart();
        }

        if (getLoader() == null) {
            WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
            webappLoader.setDelegate(getDelegate());
            setLoader(webappLoader);
        }

        // An explicit cookie processor hasn't been specified; use the default
        if (cookieProcessor == null) {
            cookieProcessor = new LegacyCookieProcessor();
        }

        // Initialize character set mapper
        getCharsetMapper();

        // Validate required extensions
        boolean dependencyCheck = true;
        try {
            dependencyCheck = ExtensionValidator.validateApplication
                (getResources(), this);
        } catch (IOException ioe) {
            log.error(sm.getString("standardContext.extensionValidationError"), ioe);
            dependencyCheck = false;
        }

        if (!dependencyCheck) {
            // do not make application available if dependency check fails
            ok = false;
        }

        // Reading the "catalina.useNaming" environment variable
        String useNamingProperty = System.getProperty("catalina.useNaming");
        if ((useNamingProperty != null)
            && (useNamingProperty.equals("false"))) {
            useNaming = false;
        }

        if (ok && isUseNaming()) {
            if (getNamingContextListener() == null) {
                NamingContextListener ncl = new NamingContextListener();
                ncl.setName(getNamingContextName());
                ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
                addLifecycleListener(ncl);
                setNamingContextListener(ncl);
            }
        }

        // Standard container startup
        if (log.isDebugEnabled())
            log.debug("Processing standard container startup");


        // Binding thread
        ClassLoader oldCCL = bindThread();

        try {
            if (ok) {
                // Start our subordinate components, if any
                Loader loader = getLoader();
                if ((loader != null) && (loader instanceof Lifecycle))
                    ((Lifecycle) loader).start();

                // since the loader just started, the webapp classloader is now
                // created.
                setClassLoaderProperty("clearReferencesRmiTargets",
                        getClearReferencesRmiTargets());
                setClassLoaderProperty("clearReferencesStatic",
                        getClearReferencesStatic());
                setClassLoaderProperty("clearReferencesStopThreads",
                        getClearReferencesStopThreads());
                setClassLoaderProperty("clearReferencesStopTimerThreads",
                        getClearReferencesStopTimerThreads());
                setClassLoaderProperty("clearReferencesHttpClientKeepAliveThread",
                        getClearReferencesHttpClientKeepAliveThread());
                setClassLoaderProperty("clearReferencesObjectStreamClassCaches",
                        getClearReferencesObjectStreamClassCaches());

                // By calling unbindThread and bindThread in a row, we setup the
                // current Thread CCL to be the webapp classloader
                unbindThread(oldCCL);
                oldCCL = bindThread();

                // Initialize logger again. Other components might have used it
                // too early, so it should be reset.
                logger = null;
                getLogger();

                Realm realm = getRealmInternal();

                if (realm != null) {
                    if (realm instanceof Lifecycle)
                        ((Lifecycle) realm).start();

                    // Place the CredentialHandler into the ServletContext so
                    // applications can have access to it. Wrap it in a "safe"
                    // handler so application's can't modify it.
                    CredentialHandler safeHandler = new CredentialHandler() {
                        @Override
                        public boolean matches(String inputCredentials, String storedCredentials) {
                            return getRealmInternal().getCredentialHandler().matches(inputCredentials, storedCredentials);
                        }

                        @Override
                        public String mutate(String inputCredentials) {
                            return getRealmInternal().getCredentialHandler().mutate(inputCredentials);
                        }
                    };
                    context.setAttribute(Globals.CREDENTIAL_HANDLER, safeHandler);
                }

                // Notify our interested LifecycleListeners
                fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

                // Start our child containers, if not already started
                for (Container child : findChildren()) {
                    if (!child.getState().isAvailable()) {
                        child.start();
                    }
                }

                // Start the Valves in our pipeline (including the basic),
                // if any
                if (pipeline instanceof Lifecycle) {
                    ((Lifecycle) pipeline).start();
                }

                // Acquire clustered manager
                Manager contextManager = null;
                Manager manager = getManager();
                if (manager == null) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("standardContext.cluster.noManager",
                                Boolean.valueOf((getCluster() != null)),
                                Boolean.valueOf(distributable)));
                    }
                    if ( (getCluster() != null) && distributable) {
                        try {
                            contextManager = getCluster().createManager(getName());
                        } catch (Exception ex) {
                            log.error("standardContext.clusterFail", ex);
                            ok = false;
                        }
                    } else {
                        contextManager = new StandardManager();
                    }
                }

                // Configure default manager if none was specified
                if (contextManager != null) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("standardContext.manager",
                                contextManager.getClass().getName()));
                    }
                    setManager(contextManager);
                }

                if (manager!=null && (getCluster() != null) && distributable) {
                    //let the cluster know that there is a context that is distributable
                    //and that it has its own manager
                    getCluster().registerManager(manager);
                }
            }

            if (!getConfigured()) {
                log.error(sm.getString("standardContext.configurationFail"));
                ok = false;
            }

            // We put the resources into the servlet context
            if (ok)
                getServletContext().setAttribute
                    (Globals.RESOURCES_ATTR, getResources());

            if (ok ) {
                if (getInstanceManager() == null) {
                    javax.naming.Context context = null;
                    if (isUseNaming() && getNamingContextListener() != null) {
                        context = getNamingContextListener().getEnvContext();
                    }
                    Map<String, Map<String, String>> injectionMap = buildInjectionMap(
                            getIgnoreAnnotations() ? new NamingResourcesImpl(): getNamingResources());
                    setInstanceManager(new DefaultInstanceManager(context,
                            injectionMap, this, this.getClass().getClassLoader()));
                    getServletContext().setAttribute(
                            InstanceManager.class.getName(), getInstanceManager());
                }
            }

            // Create context attributes that will be required
            if (ok) {
                getServletContext().setAttribute(
                        JarScanner.class.getName(), getJarScanner());
            }

            // Set up the context init params
            mergeParameters();

            // Call ServletContainerInitializers
            for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
                initializers.entrySet()) {
                try {
                    entry.getKey().onStartup(entry.getValue(),
                            getServletContext());
                } catch (ServletException e) {
                    log.error(sm.getString("standardContext.sciFail"), e);
                    ok = false;
                    break;
                }
            }

            // Configure and call application event listeners
            if (ok) {                                  //开始启动listener
                if (!listenerStart()) {
                    log.error(sm.getString("standardContext.listenerFail"));
                    ok = false;
                }
            }

            // Check constraints for uncovered HTTP methods
            // Needs to be after SCIs and listeners as they may programmatically
            // change constraints
            if (ok) {
                checkConstraintsForUncoveredMethods(findConstraints());
            }

            try {
                // Start manager
                Manager manager = getManager();
                if ((manager != null) && (manager instanceof Lifecycle)) {
                    ((Lifecycle) manager).start();
                }
            } catch(Exception e) {
                log.error(sm.getString("standardContext.managerFail"), e);
                ok = false;
            }

            // Configure and call application filters
            if (ok) {                                   //启动filter
                if (!filterStart()) {
                    log.error(sm.getString("standardContext.filterFail"));
                    ok = false;
                }
            }

            // Load and initialize all "load on startup" servlets
            if (ok) {                               //初始化Servlets
                if (!loadOnStartup(findChildren())){
                    log.error(sm.getString("standardContext.servletFail"));
                    ok = false;
                }
            }

            // Start ContainerBackgroundProcessor thread
            super.threadStart();
        } finally {
            // Unbinding thread
            unbindThread(oldCCL);
        }

        // Set available status depending upon startup success
        if (ok) {
            if (log.isDebugEnabled())
                log.debug("Starting completed");
        } else {
            log.error(sm.getString("standardContext.startFailed", getName()));
        }

        startTime=System.currentTimeMillis();

        // Send j2ee.state.running notification
        if (ok && (this.getObjectName() != null)) {
            Notification notification =
                new Notification("j2ee.state.running", this.getObjectName(),
                                 sequenceNumber.getAndIncrement());
            broadcaster.sendNotification(notification);
        }

        // The WebResources implementation caches references to JAR files. On
        // some platforms these references may lock the JAR files. Since web
        // application start is likely to have read from lots of JARs, trigger
        // a clean-up now.
        getResources().gc();

        // Reinitializing if something went wrong
        if (!ok) {
            setState(LifecycleState.FAILED);
        } else {
            setState(LifecycleState.STARTING);
        }
    }

1、namingResources.start();

 NamingResourcesImpl继承自NameingResources,NamingResourcesImpl主要是管理j2EE命名的上下文和其关联的JNDI的上下文。

   NamingResourcesImpl类:
 @Override
    protected void startInternal() throws LifecycleException {
        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);
    }

2、 resourcesStart();

  

 resources.start();具体实现是在WebappClassLoaderBase类的start()方法里面。

WebappClassLoaderBase类的start() 主要是加载 WEB-INF/classes下的class和WEB-INF/lib下的class.

  StandardContext类:

    private WebResourceRoot resources;
/**
     * Allocate resources, including proxy.
     * Return <code>true</code> if initialization was successfull,
     * or <code>false</code> otherwise.
     */
    public void resourcesStart() throws LifecycleException {

        // Check current status in case resources were added that had already
        // been started
        if (!resources.getState().isAvailable()) {
            resources.start();
        }

        if (effectiveMajorVersion >=3 && addWebinfClassesResources) {
            WebResource webinfClassesResource = resources.getResource(
                    "/WEB-INF/classes/META-INF/resources");
            if (webinfClassesResource.isDirectory()) {
                getResources().createWebResourceSet(
                        WebResourceRoot.ResourceSetType.RESOURCE_JAR, "/",
                        webinfClassesResource.getURL(), "/");
            }
        }
    }
 org.apache.catalina.loader.WebappClassLoaderBase类
 /**
     * Repositories managed by this class rather than the super class.
     */
    private List<URL> localRepositories = new ArrayList<>();


    private volatile LifecycleState state = LifecycleState.NEW;
    private final HashMap<String,Long> jarModificationTimes = new HashMap<>();

 /**
     * Start the class loader.
     *
     * @exception LifecycleException if a lifecycle error occurs
     */
    @Override
    public void start() throws LifecycleException {

        state = LifecycleState.STARTING_PREP;

        WebResource classes = resources.getResource("/WEB-INF/classes");
        if (classes.isDirectory() && classes.canRead()) {
            localRepositories.add(classes.getURL());
        }
        WebResource[] jars = resources.listResources("/WEB-INF/lib");
        for (WebResource jar : jars) {
            if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
                localRepositories.add(jar.getURL());
                jarModificationTimes.put(
                        jar.getName(), Long.valueOf(jar.getLastModified()));
            }
        }

        state = LifecycleState.STARTING;

        String encoding = null;
        try {
            encoding = System.getProperty("file.encoding");
        } catch (SecurityException e) {
            return;
        }
        if (encoding.indexOf("EBCDIC")!=-1) {
            needConvert = true;
        }

        state = LifecycleState.STARTED;
    }

3、getCharsetMapper方法:

   StandardContext类:

 /**
     * Return the Locale to character set mapper for this Context.
     */
    public CharsetMapper getCharsetMapper() {

        // Create a mapper the first time it is requested
        if (this.charsetMapper == null) {
            try {
                Class<?> clazz = Class.forName(charsetMapperClass);
                this.charsetMapper = (CharsetMapper) clazz.getDeclaredConstructor().newInstance();
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                this.charsetMapper = new CharsetMapper();
            }
        }

        return (this.charsetMapper);

    }

package org.apache.catalina.util;

import java.io.InputStream;
import java.util.Locale;
import java.util.Properties;
import org.apache.tomcat.util.ExceptionUtils;

/**
 * Utility class that attempts to map from a Locale to the corresponding
 * character set to be used for interpreting input text (or generating
 * output text) when the Content-Type header does not include one.  You
 * can customize the behavior of this class by modifying the mapping data
 * it loads, or by subclassing it (to change the algorithm) and then using
 * your own version for a particular web application.
 *
 * @author Craig R. McClanahan
 */
public class CharsetMapper {


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


    /**
     * Default properties resource name.
     */
    public static final String DEFAULT_RESOURCE =
      "/org/apache/catalina/util/CharsetMapperDefault.properties";

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

    /**
     * Construct a new CharsetMapper using the default properties resource.
     */
    public CharsetMapper() {
        this(DEFAULT_RESOURCE);
    }
    public CharsetMapper(String name) {
        try (InputStream stream = this.getClass().getResourceAsStream(name)) {
            map.load(stream);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            throw new IllegalArgumentException(t.toString());
        }
    }

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

    private Properties map = new Properties();

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

    public String getCharset(Locale locale) {
        // Match full language_country_variant first, then language_country,
        // then language only
        String charset = map.getProperty(locale.toString());
        if (charset == null) {
            charset = map.getProperty(locale.getLanguage() + "_"
                    + locale.getCountry());
            if (charset == null) {
                charset = map.getProperty(locale.getLanguage());
            }
        }
        return (charset);
    }

    public void addCharsetMappingFromDeploymentDescriptor(String locale, String charset) {
        map.put(locale, charset);
    }


}

4、ExtensionValidator.validateApplication(getResources(), this);方法

   

 /**
     * Runtime validation of a Web Application.
     *
     * This method uses JNDI to look up the resources located under a
     * <code>DirContext</code>. It locates Web Application MANIFEST.MF
     * file in the /META-INF/ directory of the application and all
     * MANIFEST.MF files in each JAR file located in the WEB-INF/lib
     * directory and creates an <code>ArrayList</code> of
     * <code>ManifestResource</code> objects. These objects are then passed
     * to the validateManifestResources method for validation.
     *
     * @param resources The resources configured for this Web Application
     * @param context   The context from which the Logger and path to the
     *                  application
     *
     * @return true if all required extensions satisfied
     */
    public static synchronized boolean validateApplication(
                                           WebResourceRoot resources,
                                           Context context)
                    throws IOException {

        String appName = context.getName();
        ArrayList<ManifestResource> appManifestResources = new ArrayList<>();

        // Web application manifest
        WebResource resource = resources.getResource("/META-INF/MANIFEST.MF");
        if (resource.isFile()) {
            try (InputStream inputStream = resource.getInputStream()) {
                Manifest manifest = new Manifest(inputStream);
                ManifestResource mre = new ManifestResource
                    (sm.getString("extensionValidator.web-application-manifest"),
                    manifest, ManifestResource.WAR);
                appManifestResources.add(mre);
            }
        }

        // Web application library manifests
        WebResource[] manifestResources =
                resources.getClassLoaderResources("/META-INF/MANIFEST.MF");
        for (WebResource manifestResource : manifestResources) {
            if (manifestResource.isFile()) {
                // Primarily used for error reporting
                String jarName = manifestResource.getURL().toExternalForm();
                Manifest jmanifest = manifestResource.getManifest();
                if (jmanifest != null) {
                    ManifestResource mre = new ManifestResource(jarName,
                            jmanifest, ManifestResource.APPLICATION);
                    appManifestResources.add(mre);
                }
            }
        }

        return validateManifestResources(appName, appManifestResources);
    }

主要是校验/META-INF/MANIFEST.MF文件里面的信息。

/META-INF/MANIFEST.MF文件内容示例如下:

5、Realm--> CredentialHandler safeHandler = new CredentialHandler()

   安全域,主要验证平局是否可以授权进入 这个ServletContext,是否是安全的。(Credential:凭据)

Realm realm = getRealmInternal();

                if (realm != null) {
                    if (realm instanceof Lifecycle)
                        ((Lifecycle) realm).start();

                    // Place the CredentialHandler into the ServletContext so
                    // applications can have access to it. Wrap it in a "safe"
                    // handler so application's can't modify it.
                    CredentialHandler safeHandler = new CredentialHandler() {
                        @Override
                        public boolean matches(String inputCredentials, String storedCredentials) {
                            return getRealmInternal().getCredentialHandler().matches(inputCredentials, storedCredentials);
                        }

                        @Override
                        public String mutate(String inputCredentials) {
                            return getRealmInternal().getCredentialHandler().mutate(inputCredentials);
                        }
                    };
                    context.setAttribute(Globals.CREDENTIAL_HANDLER, safeHandler);
                }

6、mergeParameters()方法合并context的初始化参数

 /**
     * Merge the context initialization parameters specified in the application
     * deployment descriptor with the application parameters described in the
     * server configuration, respecting the <code>override</code> property of
     * the application parameters appropriately.
     */
    private void mergeParameters() {
        Map<String,String> mergedParams = new HashMap<>();

        String names[] = findParameters();
        for (int i = 0; i < names.length; i++) {
            mergedParams.put(names[i], findParameter(names[i]));
        }

        ApplicationParameter params[] = findApplicationParameters();
        for (int i = 0; i < params.length; i++) {
            if (params[i].getOverride()) {
                if (mergedParams.get(params[i].getName()) == null) {
                    mergedParams.put(params[i].getName(),
                            params[i].getValue());
                }
            } else {
                mergedParams.put(params[i].getName(), params[i].getValue());
            }
        }

        ServletContext sc = getServletContext();
        for (Map.Entry<String,String> entry : mergedParams.entrySet()) {
            sc.setInitParameter(entry.getKey(), entry.getValue());
        }

    }

7、listenerStart()方法

  StandardContext类:
/**
     * Configure the set of instantiated application event listeners
     * for this Context.  Return <code>true</code> if all listeners wre
     * initialized successfully, or <code>false</code> otherwise.
     */
    public boolean listenerStart() {

        if (log.isDebugEnabled())
            log.debug("Configuring application event listeners");

        // Instantiate the required listeners
        String listeners[] = findApplicationListeners();
        Object results[] = new Object[listeners.length];
        boolean ok = true;
        for (int i = 0; i < results.length; i++) {
            if (getLogger().isDebugEnabled())
                getLogger().debug(" Configuring event listener class '" +
                    listeners[i] + "'");
            try {
                String listener = listeners[i];
                results[i] = getInstanceManager().newInstance(listener);
            } catch (Throwable t) {
                t = ExceptionUtils.unwrapInvocationTargetException(t);
                ExceptionUtils.handleThrowable(t);
                getLogger().error(sm.getString(
                        "standardContext.applicationListener", listeners[i]), t);
                ok = false;
            }
        }
        if (!ok) {
            getLogger().error(sm.getString("standardContext.applicationSkipped"));
            return (false);
        }

        // Sort listeners in two arrays
        ArrayList<Object> eventListeners = new ArrayList<>();
        ArrayList<Object> lifecycleListeners = new ArrayList<>();
        for (int i = 0; i < results.length; i++) {
            if ((results[i] instanceof ServletContextAttributeListener)
                || (results[i] instanceof ServletRequestAttributeListener)
                || (results[i] instanceof ServletRequestListener)
                || (results[i] instanceof HttpSessionIdListener)
                || (results[i] instanceof HttpSessionAttributeListener)) {
                eventListeners.add(results[i]);
            }
            if ((results[i] instanceof ServletContextListener)
                || (results[i] instanceof HttpSessionListener)) {
                lifecycleListeners.add(results[i]);
            }
        }

        // Listener instances may have been added directly to this Context by
        // ServletContextInitializers and other code via the pluggability APIs.
        // Put them these listeners after the ones defined in web.xml and/or
        // annotations then overwrite the list of instances with the new, full
        // list.
        for (Object eventListener: getApplicationEventListeners()) {
            eventListeners.add(eventListener);
        }
        setApplicationEventListeners(eventListeners.toArray());
        for (Object lifecycleListener: getApplicationLifecycleListeners()) {
            lifecycleListeners.add(lifecycleListener);
            if (lifecycleListener instanceof ServletContextListener) {
                noPluggabilityListeners.add(lifecycleListener);
            }
        }
        setApplicationLifecycleListeners(lifecycleListeners.toArray());

        // Send application start events

        if (getLogger().isDebugEnabled())
            getLogger().debug("Sending application start events");

        // Ensure context is not null
        getServletContext();
        context.setNewServletContextListenerAllowed(false);

        Object instances[] = getApplicationLifecycleListeners();
        if (instances == null || instances.length == 0) {
            return ok;
        }

        ServletContextEvent event = new ServletContextEvent(getServletContext());
        ServletContextEvent tldEvent = null;
        if (noPluggabilityListeners.size() > 0) {
            noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
            tldEvent = new ServletContextEvent(noPluggabilityServletContext);
        }
        for (int i = 0; i < instances.length; i++) {
            if (!(instances[i] instanceof ServletContextListener))
                continue;
            ServletContextListener listener =
                (ServletContextListener) instances[i];
            try {
                fireContainerEvent("beforeContextInitialized", listener);
                if (noPluggabilityListeners.contains(listener)) {
                    listener.contextInitialized(tldEvent);
                } else {
                    listener.contextInitialized(event);
                }
                fireContainerEvent("afterContextInitialized", listener);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                fireContainerEvent("afterContextInitialized", listener);
                getLogger().error
                    (sm.getString("standardContext.listenerStart",
                                  instances[i].getClass().getName()), t);
                ok = false;
            }
        }
        return (ok);

    }

8、checkConstraintsForUncoveredMethods(findConstraints());

 StandardContext类:

 private void checkConstraintsForUncoveredMethods(
            SecurityConstraint[] constraints) {
        SecurityConstraint[] newConstraints =
                SecurityConstraint.findUncoveredHttpMethods(constraints,
                        getDenyUncoveredHttpMethods(), getLogger());
        for (SecurityConstraint constraint : newConstraints) {
            addConstraint(constraint);
        }
    }

 而SecurityConstraint表示WEB应用程序的安全性约束,

就是<security-constraint> 描述文件 所配置的那样。<http-method>是<security-constraint> 子元素.

如果没有 <http-method> 元素,这表示将禁止所有 HTTP 方法访问相应的资源。 

​
<security-constraint>   
  <display-name>porject</display-name>   
  <web-resource-collection>   
   <web-resource-name>baseproject</web-resource-name>   
   <url-pattern>*.jsp</url-pattern>   
   <url-pattern>*.do</url-pattern>   
   <http-method>GET</http-method>     
   <http-method>HEAD</http-method>   
   <http-method>TRACE</http-method>   
   <http-method>POST</http-method>   
   <http-method>DELETE</http-method>   
   <http-method>OPTIONS</http-method>   
  </web-resource-collection>   
  <auth-constraint>   
   <description>porject</description>   
   <role-name>All Role</role-name>   
  </auth-constraint>   
  <user-data-constraint>   
   <transport-guarantee>NONE</transport-guarantee>   
  </user-data-constraint>   
</security-constraint>   


​
一个是对该url-pattern进行了role的限制,即配置auth-constraint了

<security-constraint>
    <web-resource-collection>
      <web-resource-name>tests</web-resource-name>
      <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>    //增加了这个就表示对访问/*有角色访问限制
       <role-name>role</role-name>
    </auth-constraint>
  </security-constraint> 

  

9、filterStart()方法:

StandardContext类: 

/**
     * Configure and initialize the set of filters for this Context.
     * Return <code>true</code> if all filter initialization completed
     * successfully, or <code>false</code> otherwise.
     */
    public boolean filterStart() {

        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Starting filters");
        }
        // Instantiate and record a FilterConfig for each defined filter
        boolean ok = true;
        synchronized (filterConfigs) {
            filterConfigs.clear();
            for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
                String name = entry.getKey();
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug(" Starting filter '" + name + "'");
                }
                try {
                    ApplicationFilterConfig filterConfig =
                            new ApplicationFilterConfig(this, entry.getValue());
                    filterConfigs.put(name, filterConfig);
                } catch (Throwable t) {
                    t = ExceptionUtils.unwrapInvocationTargetException(t);
                    ExceptionUtils.handleThrowable(t);
                    getLogger().error(sm.getString(
                            "standardContext.filterStart", name), t);
                    ok = false;
                }
            }
        }

        return ok;
    }

10、loadOnStartup()方法:

 /**
     * Load and initialize all servlets marked "load on startup" in the
     * web application deployment descriptor.
     *
     * @param children Array of wrappers for all currently defined
     *  servlets (including those not declared load on startup)
     */
    public boolean loadOnStartup(Container children[]) {

        // Collect "load on startup" servlets that need to be initialized
        TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
        for (int i = 0; i < children.length; i++) {
            Wrapper wrapper = (Wrapper) children[i];
            int loadOnStartup = wrapper.getLoadOnStartup();
            if (loadOnStartup < 0)
                continue;
            Integer key = Integer.valueOf(loadOnStartup);
            ArrayList<Wrapper> list = map.get(key);
            if (list == null) {
                list = new ArrayList<>();
                map.put(key, list);
            }
            list.add(wrapper);
        }

        // Load the collected "load on startup" servlets
        for (ArrayList<Wrapper> list : map.values()) {
            for (Wrapper wrapper : list) {
                try {
                    wrapper.load();
                } catch (ServletException e) {
                    getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
                          getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
                    // NOTE: load errors (including a servlet that throws
                    // UnavailableException from the init() method) are NOT
                    // fatal to application startup
                    // unless failCtxIfServletStartFails="true" is specified
                    if(getComputedFailCtxIfServletStartFails()) {
                        return false;
                    }
                }
            }
        }
        return true;

    }

Wrapper:

 
StandardWrapper类:
 /**
     * Start this component and implement the requirements
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    @Override
    protected synchronized void startInternal() throws LifecycleException {

        // Send j2ee.state.starting notification
        if (this.getObjectName() != null) {
            Notification notification = new Notification("j2ee.state.starting",
                                                        this.getObjectName(),
                                                        sequenceNumber++);
            broadcaster.sendNotification(notification);    //发送通知用于JMX
        }

        // Start up this component
        super.startInternal();

        setAvailable(0L);

        // Send j2ee.state.running notification
        if (this.getObjectName() != null) {
            Notification notification =
                new Notification("j2ee.state.running", this.getObjectName(),
                                sequenceNumber++);
            broadcaster.sendNotification(notification);  //发送通知用于JMX
        }

    }


    /**
     * Set the available date/time for this servlet, in milliseconds since the
     * epoch.  If this date/time is Long.MAX_VALUE, it is considered to mean
     * that unavailability is permanent and any request for this servlet will return
     * an SC_NOT_FOUND error. If this date/time is in the future, any request for
     * this servlet will return an SC_SERVICE_UNAVAILABLE error.
     *
     * @param available The new available date/time
     */
    @Override
    public void setAvailable(long available) {      //为servlet设置可用的时间

        long oldAvailable = this.available;
        if (available > System.currentTimeMillis())
            this.available = available;
        else
            this.available = 0L;
        support.firePropertyChange("available", Long.valueOf(oldAvailable),
                                   Long.valueOf(this.available));

    }

猜你喜欢

转载自blog.csdn.net/Jacabe/article/details/82227153
今日推荐