如果需要在一个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(); } } }