Tomcatライフサイクル管理


これまでのTomcatの全体的なアーキテクチャでは、Tomcatには複数のコンポーネントが含まれていましたが、今日は、Tomcatがこれらのコンポーネントのライフサイクルをどのように管理しているかを見てみましょう.コンポーネントとコンポーネントの間には、起動と停止を同時に行うために相互関係を確立する必要があることを知っています.Tomcat内部では,観察者モードを用いてこれらのコンポーネント間の関係を整理した.Tomcatが起動すると、どのような処理が行われるかを見てみましょう.ログ:
……

2010-6-19 15:41:18 org.apache.catalina.core.StandardService start
   : Starting service Catalina
2010-6-19 15:41:18 org.apache.catalina.core.StandardEngine start
   : Starting Servlet Engine: Apache Tomcat/6.0.18
…
2010-6-19 15:41:19 org.apache.coyote.http11.Http11Protocol start
   : Starting Coyote HTTP/1.1 on http-8080
2010-6-19 15:41:19 org.apache.jk.common.ChannelSocket init
   : JK: ajp13 listening on /0.0.0.0:8009
2010-6-19 15:41:19 org.apache.jk.server.JkMain start
   : Jk running ID=0 time=0/182 config=null
2010-6-19 15:41:19 org.apache.catalina.startup.Catalina start
   : Server startup in 1706 ms
 
起動順序は次のとおりです.
StandardService --> StandardEngine-->Http11Protocol-->JkMain-->Catalina
 
まず、Tomcat内部のライフサイクルに関する定義はインタフェースLifecycleにあります.
 
package org.apache.catalina;


public interface Lifecycle {

    public static final String INIT_EVENT = "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 DESTROY_EVENT = "destroy";
    public static final String PERIODIC_EVENT = "periodic";
    public void addLifecycleListener(LifecycleListener listener);
    public LifecycleListener[] findLifecycleListeners();
    public void removeLifecycleListener(LifecycleListener listener);
    public void start() throws LifecycleException;
    public void stop() throws LifecycleException;
}

 
さらにライフサイクルに関するイベントは、LifecycleEventクラスで定義されます.
 
 
 
package org.apache.catalina;



import java.util.EventObject;


public final class LifecycleEvent

    extends EventObject {
    // ----------------------------------------------------------- Constructors
    public LifecycleEvent(Lifecycle lifecycle, String type) {
        this(lifecycle, type, null);
    }

    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
        super(lifecycle);
        this.lifecycle = lifecycle;
        this.type = type;
        this.data = data;
    }

    private Object data = null;

    private Lifecycle lifecycle = null;

    private String type = null;

    public Object getData() {
        return (this.data);
    }

    public Lifecycle getLifecycle() {
        return (this.lifecycle);
    }

    public String getType() {
        return (this.type);
    }

}

 
 
イベントのリスニングについては、インタフェースLifecycleListenerで定義されています
 
 
package org.apache.catalina;

public interface LifecycleListener {
    public void lifecycleEvent(LifecycleEvent event);
}

 
また、ライフサイクルに関するイベントをトリガするLifecycleSupportという特別なクラスも紹介する必要がある.
 
package org.apache.catalina.util;

import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;

public final class LifecycleSupport {
    public LifecycleSupport(Lifecycle lifecycle) {
        super();
        this.lifecycle = lifecycle;
    }

    private Lifecycle lifecycle = null;
    private LifecycleListener listeners[] = new LifecycleListener[0];
    private final Object listenersLock = new Object(); // Lock object for changes to listeners
    public void addLifecycleListener(LifecycleListener listener) {
      synchronized (listenersLock) {
          LifecycleListener results[] =
            new LifecycleListener[listeners.length + 1];
          for (int i = 0; i < listeners.length; i++)
              results[i] = listeners[i];
          results[listeners.length] = listener;
          listeners = results;
      }
    }

    public LifecycleListener[] findLifecycleListeners() {
        return listeners;
    }

    public void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        LifecycleListener interested[] = listeners;
        for (int i = 0; i < interested.length; i++)
            interested[i].lifecycleEvent(event);
    }

    public void removeLifecycleListener(LifecycleListener listener) {
        synchronized (listenersLock) {
            int n = -1;
            for (int i = 0; i < listeners.length; i++) {
                if (listeners[i] == listener) {
                    n = i;
                    break;
                }
            }
            if (n < 0)
                return;
            LifecycleListener results[] =
              new LifecycleListener[listeners.length - 1];

            int j = 0;
            for (int i = 0; i < listeners.length; i++) {
                if (i != n)
                    results[j++] = listeners[i];
            }
            listeners = results;
        }
    }
}

 
 
次に、StandardServiceクラスの起動方法を見てみましょう.
サービスが実際に起動すると、まず起動前のイベントをトリガーし、それからそれ自体を起動します.次に、関連するコンテナオブジェクトを起動します.次に、すべてのサブコンポーネント-コネクタをすべて起動します.次は詳細なコード/
public class StandardService
        implements Lifecycle, Service, MBeanRegistration 
 {
   .............
    /**
     * The lifecycle event support for this component.
     */
    private LifecycleSupport lifecycle = new LifecycleSupport(this);
    .............
    /**
     * The set of Connectors associated with this Service.
            Service      .
     */
    protected Connector connectors[] = new Connector[0];
    
    public void start() throws LifecycleException {
        // Validate and update our current component state
        if (log.isInfoEnabled() && started) {
            log.info(sm.getString("standardService.start.started"));
        }
        
        if( ! initialized )
            init(); 

        //          
        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
        if(log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name", this.name));

        lifecycle.fireLifecycleEvent(START_EVENT, null);  <<<<(1)
        started = true;
        // Start our defined Container first          
        if (container != null) {
            synchronized (container) {
                if (container instanceof Lifecycle) {
                    ((Lifecycle) container).start();
                }
            }
        }

        synchronized (executors) {
            for ( int i=0; i<executors.size(); i++ ) {
                executors.get(i).start();
            }
        }

        // Start our defined Connectors second          
        synchronized (connectors) {
            for (int i = 0; i < connectors.length; i++) {
                if (connectors[i] instanceof Lifecycle)
                    ((Lifecycle) connectors[i]).start();
            }
        }
        
        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

    }


 
コード(1)には、サービスが正式に起動する前に、起動前のイベントが出発することが示されています.この方法は具体的に何をしているのか見てみましょう.LifecycleSupportクラスのfireLifecycleEvent(START_EVENT,null)を呼び出します.方法
 
public void fireLifecycleEvent(String type, Object data) {

        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        LifecycleListener interested[] = listeners;
        for (int i = 0; i < interested.length; i++)
            interested[i].lifecycleEvent(event);

    }

内部では、サービスが起動されると、ここで本当に呼び出されるのは、クラスServer LifecycleListenerのメソッドです.
package org.apache.catalina.mbeans;





public class ServerLifecycleListener

    implements ContainerListener, LifecycleListener, PropertyChangeListener {



    public void lifecycleEvent(LifecycleEvent event) {

        Lifecycle lifecycle = event.getLifecycle();
        if (Lifecycle.START_EVENT.equals(event.getType())) {

            if (lifecycle instanceof Server) {
                createMBeans();
            }

            // We are embedded.
            if( lifecycle instanceof Service ) {
                try {
                    //Service   ,     
                    MBeanFactory factory = new MBeanFactory();
                    createMBeans(factory);
                    createMBeans((Service)lifecycle);
                } catch( Exception ex ) {
                    log.error("Create mbean factory");
                }
            }

            /*
            // Ignore events from StandardContext objects to avoid
            // reregistering the context
            if (lifecycle instanceof StandardContext)
                return;
            createMBeans();
            */

        } else if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())) {
            try {
                if (lifecycle instanceof Server) {
                    destroyMBeans((Server)lifecycle);
                }
                if (lifecycle instanceof Service) {
                    destroyMBeans((Service)lifecycle);
                }
            } catch (MBeanException t) {

                Exception e = t.getTargetException();
                if (e == null) {
                    e = t;
                }
                log.error("destroyMBeans: MBeanException", e);

            } catch (Throwable t) {

                log.error("destroyMBeans: Throwable", t);

            }
            // FIXME: RMI adaptor should be stopped; however, this is
            // undocumented in MX4J, and reports exist in the MX4J bug DB that
            // this doesn't work

        }

        if ((Context.RELOAD_EVENT.equals(event.getType()))
            || (Lifecycle.START_EVENT.equals(event.getType()))) {

            // Give context a new handle to the MBean server if the
            // context has been reloaded since reloading causes the
            // context to lose its previous handle to the server
            if (lifecycle instanceof StandardContext) {
                // If the context is privileged, give a reference to it
                // in a servlet context attribute
                StandardContext context = (StandardContext)lifecycle;
                if (context.getPrivileged()) {
                    context.getServletContext().setAttribute
                        (Globals.MBEAN_REGISTRY_ATTR,
                         MBeanUtils.createRegistry());
                    context.getServletContext().setAttribute
                        (Globals.MBEAN_SERVER_ATTR,
                         MBeanUtils.createServer());
                }
            }

        }

    }


}

 
ここで簡単に説明すると、Tomcat内部の起動フローは、オブザーバーモードとリスナーモードによって処理され、コンポーネントの起動において、上位コンポーネントを起動するとともに、まず次のレベルのコンポーネントを起動する(もちろん、親コンポーネント内部では、そのすべてのサブコンポーネント参照を含む1つの集合が必要である).