tomcat(13)Host和Engine容器

【0】README
0.1)本文部分文字描述转自 “how tomcat works”,旨在学习 “tomcat(13)Host和Engine容器” 的基础知识;
0.2)Host容器:若你想在同一个tomcat部署 上运行多个Context 容器的话,就需要使用 Host 容器;它是Context容器的父容器;(干货——若你想在同一个tomcat部署 上运行多个Context 容器的话,就需要使用 Host 容器,Host容器表示包含有一个或多个 Context容器的虚拟主机
0.3)Engine容器:表示Catalina的整个servlet 引擎。如果使用了Engine容器,那么它总是处于容器层级的最顶层;添加到Engine 容器中的子容器通常是 org.apache.catalina.Host的实现 。默认情况下,tomcat 会使用 Engine容器的,并且有一个Host容器作为其子容器;(干货——Engine表示Catalina的整个servlet 引擎,并且有一个Host容器作为其子容器

0.4)本文内容概览
step1)首先介绍Host接口及其 相关实现类 StandardHost类, StandardHostMapper 类 和 StandardHostValve类;
step2)接下来使用一个应用程序来说明如何使用Host实例作为一个顶层servlet容器;
step3)介绍Engine接口 及其 相关实现类 StandardEngine 和 StandardEngineValve类,并使用一个app 说明如何使用Engine实例作为顶层 servlet容器;
 0.5)for complete source code, please visit  https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter13

【1】Host接口(org.apache.catalina.Host)
1)源码如下  
public interface Host extends Container { // org.apache.catalina.Hostpublic 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);  // highlight line.public void removeAlias(String alias);
}
对以上代码的分析(Analysis):Host接口中比较重要的方法是 map()方法,该方法返回一个 用来处理引入的http 请求的Context容器的实例;具体实现在 StandardHost类中;

【2】StandardHost类
1)StandardHost的构造函数
public class StandardHost  extends ContainerBase implements Deployer, Host { //org.apache.catalina.core.StandardHostpublic StandardHost() {super();pipeline.setBasic(new StandardHostValve());}
2)调用其start()方法时,会添加两个阀,分别是ErrorReportValve类和ErrorDispatchValve类的实例。start()方法源代码如下:
public synchronized void start() throws LifecycleException {  //org.apache.catalina.core.StandardHost.start().// Set error report valveif ((errorReportValveClass != null)&& (!errorReportValveClass.equals(""))) {try {Valve valve = (Valve) Class.forName(errorReportValveClass).newInstance();addValve(valve); // highlight line.} catch (Throwable t) {log(sm.getString("standardHost.invalidErrorReportValveClass",errorReportValveClass));}}// Set dispatcher valveaddValve(new ErrorDispatcherValve()); // highlight line.super.start(); // highlight line.}
// private String errorReportValveClass =  "org.apache.catalina.valves.ErrorReportValve"; // defined in StandardHost
public synchronized void addValve(Valve valve) { //org.apache.catalina.core.ContainerBase.addValve().pipeline.addValve(valve);fireContainerEvent(ADD_VALVE_EVENT, valve);}
3)每当引入一个http请求时都会调用Host.invoke()方法:由于StandardHost类并没有invoke方法,故调用ContainerBase.invoke()方法,接着调用StandardHost实例的基础阀StandardHostValve.invoke()方法;而StandardHostValve.invoke()方法会调用 StandardHost.map()方法来获取相应的Context实例来处理http 请求。
4)StandardHost.map()方法源代码如下:
 public Context map(String uri) { //org.apache.catalina.core.StandardHost.map().if (debug > 0)log("Mapping request URI '" + uri + "'");if (uri == null)return (null);// Match on the longest possible context path prefixif (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 Contextif (context == null) {if (debug > 1)log("  Trying the default context");context = (Context) findChild("");}// Complain if no Context has been selectedif (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);}
Attention)在tomcat4中, ContainerBase类也有map()方法,而 ContainerBase.map()方法 会调用 StandardHost.map()方法;(而在tomcat5中,映射器组件已经移除,Context实例是通过request对象来获取的)

【3】StandardHostMapper类(org.apache.catalina.core.StandardHostMapper)
1)在tomcat4中, ContainerBase类(即StandardHost的父类)会调用其 addDefaultMapper() 方法创建一个默认映射器。默认映射器的类型由 mapperClass属性的值决定,ContainerBase.addDefaultMapper()方法的源代码如下: 
protected void addDefaultMapper(String mapperClass) { //org.apache.catalina.core.ContainerBase.addDefaultMapper().if (mapperClass == null)return;if (mappers.size() >= 1)return;// Instantiate and add a default Mappertry {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);}}
对上述代码的分析(Analysis):
A1)mapperClass的值定义在 StandardHost 类中:
private String mapperClass =  "org.apache.catalina.core.StandardHostMapper"; //defined in org.apache.catalina.core.StandardHost
A2)StandardHost.start()方法:会在方法末尾调用父类的 start()方法(ContainerBase.start()方法),确保默认映射器创建完成;
public synchronized void start() throws LifecycleException { //org.apache.catalina.core.ContainerBase.start().// Validate and update our current component stateif (started)throw new LifecycleException(sm.getString("containerBase.alreadyStarted", logName()));// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);addDefaultMapper(this.mapperClass);started = true;// Start our subordinate components, if anyif ((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 anyMapper mappers[] = findMappers();for (int i = 0; i < mappers.length; i++) {if (mappers[i] instanceof Lifecycle)((Lifecycle) mappers[i]).start();}// Start our child containers, if anyContainer 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 anyif (pipeline instanceof Lifecycle)((Lifecycle) pipeline).start();// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(START_EVENT, null);// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);}
2)StandardHostMapper类中最重要的方法是 map()方法,源代码如下:
      public Container map(Request request, boolean update) { //org.apache.catalina.core.StandardHostMapper.map()// Has this request already been mapped?if (update && (request.getContext() != null))return (request.getContext());// Perform mapping on our request URIString uri = ((HttpRequest) request).getDecodedRequestURI();Context context = host.map(uri); // highlight line.// Update the request (if requested) and return the selected Contextif (update) {request.setContext(context);if (context != null)((HttpRequest) request).setContextPath(context.getPath());else((HttpRequest) request).setContextPath(null);}return (context);}
Attention)StandardHostMapper.map()方法仅仅调用了 Host.map()方法;

【4】StandardHostValve类(org.apache.catalina.core.StandardHostValve)
1)StandardHostValve是StandardHost 的基础阀。当 有引入的http 请求时,会调用 StandardHostValve.invoke()方法对其进行处理; 
public void invoke(Request request, Response response,ValveContext valveContext)  //org.apache.catalina.core.StandardHostValve.invoke().throws IOException, ServletException {// Validate the request and response object typesif (!(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 RequestStandardHost host = (StandardHost) getContainer();Context context = (Context) host.map(request, true); // highlight line.if (context == null) {((HttpServletResponse) response.getResponse()).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,sm.getString("standardHost.noContext"));return;}// Bind the context CL to the current threadThread.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(); //highlight line.}}// Ask this Context to process this requestcontext.invoke(request, response);Thread.currentThread().setContextClassLoader(StandardHostValve.class.getClassLoader());}
对以上代码的分析(Analysis):
step1)在tomcat4中, 首先invoke()方法会调用 StandardHost.map() 方法来获取一个相应的 Context实例;
// Select the Context to be used for this RequestStandardHost host = (StandardHost) getContainer();Context context = (Context) host.map(request, true); // highlight line.
Attention)在获取Context实例的代码中有一个往复的过程。上面的map()方法需要两个参数,该方法定义在 ContainerBase类中。ContainerBase.map()方法会找到其子容器的映射器,并调用其map()方法;
step2)然后,invoke()方法会获取与该request对象相关联的session 对象,并调用其 access()方法。access()方法会修改 session对象的最后访问时间。
step3)org.apache.catalina.session.StandardSession.access()方法的源代码如下:
 public void access() {this.isNew = false;this.lastAccessedTime = this.lastUsedTime;this.lastUsedTime = System.currentTimeMillis();}
step4)最后StandardHostValve.invoke()方法调用Context实例的 invoke()来处理http 请求;

【5】为什么必须要有一个Host容器
1)在tomcat4 和 tomcat5的部署中,若一个Context实例使用ContextConfig对象进行设置,就必须使用一个 Host对象,原因如下(reasons):
r1)使用ContextConfig对象 需要知道应用程序 web.xml 文件的位置。在其 applicationConfig() 方法中,它会试图打开web.xml ;applicationConfig()方法的源码片段如下:
private void applicationConfig() { // org.apache.catalina.startup.ContextConfig.applicationConfig()// Open the application web.xml file, if it existsInputStream stream = null;ServletContext servletContext = context.getServletContext();if (servletContext != null)stream = servletContext.getResourceAsStream(Constants.ApplicationWebXml); //  public static final String ApplicationWebXml = "/WEB-INF/web.xml";if (stream == null) {log(sm.getString("contextConfig.applicationMissing"));return;}// Process the application web.xml filesynchronized (webDigester) { // highlight code begins.try {URL url =servletContext.getResource(Constants.ApplicationWebXml);InputSource is = new InputSource(url.toExternalForm());is.setByteStream(stream);webDigester.setDebug(getDebug());if (context instanceof StandardContext) {((StandardContext) context).setReplaceWelcomeFiles(true);}webDigester.clear();webDigester.push(context);webDigester.parse(is);webDigester.push(null);} catch (SAXParseException e) {log(sm.getString("contextConfig.applicationParse"), e);log(sm.getString("contextConfig.applicationPosition","" + e.getLineNumber(),"" + e.getColumnNumber()));ok = false;} catch (Exception e) {log(sm.getString("contextConfig.applicationParse"), e);ok = false;} finally {try {if (stream != null) {stream.close();}} catch (IOException e) {log(sm.getString("contextConfig.applicationClose"), e);}}        }    }<strong style="font-family: SimSun; background-color: rgb(255, 255, 255);"> </strong>
对以上代码的分析(Analysis):
A1)其中public static final String ApplicationWebXml = "/WEB-INF/web.xml";web.xml 文件的相对路径,servletContext是一个 org.apache.catalina.core.ApplicationContext类型(实现了 javax.servlet.servletContext接口)的对象;
A2)下面是 ApplicationContext.getResource()方法的部分实现代码:
public URL getResource(String path)  throws MalformedURLException { // org.apache.catalina.core.ApplicationContext.getResource(). path = normalize(path);if (path == null)return (null);DirContext resources = context.getResources();if (resources != null) {String fullPath = context.getName() + path;String hostName = context.getParent().getName(); //highlight line.try {resources.lookup(path);if( System.getSecurityManager() != null ) {try {PrivilegedGetResource dp =new PrivilegedGetResource(hostName, fullPath, resources);return (URL)AccessController.doPrivileged(dp);} catch( PrivilegedActionException pe) {throw pe.getException();}} else {return new URL("jndi", null, 0, getJNDIUri(hostName, fullPath),new DirContextURLStreamHandler(resources));}} catch (Exception e) {//e.printStackTrace();}}return (null);}
Attention)上述“highlight line 所标识的行表明了,如果要使用 ContextConfig实例来进行配置的话,Context实例必须有一个 Host实例作为其父容器,否则context.getParent().getName()会抛出异常导致获取资源文件(web.xml)的URL不成功;


【6】应用程序1(StandardHost测试用例)
6.1)源代码
public final class Bootstrap1 {public static void main(String[] args) {//invoke: http://localhost:8080/app1/Primitive or http://localhost:8080/app1/ModernSystem.setProperty("catalina.base", System.getProperty("user.dir"));Connector connector = new HttpConnector();Wrapper wrapper1 = new StandardWrapper();wrapper1.setName("Primitive");//wrapper1.setServletClass("servlet.PrimitiveServlet");wrapper1.setServletClass("PrimitiveServlet");Wrapper wrapper2 = new StandardWrapper();wrapper2.setName("Modern");//wrapper2.setServletClass("servlet.ModernServlet");wrapper2.setServletClass("ModernServlet");Context context = new StandardContext();// StandardContext's start method adds a default mappercontext.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(); // 与以往的Bootstrap.java不同的是,这里是host.start() 而不是 context.start()// make the application wait until we press a key.System.in.read();((Lifecycle) host).stop();}catch (Exception e) {e.printStackTrace();}}
}
对以上代码的分析(Analysis):
A1)本文结合 “tomcat(11)的Tomcat 处理http请求的方法调用协作图(以Context容器为起点)”总结了“《Tomcat 处理http请求的方法调用协作图(以Host容器为起点)》”,如下:


6.2)console info
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-common
jar;lib/commons-collections.jar;lib/naming-resources.jar;lib/commons-digester.jar;lib/catalina.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWo
ks\webroot com.tomcat.chapter13.startup.Bootstrap1
HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
WebappLoader[/app1]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\localhost\app1
StandardManager[/app1]: Seeding random number generator class java.security.SecureRandom
StandardManager[/app1]: Seeding of random number generator has been completed
StandardManager[/app1]: IOException while loading persisted sessions: java.io.EOFException
java.io.EOFException // <span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px;">这是从文件中加载 session对象到内存,由于没有相关文件,所以加载失败,抛出异常,但这不会影响我们访问servlet,大家不要惊慌;  </span><span style="margin: 0px; padding: 0px; border: none; font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px;">  </span>at java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardHost.start(StandardHost.java:738)at com.tomcat.chapter13.startup.Bootstrap1.main(Bootstrap1.java:59)
StandardManager[/app1]: Exception loading sessions from persistent storage
java.io.EOFExceptionat java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardHost.start(StandardHost.java:738)at com.tomcat.chapter13.startup.Bootstrap1.main(Bootstrap1.java:59)
ModernServlet -- init
StandardHost[localhost]: MAPPING configuration error for request URI /favicon.ico
6.3)访问效果


【7】Engine接口(org.apche.catalina.Engine)
1)intro to Engine:Engine容器是org.apche.catalina.Engine接口的实例。Engine容器也就是tomcat的servlet 引擎。当部署tomcat时 要支持多个虚拟机(Host)的话,就需要使用 Engine容器。一般 case下,部署的tomcat 都会使用一个 Engine容器;
2)org.apche.catalina.Engine定义如下:
public interface Engine extends Container {  public String getDefaultHost();  public void setDefaultHost(String defaultHost);  public String getJvmRoute();  public void setJvmRoute(String jvmRouteId);  public Service getService();  public void setService(Service service);   public void addDefaultContext(DefaultContext defaultContext);   public DefaultContext getDefaultContext();  public void importDefaultContext(Context context);
}
3)在Engine容器中,可以设置一个默认的Host容器或Context容器。注意,Engine容器可以与一个服务实例相关联;

【8】StandardEngine类(org.apache.catalina.core.StandardEngine)
1)其构造函数如下:
public class StandardEngine  extends ContainerBase implements Engine { public StandardEngine() { super();pipeline.setBasic(new StandardEngineValve());}
2)作为一个顶层容器,Engine容器可以有子容器,而它的子容器只能是Host容器;将一个非Host容器设置为其子容器,则抛出异常,参见StandardEngine.addChild(): 
public void addChild(Container child) { // org.apache.catalina.core.StandardEngine.addChild().if (!(child instanceof Host))throw new IllegalArgumentException(sm.getString("standardEngine.notHost"));super.addChild(child);}
3)当然了,由于Engine是顶层容器,它就不能再有父容器了,若调用 StandardEngine.setParent方法为其设置父容器时,抛出异常:
 public void setParent(Container container) {   // org.apache.catalina.core.StandardEngine.setParent().throw new IllegalArgumentException(sm.getString("standardEngine.notParent"));}
【9】StandardEngineValve类(org.apache.catalina.core.StandardEngineValve)
1)StandardEngineValve:是 StandardEngine容器的基础阀;
2)StandardEngineValve.invoke()方法如下:
 public void invoke(Request request, Response response,ValveContext valveContext) // org.apache.catalina.core.StandardEngineValve.invoke().throws IOException, ServletException {// Validate the request and response object typesif (!(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 headerHttpServletRequest 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 RequestStandardEngine 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 requesthost.invoke(request, response);}
对以上代码的分析(Analysis):
step1)在验证了request和response对象的类型后,invoke()方法得到Host实例,用于处理该请求;
step2)invoke()方法会通过调用Engine实例的map()方法获取Host对象;
step3)得到Host对象后,调用其invoke() 方法处理请求;

【10】应用程序2(StandardEngine测试用例)
1)源代码:
public final class Bootstrap2 {public static void main(String[] args) {//invoke: http://localhost:8080/app1/Primitive or http://localhost:8080/app1/ModernSystem.setProperty("catalina.base", System.getProperty("user.dir"));Connector connector = new HttpConnector();Wrapper wrapper1 = new StandardWrapper();wrapper1.setName("Primitive");//wrapper1.setServletClass("servlet.PrimitiveServlet");wrapper1.setServletClass("PrimitiveServlet");Wrapper wrapper2 = new StandardWrapper();wrapper2.setName("Modern");//wrapper2.setServletClass("servlet.ModernServlet");wrapper2.setServletClass("ModernServlet");Context context = new StandardContext();// StandardContext's start method adds a default mappercontext.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(); // 与以往不同的是,这里是engine.start()而不是 context.start(),因为最上层容易已经改变了,而enginge包含host,host包含context,context包含wrapper.}catch (Exception e) {e.printStackTrace();}}
}
对以上代码的分析(Analysis):
A1)同理,本小节也总结了 “Tomcat 处理http请求的方法调用协作图(以Engine容器为起点)”,如下:

A2)对上面的协作图的调用steps做分析:
step1)当http 请求到达 tomcat server时,HttpConnector得到 ServerSocket((accept()方法)),然后调用最上层容器的invoke()方法,而所有容器都继承自ContainerBase,所以实际上调用的是 ContainerBase.invoke()方法;
step2)之后调用管道StandardPipeline.invoke()方法,进而调用管道的非基础阀和基础阀的invoke方法(而基础阀在容器的构造器中设置了);(干货——所有容器都继承自ContainerBase,而管道StandardPipeline是在 ContainerBase中创建的,所以所有容器都共用同一个管道对象,而当调用到某容器的时候,设置其对应的基础阀即可)
step3)基础阀的invoke()方法会调用下一层级容器的invoke方法,接着继续调用管道StandardPipeline.invoke()方法,一直进行下去(回到step2的过程).......直到到达Wrapper容器(因为它是最小的容器,是一个具体servlet的封装);
step4)Wrapper.invoke()方法,同样也要调用其管道StandardPipeline.invoke()方法,接着调用基础阀StandardWrapperValve,与其他基础阀不同的是,StandardWrapperValve.invoke()方法会调用 ApplicationFilterChain.doFilter()方法,接着调用具体的HttpServlet.allocate()方法和service()方法,写出响应info(html)到client,ending(Bingo)

2)console info
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-common.
jar;lib/commons-collections.jar;lib/naming-resources.jar;lib/commons-digester.jar;lib/catalina.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWok
s\webroot com.tomcat.chapter13.startup.Bootstrap2
HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
Apache Tomcat/4.1.24
WebappLoader[/app1]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\localhost\app1
WebappLoader[/app1]: Deploy class files /WEB-INF/classes to E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\webapps\app1\WEB-INF\classes
StandardManager[/app1]: Seeding random number generator class java.security.SecureRandom
StandardManager[/app1]: Seeding of random number generator has been completed
StandardManager[/app1]: IOException while loading persisted sessions: java.io.EOFException
java.io.EOFExceptionat java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardHost.start(StandardHost.java:738)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:347)at com.tomcat.chapter13.startup.Bootstrap2.main(Bootstrap2.java:67)
StandardManager[/app1]: Exception loading sessions from persistent storage
java.io.EOFExceptionat java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardHost.start(StandardHost.java:738)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:347)at com.tomcat.chapter13.startup.Bootstrap2.main(Bootstrap2.java:67)
ModernServlet -- init
StandardHost[localhost]: MAPPING configuration error for request URI /favicon.ico
StandardHost[localhost]: MAPPING configuration error for request URI /favicon.ico
3)访问效果



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331174.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring react_使用Spring WebFlux构建React性REST API –第2部分

spring react在上一篇文章的续篇中&#xff0c;我们将看到React式流规范及其实现之一&#xff0c;称为Project Reactor 。 React流规范定义了以下接口。 让我们看看这些接口的细节。 发布者→发布者是潜在数量不受限制的序列元素的提供者&#xff0c;可按其订阅者的要求发布它…

tomcat(14)服务器组件和服务组件

【0】README0.1&#xff09;本文部分文字描述转自 “how tomcat works”&#xff0c;旨在学习 “tomcat(14)服务器组件和服务组件” 的基础知识&#xff1b;0.2&#xff09;for complete source code ,please visit https://github.com/pacosonTang/HowTomcatWorks/tree/maste…

组合模式的安全模式与透明模式

转载自 树形结构的处理——组合模式&#xff08;四&#xff09; 1 透明组合模式与安全组合模式 通过引入组合模式&#xff0c;Sunny公司设计的杀毒软件具有良好的可扩展性&#xff0c;在增加新的文件类型时&#xff0c;无须修改现有类库代码&#xff0c;只需增加一个新的文件类…

spring react_使用Spring WebFlux构建React性REST API –第1部分

spring react在本文中&#xff0c;我们将看到如何使用Spring WebFlux构建响应式REST API。 在进入React式API之前&#xff0c;让我们看看系统是如何发展的&#xff0c;传统REST实现遇到的问题以及现代API的需求。 如果您查看从旧版系统到下文所述的现代系统的期望&#xff0c;…

tomcat(15)Digester库

【0】README0.1&#xff09;本文部分文字描述转自 “how tomcat works”&#xff0c;旨在学习 “tomcat(15)Digester库” 的基础知识&#xff1b;2&#xff09;problemsolution&#xff1a;2.1&#xff09;problem&#xff1a;如下面的代码&#xff0c;Bootstrap类实例化连接器…

api restful_HATEOAS的RESTful服务。 记录超媒体API

api restful1.简介 希望本教程的前一部分不仅揭示了超媒体和HATEOAS的深远意义&#xff0c;而且使我们确信这些都是RESTful Web服务和API的基本构建模块。 在这一部分中&#xff0c;我们将继续侧重于文档方面&#xff0c;以解决如何预先传递Web服务或API功能的问题。 目录 1.…

模版方法模式

转载自 23种设计模式&#xff08;6&#xff09;&#xff1a;模版方法模式定义&#xff1a;定义一个操作中算法的框架&#xff0c;而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变算法的结构即可重定义该算法中的某些特定步骤。 类型&#xff1a;行为类模式 类图&#…

java并发编程实践(1)intro

【0】README0.1&#xff09;本文部分文字描述转自“java并发编程实践”&#xff0c;旨在学习“java并发编程实践(1)intro”的相关知识&#xff1b;【3】线程带来的风险【3.1】安全性问题1&#xff09;intro&#xff1a;在没有充足同步的case下&#xff0c;多个线程的操作执行顺…

读入的字节都写入字节数组中_使用Java将文件读入字节数组的7个示例

读入的字节都写入字节数组中嗨&#xff0c;大家好&#xff0c;Java程序员经常在现实世界中遇到编程问题&#xff0c;他们需要将文件中的数据加载到字节数组中&#xff0c;该文件可以是文本文件或二进制文件。 一个示例是将文件的内容转换为String以显示。 不幸的是&#xff0c;…

迭代子模式

转载自 《JAVA与模式》之迭代子模式 在阎宏博士的《JAVA与模式》一书中开头是这样描述迭代子&#xff08;Iterator&#xff09;模式的&#xff1a; 迭代子模式又叫游标(Cursor)模式&#xff0c;是对象的行为模式。迭代子模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部…

java并发编程实践(2)线程安全性

【0】README 0.0&#xff09;本文部分文字描述转自&#xff1a;“java并发编程实战”&#xff0c; 旨在学习“java并发编程实践(2)线程安全性” 的相关知识&#xff1b;0.1&#xff09;几个术语&#xff08;terms&#xff09;t1&#xff09;对象的状态&#xff1a;是指存储在状…

自动配置jdk_JDK 15中自动自动发送更好的NullPointerException消息

自动配置jdk我讨论了期待已久的和高度赞赏改进的NullPointerException &#xff08; NPE在帖&#xff09;消息“ 更好的默认NullPointerException异常消息即将到Java&#xff1f; ”和“ JDK 14中更好的NPE消息 ”。 当此JEP 358驱动的功能添加到JDK 14中时 &#xff0c;想要从…

tomat(16)关闭钩子

【0】REAMDE0&#xff09;本文部分文字描述转自&#xff1a;“how tomcat works”&#xff0c;旨在学习“tomat(16)关闭钩子”的相关知识&#xff1b;1&#xff09;problemsolution&#xff1a;1.1&#xff09;problem&#xff1a;在很多实际环境中&#xff0c;当用户关闭应用程…

apache.camel_Apache Camel 3.1 – XML路由的快速加载

apache.camelCamel 3.1中添加的一项功能是能够更快地加载XML路由。 这是我们为使Camel变得更小&#xff0c;更快而进行的总体工作的一部分。 您可能会说ewww XML。 但坦率地说&#xff0c;有很多Camel用户使用XML定义路由来构建应用程序。 在Camel 2.x中&#xff0c;您将不得不…

程序中 设置jvm 参数_高效应用程序的7个JVM参数

程序中 设置jvm 参数在撰写本文时&#xff08;2020年3月&#xff09;&#xff0c;围绕垃圾收集和内存&#xff0c;您可以将600多个参数传递给JVM。 如果您包括其他方面&#xff0c;则JVM参数总数将很容易超过1000个。 &#x1f60a;。 任何人都无法消化和理解太多的论据。 在本…

tomcat(17)启动tomcat

【0】README 1&#xff09;本文部分文字描述转自“how tomcat works”&#xff0c;旨在学习“tomcat(17)启动tomcat”的相关知识&#xff1b;2&#xff09;本文重点关注启动Tomcat时会用到的两个类&#xff0c;分别是Catalina类和 Bootstrap类&#xff1b;&#xff08;干货——…

八爪鱼 是java做的吗_章鱼扫描仪:Java构建工具和恶意软件

八爪鱼 是java做的吗Alvaro Munoz最近在GitHub Security Lab网站上发布了“ Octopus扫描程序恶意软件&#xff1a;攻击开源供应链 ”。 我发现这篇文章很有趣&#xff0c;原因有很多&#xff0c;其中包括对Octopus Scanner恶意软件的工作原理和发现方式的详细介绍&#xff0c; …

spring(1)Spring之旅

【0】README0.1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在reviewSpring(1)Spring之旅 的相关知识&#xff1b;【1】简化java 开发1&#xff09;为了降低java开发的复杂性&#xff0c;Spring采取了以…

apache.camel_Apache Camel K 1.0在这里–您为什么要关心

apache.camel昨天我们发布了Apache Camel K 1.0&#xff0c;并在社交媒体和Camel网站上宣布了该版本。 那么&#xff0c;骆驼K是什么&#xff0c;为什么你要在乎呢&#xff1f; 这是一个很好的问题&#xff0c;我想通过提及伟大的思想来帮助回答这个问题。 Hugo Guerrero发表…

DRF之初识

目录 一、序列化和反序列化 【1】序列化 【2】反序列化 【3】小结 二、DRF的安装和快速使用 (1) 安装DRF&#xff1a; (2) 配置DRF&#xff1a; (3) 创建序列化器(Serializer)&#xff1a; (4) 创建视图(View)&#xff1a; (5) 配置URL路由&#xff1a; 【补充】下载…