Tomcatライフサイクル管理
これまでのTomcatの全体的なアーキテクチャでは、Tomcatには複数のコンポーネントが含まれていましたが、今日は、Tomcatがこれらのコンポーネントのライフサイクルをどのように管理しているかを見てみましょう.コンポーネントとコンポーネントの間には、起動と停止を同時に行うために相互関係を確立する必要があることを知っています.Tomcat内部では,観察者モードを用いてこれらのコンポーネント間の関係を整理した.Tomcatが起動すると、どのような処理が行われるかを見てみましょう.ログ:
起動順序は次のとおりです.
まず、Tomcat内部のライフサイクルに関する定義はインタフェースLifecycleにあります.
さらにライフサイクルに関するイベントは、LifecycleEventクラスで定義されます.
イベントのリスニングについては、インタフェースLifecycleListenerで定義されています
また、ライフサイクルに関するイベントをトリガするLifecycleSupportという特別なクラスも紹介する必要がある.
次に、StandardServiceクラスの起動方法を見てみましょう.
サービスが実際に起動すると、まず起動前のイベントをトリガーし、それからそれ自体を起動します.次に、関連するコンテナオブジェクトを起動します.次に、すべてのサブコンポーネント-コネクタをすべて起動します.次は詳細なコード/
コード(1)には、サービスが正式に起動する前に、起動前のイベントが出発することが示されています.この方法は具体的に何をしているのか見てみましょう.LifecycleSupportクラスのfireLifecycleEvent(START_EVENT,null)を呼び出します.方法
内部では、サービスが起動されると、ここで本当に呼び出されるのは、クラスServer LifecycleListenerのメソッドです.
ここで簡単に説明すると、Tomcat内部の起動フローは、オブザーバーモードとリスナーモードによって処理され、コンポーネントの起動において、上位コンポーネントを起動するとともに、まず次のレベルのコンポーネントを起動する(もちろん、親コンポーネント内部では、そのすべてのサブコンポーネント参照を含む1つの集合が必要である).
……
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つの集合が必要である).