在前面的文章中,已經學會了如何通過實例化一個連接器和容器來獲得一個servlet容器,并將連接器和容器相關聯;但在前面的文章中只有一個連接器可用,該連接器服務8080端口上的HTTP請求,無法添加另一個連接器來服務諸如HTTPS之類的其他請求;此外,在前面的文章中的應用程序中有些缺憾,即缺少一種啟動/關閉servlet容器的機制。
org.apache.catalina.Server接口的實例表示Catalina的整個servlet引擎,囊括了所有的組件;它使用一個優雅的方式來啟動/關閉整個系統,不需要再對連接器和容器分別啟動/關閉(當啟動Server組件時,它會啟動其中所有的組件,然后它就無限期地等待關閉命令;如果想要關閉系統,可以向指定端口發送一條關閉命令,Server組件接收到關閉命令后,就會關閉其中所有的組件)。
Server組件使用了另一個組件(即Service組件)來包含其他組件,如一個容器組件和一個或多個連接器組件。
下面是Server接口的定義
public
interface
Server {
public
String getInfo();
public
NamingResources getGlobalNamingResources();
public
void
setGlobalNamingResources(NamingResources globalNamingResources);
public
int
getPort();
public
void
setPort(
int
port);
public
String getShutdown();
public
void
setShutdown(String shutdown);
public
void
addService(Service service);
public
void
await();
public
Service findService(String name);
public
Service[] findServices();
public
void
removeService(Service service);
public
void
initialize()
throws
LifecycleException;
}
shutdown屬性保存了必須發送給Server實例用來關閉整個系統的關閉命令,port屬性定義了Server組件會從哪個端口獲取關閉命令,可以調用addService()方法為Server組件添加Service組件,或通過removeService()方法刪除某個Service組件,findService()方法將會返回添加到該Server組件中的所有Service組件,initialize()方法包含在系統啟動前要執行的一些代碼
?org.apache.catalina.core.StandardServer類是Server接口的標準實現,介紹這個類是因為我們對其中的關閉機制比較感興趣,而這也是這個類中最重要的特性;該類的許多方法都與新server.xml文件中的服務器配置的存儲相關,但這并不是本文的重點。
一個Server組件可以有多個Service組件,StandardServer類提供了addService()方法、 removeService()方法和findServices()方法的實現
StandardServer類有四個與生命周期相關的方法,分別是initialize()方法、 start()方法、 stop()方法和await()方法,就像其他組件一樣,可以初始化并啟動Server組件,然后調用await()方法及stop()方法。調用await()方法后會一直阻塞住,直到它總8085端口上接收到關閉命令。當await()方法返回時,會運行stop()方法來關閉其下的所有子組件。
Server實例的initialize()方法用于初始化添加到其中的Service組件,下面是tomcat4中StandardServer類中initialize()方法的實現
public
void
initialize()
throws
LifecycleException {
if
(initialized)
throw
new
LifecycleException (
sm.getString(
"standardServer.initialize.initialized"
));
initialized
=
true
;
//
Initialize our defined Services
for
(
int
i = 0; i < services.length; i++
) {
services[i].initialize();
}
}
start()方法用于啟動Server組件,在StandardServer類的start()方法的實現中,它會啟動其所有的Service組件,這些Service組件它們逐個啟動所有其他的組件,如連接器組件和servlet容器。
public
void
start()
throws
LifecycleException {
//
Validate and update our current component state
if
(started)
throw
new
LifecycleException
(sm.getString(
"standardServer.start.started"
));
//
Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT,
null
);
lifecycle.fireLifecycleEvent(START_EVENT,
null
);
started
=
true
;
//
Start our defined Services
synchronized
(services) {
for
(
int
i = 0; i < services.length; i++
) {
if
(services[i]
instanceof
Lifecycle)
((Lifecycle) services[i]).start();
}
}
//
Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT,
null
);
}
stop()方法用于關閉Server組件
public
void
stop()
throws
LifecycleException {
//
Validate and update our current component state
if
(!
started)
throw
new
LifecycleException
(sm.getString(
"standardServer.stop.notStarted"
));
//
Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT,
null
);
lifecycle.fireLifecycleEvent(STOP_EVENT,
null
);
started
=
false
;
//
Stop our defined Services
for
(
int
i = 0; i < services.length; i++
) {
if
(services[i]
instanceof
Lifecycle)
((Lifecycle) services[i]).stop();
}
//
Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT,
null
);
}
await()方法負責停止整個tomcat部署的機制
/**
* Wait until a proper shutdown command is received, then return.
*/
public
void
await() {
//
Set up a server socket to wait on
ServerSocket serverSocket =
null
;
try
{
serverSocket
=
new
ServerSocket(port, 1
,
InetAddress.getByName(
"127.0.0.1"
));
}
catch
(IOException e) {
System.err.println(
"StandardServer.await: create[" +
port
+ "]: " +
e);
e.printStackTrace();
System.exit(
1
);
}
//
Loop waiting for a connection and a valid command
while
(
true
) {
//
Wait for the next connection
Socket socket =
null
;
InputStream stream
=
null
;
try
{
socket
=
serverSocket.accept();
socket.setSoTimeout(
10 * 1000);
//
Ten seconds
stream =
socket.getInputStream();
}
catch
(AccessControlException ace) {
System.err.println(
"StandardServer.accept security exception: "
+
ace.getMessage());
continue
;
}
catch
(IOException e) {
System.err.println(
"StandardServer.await: accept: " +
e);
e.printStackTrace();
System.exit(
1
);
}
//
Read a set of characters from the socket
StringBuffer command =
new
StringBuffer();
int
expected = 1024;
//
Cut off to avoid DoS attack
while
(expected <
shutdown.length()) {
if
(random ==
null
)
random
=
new
Random(System.currentTimeMillis());
expected
+= (random.nextInt() % 1024
);
}
while
(expected > 0
) {
int
ch = -1
;
try
{
ch
=
stream.read();
}
catch
(IOException e) {
System.err.println(
"StandardServer.await: read: " +
e);
e.printStackTrace();
ch
= -1
;
}
if
(ch < 32)
//
Control character or EOF terminates loop
break
;
command.append((
char
) ch);
expected
--
;
}
//
Close the socket now that we are done with it
try
{
socket.close();
}
catch
(IOException e) {
;
}
//
Match against our command string
boolean
match =
command.toString().equals(shutdown);
if
(match) {
break
;
}
else
System.err.println(
"StandardServer.await: Invalid command '" +
command.toString()
+ "' received"
);
}
//
Close the server socket and return
try
{
serverSocket.close();
}
catch
(IOException e) {
;
}
}
await()方法創建一個ServerSocket對象,監聽8085端口,并在while循環中調用它的accept()方法,擋在指定端口上接收到消息時,才會從accept()方法中返回,然后將接收到的消息與關閉命令的字符串相比較,相同的話就跳出while循環,關閉ServerSocket,否則會再次循環,繼續等待消息。
Service組件是org.apache.catalina.Service接口的實例,一個Service組件可以有一個servlet容器和多個連接器實例,可以自由地把連接器實例添加到Service組件中,所有的連接器都會與該servlet容器相關聯
下面是Service接口的定義
public
interface
Service {
public
Container getContainer();
public
void
setContainer(Container container);
public
String getInfo();
public
String getName();
public
void
setName(String name);
public
Server getServer();
public
void
setServer(Server server);
public
void
addConnector(Connector connector);
public
Connector[] findConnectors();
public
void
removeConnector(Connector connector);
public
void
initialize()
throws
LifecycleException;
}
org.apache.catalina.core.StandardService類是Service接口的標準實現,StandardService類的initialize()方法用于初始化添加到其中的所有連接器;此外,還實現了org.apache.catalina.Lifecycle接口,它的start()方法用于啟動servlet容器和所有連接器
StandardService實例中有兩種組件,分別是連接器和servlet容器,其中servlet容器只有一個,而連接器則可以有多個,多個連接器使tomcat可以為多種不同的請求協議提供服務。例如,一個連接器處理HTTP請求,而另一個可以處理HTTPS請求。
StandardService類使用變量container來指向一個Container接口的實例,使用數組connectors來保存所有連接器的引用
private Container container = null;
private Connector connectors[] = new Connector[0];
需要調用setContainer()方法將servlet容器與Service組件相關聯:
public
void
setContainer(Container container) {
Container oldContainer
=
this
.container;
if
((oldContainer !=
null
) && (oldContainer
instanceof
Engine))
((Engine) oldContainer).setService(
null
);
this
.container =
container;
if
((
this
.container !=
null
) && (
this
.container
instanceof
Engine))
((Engine)
this
.container).setService(
this
);
if
(started && (
this
.container !=
null
) &&
(
this
.container
instanceof
Lifecycle)) {
try
{
((Lifecycle)
this
.container).start();
}
catch
(LifecycleException e) {
;
}
}
synchronized
(connectors) {
for
(
int
i = 0; i < connectors.length; i++
)
connectors[i].setContainer(
this
.container);
}
if
(started && (oldContainer !=
null
) &&
(oldContainer
instanceof
Lifecycle)) {
try
{
((Lifecycle) oldContainer).stop();
}
catch
(LifecycleException e) {
;
}
}
//
Report this property change to interested listeners
support.firePropertyChange("container", oldContainer,
this
.container);
}
與Service組件相關聯的servlet容器的實例將被傳給每個連接器對象的setContainer()方法,這樣在Service組件中就可以形成每個連接器和servlet容器之間的關聯關系。
可以調用addConnector()方法將連接器添加到Service組件中,調用removeConnector()方法將某個連接器移除
public
void
addConnector(Connector connector) {
synchronized
(connectors) {
connector.setContainer(
this
.container);
connector.setService(
this
);
Connector results[]
=
new
Connector[connectors.length + 1
];
System.arraycopy(connectors,
0, results, 0
, connectors.length);
results[connectors.length]
=
connector;
connectors
=
results;
if
(initialized) {
try
{
connector.initialize();
}
catch
(LifecycleException e) {
e.printStackTrace(System.err);
}
}
if
(started && (connector
instanceof
Lifecycle)) {
try
{
((Lifecycle) connector).start();
}
catch
(LifecycleException e) {
;
}
}
//
Report this property change to interested listeners
support.firePropertyChange("connector",
null
, connector);
}
}
在上面方法中,會初始化并啟動添加到其中的連接器。
public
void
removeConnector(Connector connector) {
synchronized
(connectors) {
int
j = -1
;
for
(
int
i = 0; i < connectors.length; i++
) {
if
(connector ==
connectors[i]) {
j
=
i;
break
;
}
}
if
(j < 0
)
return
;
if
(started && (connectors[j]
instanceof
Lifecycle)) {
try
{
((Lifecycle) connectors[j]).stop();
}
catch
(LifecycleException e) {
;
}
}
connectors[j].setContainer(
null
);
connector.setService(
null
);
int
k = 0
;
Connector results[]
=
new
Connector[connectors.length - 1
];
for
(
int
i = 0; i < connectors.length; i++
) {
if
(i !=
j)
results[k
++] =
connectors[i];
}
connectors
=
results;
//
Report this property change to interested listeners
support.firePropertyChange("connector", connector,
null
);
}
}
與生命周期相關的方法包括從Lifecycle接口中實現的start()方法和stop()方法,在加上initialize()方法,其中initialize()方法會調用該Service組件中所有連接器的上initialize()方法:
public
void
initialize()
throws
LifecycleException {
if
(initialized)
throw
new
LifecycleException (
sm.getString(
"standardService.initialize.initialized"
));
initialized
=
true
;
//
Initialize our defined Connectors
synchronized
(connectors) {
for
(
int
i = 0; i < connectors.length; i++
) {
connectors[i].initialize();
}
}
}
start()方法負責啟動被添加到該Service組件中的連接器和servlet容器:
public
void
start()
throws
LifecycleException {
//
Validate and update our current component state
if
(started) {
throw
new
LifecycleException
(sm.getString(
"standardService.start.started"
));
}
//
Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT,
null
);
System.out.println
(sm.getString(
"standardService.start.name",
this
.name));
lifecycle.fireLifecycleEvent(START_EVENT,
null
);
started
=
true
;
//
Start our defined Container first
if
(container !=
null
) {
synchronized
(container) {
if
(container
instanceof
Lifecycle) {
((Lifecycle) container).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
);
}
stop()方法用于關閉與該Service組件相關聯的servlet容器和所有連接器
public
void
stop()
throws
LifecycleException {
//
Validate and update our current component state
if
(!
started) {
throw
new
LifecycleException
(sm.getString(
"standardService.stop.notStarted"
));
}
//
Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT,
null
);
lifecycle.fireLifecycleEvent(STOP_EVENT,
null
);
System.out.println
(sm.getString(
"standardService.stop.name",
this
.name));
started
=
false
;
//
Stop our defined Connectors first
synchronized
(connectors) {
for
(
int
i = 0; i < connectors.length; i++
) {
if
(connectors[i]
instanceof
Lifecycle)
((Lifecycle) connectors[i]).stop();
}
}
//
Stop our defined Container second
if
(container !=
null
) {
synchronized
(container) {
if
(container
instanceof
Lifecycle) {
((Lifecycle) container).stop();
}
}
}
//
Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT,
null
);
}
在前面文章的應用程序中,通過按某個鍵或強制中斷的方式關閉servlet容器,Stopper類提供了一種更優雅的方式來關閉Catalina服務器;此外,它也保證了所有的生命周期組件的stop()方法都能夠調用。
public
class
Stopper {
public
static
void
main(String[] args) {
//
the following code is taken from the Stop method of
//
the org.apache.catalina.startup.Catalina class
int
port = 8005
;
try
{
Socket socket
=
new
Socket("127.0.0.1"
, port);
OutputStream stream
=
socket.getOutputStream();
String shutdown
= "SHUTDOWN"
;
for
(
int
i = 0; i < shutdown.length(); i++
)
stream.write(shutdown.charAt(i));
stream.flush();
stream.close();
socket.close();
System.out.println(
"The server was successfully shut down."
);
}
catch
(IOException e) {
System.out.println(
"Error. The server has not been started."
);
}
}
}
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創?
轉載請注明出處 博客園 刺猬的溫馴?
本人郵箱: ? chenying998179 # 163.com ( #改為@ )
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

