How tomcat works——13 Host 和 Engine
2017-03-08 20:49
387 查看
概述
本章要讨论的2个主题是主机(host)和引擎(Engine)。如果需要在一个 Tomcat部署中部署多个上下文,需要使用一个主机。理论上,当只有一个上下文容器时不需要主机,正如下面 org.apache.catalina.Context 接口中描述:
“上下文容器的父容器通常是主机,但是可能有一些其它实现,没有必要的时候也可以忽略”
然而在实践中,一个 Tomcat 部署往往需要一个主机。至于为什么,将会在本章后面的“为什么需要Host” 一节中看到。
Engine表示整个 Catalina 的 Servlet 引擎。如果使用的话,它通常位于容器等级的最高层。可以添加到Engine上的子容器通常实现于org.apache.catalina.Host 或org.apache.catalina.Context。在一个 Tomcat 部署中,默认是使用引擎容器的。在该部署中,Engine有一个Host,默认主机。
本章讨论了跟 Host 和 Engine 接口相关的类。首先介绍了 Host 相关的StandardHost、StandardHostMapper(Tomcat4)以及 StandardHostValve 类。接下来是用一个Demo来示范演示了Host作为顶层容器的情况。Engine是本章讨论的第二个主题,介绍了StandardEngine和StandardEngineValve类。接下来是本章的第2个应用Demo,说明了如何将Engine作为顶层容器使用。
13.1 Host接口
主机是用 org.apache.catalina.Host 接口表示的。本接口继承了 Container 接口,如 Listing13.1 所示:
Listing 13.1: The Host interface
特别重要的是map ()方法,它返回合适的上下文来处理请求,该方法的实现可以在StandardHost 类中找到,将在下一小节中讨论。
13.2 StandardHost类
org.apache.catalina.core.StandardHost 类是对 Host 接口的标准实现。该类继承了 org.apache.catalina.core.ContainerBase 类并实现了 Host和Deployer 接口。Deployer 接口将在第 17 章讨论。
跟 StandardContext 和 StandardWrapper 类相似,StandardHost 类的构造函数在它的管道中添加一基础阀门:
如你所见,该基础阀门类型为 org.apache.catalina.core.StandardHostValve。
当启动时,如 start()方法被调用时,StandardHost 上面添加2个阀门:ErrorReportValve 和 ErrorDispatcherValve。它们都在org.apache.catalina.valves 包中。Tomcat4 中 StandardHost 的 start()方法如Listing13.2 所示:
Listing 13.2: The start method of StandardHost
注意:在 Tomcat5 中,start()方法相似,不同点在于包括了构建 JMX 对象的代码,JMX 将在第 20 章讨论。
errorReportValveClass 的值定义如下:
对于每一个请求,都会调用主机的 invoke()方法。由于 StanardHost 类并没有实现invoke()方法,所以会调用它的父类ContainerBase类的invoke()方法。该invoke()方法会转而调用 StandardHost 的基础阀门 StandardHostValve 的 invoke() 方法。StandardHostValve 阀门的 invoke()方法将在 “StandardHostValve类”小节中讨论。特别是StandardHostValve 的 invoke()方法调用 StandardHost 类的 map()方法来获得一个合适的上下文容器进行请求处理。StandardHost 的 map()方法如 Listing13.3 所示:
Listing 13.3: The map method in the StandardHost class
注意在 Tomcat4 中,ContainerBase 类也声明了一个 map()方法如下签名:
在 Tomcat4 中,StandardHostVavle 的 invoke()方法调用 ContainerBase 的 map()方法,它转而调用 StandardHost 的 map()方法。在 Tomcat5 中,没有映射器组件,适当的上下文将从请求对象中获得。
13.3 StandardHostMapper类
在 Tomcat4 中,StandardHost 的父类 ContainerBase 在start()方法中调用 addDefaultMapper()方法创建一个默认映射器。默认映射器的类型由 mapperClass 属性指定。如下是ContainerBase 的 addDefaulstMapper()方法:
StandardHost 定义 mapperClass 变量如下:
另外,StandardHost 类的 start()方法在它的最后调用super.start()来保证创建一个默认的映射器。
注意:Tomcat4 中的 standardContext 使用了略微不同的方法来创建一个默认映射器。它的 start() 方法中并没有调用 super.start()。相反 StandardContext 的start()方法调用 addDefaultMapper()来传递 mapperClass 变量。
StandardHostMapper 中最重要的是 map()方法,下面是它的实现:
注意,map() 方法仅仅是简单地调用了 Host 的 map()方法。
13.4 StandardHostValve类
org.apache.catalina.core.StandardHostValve 类是 StandardHost 的基础阀门。当有 HTTP 请求时会调用它的 invoke()方法,代码如Listing 13.4:
Listing 13.4: The invoke method of StandardHostValve
在 Tomcat4 中invoke()方法中调用 StandardHost 的map()方法来获得一个合适的上下文:
注意:在得到上下对象的时候需要一个往返过程。map() 方法接收2个参数,该方法是在 ContainerBase 中的。然后 ContainerBase 类又在它的子对象(这里是StandardHost对象)中查找合适的映射器并调用它的 map()方法。
invoke()方法解析得到一个 Session 对象并调用它的 access()方法更新它的最后进入访问时间。如下是org.apache.catalina.session.StandardSession 类中的 access() 方法实现:
最后,invoke()方法调用上下文容器的 invoke()方法,让上下文来处理请求。
13.5为什么需要Host
一Tomcat 部署必须有一个Host,如果该Context使用 ContextConfig 来配置。原因如下:
ContextConfig 需要应用文件 web.xml 的位置,它在applicationConfig()方法中尝试打开该文件,下面是该方法的片段:
Constants.ApplicationWebXml 定义的是 /WEB-INF/web.xml文件的相对地址,servletContext是一个 org.apache.catalina.core.ApplicationContext 类型的对象(它实现了javax.servlet.ServletContext)。
下面是 ApplicationContext 类中的 getResource()方法:
最后一行清楚表明context的父容器(host)是必须的,如果使用 ContextConfig来配置的话。在第 15 章中将会介绍如何解析 web.xml 文件。简单的说,除非自己写 ContextConfig 类,否则你必须有一个主机。
13.6 应用Demo1
本章第一个应用Demo演示了如何将一个主机作为顶层容器使用。该程序有2个类组成:ex13.pyrmont.core.SimpleContextConfig 和ex13.pyrmont.startup.Bootstrap1 类。SimpleContextConfig 类跟第 11 章中的相同,Boostrap2 类如 Listing13.5 所示:
Listing 13.5: The Bootstrap1 Class
13.6.1 运行Demo
在 Windows 下面可以在工作目录下输入如下命令运行该程序:
在 Linux 下面需要使用分号来分隔开两个库
使用如下 URL 可以调用 PrimitiveServlet和ModernServlet:
本章要讨论的2个主题是主机(host)和引擎(Engine)。如果需要在一个 Tomcat部署中部署多个上下文,需要使用一个主机。理论上,当只有一个上下文容器时不需要主机,正如下面 org.apache.catalina.Context 接口中描述:
“上下文容器的父容器通常是主机,但是可能有一些其它实现,没有必要的时候也可以忽略”
然而在实践中,一个 Tomcat 部署往往需要一个主机。至于为什么,将会在本章后面的“为什么需要Host” 一节中看到。
Engine表示整个 Catalina 的 Servlet 引擎。如果使用的话,它通常位于容器等级的最高层。可以添加到Engine上的子容器通常实现于org.apache.catalina.Host 或org.apache.catalina.Context。在一个 Tomcat 部署中,默认是使用引擎容器的。在该部署中,Engine有一个Host,默认主机。
本章讨论了跟 Host 和 Engine 接口相关的类。首先介绍了 Host 相关的StandardHost、StandardHostMapper(Tomcat4)以及 StandardHostValve 类。接下来是用一个Demo来示范演示了Host作为顶层容器的情况。Engine是本章讨论的第二个主题,介绍了StandardEngine和StandardEngineValve类。接下来是本章的第2个应用Demo,说明了如何将Engine作为顶层容器使用。
13.1 Host接口
主机是用 org.apache.catalina.Host 接口表示的。本接口继承了 Container 接口,如 Listing13.1 所示:
Listing 13.1: The Host interface
package org.apache.catalina; 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); }
特别重要的是map ()方法,它返回合适的上下文来处理请求,该方法的实现可以在StandardHost 类中找到,将在下一小节中讨论。
13.2 StandardHost类
org.apache.catalina.core.StandardHost 类是对 Host 接口的标准实现。该类继承了 org.apache.catalina.core.ContainerBase 类并实现了 Host和Deployer 接口。Deployer 接口将在第 17 章讨论。
跟 StandardContext 和 StandardWrapper 类相似,StandardHost 类的构造函数在它的管道中添加一基础阀门:
public StandardHost() { super(); pipeline.setBasic(new StandardHostValve()); }
如你所见,该基础阀门类型为 org.apache.catalina.core.StandardHostValve。
当启动时,如 start()方法被调用时,StandardHost 上面添加2个阀门:ErrorReportValve 和 ErrorDispatcherValve。它们都在org.apache.catalina.valves 包中。Tomcat4 中 StandardHost 的 start()方法如Listing13.2 所示:
Listing 13.2: The start method of StandardHost
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(); }
注意:在 Tomcat5 中,start()方法相似,不同点在于包括了构建 JMX 对象的代码,JMX 将在第 20 章讨论。
errorReportValveClass 的值定义如下:
private String errorReportValveClass ="org.apache.catalina.valves.ErrorReportValve";
对于每一个请求,都会调用主机的 invoke()方法。由于 StanardHost 类并没有实现invoke()方法,所以会调用它的父类ContainerBase类的invoke()方法。该invoke()方法会转而调用 StandardHost 的基础阀门 StandardHostValve 的 invoke() 方法。StandardHostValve 阀门的 invoke()方法将在 “StandardHostValve类”小节中讨论。特别是StandardHostValve 的 invoke()方法调用 StandardHost 类的 map()方法来获得一个合适的上下文容器进行请求处理。StandardHost 的 map()方法如 Listing13.3 所示:
Listing 13.3: The map method in the StandardHost class
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 中,ContainerBase 类也声明了一个 map()方法如下签名:
public Container map(Request request, boolean update);
在 Tomcat4 中,StandardHostVavle 的 invoke()方法调用 ContainerBase 的 map()方法,它转而调用 StandardHost 的 map()方法。在 Tomcat5 中,没有映射器组件,适当的上下文将从请求对象中获得。
13.3 StandardHostMapper类
在 Tomcat4 中,StandardHost 的父类 ContainerBase 在start()方法中调用 addDefaultMapper()方法创建一个默认映射器。默认映射器的类型由 mapperClass 属性指定。如下是ContainerBase 的 addDefaulstMapper()方法:
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); } }
StandardHost 定义 mapperClass 变量如下:
private String mapperClass = "org.apache.catalina.core.StandardHostMapper";
另外,StandardHost 类的 start()方法在它的最后调用super.start()来保证创建一个默认的映射器。
注意:Tomcat4 中的 standardContext 使用了略微不同的方法来创建一个默认映射器。它的 start() 方法中并没有调用 super.start()。相反 StandardContext 的start()方法调用 addDefaultMapper()来传递 mapperClass 变量。
StandardHostMapper 中最重要的是 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()方法。
13.4 StandardHostValve类
org.apache.catalina.core.StandardHostValve 类是 StandardHost 的基础阀门。当有 HTTP 请求时会调用它的 invoke()方法,代码如Listing 13.4:
Listing 13.4: The invoke method of StandardHostValve
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()方法来获得一个合适的上下文:
// Select the Context to be used for this Request StandardHost host = (StandardHost) getContainer(); Context context = (Context) host.map(request, true);
注意:在得到上下对象的时候需要一个往返过程。map() 方法接收2个参数,该方法是在 ContainerBase 中的。然后 ContainerBase 类又在它的子对象(这里是StandardHost对象)中查找合适的映射器并调用它的 map()方法。
invoke()方法解析得到一个 Session 对象并调用它的 access()方法更新它的最后进入访问时间。如下是org.apache.catalina.session.StandardSession 类中的 access() 方法实现:
public void access() { this.isNew = false; this.lastAccessedTime = this.thisAccessedTime; this.thisAccessedTime = System.currentTimeMillis(); }
最后,invoke()方法调用上下文容器的 invoke()方法,让上下文来处理请求。
13.5为什么需要Host
一Tomcat 部署必须有一个Host,如果该Context使用 ContextConfig 来配置。原因如下:
ContextConfig 需要应用文件 web.xml 的位置,它在applicationConfig()方法中尝试打开该文件,下面是该方法的片段:
synchronized (webDigester) { try { URL url = servletContext.getResource(Constants.ApplicationWebXml); InputSource is = new InputSource(url.toExternalForm()); is.setByteStream(stream); ... webDigester.parse(is); ...
Constants.ApplicationWebXml 定义的是 /WEB-INF/web.xml文件的相对地址,servletContext是一个 org.apache.catalina.core.ApplicationContext 类型的对象(它实现了javax.servlet.ServletContext)。
下面是 ApplicationContext 类中的 getResource()方法:
public URL getResource(String path)throws MalformedURLException { DirContext resources = context.getResources(); if (resources != null) { String fullPath = context.getName() + path; // this is the problem. Host must not be null String hostName = context.getParent().getName();
最后一行清楚表明context的父容器(host)是必须的,如果使用 ContextConfig来配置的话。在第 15 章中将会介绍如何解析 web.xml 文件。简单的说,除非自己写 ContextConfig 类,否则你必须有一个主机。
13.6 应用Demo1
本章第一个应用Demo演示了如何将一个主机作为顶层容器使用。该程序有2个类组成:ex13.pyrmont.core.SimpleContextConfig 和ex13.pyrmont.startup.Bootstrap1 类。SimpleContextConfig 类跟第 11 章中的相同,Boostrap2 类如 Listing13.5 所示:
Listing 13.5: The Bootstrap1 Class
package ex13.pyrmont.startup; //explain Host import ex13.pyrmont.core.SimpleContextConfig; import org.apache.catalina.Connector; import org.apache.catalina.Context; import org.apache.catalina.Host; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleListener; import org.apache.catalina.Loader; import org.apache.catalina.Wrapper; import org.apache.catalina.connector.http.HttpConnector; import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardHost; import org.apache.catalina.core.StandardWrapper; import org.apache.catalina.loader.WebappLoader; public final class Bootstrap1 { public static void main(String[] args) { //invoke: http://localhost:8080/app1/Primitive or http://localhost:8080/app1/Modern 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"); 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(); } } }
13.6.1 运行Demo
在 Windows 下面可以在工作目录下输入如下命令运行该程序:
java -classpath ./lib/servlet.jar;./lib/commons-collections.jar;./lib/commons—digester.jar;./ ex13.pyrmont.startup.Bootstrap1
在 Linux 下面需要使用分号来分隔开两个库
java -classpath ./lib/servlet.jar:./lib/commons-collections.jar:./lib/commons-digester.jar:./ ex13.pyrmont.startup.Bootstrap1
使用如下 URL 可以调用 PrimitiveServlet和ModernServlet:
http://localhost:8080/app1/Primitive http://localhost:8080/app1/Modern[/code]
13.7 Engine接口
org.apache.catalina.Engine 接口用来表示一个引擎。Engine表示整个 Catalina的 Servlet 引擎。当我们想要支持多个虚拟主机时,需要一个引擎,实际上Tomcat 部署正是使用了引擎。Engine 接口如 Listing 13.6 所示:
Listing 13.6: The Engine Interfacepublic 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); }
可以给引擎设置默认主机或上下文。注意引擎也跟服务(service)相关联。将在第 14 章介绍Services 相关内容。
13.8 StandardEngine类
类 org.apache.catalina.core.StandardEngine 是 Engine 接口的标准实现,跟StandardContext 和 StandardHost 相比,StandardEngine 类相对较小。初始化时,StandardEngine 类需要添加一个基础阀门,下面是该类构造函数:public StandardEngine() { super(); pipeline.setBasic(new StandardEngineValve()); }
作为顶层容器,StandardEngine 可以有子容器,它的子容器必须是主机(host)。如果你尝试给它添加一个非主机容器,会产生异常。如下是StandardEngine 类的 addChile()方法:public void addChild(Container child) { if (!(child instanceof Host)) throw new IllegalArgumentException(sm.getString("StandardEngine.notHost")); super.addChild(child); }
由于位于容器顶层,所以引擎不能有父容器,当你尝试给引擎设置父容器时会产生异常,下面是 StandardEngine 类的 setParent()方法:public void setParent(Container container) { throw new IllegalArgumentException(sm.getString("standardEngine.notParent")); }
13.9 StandardEngineValve类
org.apache.catalina.core.StandardEngineValve 是StandardEngine 的基础阀门,它的 invoke() 方法如 Listing13.7。
Listing 13.7: The invoke method of StandardEngineValvepublic 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); }
在验证了请求对象和响应对象之后,invoke()方法获得一个 Host 实例来处理请求。是通过调用引擎的 map()方法得到主机。一旦获得了一个主机,它的 invoke()方法将会被调用。
13.10 应用Demo2
本章的第二个应用Demo用于演示引擎作为顶层容器。该Demo使用了2个类,ex13.pyrmont.core.SimpleContextConfig 和ex13.pyrmont.startup.Bootstrap2 类。Bootstrap2 类如 Listing13.8 所示。
Listing 13.8: The Bootstrap2 classpackage ex13.pyrmont.startup; //Use engine import ex13.pyrmont.core.SimpleContextConfig; import org.apache.catalina.Connector; import org.apache.catalina.Context; import org.apache.catalina.Engine; import org.apache.catalina.Host; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleListener; import org.apache.catalina.Loader; import org.apache.catalina.Wrapper; import org.apache.catalina.connector.http.HttpConnector; import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardEngine; import org.apache.catalina.core.StandardHost; import org.apache.catalina.core.StandardWrapper; import org.apache.catalina.loader.WebappLoader; public final class Bootstrap2 { public static void main(String[] args) { //invoke: http://localhost:8080/app1/Primitive or http://localhost:8080/app1/Modern 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(); } } }
13.10.1 运行Demo
在 Windows 下面可以在工作目录下输入如下命令运行该程序:java -classpath ./lib/servlet.jar;./lib/commons-collections.jar;./lib/commons—digester.jar;./ ex13.pyrmont.startup.Bootstrap2
在 Linux 下面需要使用分号来分隔开两个库java -classpath ./lib/servlet.jar:./lib/commons-collections.jar:./lib/commons-digester.jar:./ ex13.pyrmont.startup.Bootstrap2
使用如下 URL 可以调用 PrimitiveServlet和ModernServlet:http://localhost:8080/app1/Primitive http://localhost:8080/app1/Modern[/code]
13.11小结
在本章中,我们讨论了两种类型的容器:主机(Host)和引擎(Engine)。本章还介绍了这两种容器的相关类。并且用2个Demo演示了如何让这两种容器作为顶层容器来工作。
相关文章推荐
- 《how tomcat work》 搬运工 Chapter 13: Host and Engine
- How Tomcat works之 Host and Engine
- How tomcat works 读书笔记十三 Host和Engine
- How tomcat works 读书笔记十三 Host和Engine
- How Tomcat Works 13
- Tomcat源码学习(13)-How Tomcat works(转)
- Tomcat源码学习(13)-How Tomcat works(转)
- tomcat(13)Host和Engine容器
- How Tomcat works 之第十三章之 StandardHostMapper
- 《How Tomcat Works》翻译(3)之Context容器
- How Tomcat Works(四)
- how tomcat works(第十章)
- How Tomcat works — 三、tomcat启动(2)
- how tomcat works 8 Loader
- How tomcat works——18 部署
- How a Regex Engine Works Internally
- How tomcat works——8 类加载器(Loader)
- How tomcat works 读书笔记十七 启动tomcat 下
- Tomcat源码学习(2)-How Tomcat works(转)
- How tomcat works——9 session管理