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(); 
		}
	}
}

猜你喜欢

转载自501565246-qq-com.iteye.com/blog/1727597