tomcat(18)部署器

【0】README
-1)先上干货:本文重点分析了tomcat 如何部署WAR文件的项目形式 以及 普通文件夹的项目形式;不管是WAR文件 还是 普通文件夹的项目形式,在tomcat中,它们都是Context容器;(Bingo)
0)本文部分文字描述转自“how tomcat works”,旨在学习“tomcat(18)部署器”的相关知识;
1)intro:要使用一个web 应用程序,必须要将表示该应用程序的 Context实例部署到一个Host 实例中;(干货——要使用一个web 应用程序,必须要将表示该应用程序的 Context实例部署到一个Host 实例中
2)在tomcat中的部署方式:Context实例可以用WAR 文件的形式来部署,也可以将整个web 应用程序copy 到 tomcat安装目录下的 webapp 下;
Supplement) 在tomcat4 和 tomcat5中是,使用了两个应用程序来管理tomcat 和 部署tomcat中的 web应用程序,分别是 manager 和 admin 应用程序;
s1)这两个应用程序所使用到的类文件都位于 %CATALINA_HOME%/server/webapps 目录下,并且分别使用了两个描述符文件:manager.xml and admin.xml;(干货——描述符文件:manager.xml and admin.xml
s2)在tomcat4中, 在%CATALINA_HOME%/server/webapps 目录下,有3个描述符文件;
s3)在tomcat5中,分别位于 %CATALINA_HOME%/server/webapps/admin 目录 和 %CATALINA_HOME%/server/webapps/manager 目录下;
3)intro to 部署器:部署器是 org.apache.catalina.Deployer 接口的实例,部署器是与一个Host容器相关联, 用来安装 Context实例;
3.1)安装Context实例的意思是:创建一个 StandardContext实例,将该实例添加到Host实例中;创建的Context实例会随其父容器——Host实例一起启动。
3.2)部署器也可以单独地启动和关闭Context实例;
public final class Bootstrap {  public static void main(String[] args) {System.setProperty("catalina.base", System.getProperty("user.dir"));Connector connector = new HttpConnector();Context context = new StandardContext();// StandardContext's start method adds a default mappercontext.setPath("/app1");context.setDocBase("app1");LifecycleListener listener = new ContextConfig();((Lifecycle) context).addLifecycleListener(listener);Host host = new StandardHost();host.addChild(context); //highlight line.将StandardContext实例添加到 Host实例中.host.setName("localhost");host.setAppBase("webapps");Loader loader = new WebappLoader();context.setLoader(loader);connector.setContainer(host);try {connector.initialize();((Lifecycle) connector).start();((Lifecycle) host).start();Container[] c = context.findChildren();int length = c.length;for (int i=0; i<length; i++) {Container child = c[i];System.out.println(child.getName());}// make the application wait until we press a key.System.in.read();((Lifecycle) host).stop();}catch (Exception e) {e.printStackTrace();}}
}
【1】 部署一个web 应用程序
1)在实际环境中,如何将Context实例添加到 Host容器呢?答案在于:StandardHost实例中使用的org.apache.catalina.startup.HostConfig 类的 生命周期监听器;(干货——HostConfig 类似于StandardContext的ContextConfig监听器)
2)org.apache.catalina.startrup.Catalina类是启动类,使用Digester对象来解析server.xml文件,将其中的XML 元素 转换为 java对象:Catalina类定义了 createStartDigester()方法来为 Digester对象添加规则。createStartDigester()方法中的其中一行如下所示:
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
对以上代码的分析(Analysis):HostRuleSet extends RuleSetBase类,HostRuleSet必须提供addRuleInstance()方法的实现,需要在该方法中定义 RuleSet的规则;下面是 HostRuleSet.addRuleInstance()方法:(因为父类 RuleSetBase 实现了RuleSet接口,提供了getNamespaceURI的具体实现,而addRuleInstances()方法声明为了抽象方法,具体参见 tomcat(15)Digester库 中章节“【1.5】”)
public void addRuleInstances(Digester digester) { //org.apache.catalina.startup.HosRuleSet.addRuleInstances().digester.addObjectCreate(prefix + "Host","org.apache.catalina.core.StandardHost","className");digester.addSetProperties(prefix + "Host");digester.addRule(prefix + "Host",new CopyParentClassLoaderRule(digester));digester.addRule(prefix + "Host",new LifecycleListenerRule(digester,"org.apache.catalina.startup.HostConfig", // highlight line."hostConfigClass"));//......}
对以上代码的分析(Analysis):
A1)当 在 server.xml 文件中遇到符合 "Server/Service/Engine/Host" 模式的标签时:会创建 org.apache.catalina.startup.HostConfig 类的一个实例,并将其添加到 Host实例中,作为生命周期监听器;

A2)that's to say, HostConfig 类会处理 StandardHost.start()方法 和 stop()方法的触发事件;
<?xml version='1.0' encoding='utf-8'?> <!--conf/server.xml源码如下 -->
<!--Licensed to the Apache Software Foundation (ASF) under one or morecontributor license agreements.  See the NOTICE file distributed withthis work for additional information regarding copyright ownership.The ASF licenses this file to You under the Apache License, Version 2.0(the "License"); you may not use this file except in compliance withthe License.  You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.
-->
<!-- Note:  A "Server" is not itself a "Container", so you may notdefine subcomponents such as "Valves" at this level.Documentation at /docs/config/server.html-->
<Server port="8005" shutdown="SHUTDOWN"><!-- Security listener. Documentation at /docs/config/listeners.html<Listener className="org.apache.catalina.security.SecurityListener" />--><!--APR library loader. Documentation at /docs/apr.html --><Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /><!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html --><Listener className="org.apache.catalina.core.JasperListener" /><!-- Prevent memory leaks due to use of particular java/javax APIs--><Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /><Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /><Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /><!-- Global JNDI resourcesDocumentation at /docs/jndi-resources-howto.html--><GlobalNamingResources><!-- Editable user database that can also be used byUserDatabaseRealm to authenticate users--><Resource name="UserDatabase" auth="Container"type="org.apache.catalina.UserDatabase"description="User database that can be updated and saved"factory="org.apache.catalina.users.MemoryUserDatabaseFactory"pathname="conf/tomcat-users.xml" /></GlobalNamingResources><!-- A "Service" is a collection of one or more "Connectors" that sharea single "Container" Note:  A "Service" is not itself a "Container",so you may not define subcomponents such as "Valves" at this level.Documentation at /docs/config/service.html--><Service name="Catalina"><!--The connectors can use a shared executor, you can define one or more named thread pools--><!--<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"maxThreads="150" minSpareThreads="4"/>--><!-- A "Connector" represents an endpoint by which requests are receivedand responses are returned. Documentation at :Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)Java AJP  Connector: /docs/config/ajp.htmlAPR (HTTP/AJP) Connector: /docs/apr.htmlDefine a non-SSL HTTP/1.1 Connector on port 8080--><Connector port="8888" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" /><!-- A "Connector" using the shared thread pool--><!--<Connector executor="tomcatThreadPool"port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />--><!-- Define a SSL HTTP/1.1 Connector on port 8443This connector uses the JSSE configuration, when using APR, theconnector should be using the OpenSSL style configurationdescribed in the APR documentation --><!--<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"maxThreads="150" scheme="https" secure="true"clientAuth="false" sslProtocol="TLS" />--><!-- Define an AJP 1.3 Connector on port 8009 --><Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /><!-- An Engine represents the entry point (within Catalina) that processesevery request.  The Engine implementation for Tomcat stand aloneanalyzes the HTTP headers included with the request, and passes themon to the appropriate Host (virtual host).Documentation at /docs/config/engine.html --><!-- You should set jvmRoute to support load-balancing via AJP ie :<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">--><Engine name="Catalina" defaultHost="localhost"><!--For clustering, please take a look at documentation at:/docs/cluster-howto.html  (simple how to)/docs/config/cluster.html (reference documentation) --><!--<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>--><!-- Use the LockOutRealm to prevent attempts to guess user passwordsvia a brute-force attack --><Realm className="org.apache.catalina.realm.LockOutRealm"><!-- This Realm uses the UserDatabase configured in the global JNDIresources under the key "UserDatabase".  Any editsthat are performed against this UserDatabase are immediatelyavailable for use by the Realm.  --><Realm className="org.apache.catalina.realm.UserDatabaseRealm"resourceName="UserDatabase"/></Realm><Host name="localhost"  appBase="webapps"unpackWARs="true" autoDeploy="true"><!-- SingleSignOn valve, share authentication between web applicationsDocumentation at: /docs/config/valve.html --><!--<Valve className="org.apache.catalina.authenticator.SingleSignOn" />--><!-- Access log processes all example.Documentation at: /docs/config/valve.htmlNote: The pattern used is equivalent to using pattern="common" --><Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"prefix="localhost_access_log." suffix=".txt"pattern="%h %l %u %t "%r" %s %b" /></Host></Engine></Service>
</Server>

Attention)不要问我,为什么StandardHost的生命周期监听器是 HostConfig?详情参见 tomcat(17)启动tomcat,结合org.apache.catalina.startup.Catalina.createStartDigester() 所设定的规则 和 conf/server.xml 的代码,你就懂的。

3)HostConfig.lifecycleEvent()方法的源代码如下:因为HostConifg的实例是 StandardHost实例的监听器,每当StandardHost实例启动或关闭时,都会调用 lifecycleEvent()方法;
 public void lifecycleEvent(LifecycleEvent event) { //org.apache.catalina.startup.HostConfig.lifecycleEvent().// Identify the host we are associated withtry {host = (Host) event.getLifecycle();if (host instanceof StandardHost) {int hostDebug = ((StandardHost) host).getDebug();if (hostDebug > this.debug) {this.debug = hostDebug;}setDeployXML(((StandardHost) host).isDeployXML());setLiveDeploy(((StandardHost) host).getLiveDeploy());setUnpackWARs(((StandardHost) host).isUnpackWARs());}} catch (ClassCastException e) {log(sm.getString("hostConfig.cce", event.getLifecycle()), e);return;}// Process the event that has occurredif (event.getType().equals(Lifecycle.START_EVENT))start(); // highlight line.else if (event.getType().equals(Lifecycle.STOP_EVENT))stop(); // highlight line.}
对以上代码的分析(Analysis):如果变量host是 StandardHost的实例,则调用 setDeployXML方法,setLiveDeploy方法,setUnpackWARs方法;
A1)setDeployXML方法:指明了Host实例是否需要部署一个 Context实例的描述符文件;
 public void setDeployXML(boolean deployXML) {this.deployXML= deployXML;}
A2)setLiveDeploy方法:指明了Host实例 是否需要周期性检查一个新的 部署;
public void setLiveDeploy(boolean liveDeploy) {this.liveDeploy = liveDeploy;}
A3)setUnpackWARs方法:指定是要将WAR 文件形式的web 应用程序解压缩;
 public void setUnpackWARs(boolean unpackWARs) {this.unpackWARs = unpackWARs;}
4)lifecycleEvent方法 调用start()方法;
 protected void start() { //org.apache.catalina.startup.HostConfig.start().if (debug >= 1)log(sm.getString("hostConfig.start"));if (host.getAutoDeploy()) {deployApps();}if (isLiveDeploy()) {threadStart();}}    protected void stop() {if (debug >= 1)log(sm.getString("hostConfig.stop"));threadStop();undeployApps();}
对以上代码的分析(Analysis):
A1)若autoDeploy为true时: start()方法调用deployApps()方法;
protected void deployApps() {if (!(host instanceof Deployer))return;if (debug >= 1)log(sm.getString("hostConfig.deploying"));File appBase = appBase(); // highlight line.if (!appBase.exists() || !appBase.isDirectory())return;String files[] = appBase.list();deployDescriptors(appBase, files);deployWARs(appBase, files);deployDirectories(appBase, files);}
 protected File appBase() {File file = new File(host.getAppBase());if (!file.isAbsolute())file = new File(System.getProperty("catalina.base"),host.getAppBase());return (file);}
A2)若liveDeploy为true时:start()方法调用threadStart()方法;

5)deployApps方法源代码如下:
protected void deployApps() {  //org.apache.catalina.startup.HostConfig.deployApps().if (!(host instanceof Deployer))return;if (debug >= 1)log(sm.getString("hostConfig.deploying"));File appBase = appBase();if (!appBase.exists() || !appBase.isDirectory())return;String files[] = appBase.list();deployDescriptors(appBase, files); //highlight line.deployWARs(appBase, files); //highlight line.deployDirectories(appBase, files); //highlight line.}
对以上代码的分析(Analysis):
A1)该方法会获取host的实例的appBase属性的值,默认为 webapps 的值(参见 server.xml);部署进程会将 %CATALINA_HOME%/webapps 目录下的所有目录都看做是 web 应用程序的目录来执行部署工作。此外,该目录中所有的WAR 文件和描述符文件也都会进行部署;(干货——想想以前总是要把项目打个war 包,放到webapps 目录下,我在这里找到了答案。)
A2)deployApps()方法会调用其他3个方法:deployDescriptors方法,deployWARs方法,deployDirectories方法;
A2.1)deployDescriptors方法:
protected void deployDescriptors(File appBase, String[] files) {if (!deployXML)return;for (int i = 0; i < files.length; i++) {if (files[i].equalsIgnoreCase("META-INF"))continue;if (files[i].equalsIgnoreCase("WEB-INF"))continue;if (deployed.contains(files[i]))continue;File dir = new File(appBase, files[i]);if (files[i].toLowerCase().endsWith(".xml")) {deployed.add(files[i]);// Calculate the context path and make sure it is uniqueString file = files[i].substring(0, files[i].length() - 4);String contextPath = "/" + file;if (file.equals("ROOT")) {contextPath = "";}if (host.findChild(contextPath) != null) {continue;}// Assume this is a configuration descriptor and deploy itlog(sm.getString("hostConfig.deployDescriptor", files[i]));try {URL config =new URL("file", null, dir.getCanonicalPath());((Deployer) host).install(config, null);} catch (Throwable t) {log(sm.getString("hostConfig.deployDescriptor.error",files[i]), t);}}}}
A2.2)deployWARs方法:
protected void deployWARs(File appBase, String[] files) {for (int i = 0; i < files.length; i++) {if (files[i].equalsIgnoreCase("META-INF"))continue;if (files[i].equalsIgnoreCase("WEB-INF"))continue;if (deployed.contains(files[i]))continue;File dir = new File(appBase, files[i]);if (files[i].toLowerCase().endsWith(".war")) {deployed.add(files[i]);// Calculate the context path and make sure it is uniqueString contextPath = "/" + files[i];int period = contextPath.lastIndexOf(".");if (period >= 0)contextPath = contextPath.substring(0, period);if (contextPath.equals("/ROOT"))contextPath = "";if (host.findChild(contextPath) != null)continue;if (isUnpackWARs()) {// Expand and deploy this application as a directorylog(sm.getString("hostConfig.expand", files[i]));try {URL url = new URL("jar:file:" +dir.getCanonicalPath() + "!/");String path = ExpandWar.expand(host,url);url = new URL("file:" + path);((Deployer) host).install(contextPath, url);} catch (Throwable t) {log(sm.getString("hostConfig.expand.error", files[i]),t);}} else {// Deploy the application in this WAR filelog(sm.getString("hostConfig.deployJar", files[i]));try {URL url = new URL("file", null,dir.getCanonicalPath());url = new URL("jar:" + url.toString() + "!/");((Deployer) host).install(contextPath, url);} catch (Throwable t) {log(sm.getString("hostConfig.deployJar.error",files[i]), t);}}}}}
A2.3)deployDirectories方法:
protected void deployDirectories(File appBase, String[] files) {for (int i = 0; i < files.length; i++) {if (files[i].equalsIgnoreCase("META-INF"))continue;if (files[i].equalsIgnoreCase("WEB-INF"))continue;if (deployed.contains(files[i]))continue;File dir = new File(appBase, files[i]);if (dir.isDirectory()) {deployed.add(files[i]);                File webInf = new File(dir, "/WEB-INF");if (!webInf.exists() || !webInf.isDirectory() ||!webInf.canRead())continue;// Calculate the context path and make sure it is uniqueString contextPath = "/" + files[i];if (files[i].equals("ROOT"))contextPath = "";if (host.findChild(contextPath) != null)continue;// Deploy the application in this directorylog(sm.getString("hostConfig.deployDir", files[i]));try {URL url = new URL("file", null, dir.getCanonicalPath());((Deployer) host).install(contextPath, url);} catch (Throwable t) {log(sm.getString("hostConfig.deployDir.error", files[i]),t);}}}}


【1.1】 部署一个描述符
1)可以编写一个  XML 文件来描述 Context对象;如,在tomcat4和tomcat5中的admin 和 manager 应用中就分别使用了如下两个XML 文件;(tomcat distribution list http://archive.apache.org/dist/tomcat/)
<!-- tomcat4下的admin.xml 文件(\container\webapps\admin)-->
<Context path="/admin" docBase="../server/webapps/admin"debug="0" privileged="true"><!-- Uncomment this Valve to limit access to the Admin app to localhostfor obvious security reasons. Allow may be a comma-separated list ofhosts (or even regular expressions).<Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="127.0.0.1"/>--><Logger className="org.apache.catalina.logger.FileLogger"prefix="localhost_admin_log." suffix=".txt"timestamp="true"/>
</Context>
<!-- tomcat5下的 manager.xml 文件(\container\webapps\manager -->
<Context docBase="${catalina.home}/server/webapps/manager"privileged="true" antiResourceLocking="false" antiJARLocking="false" useHttpOnly="true"><!-- Link to the user database we will get roles from --><ResourceLink name="users" global="UserDatabase"type="org.apache.catalina.UserDatabase"/>
</Context>
Attention)这两个描述符都有一个Context 元素。Context元素中的 docBase属性的值分别为 %CATALINA_HOME%/server/webapps/admin and %CATALINA_HOME%/server/webapps/manager,这表明,admin 应用程序和manager应用程序并没有部署到默认的地方;

2)HostConfig类使用了 deployDescriptor()方法来部署XML 文件。在tomcat4中, 这些文件位于 %CATALINA_HOME%/webapps 目录下;在tomcat5中, 位于 %CATALINA_HOME%/server/webapps 子目录下;


【1.2】部署一个WAR文件
1)intro:可以将web 应用程序以一个 WAR形式的文件来部署。HostConfig.deployWARs()方法 将位于 %CATALINA_HOME%/webapps 目录下的任何WAR文件进行部署;
protected void deployWARs(File appBase, String[] files) {for (int i = 0; i < files.length; i++) {if (files[i].equalsIgnoreCase("META-INF"))continue;if (files[i].equalsIgnoreCase("WEB-INF"))continue;if (deployed.contains(files[i]))continue;File dir = new File(appBase, files[i]);if (files[i].toLowerCase().endsWith(".war")) {deployed.add(files[i]);// Calculate the context path and make sure it is uniqueString contextPath = "/" + files[i];int period = contextPath.lastIndexOf(".");if (period >= 0)contextPath = contextPath.substring(0, period);if (contextPath.equals("/ROOT"))contextPath = "";if (host.findChild(contextPath) != null)continue;if (isUnpackWARs()) {// Expand and deploy this application as a directorylog(sm.getString("hostConfig.expand", files[i]));try {URL url = new URL("jar:file:" +dir.getCanonicalPath() + "!/");String path = ExpandWar.expand(host,url);url = new URL("file:" + path);((Deployer) host).install(contextPath, url);} catch (Throwable t) {log(sm.getString("hostConfig.expand.error", files[i]),t);}} else {// Deploy the application in this WAR filelog(sm.getString("hostConfig.deployJar", files[i]));try {URL url = new URL("file", null,dir.getCanonicalPath());url = new URL("jar:" + url.toString() + "!/");((Deployer) host).install(contextPath, url);} catch (Throwable t) {log(sm.getString("hostConfig.deployJar.error",files[i]), t);}}}}}

【1.3】部署一个目录
1)intro:可以直接将web 应用程序的整个目录copy 到 %CATALINA_HOME%/webapps 目录下来完成web 应用程序的部署。HostConfig.deployDirectories()方法完成对这些web 应用程序的部署;
protected void deployDirectories(File appBase, String[] files) {for (int i = 0; i < files.length; i++) {if (files[i].equalsIgnoreCase("META-INF"))continue;if (files[i].equalsIgnoreCase("WEB-INF"))continue;if (deployed.contains(files[i]))continue;File dir = new File(appBase, files[i]);if (dir.isDirectory()) {deployed.add(files[i]);// Make sure there is an application configuration directory// This is needed if the Context appBase is the same as the// web server document root to make sure only web applications// are deployed and not directories for web space.File webInf = new File(dir, "/WEB-INF");if (!webInf.exists() || !webInf.isDirectory() ||!webInf.canRead())continue;// Calculate the context path and make sure it is uniqueString contextPath = "/" + files[i];if (files[i].equals("ROOT"))contextPath = "";if (host.findChild(contextPath) != null)continue;// Deploy the application in this directorylog(sm.getString("hostConfig.deployDir", files[i]));try {URL url = new URL("file", null, dir.getCanonicalPath());((Deployer) host).install(contextPath, url);} catch (Throwable t) {log(sm.getString("hostConfig.deployDir.error", files[i]),t);}}}}

Attention)上面两种图,我们分析了 HostConfig 如何部署WAR 文件和 普通文件夹,其实它们都是项目的打包形式,现在回想起以前部署 java web 项目到 tomcat的时候,我们为什么要那样操作(将项目文件夹编译后copy 到webapps dir下,也可以将项目打个WAR包进行部署),这两张图是不是给出了很好的诠释。(Bingo)

【1.4】动态部署
1)intro:StandardHost实例使用HostConfig对象作为生命周期监听器,当StandardHost对象启动时,它的start()方法会触发一个START事件;
2)为了响应START 事件:HostConfig.lifecycleEvent()方法和 HostConfig中的事件处理事件调用 start()方法;
3)在tomcat4中,在start()方法的最后一行,当isliveDeploy==true时(default case下,该属性为true),start()方法会调用 threadStart()方法: 
protected void start() {  // org.apache.catalina.startup.HostConfig.start().if (debug >= 1)log(sm.getString("hostConfig.start"));if (host.getAutoDeploy()) {deployApps();}if (isLiveDeploy()) { //highlight line.threadStart();}}
4)threadStart()方法会派生一个新线程并调用run()方法。
protected void threadStart() {  // org.apache.catalina.startup.HostConfig.threadStart().// Has the background thread already been started?if (thread != null)return;// Start the background threadif (debug >= 1)log(" Starting background thread");threadDone = false;threadName = "HostConfig[" + host.getName() + "]";thread = new Thread(this, threadName);thread.setDaemon(true);thread.start();}
public void run() {  //org.apache.catalina.startup.HostConfig.run().if (debug >= 1)log("BACKGROUND THREAD Starting");// Loop until the termination semaphore is setwhile (!threadDone) {// Wait for our check intervalthreadSleep();// Deploy apps if the Host allows auto deployingdeployApps();// Check for web.xml modificationcheckWebXmlLastModified();}if (debug >= 1)log("BACKGROUND THREAD Stopping");}
对以上代码的分析(Analysis):threadSleep()方法:会使该线程休眠一段时间;
 protected void threadSleep() {try {Thread.sleep(checkInterval * 1000L);}  // ......}
5)在tomcat5中,HostConfig 类没有再使用专用线程来执行检查工作,而是由StandardHost.backgroundProcess()方法周期性地触发一个 "check"事件;
Attention)backgroundProcess()方法会由一个专门的线程来周期性地调用,用来执行容器中所有的后台处理工作;
public void backgroundProcess() {lifecycle.fireLifecycleEvent("check", null);
}
public void lifecycleEvent(LifecycleEvent event) { //org.apache.catalina.startup.HostConfig.lifecycleEvent().if (event.getType().equals(Lifecycle.PERIODIC_EVENT))check(); // highlight line.// Identify the host we are associated withtry {host = (Host) event.getLifecycle();if (host instanceof StandardHost) {setDeployXML(((StandardHost) host).isDeployXML());setUnpackWARs(((StandardHost) host).isUnpackWARs());setXmlNamespaceAware(((StandardHost) host).getXmlNamespaceAware());setXmlValidation(((StandardHost) host).getXmlValidation());}} catch (ClassCastException e) {log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);return;}// Process the event that has occurredif (event.getType().equals(Lifecycle.START_EVENT))start();else if (event.getType().equals(Lifecycle.STOP_EVENT))stop();}
protected void check() {  //org.apache.catalina.startup.HostConfig.check().if (host.getAutoDeploy()) {// Check for resources modification to trigger redeploymentDeployedApplication[] apps =(DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);for (int i = 0; i < apps.length; i++) {if (!isServiced(apps[i].name))checkResources(apps[i]);}// Hotdeploy applicationsdeployApps();// ......checkContextLastModified();}}
对以上代码的分析(Analysis):
A1)check方法会调用 deployApps()方法,该方法都会完成web 应用程序的部署工作,该方法会调用 deployDescriptors方法,deployWARs方法,deployDirectories方法;
 protected void deployApps() {  //org.apache.catalina.startup.HostConfig.deployApps().if (!(host instanceof Deployer))return;if (debug >= 1)log(sm.getString("hostConfig.deploying"));File appBase = appBase();if (!appBase.exists() || !appBase.isDirectory())return;String files[] = appBase.list();deployDescriptors(appBase, files);deployWARs(appBase, files);deployDirectories(appBase, files);}
A2)check()方法还会调用checkContextLastModified方法,后者遍历所有已经部署的Context,检查web.xml 文件的 时间戳,以及每个Context中 WEB-INF 目录下的内容;如果某个检查的资源被修改了,会重新启动相应的Context实例。此外,该方法还会检查所有已经部署的WAR文件的时间戳,如果某个应用程序的WAR 文件被修改了,会重新对该应用程序进行部署;

【2】Deployer接口
1)intro:部署器是 org.apache.catalina.Deployer 接口的实例;
2)StandardHost类实现了 Deployer 接口 :所以,StandardHost 实例也是一个部署器,它也是一个容器,web 应用可以部署到其中,或从其中取消部署;
public class StandardHost  extends ContainerBase implements Deployer, Host {
//......
3)Deployer接口的源代码如下:
public interface Deployer  {  //org.apache.catalina.Deployer.public static final String PRE_INSTALL_EVENT = "pre-install";public static final String INSTALL_EVENT = "install"; public static final String REMOVE_EVENT = "remove";  public String getName();    public void install(String contextPath, URL war) throws IOException; public void install(URL config, URL war) throws IOException;   public Context findDeployedApp(String contextPath); public String[] findDeployedApps();  public void remove(String contextPath) throws IOException;public void remove(String contextPath, boolean undeploy) throws IOException; public void start(String contextPath) throws IOException;    public void stop(String contextPath) throws IOException;
}
// the follwing code is defined in org.apache.catalina.core.StandardHostpublic void install(String contextPath, URL war) throws IOException {deployer.install(contextPath, war);}public synchronized void install(URL config, URL war) throws IOException {deployer.install(config, war);}public Context findDeployedApp(String contextPath) {return (deployer.findDeployedApp(contextPath));}public String[] findDeployedApps() {return (deployer.findDeployedApps());}public void remove(String contextPath) throws IOException {deployer.remove(contextPath);}public void remove(String contextPath, boolean undeploy) throws IOException {deployer.remove(contextPath,undeploy);}public void start(String contextPath) throws IOException { deployer.start(contextPath);}public void stop(String contextPath) throws IOException { deployer.stop(contextPath);}protected void addDefaultMapper(String mapperClass) {super.addDefaultMapper(this.mapperClass);}
【3】StandardHostDeployer类(org.apache.catalina.core.StandardHostDeployer
1)intro to org.apache.catalina.core.StandardHostDeployer:该类是一个辅助类,帮助完成将web 应用程序部署到StandardHost 实例的工作。StandardHostDeployer实例由 StandardHost 对象来调用,在其构造函数中,会传入 StandardHost 类的一个实例:
public StandardHostDeployer(StandardHost host) {super();this.host = host;}
【3.1】安装一个描述符
1)intro to install()方法:当 HostConfig.deployDescriptors()方法调用了 StandardHost.install()方法后,StandardHost实例调用该 install()方法;然后再调用 StandardHostDeployer.install()方法;
 protected void deployDescriptors(File appBase, String[] files) { // org.apache.catalina.startup.HostConfig.deployDescriptors()if (!deployXML)return;for (int i = 0; i < files.length; i++) {if (files[i].equalsIgnoreCase("META-INF"))continue;if (files[i].equalsIgnoreCase("WEB-INF"))continue;if (deployed.contains(files[i]))continue;File dir = new File(appBase, files[i]);if (files[i].toLowerCase().endsWith(".xml")) {deployed.add(files[i]);// Calculate the context path and make sure it is uniqueString file = files[i].substring(0, files[i].length() - 4);String contextPath = "/" + file;if (file.equals("ROOT")) {contextPath = "";}if (host.findChild(contextPath) != null) {continue;}// Assume this is a configuration descriptor and deploy itlog(sm.getString("hostConfig.deployDescriptor", files[i]));try {URL config =new URL("file", null, dir.getCanonicalPath());((Deployer) host).install(config, null); // highlight line.} catch (Throwable t) {log(sm.getString("hostConfig.deployDescriptor.error",files[i]), t);}}}}
public synchronized void install(URL config, URL war) throws IOException { // org.apache.catalina.core.StandardHost.install().deployer.install(config, war); // highlight line.}
 public synchronized void install(URL config, URL war) throws IOException { // org.apache.catalina.core.StandardHostDeployer.install().// Validate the format and state of our argumentsif (config == null)throw new IllegalArgumentException(sm.getString("standardHost.configRequired"));if (!host.isDeployXML())throw new IllegalArgumentException(sm.getString("standardHost.configNotAllowed"));// Calculate the document base for the new web application (if needed)String docBase = null; // Optional override for value in config fileif (war != null) {String url = war.toString();host.log(sm.getString("standardHost.installingWAR", url));// Calculate the WAR file absolute pathnameif (url.startsWith("jar:")) {url = url.substring(4, url.length() - 2);}if (url.startsWith("file://"))docBase = url.substring(7);else if (url.startsWith("file:"))docBase = url.substring(5);elsethrow new IllegalArgumentException(sm.getString("standardHost.warURL", url));}// Install the new web applicationthis.context = null;this.overrideDocBase = docBase;InputStream stream = null;try {stream = config.openStream();Digester digester = createDigester();digester.setDebug(host.getDebug());digester.clear();digester.push(this);digester.parse(stream);stream.close();stream = null;} catch (Exception e) {host.log(sm.getString("standardHost.installError", docBase), e);throw new IOException(e.toString());} finally {if (stream != null) {try {stream.close();} catch (Throwable t) {;}}}}

【3.2】安装一个WAR 文件或目录
1)org.apache.catalina.core.StandardHostDeployer.install(String contextPath, URL war)方法: 接收一个表示上下文路径的字符串和一个表示WAR 文件的URL;
public synchronized void install(URL config, URL war) throws IOException { // org.apache.catalina.core.StandardHost.install().deployer.install(config, war); // highlight line.}public synchronized void install(URL config, URL war) throws IOException { // org.apache.catalina.core.StandardHostDeployer.install().// Validate the format and state of our argumentsif (config == null)throw new IllegalArgumentException(sm.getString("standardHost.configRequired"));if (!host.isDeployXML())throw new IllegalArgumentException(sm.getString("standardHost.configNotAllowed"));// Calculate the document base for the new web application (if needed)String docBase = null; // Optional override for value in config fileif (war != null) {String url = war.toString();host.log(sm.getString("standardHost.installingWAR", url));// Calculate the WAR file absolute pathnameif (url.startsWith("jar:")) {url = url.substring(4, url.length() - 2);}if (url.startsWith("file://"))docBase = url.substring(7);else if (url.startsWith("file:"))docBase = url.substring(5);elsethrow new IllegalArgumentException(sm.getString("standardHost.warURL", url));}// Install the new web applicationthis.context = null;this.overrideDocBase = docBase;InputStream stream = null;try {stream = config.openStream();Digester digester = createDigester();digester.setDebug(host.getDebug());digester.clear();digester.push(this);digester.parse(stream);stream.close();stream = null;} catch (Exception e) {host.log(sm.getString("standardHost.installError", docBase), e);throw new IOException(e.toString());} finally {if (stream != null) {try {stream.close();} catch (Throwable t) {;}}}}public synchronized void install(String contextPath, URL war) throws IOException { // Validate the format and state of our argumentsif (contextPath == null)throw new IllegalArgumentException(sm.getString("standardHost.pathRequired"));if (!contextPath.equals("") && !contextPath.startsWith("/"))throw new IllegalArgumentException(sm.getString("standardHost.pathFormat", contextPath));if (findDeployedApp(contextPath) != null)throw new IllegalStateException(sm.getString("standardHost.pathUsed", contextPath));if (war == null)throw new IllegalArgumentException(sm.getString("standardHost.warRequired"));// Calculate the document base for the new web applicationhost.log(sm.getString("standardHost.installing",contextPath, war.toString()));String url = war.toString();String docBase = null;boolean isWAR = false;if (url.startsWith("jar:")) {url = url.substring(4, url.length() - 2);if (!url.toLowerCase().endsWith(".war")) {throw new IllegalArgumentException(sm.getString("standardHost.warURL", url));}isWAR = true;}if (url.startsWith("file://"))docBase = url.substring(7);else if (url.startsWith("file:"))docBase = url.substring(5);elsethrow new IllegalArgumentException(sm.getString("standardHost.warURL", url));// Determine if directory/war to install is in the host appBaseboolean isAppBase = false;File appBase = new File(host.getAppBase());if (!appBase.isAbsolute())appBase = new File(System.getProperty("catalina.base"),host.getAppBase());File contextFile = new File(docBase);File baseDir = contextFile.getParentFile();if (appBase.getCanonicalPath().equals(baseDir.getCanonicalPath())) {isAppBase = true;}// For security, if deployXML is false only allow directories// and war files from the hosts appBaseif (!host.isDeployXML() && !isAppBase) {throw new IllegalArgumentException(sm.getString("standardHost.installBase", url));}// Make sure contextPath and directory/war names match when// installing from the host appBaseif (isAppBase && (host.getAutoDeploy() || host.getLiveDeploy())) {String filename = contextFile.getName();if (isWAR) {filename = filename.substring(0,filename.length()-4);}if (contextPath.length() == 0) {if (!filename.equals("ROOT")) {throw new IllegalArgumentException(sm.getString("standardHost.pathMatch", "/", "ROOT"));}} else if (!filename.equals(contextPath.substring(1))) {throw new IllegalArgumentException(sm.getString("standardHost.pathMatch", contextPath, filename));}}// Expand war file if host wants wars unpackedif (isWAR && host.isUnpackWARs()) {if (contextPath.equals("")) {docBase = ExpandWar.expand(host,war,"/ROOT");} else {docBase = ExpandWar.expand(host,war,contextPath);}}// Install the new web applicationtry {Class clazz = Class.forName(host.getContextClass());Context context = (Context) clazz.newInstance();context.setPath(contextPath);context.setDocBase(docBase);if (context instanceof Lifecycle) {clazz = Class.forName(host.getConfigClass());LifecycleListener listener =(LifecycleListener) clazz.newInstance();((Lifecycle) context).addLifecycleListener(listener);}host.fireContainerEvent(PRE_INSTALL_EVENT, context);host.addChild(context);host.fireContainerEvent(INSTALL_EVENT, context);} catch (Exception e) {host.log(sm.getString("standardHost.installError", contextPath),e);throw new IOException(e.toString());}}
Attention)当安装一个Context后,就会将其添加到 StandardHost实例中;

【3.3】启动Context实例
1)org.apache.catalina.core.StandardHostDeployer.start()方法:用于启动Context实例,以下代码给出了start()方法的实现;
public void start(String contextPath) throws IOException {// Validate the format and state of our argumentsif (contextPath == null)throw new IllegalArgumentException(sm.getString("standardHost.pathRequired"));if (!contextPath.equals("") && !contextPath.startsWith("/"))throw new IllegalArgumentException(sm.getString("standardHost.pathFormat", contextPath));Context context = findDeployedApp(contextPath);if (context == null)throw new IllegalArgumentException(sm.getString("standardHost.pathMissing", contextPath));host.log("standardHost.start " + contextPath);try {((Lifecycle) context).start();} catch (LifecycleException e) {host.log("standardHost.start " + contextPath + ": ", e);throw new IllegalStateException("standardHost.start " + contextPath + ": " + e);}}
【3.4】停止一个Context实例
1)org.apache.catalina.core.StandardHostDeployer.stop()方法用于停止 Context实例,以下代码给出了stop()方法的实现;
public void stop(String contextPath) throws IOException {// Validate the format and state of our argumentsif (contextPath == null)throw new IllegalArgumentException(sm.getString("standardHost.pathRequired"));if (!contextPath.equals("") && !contextPath.startsWith("/"))throw new IllegalArgumentException(sm.getString("standardHost.pathFormat", contextPath));Context context = findDeployedApp(contextPath);if (context == null)throw new IllegalArgumentException(sm.getString("standardHost.pathMissing", contextPath));host.log("standardHost.stop " + contextPath);try {((Lifecycle) context).stop();} catch (LifecycleException e) {host.log("standardHost.stop " + contextPath + ": ", e);throw new IllegalStateException("standardHost.stop " + contextPath + ": " + e);}}
Conclusion)
C1)部署器:是用来部署和安装web 应用程序的组件,是org.apache.catalina.Deployer接口的实例;
C2)StandardHost:是Deployer接口的一个实例,使其成为一个 可以向其中部署web 应用程序的特殊容器;
C3)StandardHost类会将部署和安装web 应用程序的任务委托给其辅助类 org.apache.catalina.core.StandardHostDeployer类完成。
C4)StandardHostDeployer类:提供了部署和安装web 应用程序以及启动/ 关闭 Context实例的代码;

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

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

相关文章

装饰器模式和代理模式的区别

转载自 装饰器模式和代理模式的区别学习AOP时&#xff0c;教材上面都说使用的是动态代理&#xff0c;可是在印象中代理模式一直都是控制访问什么的&#xff0c;怎么又动态增加行为了&#xff0c;动态增加行为不是装饰器模式吗&#xff1f;于是找了很多资料&#xff0c;想弄清楚…

使用Java 8 Stream像操作SQL一样处理数据(上)

转载自 使用Java 8 Stream像操作SQL一样处理数据&#xff08;上&#xff09; 几乎每个Java应用都要创建和处理集合。集合对于很多编程任务来说是一个很基本的需求。举个例子&#xff0c;在银行交易系统中你需要创建一个集合来存储用户的交易请求&#xff0c;然后你需要遍历整个…

tomcat(19)Manager应用程序的servlet类

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“深入剖析tomcat”&#xff0c;旨在学习“tomcat(19)Manager应用程序的servlet类” 的相关知识&#xff1b;2&#xff09;Manager应用程序用来管理已经部署的web 应用程序&#xff1b;在tomcat7中&#xff0c;manage…

使用Java 8 Stream像操作SQL一样处理数据(下)

转载自 使用Java 8 Stream像操作SQL一样处理数据&#xff08;下&#xff09; 在上一篇文章中&#xff0c;我们介绍了Stream可以像操作数据库一样来操作集合&#xff0c;但是我们没有介绍 flatMap 和 collect 操作。这两种操作对实现复杂的查询是非常有用的。比如你可以结果 fl…

spring(4)面向切面的Spring(AOP)

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(4)面向切面的Spring&#xff08;AOP&#xff09;”的相关知识&#xff1b;2&#xff09;在软件开发中&#xff0c;散布于应…

Mybatis-plus 思维导图,让 Mybatis-plus 不再难懂

转载自 Mybatis-plus 思维导图&#xff0c;让 Mybatis-plus 不再难懂 摘要: Mybatis-Plus&#xff08;简称MP&#xff09;是一个Mybatis的增强工具&#xff0c;在 Mybatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。mybatis-plus与mybatis mybatis Mybat…

报错:The type javax.servlet.http.HttpServletRequest cannot be resolved

【0】README 0.1&#xff09;以下内容转自&#xff1a; http://blog.csdn.net/vincent080928/article/details/5392891 problem&#xff09;The type javax.servlet.http.HttpServletRequest cannot be resolved. It is indirectly referenced from required .class files. 这…

MyBatis 思维导图,让 MyBatis 不再难懂(一)

转载自 MyBatis 思维导图&#xff0c;让 MyBatis 不再难懂&#xff08;一&#xff09;写在前面与hibernate相比&#xff0c;我无疑更喜欢mybatis&#xff0c;就因为我觉得它真的好用&#xff0c;哈哈。它简单上手和掌握&#xff1b;sql语句和代码分开&#xff0c;方便统一管理和…

tomcat(20)基于JMX的管理

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“深入剖析tomcat”&#xff0c;旨在学习“tomcat(20)基于JMX的管理” 的相关知识&#xff1b;2&#xff09;晚辈我在tomcat上部署web 项目以测试JMX管理 tomcat 容器bean的效果&#xff0c;结果运行不成功&#xff0…

mybatis思维导图,让mybatis不再难懂(二)

转载自 mybatis思维导图&#xff0c;让mybatis不再难懂&#xff08;二&#xff09; 写在前面 上一篇文章写了mybatis的基本原理和配置文件的基本使用&#xff0c;这一篇写mybatis的使用&#xff0c;主要包括与sping集成、动态sql、还有mapper的xml文件一下复杂配置等。值得注意…

spring(5)构建 spring web 应用程序

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(5)构建 spring web 应用程序” 的相关知识&#xff1b;【1】spring mvc 起步【1.1】跟踪spring mvc的请求1&#xff09;请求…

Spring思维导图,让Spring不再难懂(ioc篇)

转载自 Spring思维导图&#xff0c;让Spring不再难懂&#xff08;ioc篇&#xff09; 写过java的都知道&#xff1a;所有的对象都必须创建&#xff1b;或者说&#xff1a;使用对象之前必须先创建。而使用ioc之后&#xff0c;你就可以不再手动创建对象&#xff0c;而是从ioc容器中…

spring(6) 渲染web视图

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(6) 渲染web视图” 的相关知识&#xff1b;【1】 理解视图解析【1.1】视图解析的基础知识以及spring 提供的其他视图解析器1…

Spring思维导图,让Spring不再难懂(aop篇)

转载自 Spring思维导图&#xff0c;让Spring不再难懂&#xff08;aop篇&#xff09; 什么是aop AOP&#xff08;Aspect-OrientedProgramming&#xff0c;面向方面编程&#xff09;&#xff0c;可以说是OOP&#xff08;Object-Oriented Programing&#xff0c;面向对象编程&…

tomcatSupplement(1)tomcat启动脚本分析(以Windows平台为例)

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“深入剖析tomcat”&#xff0c;旨在学习“tomcat启动脚本分析” 的相关知识&#xff1b;2&#xff09;for tomcat4 startup files&#xff0c; please visit https://github.com/pacosonTang/HowTomcatWorks/tree/ma…

Spring思维导图,让spring不再难懂(一)

转载自 Spring思维导图&#xff0c;让spring不再难懂&#xff08;一&#xff09; 摘要: Spring是一个开源框架&#xff0c;是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构&#xff0c;分层架构允许您选择使用哪一个组件&#xff0c;同时为 J2EE …

spring(7)spring mvc 的高级技术

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(7)spring mvc 的高级技术” 的相关知识&#xff1b;2&#xff09;本文将会看到如何编写控制器来处理文件上传&#xff0c;如…

Ubuntu下MySQL、Redis以及MongoDB三个数据库的启动、重启以及停止命令

一、MySQL #启动 /etc/init.d/mysql start #停止 /etc/init.d/mysql stop #重启 /etc/init.d/mysql restart 二、Redis #启动 redis-server #停止 pkill redis-server三、MongoDB #启动服务 sudo service mongod start #重启 sudo service mongod restart #终止 sudo serv…

Spring思维导图,让Spring不再难懂(mvc篇)

转载自 Spring思维导图&#xff0c;让Spring不再难懂&#xff08;mvc篇&#xff09;spring mvc简介与运行原理Spring的模型-视图-控制器&#xff08;MVC&#xff09;框架是围绕一个DispatcherServlet来设计的&#xff0c;这个Servlet会把请求分发给各个处理器&#xff0c;并支持…

spring(10)通过spring 和 JDBC征服数据库

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(10)通过spring 和 JDBC征服数据库” 的相关知识&#xff1b;【1】 spring 的数据访问哲学1&#xff09;intro&#xff1a;s…