public interface Host extends Container {
// ----------------------------------------------------- Manifest Constants
/**
* The ContainerEvent event type sent when a new alias is added
* by <code>addAlias()</code>.
*/
public static final String ADD_ALIAS_EVENT = "addAlias";
/**
* The ContainerEvent event type sent when an old alias is removed
* by <code>removeAlias()</code>.
*/
public static final String REMOVE_ALIAS_EVENT = "removeAlias";
// ------------------------------------------------------------- Properties
/**
* Return the application root for this Host. This can be an absolute
* pathname, a relative pathname, or a URL.
*/
public String getAppBase();
/**
* Set the application root for this Host. This can be an absolute
* pathname, a relative pathname, or a URL.
*
* @param appBase The new application root
*/
public void setAppBase(String appBase);
/**
* Return the value of the auto deploy flag. If true, it indicates that
* this host's child webapps should be discovred and automatically
* deployed.
*/
public boolean getAutoDeploy();
/**
* Set the auto deploy flag value for this host.
*
* @param autoDeploy The new auto deploy flag
*/
public void setAutoDeploy(boolean autoDeploy);
/**
* Set the DefaultContext
* for new web applications.
*
* @param defaultContext The new DefaultContext
*/
public void addDefaultContext(DefaultContext defaultContext);
/**
* Retrieve the DefaultContext for new web applications.
*/
public DefaultContext getDefaultContext();
/**
* Return the canonical, fully qualified, name of the virtual host
* this Container represents.
*/
public String getName();
/**
* Set the canonical, fully qualified, name of the virtual host
* this Container represents.
*
* @param name Virtual host name
*
* @exception IllegalArgumentException if name is null
*/
public void setName(String name);
// --------------------------------------------------------- Public Methods
/**
* Import the DefaultContext config into a web application context.
*
* @param context web application context to import default context
*/
public void importDefaultContext(Context context);
/**
* Add an alias name that should be mapped to this same Host.
*
* @param alias The alias to be added
*/
public void addAlias(String alias);
/**
* Return the set of alias names for this Host. If none are defined,
* a zero length array is returned.
*/
public String[] findAliases();
/**
* Return the Context that would be used to process the specified
* host-relative request URI, if any; otherwise return <code>null</code>.
*
* @param uri Request URI to be mapped
*/
public Context map(String uri);
/**
* Remove the specified alias name from the aliases for this Host.
*
* @param alias Alias name to be removed
*/
public void removeAlias(String alias);
}
/**
* Create a new StandardHost component with the default basic Valve.
*/
public StandardHost() {
super();
pipeline.setBasic(new StandardHostValve());
}
<Host name="www.dhy.com" appBase="dhy"
unpackWARs="true" autoDeploy="true">
<context path="/xpy" docBase="xpy" ></context>
</Host>
Deployer接口具体是干啥的,大家看了上面的xml配置,估计就会明白了。
Host对应一个域名,该域名在操作系统层面的文件系统中映射到一个目录,该目录下可以有很多子目录,这些子目录会挨个映射到当前Host下面所管理的context容器上去
/**
* Start this host
*/
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();
}
/**
* The Java class name of the default error reporter implementation class
* for deployed web applications.
*/
private String errorReportValveClass =
"org.apache.catalina.valves.ErrorReportValve";
这里Host的start初始化方法主要就是添加了两个关于错误日志处理的阀门,随后调用父类ContainerBase的start方法,启动相关组件,调用子容器的start初始化方法,因为这些逻辑都是通用的,因此都放到了父类的start方法中实现
父类ContainerBase的start方法
/**
* Prepare for active use of the public methods of this Component.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents it from being started
*/
@Override
public synchronized void start() throws LifecycleException {
// Validate and update our current component state
if (started)
throw new LifecycleException
(sm.getString("containerBase.alreadyStarted", logName()));
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
addDefaultMapper(this.mapperClass);
started = true;
// Start our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
if ((logger != null) && (logger instanceof Lifecycle))
((Lifecycle) logger).start();
if ((manager != null) && (manager instanceof Lifecycle))
((Lifecycle) manager).start();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
if ((resources != null) && (resources instanceof Lifecycle))
((Lifecycle) resources).start();
// Start our Mappers, if any
Mapper mappers[] = findMappers();
for (int i = 0; i < mappers.length; i++) {
if (mappers[i] instanceof Lifecycle)
((Lifecycle) mappers[i]).start();
}
// Start our child containers, if any
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle)
((Lifecycle) children[i]).start();
}
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(START_EVENT, null);
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
ContainerBase类的invoke方法因为是通用逻辑抽取,因此实现是固定的:
public void invoke(Request request, Response response)
throws IOException, ServletException {
//调用当前容器管道中的阀门进行处理
pipeline.invoke(request, response);
}
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();
//根据map方法找到当前请求对应的context对象
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());
//如果存在session的话,更新最后一次访问的时间
// 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对象进行处理
context.invoke(request, response);
}
这里调用的其实是父类ContainerBase的map方法:
public Container map(Request request, boolean update) {
// Select the Mapper we will use
Mapper mapper = findMapper(request.getRequest().getProtocol());
if (mapper == null)
return (null);
// Use this Mapper to perform this mapping
//这里最终调用的是StandardHostMapper的map方法,下面再分析,这里最终传入的其实就是请求URI中的context路径部分
return (mapper.map(request, update));
}
StandardHost类的map方法:
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;
//拿到当前请求context路径部分
String mapuri = uri;
while (true) {
//去子容器中找到该context
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);
}
说明继承了ContainerBase的容器,只要调用了父类的start方法,就会添加一个默认的映射器
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);
}
}
/**
* The Java class name of the default Mapper class for this Container.
*/
private String mapperClass =
"org.apache.catalina.core.StandardHostMapper";
上面源码已经给出过了
关于这一点可以参考第12节
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();
//调用Host的map方法获得当前URI对应的context对象
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);
}
applicationConfig方法主要是来解析web.xml配置文件的,该方法在start方法中被调用,start方法在ContextConfig监听到Context容器的启动的start事件时,被调用。
connector改为关联一个Host了,然后会设置Host下面的context,host对应的域名和目录名
Host洋洋洒洒讲了那么多,下面对Host这部分内容做一个小结先:
public interface Engine extends Container {
// ------------------------------------------------------------- Properties
/**
* Return the default hostname for this Engine.
*/
public String getDefaultHost();
/**
* Set the default hostname for this Engine.
*
* @param defaultHost The new default host
*/
public void setDefaultHost(String defaultHost);
/**
* Retrieve the JvmRouteId for this engine.
*/
public String getJvmRoute();
/**
* Set the JvmRouteId for this engine.
*
* @param jvmRouteId the (new) JVM Route ID. Each Engine within a cluster
* must have a unique JVM Route ID.
*/
public void setJvmRoute(String jvmRouteId);
/**
* Return the <code>Service</code> with which we are associated (if any).
*/
public Service getService();
/**
* Set the <code>Service</code> with which we are associated (if any).
*
* @param service The service that owns this Engine
*/
public void setService(Service service);
/**
* Set the DefaultContext
* for new web applications.
*
* @param defaultContext The new DefaultContext
*/
public void addDefaultContext(DefaultContext defaultContext);
/**
* Retrieve the DefaultContext for new web applications.
*/
public DefaultContext getDefaultContext();
// --------------------------------------------------------- Public Methods
/**
* Import the DefaultContext config into a web application context.
*
* @param context web application context to import default context
*/
public void importDefaultContext(Context context);
}
/**
* Create a new StandardEngine component with the default basic Valve.
*/
public StandardEngine() {
super();
pipeline.setBasic(new StandardEngineValve());
}
/**
* Add a child Container, only if the proposed child is an implementation
* of Host.
*
* @param child Child container to be added
*/
public void addChild(Container child) {
if (!(child instanceof Host))
throw new IllegalArgumentException
(sm.getString("standardEngine.notHost"));
super.addChild(child);
}
/**
* Disallow any attempt to set a parent for this Container, since an
* Engine is supposed to be at the top of the Container hierarchy.
*
* @param container Proposed parent Container
*/
public void setParent(Container container) {
throw new IllegalArgumentException
(sm.getString("standardEngine.notParent"));
}
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();
//通过request找到对应的host,不用我多说了吧,和context的步骤基本一致
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);
}