Tomcat啟動流程分析
阿新 • • 發佈:2019-01-07
來源
本文整理自 <Tomcat核心設計剖析>、<Tomcat結構解析> 加上自己的理解、原始碼來自 Tomcat8.5 版本
Tomcat啟動流程分析
Init流程時序圖
Start流程時序圖
Bootstrap
// org.apache.catalina.startup.Bootstrap public static void main(String args[]) { if (daemon == null) { // 先建立一個 bootstrap 例項 Bootstrap bootstrap = new Bootstrap(); try { bootstrap.init(); } catch (Throwable t) { return; } daemon = bootstrap; } else { Thread.currentThread().setContextClassLoader(daemon.catalinaLoader); } try { // 預設執行 start 方法 String command = "start"; if (args.length > 0) { command = args[args.length - 1]; } if (command.equals("startd")) { args[args.length - 1] = "start"; daemon.load(args); daemon.start(); } else if (command.equals("stopd")) { args[args.length - 1] = "stop"; daemon.stop(); } else if (command.equals("start")) { // 是否讓主執行緒不退出 daemon.setAwait(true); // 反射呼叫 catalinaDaemon#load 方法,根據server.xml 建立服務 daemon.load(args); // 反射呼叫 catalinaDaemon#start 方法,啟動服務 daemon.start(); } else if (command.equals("stop")) { daemon.stopServer(args); } else if (command.equals("configtest")) { daemon.load(args); if (null==daemon.getServer()) { System.exit(1); } System.exit(0); } } } /** * Initialize daemon. * @throws Exception Fatal initialization error */ public void init() throws Exception { // 初始化類載入器 initClassLoaders(); Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina"); // 反射生成 Catalina 類 Object startupInstance = startupClass.getConstructor().newInstance(); String methodName = "setParentClassLoader"; Class<?> paramTypes[] = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader; // 反射呼叫 Catalina 類的 setParentClassLoader 方法。 Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance; } private void initClassLoaders() { try { commonLoader = createClassLoader("common", null); if( commonLoader == null ) { commonLoader=this.getClass().getClassLoader(); } catalinaLoader = createClassLoader("server", commonLoader); sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { System.exit(1); } }
流程圖
Catalina
// org.apache.catalina.startup.Catalina // 設定 await值,會在start方法中伺服器啟動完成後來判斷是否進入等待狀態 // true :後續會去監聽8005埠 // false:執行完成後就退出 public void setAwait(boolean b) { await = b; } public void load() { // 已經載入就返回 if (loaded) { return; } // 設定載入狀態 loaded = true; // 在解析之前,設定一些系統屬性 initNaming(); // 通過 digester 解析 server.xml 配置檔案 Digester digester = createStartDigester(); InputSource inputSource = null; InputStream inputStream = null; File file = null; try { try { // 預設指定了 conf/server.xml file = configFile(); inputStream = new FileInputStream(file); inputSource = new InputSource(file.toURI().toURL().toString()); } if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream(getConfigFile()); inputSource = new InputSource (getClass().getClassLoader() .getResource(getConfigFile()).toString()); } } if (inputStream == null) { // 嘗試載入 server-embed.xml try { inputStream = getClass().getClassLoader() .getResourceAsStream("server-embed.xml"); inputSource = new InputSource (getClass().getClassLoader() .getResource("server-embed.xml").toString()); } } // 依舊找不到檔案,返回 if (inputStream == null || inputSource == null) { return; } try { inputSource.setByteStream(inputStream); digester.push(this); // 解析 xml digester.parse(inputSource); } } getServer().setCatalina(this); // 在 static中定義, getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Replace System.out and System.err with a custom PrintStream initStreams(); // 建立 Server getServer().init(); } /* Digester 查相關資料:Java解析xml主要由DOM4J(一次讀取到記憶體並解析)、SAX(一次解析一部分),digester本身採用SAX的解析方式,並提供了一層包裝,對使用者更加友好,後來獨立出來成為apache的Commons下面的[一個單獨的子專案](http://commons.apache.org/proper/commons-digester/)。 */ public void start() { if (getServer() == null) { load(); } if (getServer() == null) { return; } try { // 呼叫Server的start方法啟動伺服器 getServer().start(); } catch (LifecycleException e) { getServer().destroy(); return; } // 註冊 關閉 鉤子方法 if (useShutdownHook) { if (shutdownHook == null) { shutdownHook = new CatalinaShutdownHook(); } Runtime.getRuntime().addShutdownHook(shutdownHook); LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook( false); } } // 此處判斷 主執行緒是否await if (await) { // 在 StandardServer 中呼叫,用來監聽 8005 埠, // 收到 SHUTDOWN 命令關閉 Server await(); stop(); } }
Init流程圖
Start流程圖
StandardServer
@Override protected void initInternal() throws LifecycleException { super.initInternal(); // 註冊全域性 String cache onameStringCache = register(new StringCache(), "type=StringCache"); // 註冊 MBeanFactory MBeanFactory factory = new MBeanFactory(); factory.setContainer(this); onameMBeanFactory = register(factory, "type=MBeanFactory"); // 註冊命名資源 globalNamingResources.init(); // Populate the extension validator with JARs from common and shared // class loaders if (getCatalina() != null) { ClassLoader cl = getCatalina().getParentClassLoader(); while (cl != null && cl != ClassLoader.getSystemClassLoader()) { if (cl instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) cl).getURLs(); for (URL url : urls) { if (url.getProtocol().equals("file")) { File f = new File (url.toURI()); if (f.isFile() && f.getName().endsWith(".jar")) { ExtensionValidator.addSystemResource(f); } } } } cl = cl.getParent(); } } // 初始化定義的所有 Service for (int i = 0; i < services.length; i++) { services[i].init(); } } @Override protected void startInternal() throws LifecycleException { // 設定狀態 fireLifecycleEvent(CONFIGURE_START_EVENT, null); // 觸發事件 setState(LifecycleState.STARTING); globalNamingResources.start(); synchronized (servicesLock) { for (int i = 0; i < services.length; i++) { services[i].start(); } } } @Override public void await() { try { // port 預設 8005 awaitSocket = new ServerSocket(port, 1, InetAddress.getByName(address)); } catch (IOException e) { return; } // 匹配 SHUTDOWN 命令,用來關閉伺服器 boolean match = command.toString().equals(shutdown); if (match) { break; } ServerSocket serverSocket = awaitSocket; awaitThread = null; awaitSocket = null; // 關閉Server socket並返回 if (serverSocket != null) { serverSocket.close(); } }
StandardService
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Engine初始化,一個Service對應一個Engine
if (engine != null) {
engine.init();
}
// 初始化Executor,預設 不會執行
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// 初始化 MapperListener , 預設 LifecycleMBeanBase.java
mapperListener.init();
// 初始化 Connector,server.xml 配置的 Connector
synchronized (connectorsLock) {
for (Connector connector : connectors) {
connector.init();
}
}
}
@Override
protected void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
mapperListener.start();
synchronized (connectorsLock) {
for (Connector connector: connectors) {
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}
ContainerBase
是容器的抽象父類,定義了容器生命週期中公共方法。Engine、Host、Context、Wrapper繼承此父類。
@Override
protected void initInternal() throws LifecycleException {
// 建立 執行緒池
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}
@Override
protected synchronized void startInternal() throws LifecycleException {
// 如果有 Cluster和 Realm則呼叫其 start 方法
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// 通過 Future 呼叫所有子容器的 start方法
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
boolean fail = false;
for (Future<Void> result : results) {
try {
// 獲取子容器 start 方法結果
result.get();
} catch (Exception e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
fail = true;
}
}
if (fail) {
throw new LifecycleException(
sm.getString("containerBase.threadedStartFailed"));
}
// 啟用管道,後面介紹到
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
// 設定狀態值
setState(LifecycleState.STARTING);
// 啟動後臺執行緒,日誌輸出等工作,
threadStart();
}
// 開啟後臺執行緒,定時檢查 session 超時、
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();
}
// 內部類 ContainerBackgroundProcessor, 預設 10s一次
// 在 StandardEngine 建構函式中定義
protected void processChildren(Container ContainerBase.this) {
container.backgroundProcess();
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i].getBackgroundProcessorDelay() <= 0) {
processChildren(children[i]);
}
}
}
@Override
public void backgroundProcess() {
if (!getState().isAvailable())
return;
Cluster cluster = getClusterInternal();
if (cluster != null) {
cluster.backgroundProcess();
}
Realm realm = getRealmInternal();
if (realm != null) {
realm.backgroundProcess();
}
Valve current = pipeline.getFirst();
while (current != null) {
current.backgroundProcess();
current = current.getNext();
}
fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
}
// LifecycleBase
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
// 看一下 HostConfig 的 lifecycleEvent 方法
@Override
public void lifecycleEvent(LifecycleEvent event) {
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();
}
}
容器的作用
Container的4個容器是逐層包含的關係。它們之間的關係如下圖:
1、Engine:用來管理多個站點,一個Service最多隻能有一個Engine。
2、Host:代表一個站點,通過配置Host可以新增站點。
3、Context:代表一個應用程式,對應一個WEB-INF目錄。
4、Wrapper:每個Wrapper封裝一個Servlet。
容器的配置
1、
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
</Host>
</Engine>
</Service>
</Server>
- Server 在 8005埠監聽關閉命令“SHUTDOWN”;
- Server 中定義了一個 Catalina的Service;
- Service 中定義了兩個Connector:
- 一個是HTTP協議;
- 一個是AJP協議(用於整合);
- Service 中還定義了了一個Catalina的Engine;
- Engine 中定義了 localhost 的 Host;
- defaultHost:請求的域名如果在所有的Host的name和Alias中都找不到使用的預設值
- Host:
- name:表示域名;
- appBase:站點的位置;
- unpackWARS:是否自動解壓war包;
- autoDeploy:是否自動部署;
- 子標籤: :給localhost定義別名;
2、Context通過檔案配置的方式一共有5個位置可以配置:
- conf/server.xml中的Context標籤;
- conf/[enginename]/[hostname]/目錄下以應用命名的 xml 檔案。
- 應用自己的 /META-INT/context.xml;
- conf/context.xml 檔案
- conf/[enginename]/[hostname]/context.xml.default檔案;
3、前三個用於配置單獨的應用,後面2種是Context共享的。第4種是 整個 Tomcat 共享,第5種配置的內容在對應的站點(Host)中共享。第1種方式只有在Tomcat重啟才會重新載入,不推薦使用。
<!--
用於全域性配置
The contents of this file will be loaded for each web application
-->
<Context>
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
</Context>
4、Wrapper的配置,在web.xml中配置的Servlet,一個Servlet對應一個Wrapper、可以在 conf/web.xml 中配置全域性的 Wrapper,處理 Jsp的 JspServlet的配置等。
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
<!--配置了 session 超時時間-->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!-- 很多 mime 型別 -->
<mime-mapping>
</mime-mapping>
StandardEngine
public StandardEngine() {
super();
// pipeline 設定 basicValve
pipeline.setBasic(new StandardEngineValve());
setJvmRoute(System.getProperty("jvmRoute"));
// 設定等待時間 10
backgroundProcessorDelay = 10;
}
@Override
protected void initInternal() throws LifecycleException {
// 沒有定義 Realm ,設定一個 NullRealm,許可權訪問。
getRealm();
super.initInternal();
}
@Override
protected synchronized void startInternal() throws LifecycleException {
super.startInternal();
}
StandardHost
public StandardHost() {
super();
// 設定 BasicValve
pipeline.setBasic(new StandardHostValve());
}
@Override
protected synchronized void startInternal() throws LifecycleException {
// 設定 error 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) {
// 繫結 errorValve
Valve valve =
(Valve) Class.forName(errorValve).getConstructor().newInstance();
getPipeline().addValve(valve);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
}
super.startInternal();
}
StandardContext
public StandardContext() {
super();
// 設定 BasicValve
pipeline.setBasic(new StandardContextValve());
// 廣播通知
broadcaster = new NotificationBroadcasterSupport();
if (!Globals.STRICT_SERVLET_COMPLIANCE) {
resourceOnlyServlets.add("jsp");
}
}
@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.getAndIncrement());
broadcaster.sendNotification(notification);
}
setConfigured(false);
boolean ok = true;
// namingResources 啟動
if (namingResources != null) {
namingResources.start();
}
// Post work directory
postWorkDirectory();
// Add missing components as necessary
if (getResources() == null) { // (1) Required by Loader
try {
setResources(new StandardRoot(this));
} catch (IllegalArgumentException e) {
ok = false;
}
}
if (ok) {
// 載入 /WEB-INF/classes/META-INF/resources 目錄下資源
resourcesStart();
}
// 設定載入器
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
// cookie 處理器
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
// 初始化 CharsetMapper
getCharsetMapper();
// 驗證 /META-INF/MANIFEST.MF
boolean dependencyCheck = true;
try {
dependencyCheck = ExtensionValidator.validateApplication
(getResources(), this);
} catch (IOException ioe) {
dependencyCheck = false;
}
// // 驗證失敗, application 不可用
if (!dependencyCheck) {
ok = false;
}
// catalina.useNaming 環境變數
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());
// 註冊 LifecycleListener
addLifecycleListener(ncl);
setNamingContextListener(ncl);
}
}
// Binding thread
ClassLoader oldCCL = bindThread();
try {
if (ok) {
// Start our subordinate components, if any
Loader loader = getLoader();
if (loader instanceof Lifecycle) {
((Lifecycle) loader).start();
}
// since the loader just started, the webapp classloader is now
// created.
setClassLoaderProperty("clearReferencesRmiTargets",
getClearReferencesRmiTargets());
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(null != realm) {
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);
}
// 呼叫事件
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// 在不可用的時候,啟動子容器
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
// pipeline 的啟動
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// 獲取叢集管理器
Manager contextManager = null;
Manager manager = getManager();
if (manager == null) {
if ( (getCluster() != null) && distributable) {
try {
contextManager = getCluster().createManager(getName());
} catch (Exception ex) {
ok = false;
}
} else {
contextManager = new StandardManager();
}
}
// 配置預設管理器
if (contextManager != null) {
setManager(contextManager);
}
// 註冊管理器
if (manager!=null && (getCluster() != null) && distributable) {
getCluster().registerManager(manager);
}
}
if (!getConfigured()) {
ok = false;
}
// 將資源 放到 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());
InstanceManagerBindings.bind(getLoader().getClassLoader(), getInstanceManager());
}
// Create context attributes that will be required
if (ok) {
getServletContext().setAttribute(
JarScanner.class.getName(), getJarScanner());
}
// 設定上下文 init 引數
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;
}
}
// 呼叫 listener
if (ok) {
if (!listenerStart()) {
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 instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch(Exception e) {
ok = false;
}
// filter呼叫
if (ok) {
if (!filterStart()) {
ok = false;
}
}
// 初始化 Servlet,如果配置了 LoadOnStartUp
if (ok) {
if (!loadOnStartup(findChildren())){
ok = false;
}
}
// 啟動後臺執行緒
super.threadStart();
} finally {
unbindThread(oldCCL);
}
// 資源回收
getResources().gc();
if (!ok) {
setState(LifecycleState.FAILED);
} else {
setState(LifecycleState.STARTING);
}
}
StandardWrapper
public StandardWrapper() {
super();
swValve = new StandardWrapperValve();
// 設定 BasicValve
pipeline.setBasic(swValve);
broadcaster = new NotificationBroadcasterSupport();
}
@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);
}
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);
}
}
StandardPipeline
@Override
protected void initInternal() { }
@Override
protected synchronized void startInternal() throws LifecycleException {
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}
setState(LifecycleState.STARTING);
}