本文接下來會介紹Host容器和Engine容器,在tomcat的實際部署中,總是會使用一個Host容器;本文介紹Host接口和Engine接口及其相關類
Host容器是org.apache.catalina.Host接口的實例,Host接口繼承自Container接口, 其定義如下
public
interface
Host
extends
Container {
public
static
final
String ADD_ALIAS_EVENT = "addAlias"
;
public
static
final
String REMOVE_ALIAS_EVENT = "removeAlias"
;
public
String getAppBase();
public
void
setAppBase(String appBase);
public
boolean
getAutoDeploy();
public
void
setAutoDeploy(
boolean
autoDeploy);
public
void
addDefaultContext(DefaultContext defaultContext);
public
DefaultContext getDefaultContext();
public
String getName();
public
void
setName(String name);
public
void
importDefaultContext(Context context);
public
void
addAlias(String alias);
public
String[] findAliases();
public
Context map(String uri);
public
void
removeAlias(String alias);
}
該接口中比較重要的方法是map()方法,該方法返回一個用來處理引入的HTTP請求的Context容器的實例,該方法的具體實現在StandardHost類中
在tomcat中org.apache.catalina.core.StandardHost類是Host接口的標準實現,該類繼承自org.apache.catalina.core.ContainerBase類,實現了Host和Deployer接口
與StandardContext類和StandardWrapper類相似,StandardHost類的構造函數會將一個基礎閥的實例添加到其管道對象中:
public
StandardHost() {
super
();
pipeline.setBasic(
new
StandardHostValve());
}
基礎閥是org.apache.catalina.core.StandardHostValve類的實例
當調用其start()方法時,StandardHost實例會添加兩個閥,分別為ErrorReportValve類和ErrorDispatcherValve類的實例,這兩個閥均位于org.apache.catalina.valves包下
public
synchronized
void
start()
throws
LifecycleException {
//
Set error report valve
if
((errorReportValveClass !=
null
)
&& (!errorReportValveClass.equals(""
))) {
try
{
Valve valve
=
(Valve) Class.forName(errorReportValveClass)
.newInstance();
addValve(valve);
}
catch
(Throwable t) {
log(sm.getString
(
"standardHost.invalidErrorReportValveClass"
,
errorReportValveClass));
}
}
//
Set dispatcher valve
addValve(
new
ErrorDispatcherValve());
super
.start();
}
每當引入一個HTTP請求,都會調用Host實例的invoke()方法,這里是StandardHost的父類ContainerBase類的invoke()方法,而ContainerBase類的invoke()方法會調用StandardHost實例的基礎閥StandardHostValve實例的invoke()方法;StandardHostValve類的invoke()方法會調用StandardHost類的map()方法來獲取相應的Context實例來處理HTTP請求
public
Context map(String uri) {
if
(debug > 0
)
log(
"Mapping request URI '" + uri + "'"
);
if
(uri ==
null
)
return
(
null
);
//
Match on the longest possible context path prefix
if
(debug > 1
)
log(
" Trying the longest context path prefix"
);
Context context
=
null
;
String mapuri
=
uri;
while
(
true
) {
context
=
(Context) findChild(mapuri);
if
(context !=
null
)
break
;
int
slash = mapuri.lastIndexOf('/'
);
if
(slash < 0
)
break
;
mapuri
= mapuri.substring(0
, slash);
}
//
If no Context matches, select the default Context
if
(context ==
null
) {
if
(debug > 1
)
log(
" Trying the default context"
);
context
= (Context) findChild(""
);
}
//
Complain if no Context has been selected
if
(context ==
null
) {
log(sm.getString(
"standardHost.mappingError"
, uri));
return
(
null
);
}
//
Return the mapped Context (if any)
if
(debug > 0
)
log(
" Mapped to context '" + context.getPath() + "'"
);
return
(context);
}
在tomcat4中, StandardHost的父類ContainerBase類會調用其addDefaultMapper()方法創建一個默認映射器,默認映射器的類型由mapperClass屬性的值決定
protected
void
addDefaultMapper(String mapperClass) {
//
Do we need a default Mapper?
if
(mapperClass ==
null
)
return
;
if
(mappers.size() >= 1
)
return
;
//
Instantiate and add a default Mapper
try
{
Class clazz
=
Class.forName(mapperClass);
Mapper mapper
=
(Mapper) clazz.newInstance();
mapper.setProtocol(
"http"
);
addMapper(mapper);
}
catch
(Exception e) {
log(sm.getString(
"containerBase.addDefaultMapper"
, mapperClass),
e);
}
}
變量mapperClass的值定義在StandardHst類中
private String mapperClass =
??????? "org.apache.catalina.core.StandardHostMapper";
在tomcat4中,StandardHost類的start()方法會在方法末尾調用addDefaultMapper()方法,確保默認映射器的創建完成
當然,StandardHostMapper類中最重要的方法是map()方法,下面是map()方法的實現
public
Container map(Request request,
boolean
update) {
//
Has this request already been mapped?
if
(update && (request.getContext() !=
null
))
return
(request.getContext());
//
Perform mapping on our request URI
String uri =
((HttpRequest) request).getDecodedRequestURI();
Context context
=
host.map(uri);
//
Update the request (if requested) and return the selected Context
if
(update) {
request.setContext(context);
if
(context !=
null
)
((HttpRequest) request).setContextPath(context.getPath());
else
((HttpRequest) request).setContextPath(
null
);
}
return
(context);
}
注意,這里map()方法僅僅是簡單地調用了Host實例的map()方法
org.apache.catalina.core.StandardHostValve類是StandardHost實例的基礎閥,當有引入的HTTP請求時,會調用StandardHostValve類的invoke()方法對其進行處理
public
void
invoke(Request request, Response response,
ValveContext valveContext)
throws
IOException, ServletException {
//
Validate the request and response object types
if
(!(request.getRequest()
instanceof
HttpServletRequest) ||
!(response.getResponse()
instanceof
HttpServletResponse)) {
return
;
//
NOTE - Not much else we can do generically
}
//
Select the Context to be used for this Request
StandardHost host =
(StandardHost) getContainer();
Context context
= (Context) host.map(request,
true
);
if
(context ==
null
) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString(
"standardHost.noContext"
));
return
;
}
//
Bind the context CL to the current thread
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
//
Update the session last access time for our session (if any)
HttpServletRequest hreq =
(HttpServletRequest) request.getRequest();
String sessionId
=
hreq.getRequestedSessionId();
if
(sessionId !=
null
) {
Manager manager
=
context.getManager();
if
(manager !=
null
) {
Session session
=
manager.findSession(sessionId);
if
((session !=
null
) &&
session.isValid())
session.access();
}
}
//
Ask this Context to process this request
context.invoke(request, response);
}
在tomcat4中,invoke()方法會調用StandardHost實例的map()方法獲取一個相應的Context實例;然后獲取與該request對象相關聯的session對象,并調用其access()方法,access()方法會修改session對象的最后訪問時間;最后調用Context實例的invoke()來處理HTTP請求
接下來描述Engine容器,Engine容器是org.apache.catalina.Engine接口的實例,Engine容器也就是Tomcat的servlet引擎
public
interface
Engine
extends
Container {
public
String getDefaultHost();
public
void
setDefaultHost(String defaultHost);
public
String getJvmRoute();
public
void
setJvmRoute(String jvmRouteId);
public
Service getService();
public
void
setService(Service service);
public
void
addDefaultContext(DefaultContext defaultContext);
public
DefaultContext getDefaultContext();
public
void
importDefaultContext(Context context);
}
在Engine容器中,可以設置一個默認的Host容器或一個默認的Context容器,注意,Engine容器可以與一個服務實例相關聯
org.apache.catalina.core.StandardEngine類是Engine接口的標準實現,在實例化的時候,StandardEngine類會添加一個基礎閥
public
StandardEngine() {
super
();
pipeline.setBasic(
new
StandardEngineValve());
}
org.apache.catalina.core.StandardEngineValve類是StandardEngine容器的基礎閥,下面是它的invoke()方法的實現代碼
public
void
invoke(Request request, Response response,
ValveContext valveContext)
throws
IOException, ServletException {
//
Validate the request and response object types
if
(!(request.getRequest()
instanceof
HttpServletRequest) ||
!(response.getResponse()
instanceof
HttpServletResponse)) {
return
;
//
NOTE - Not much else we can do generically
}
//
Validate that any HTTP/1.1 request included a host header
HttpServletRequest hrequest =
(HttpServletRequest) request;
if
("HTTP/1.1".equals(hrequest.getProtocol()) &&
(hrequest.getServerName()
==
null
)) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString(
"standardEngine.noHostHeader"
,
request.getRequest().getServerName()));
return
;
}
//
Select the Host to be used for this Request
StandardEngine engine =
(StandardEngine) getContainer();
Host host
= (Host) engine.map(request,
true
);
if
(host ==
null
) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString(
"standardEngine.noHost"
,
request.getRequest().getServerName()));
return
;
}
//
Ask this Host to process this request
host.invoke(request, response);
}
在驗證了request和response對象的類型后,invoke()方法會通過調用Engine實例的map()方法獲取Host對象;得到Host對象以后,調用其invoke()方法處理請求
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創?
轉載請注明出處 博客園 刺猬的溫馴?
本人郵箱: ? chenying998179 # 163.com ( #改為@ )
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

