18,tomcat的主机(host)和引擎
如果需要在一个Tomcat部署中部署多个上下文,需要使用一个主机引擎表示整个Catalina 的Servlet引擎。如果使用的话,它位于容器等级的最高层
可以添加到引擎上的容器包括org.apache.catalina.Host 或者org.apache.catalina.Context。
在一个Tomcat部署中,默认的容器是引擎。
Host相关的StandardHost、StandardHostMapper以及StandardHostValve类。
Host接口
主机是用org.apache.catalina.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);
}
StandardHost类
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。
The start method of StandardHost
public synchronized void start() throws LifecycleException {
if ((errorReportValveClass != null) && (!errorReportValveClass.equals(""))) {
try {
Valve valve = (Valve) Class.forName(errorReportValveClass).newInstance();
addValve(valve)
} catch (Throwable t) {}
}
addValve(new ErrorDispatcherValve());
super.start();
}
对于每一个请求,都会调用主机的invoke方法。由于StanardHost类并没有实现invoke方法,
所以会调用它的父类ContainerBase类的invoke方法。该invoke方法会转而调用StandardHost 的基本阀门StandardHostValve的invoke方法。
StandardHostValve的invoke方法调用StandardHost类的map方法获得一个合适的上下文容器来处理请求
//The map method in the StandardHost class
public Context map(String uri) {
if (uri == null)
return (null);
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) {
context = (Context) findChild("");
}
if (context == null)
return (null);
return (context);
}
StandardHostMapper类
StandardHost的父类ContainerBase使用addDefaultMapper方法创建一个默认映射器。
默认映射器的类型由mapperClass属性指定。这里是ContainerBase的addDefaulstMapper方法:
protected void addDefaultMapper(String mapperClass) {
if (mapperClass == null)
return;
if (mappers.size() >= 1)
return;
try {
Class clazz = Class.forName(mapperClass);
Mapper mapper = (Mapper) clazz.newInstance();
mapper.setProtocol("http");
addMapper(mapper);
} catch (Exception e) {}
}
StandardHost类的start方法在它的最后调用super.start(),这样保证了创建一个默认的映射器。
StandardHostMapper中最重要的方法是map方法,下面是它的实现:
public Container map(Request request, boolean update) {
// Has this request already been mapped?
if (update && (request.getContext() != null))
return (request.getContext());
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);
}
StandardHostValve类
org.apache.catalina.core.StandardHostValve类是StandardHost的基本阀门类型。
当有HTTP请求的时候会调用它的invoke方法:
public void invoke(Request request, Response response, ValveContext valveContext){
if (!(request.getRequest() instanceof HttpServletRequest) || !(response.getResponse() instanceof HttpServletResponse)) {
return;
// NOTE - Not much else we can do generically }
}
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);
}
invoke方法中调用StandardHost的map方法来获得一个合适的上下文。
StandardHost host = (StandardHost) getContainer();
Context context = (Context) host.map(request, true);
Invoke方法解析来得到一个Session对象并调用它的access方法,access方法更新它的最后进入时间,
public void access() {
this.isNew = false;
this.lastAccessedTime = this.thisAccessedTime;
this.thisAccessedTime = System.currentTimeMillis();
}
最后,invoke方法调用上下文容器的invoke方法,让上下文来处理请求。
public final class Bootstrap1 {
public static void main(String[] args) {
System.setProperty("catalina.base", System.getProperty("user.dir"));
Connector connector = new HttpConnector();
Wrapper wrapper1 = new StandardWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new StandardWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
Context context = new StandardContext();
context.setPath("/app1");
context.setDocBase("app1");
context.addChild(wrapper1);
context.addChild(wrapper2);
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
Host host = new StandardHost();
host.addChild(context);
host.setName("localhost");
host.setAppBase("webapps");
Loader loader = new WebappLoader();
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
connector.setContainer(host);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) host).start();
// make the application wait until we press a key.
System.in.read();
((Lifecycle) host).stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Engine接口
Engine接口用来表示一个引擎。引擎表示整个Catalina的Servlet引擎。
当你想要支持多个虚拟主机的时候,需要一个引擎
public interface Engine extends Container {
/** *
*Return the default hostname for this Engine.
*/
public String getDefaultHost();
//Set the default hostname for this Engine.
public void setDefaultHost(String defaultHost);
//Retrieve the JvmRouteId for this engine.
public String getJvmRoute();
public void setJvmRoute(String jvmRouteId);
//Return the <code>Service</code> with which we are associated
public Service getService();
public void setService(Service service);
public void addDefaultContext(DefaultContext defaultContext);
public DefaultContext getDefaultContext();
public void importDefaultContext(Context context);
}
可以给引擎设置默认主机或者默认上下文。注意引擎也可以跟服务相关联
StandardEngine类
StandardEngine是Engine接口的标准实现,跟StandardContext和StandardHost相比,
StandardEngine类相对较小。初始化的时候,StandardEngine类需要添加一个基本阀门
public StandardEngine() {
super();
pipeline.setBasic(new StandardEngineValve());
}
在Container容器的顶层,StandardEngine可以有子容器,它的子容器必须是主机(host)
StandardEngineValve类
StandardEngineValve是StandardEngine的基本阀门
public void invoke(Request request, Response response,ValveContext valveContext) throws IOException, ServletException {
if (!(request.getRequest() instanceof HttpServletRequest) || !(response.getResponse() instanceof HttpServletResponse)) {
return;
}
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);
}
在验证了请求对象和响应对象之后,invoke方法获得一个Host实例来处理请求。
它得到主机的方法是调用引擎的map方法。一旦获得了一个主机,它的invoke方法将会被调用。
public final class Bootstrap2 {
public static void main(String[] args) {
System.setProperty("catalina.base", System.getProperty("user.dir"));
Connector connector = new HttpConnector();
Wrapper wrapper1 = new StandardWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new StandardWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
Context context = new StandardContext();
// StandardContext's start method adds a default mapper
context.setPath("/app1");
context.setDocBase("app1");
context.addChild(wrapper1);
context.addChild(wrapper2);
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
Host host = new StandardHost();
host.addChild(context);
host.setName("localhost");
host.setAppBase("webapps");
Loader loader = new WebappLoader();
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
Engine engine = new StandardEngine();
engine.addChild(host);
engine.setDefaultHost("localhost");
connector.setContainer(engine);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) engine).start();
// make the application wait until we press a key.
System.in.read();
((Lifecycle) engine).stop();
}catch (Exception e) {
e.printStackTrace();
}
}
}
页:
[1]