在前一章 查看tomcat啟動文件都干點啥---Bootstrap.java 中我們得出結論,在Bootstrap中通過反射調用Catalina類中的getServer,start,stop,stopServer等方法,下面看一下Catalina類中給外部提供的公共方法:
Start :其中Catalina類的入口當然是start方法.start方法實現了啟動一個新的server事例的功能,看一下start方法的內容:
1
public
void
start() {
2
3
if
(getServer() ==
null
) {
4
load();
5
}
6
7
if
(getServer() ==
null
) {
8
log.fatal("Cannot start server. Server instance is not configured."
);
9
return
;
10
}
11
12
long
t1 =
System.nanoTime();
13
14
//
Start the new server
15
try
{
16
getServer().start();
17
}
catch
(LifecycleException e) {
18
log.fatal(sm.getString("catalina.serverStartFail"
), e);
19
try
{
20
getServer().destroy();
21
}
catch
(LifecycleException e1) {
22
log.debug("destroy() failed for failed Server "
, e1);
23
}
24
return
;
25
}
26
27
long
t2 =
System.nanoTime();
28
if
(log.isInfoEnabled()) {
29
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms"
);
30
}
31
32
//
Register shutdown hook
33
if
(useShutdownHook) {
34
if
(shutdownHook ==
null
) {
35
shutdownHook =
new
CatalinaShutdownHook();
36
}
37
Runtime.getRuntime().addShutdownHook(shutdownHook);
38
39
//
If JULI is being used, disable JULI's shutdown hook since
40
//
shutdown hooks run in parallel and log messages may be lost
41
//
if JULI's hook completes before the CatalinaShutdownHook()
42
LogManager logManager =
LogManager.getLogManager();
43
if
(logManager
instanceof
ClassLoaderLogManager) {
44
((ClassLoaderLogManager) logManager).setUseShutdownHook(
45
false
);
46
}
47
}
48
49
if
(await) {
50
await();
51
stop();
52
}
53
}
在Catalina中有個很重要的對象就是Server,先說明一下,在tomcat中實現Server接口的StandardServer對象,其中定義了socketServer,在此只作此說明,不展開介紹,在下一章中會專門對StandardServer類以及Server接口進行說明。
在start方法中首先需要判斷是否初始化了實現server接口的類(以后都稱作server類,不要誤解Server為一個類),如果沒有的話,那么調用load方法。
load方法中調用了一下幾個方法:
initDirs:將Bootstrap中定義的catalina.home的值賦給CATALINA_BASE_PROP屬性。以及對java.io.tmpdir屬性的驗證,下面是initDirs的代碼實現:
1
protected
void
initDirs() {
2
3
String catalinaHome =
System.getProperty(Globals.CATALINA_HOME_PROP);
4
if
(catalinaHome ==
null
) {
5
//
Backwards compatibility patch for J2EE RI 1.3
6
String j2eeHome = System.getProperty("com.sun.enterprise.home"
);
7
if
(j2eeHome !=
null
) {
8
catalinaHome=System.getProperty("com.sun.enterprise.home"
);
9
}
else
if
(System.getProperty(Globals.CATALINA_BASE_PROP) !=
null
) {
10
catalinaHome =
System.getProperty(Globals.CATALINA_BASE_PROP);
11
}
12
}
13
//
last resort - for minimal/embedded cases.
14
if
(catalinaHome==
null
) {
15
catalinaHome=System.getProperty("user.dir"
);
16
}
17
if
(catalinaHome !=
null
) {
18
File home =
new
File(catalinaHome);
19
if
(!
home.isAbsolute()) {
20
try
{
21
catalinaHome =
home.getCanonicalPath();
22
}
catch
(IOException e) {
23
catalinaHome =
home.getAbsolutePath();
24
}
25
}
26
System.setProperty(Globals.CATALINA_HOME_PROP, catalinaHome);
27
}
28
29
if
(System.getProperty(Globals.CATALINA_BASE_PROP) ==
null
) {
30
System.setProperty(Globals.CATALINA_BASE_PROP,
31
catalinaHome);
32
}
else
{
33
String catalinaBase =
System.getProperty(Globals.CATALINA_BASE_PROP);
34
File base =
new
File(catalinaBase);
35
if
(!
base.isAbsolute()) {
36
try
{
37
catalinaBase =
base.getCanonicalPath();
38
}
catch
(IOException e) {
39
catalinaBase =
base.getAbsolutePath();
40
}
41
}
42
System.setProperty(Globals.CATALINA_BASE_PROP, catalinaBase);
43
}
44
45
String temp = System.getProperty("java.io.tmpdir"
);
46
if
(temp ==
null
|| (!(
new
File(temp)).exists())
47
|| (!(
new
File(temp)).isDirectory())) {
48
log.error(sm.getString("embedded.notmp"
, temp));
49
}
50
51
}
其中首先是兼容J2EE RI 1.3,獲取com.sun.enterprise.home屬性的值賦值給catalinaHome,如果不存在com.sun.enterprise.home這個屬性,將Bootstrap中定義的catalina.home的值賦給CATALINA_BASE_PROP屬性,如果以上都不成立,那么就是獲取當前目錄賦給CATALINA_BASE_PROP屬性。其實當前目錄也就是將Bootstrap中定義的catalina.home的值。只是在tomcat中進行了很繁瑣的驗證,當然這是有必要的。
createStartDigester:用來生成server.xml的操作,下面是代碼實現:
1
protected
Digester createStartDigester() {
2
long
t1=
System.currentTimeMillis();
3
//
Initialize the digester
4
Digester digester =
new
Digester();
5
digester.setValidating(
false
);
6
digester.setRulesValidation(
true
);
7
HashMap<Class<?>, List<String>> fakeAttributes =
8
new
HashMap<Class<?>, List<String>>
();
9
ArrayList<String> attrs =
new
ArrayList<String>
();
10
attrs.add("className"
);
11
fakeAttributes.put(Object.
class
, attrs);
12
digester.setFakeAttributes(fakeAttributes);
13
digester.setUseContextClassLoader(
true
);
14
15
//
Configure the actions we will be using
16
digester.addObjectCreate("Server"
,
17
"org.apache.catalina.core.StandardServer"
,
18
"className"
);
19
digester.addSetProperties("Server"
);
20
digester.addSetNext("Server"
,
21
"setServer"
,
22
"org.apache.catalina.Server"
);
23
24
digester.addObjectCreate("Server/GlobalNamingResources"
,
25
"org.apache.catalina.deploy.NamingResources"
);
26
digester.addSetProperties("Server/GlobalNamingResources"
);
27
digester.addSetNext("Server/GlobalNamingResources"
,
28
"setGlobalNamingResources"
,
29
"org.apache.catalina.deploy.NamingResources"
);
30
31
digester.addObjectCreate("Server/Listener"
,
32
null
,
//
MUST be specified in the element
33
"className"
);
34
digester.addSetProperties("Server/Listener"
);
35
digester.addSetNext("Server/Listener"
,
36
"addLifecycleListener"
,
37
"org.apache.catalina.LifecycleListener"
);
38
39
digester.addObjectCreate("Server/Service"
,
40
"org.apache.catalina.core.StandardService"
,
41
"className"
);
42
digester.addSetProperties("Server/Service"
);
43
digester.addSetNext("Server/Service"
,
44
"addService"
,
45
"org.apache.catalina.Service"
);
46
47
digester.addObjectCreate("Server/Service/Listener"
,
48
null
,
//
MUST be specified in the element
49
"className"
);
50
digester.addSetProperties("Server/Service/Listener"
);
51
digester.addSetNext("Server/Service/Listener"
,
52
"addLifecycleListener"
,
53
"org.apache.catalina.LifecycleListener"
);
54
55
//
Executor
56
digester.addObjectCreate("Server/Service/Executor"
,
57
"org.apache.catalina.core.StandardThreadExecutor"
,
58
"className"
);
59
digester.addSetProperties("Server/Service/Executor"
);
60
61
digester.addSetNext("Server/Service/Executor"
,
62
"addExecutor"
,
63
"org.apache.catalina.Executor"
);
64
65
66
digester.addRule("Server/Service/Connector"
,
67
new
ConnectorCreateRule());
68
digester.addRule("Server/Service/Connector"
,
69
new
SetAllPropertiesRule(
new
String[]{"executor"
}));
70
digester.addSetNext("Server/Service/Connector"
,
71
"addConnector"
,
72
"org.apache.catalina.connector.Connector"
);
73
74
75
digester.addObjectCreate("Server/Service/Connector/Listener"
,
76
null
,
//
MUST be specified in the element
77
"className"
);
78
digester.addSetProperties("Server/Service/Connector/Listener"
);
79
digester.addSetNext("Server/Service/Connector/Listener"
,
80
"addLifecycleListener"
,
81
"org.apache.catalina.LifecycleListener"
);
82
83
//
Add RuleSets for nested elements
84
digester.addRuleSet(
new
NamingRuleSet("Server/GlobalNamingResources/"
));
85
digester.addRuleSet(
new
EngineRuleSet("Server/Service/"
));
86
digester.addRuleSet(
new
HostRuleSet("Server/Service/Engine/"
));
87
digester.addRuleSet(
new
ContextRuleSet("Server/Service/Engine/Host/"
));
88
addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/"
);
89
digester.addRuleSet(
new
NamingRuleSet("Server/Service/Engine/Host/Context/"
));
90
91
//
When the 'engine' is found, set the parentClassLoader.
92
digester.addRule("Server/Service/Engine"
,
93
new
SetParentClassLoaderRule(parentClassLoader));
94
addClusterRuleSet(digester, "Server/Service/Engine/Cluster/"
);
95
96
long
t2=
System.currentTimeMillis();
97
if
(log.isDebugEnabled()) {
98
log.debug("Digester for server.xml created " + ( t2-
t1 ));
99
}
100
return
(digester);
101
102
}
在具體說明之前,我覺得有必要對Digester進行一下說明,以為可能有很多人和我一樣,目前為止還還不是很清楚Digester為什么東西,其實他就是一個XML解析器,在這里就是構造一下tomcat啟動時候的各種參數,各種初始化方法,初始化server,listener,connector,Executor等數據,我覺得這里有很多內容可以展開來說,所以我打算把他放到下一個章節專門對tomcat中Digester進行說明。在這里特別需要注意的就是如下這部分內容:
1
digester.addObjectCreate("Server"
,
2
"org.apache.catalina.core.StandardServer"
,
3
"className"
);
4
digester.addSetProperties("Server"
);
5
digester.addSetNext("Server"
,
6
"setServer"
,
7
"org.apache.catalina.Server"
);
8
9
digester.addObjectCreate("Server/GlobalNamingResources"
,
10
"org.apache.catalina.deploy.NamingResources"
);
11
digester.addSetProperties("Server/GlobalNamingResources"
);
12
digester.addSetNext("Server/GlobalNamingResources"
,
13
"setGlobalNamingResources"
,
14
"org.apache.catalina.deploy.NamingResources"
);
15
16
digester.addObjectCreate("Server/Listener"
,
17
null
,
//
MUST be specified in the element
18
"className"
);
19
digester.addSetProperties("Server/Listener"
);
20
digester.addSetNext("Server/Listener"
,
21
"addLifecycleListener"
,
22
"org.apache.catalina.LifecycleListener"
);
23
24
digester.addObjectCreate("Server/Service"
,
25
"org.apache.catalina.core.StandardService"
,
26
"className"
);
27
digester.addSetProperties("Server/Service"
);
28
digester.addSetNext("Server/Service"
,
29
"addService"
,
30
"org.apache.catalina.Service"
);
31
32
digester.addObjectCreate("Server/Service/Listener"
,
33
null
,
//
MUST be specified in the element
34
"className"
);
35
digester.addSetProperties("Server/Service/Listener"
);
36
digester.addSetNext("Server/Service/Listener"
,
37
"addLifecycleListener"
,
38
"org.apache.catalina.LifecycleListener"
);
39
40
//
Executor
41
digester.addObjectCreate("Server/Service/Executor"
,
42
"org.apache.catalina.core.StandardThreadExecutor"
,
43
"className"
);
44
digester.addSetProperties("Server/Service/Executor"
);
45
46
digester.addSetNext("Server/Service/Executor"
,
47
"addExecutor"
,
48
"org.apache.catalina.Executor");
比如這里面的digester.addSetNext("Server","setServer","org.apache.catalina.Server")這句話,在Digester類中的實現如下:
1
public
void
addSetNext(String pattern, String methodName,
2
String paramType) {
3
4
addRule(pattern,
5
new
SetNextRule(methodName, paramType));
6
7
}
實現的內容就是把org.apache.catalina.Server以及setServer以SetNextRule的類型保存起來??匆幌耂etNextRule對象提供的方法,
其中end方法的實現如下:
public
void
end(String namespace, String name)
throws
Exception {
//
Identify the objects to be used
Object child = digester.peek(0
);
Object parent
= digester.peek(1
);
if
(digester.log.isDebugEnabled()) {
if
(parent ==
null
) {
digester.log.debug(
"[SetNextRule]{" + digester.match +
"} Call [NULL PARENT]." +
methodName
+ "(" + child + ")"
);
}
else
{
digester.log.debug(
"[SetNextRule]{" + digester.match +
"} Call " + parent.getClass().getName() + "." +
methodName
+ "(" + child + ")"
);
}
}
if
(methodName.equals("setServer"
)){
System.out.println(
"111111111111111111"
);
}
//
Call the specified method
IntrospectionUtils.callMethod1(parent, methodName,
child, paramType, digester.getClassLoader());
}
在這里通過反射實現的方法調用。大家可能困惑到底是在哪發出rule.end調用動作的呢?下面還是要看一下Digester類,igester繼承了org.xml.sax.ext.DefaultHandler2類,其中有一個endElement方法,這個方法在讀完XML中每個Element的時候執行,看一下endElement方法在Digester中的實現:
@Override
public
void
endElement(String namespaceURI, String localName,
String qName)
throws
SAXException {
boolean
debug =
log.isDebugEnabled();
if
(debug) {
if
(saxLog.isDebugEnabled()) {
saxLog.debug(
"endElement(" + namespaceURI + "," + localName +
"," + qName + ")"
);
}
log.debug(
" match='" + match + "'"
);
log.debug(
" bodyText='" + bodyText + "'"
);
}
//
Parse system properties
bodyText =
updateBodyText(bodyText);
//
the actual element name is either in localName or qName, depending
//
on whether the parser is namespace aware
String name =
localName;
if
((name ==
null
) || (name.length() < 1
)) {
name
=
qName;
}
//
Fire "body" events for all relevant rules
List<Rule> rules =
matches.pop();
if
((rules !=
null
) && (rules.size() > 0
)) {
String bodyText
=
this
.bodyText.toString();
for
(
int
i = 0; i < rules.size(); i++
) {
try
{
Rule rule
=
rules.get(i);
if
(debug) {
log.debug(
" Fire body() for " +
rule);
}
rule.body(namespaceURI, name, bodyText);
}
catch
(Exception e) {
log.error(
"Body event threw exception"
, e);
throw
createSAXException(e);
}
catch
(Error e) {
log.error(
"Body event threw error"
, e);
throw
e;
}
}
}
else
{
if
(debug) {
log.debug(
" No rules found matching '" + match + "'."
);
}
if
(rulesValidation) {
log.warn(
" No rules found matching '" + match + "'."
);
}
}
//
Recover the body text from the surrounding element
bodyText =
bodyTexts.pop();
if
(debug) {
log.debug(
" Popping body text '" + bodyText.toString() + "'"
);
}
//
Fire "end" events for all relevant rules in reverse order
if
(rules !=
null
) {
for
(
int
i = 0; i < rules.size(); i++
) {
int
j = (rules.size() - i) - 1
;
try
{
Rule rule
=
rules.get(j);
if
(debug) {
log.debug(
" Fire end() for " +
rule);
}
if
(name.equals("setServer"
)){
System.out.println(
"1222"
);
}
rule.end(namespaceURI, name);
}
catch
(Exception e) {
log.error(
"End event threw exception"
, e);
throw
createSAXException(e);
}
catch
(Error e) {
log.error(
"End event threw error"
, e);
throw
e;
}
}
}
//
Recover the previous match expression
int
slash = match.lastIndexOf('/'
);
if
(slash >= 0
) {
match
= match.substring(0
, slash);
}
else
{
match
= ""
;
}
}
主要功能就是找出對應的rule來逐一調用rule.end方法。根據在Catalina.java類中digester添加的rule,就執行到了StandardServer類中的addService方法,設置的server對象,這部分內容很重要。 ?
configFile:返回配置文件conf/server.xml文件。在獲取配置文件conf/server.xml出錯的時候,就嘗試去獲取server-embed.xml文件,如果都不存在,那么直接返回。記錄日志。
?initStreams:這個方法很簡單只是做了一個tomcat自定義的流的重定向,
getServer().init:設置一下server的狀態,然后初始化網絡配置。
OK,load方法就說完了,很長。
然后在start方法中啟動server。至于start方法,我們不再本文中說明,等在以后的章節會專門介紹Server。
然后在在當期運行環境中注冊一個ShutdownHook,該鉤子的作于就是當程序結束時候,將Catalina程序shutdown。
到此為止,start方法就算是說完了。其中主要內容就是如何構造一個server對象。在以后會展開說明Server對象。
Stop :另外一個被外部調用的方法就是stop方法,看一下stop方法的代碼實現:
public
void
stop() {
try
{
//
Remove the ShutdownHook first so that server.stop()
//
doesn't get invoked twice
if
(useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
//
If JULI is being used, re-enable JULI's shutdown to ensure
//
log messages are not lost
LogManager logManager =
LogManager.getLogManager();
if
(logManager
instanceof
ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
true
);
}
}
}
catch
(Throwable t) {
ExceptionUtils.handleThrowable(t);
//
This will fail on JDK 1.2. Ignoring, as Tomcat can run
//
fine without the shutdown hook.
}
//
Shut down the server
try
{
Server s
=
getServer();
LifecycleState state
=
s.getState();
if
(LifecycleState.STOPPING_PREP.compareTo(state) <= 0
&& LifecycleState.DESTROYED.compareTo(state) >= 0
) {
//
Nothing to do. stop() was already called
}
else
{
s.stop();
s.destroy();
}
}
catch
(LifecycleException e) {
log.error(
"Catalina.stop"
, e);
}
}
首先要移除在start方法中注冊的鉤子,否則在程序結束以后再次觸發鉤子中定義的事件,肯定會出錯。然后就獲取server對象,檢查狀態,如果在運行那么停止,然后將資源釋放。stop方法簡單很多。
stopServer: 先檢查Server對象是否存在,如果不存在就創建一個新的,然后關閉server以及Server中定義的socket。
?
Catalina中的內容大概就這么多了,很不過癮的地方就是內容很多,沒有辦法全部展開,尤其是實現Server接口的Server對象,構建server的方法,希望在下面的章節中把如何通過Digester構建server,以及與次有很重要關系的Tomca的結構比如server,services,connector,container等說清楚。
如果有不正確的地方請指正。大家共同學習。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

