义乌外贸建网站,西安大雁塔音乐喷泉时间,wordpress设置导航栏,o2o商超网站建设【0】README-1#xff09;先上干货#xff1a;本文重点分析了tomcat 如何部署WAR文件的项目形式 以及 普通文件夹的项目形式#xff1b;不管是WAR文件 还是 普通文件夹的项目形式#xff0c;在tomcat中#xff0c;它们都是Context容器#xff1b;#xff08;Bingo#…【0】README-1先上干货本文重点分析了tomcat 如何部署WAR文件的项目形式 以及 普通文件夹的项目形式不管是WAR文件 还是 普通文件夹的项目形式在tomcat中它们都是Context容器Bingo0本文部分文字描述转自“how tomcat works”旨在学习“tomcat(18)部署器”的相关知识1intro要使用一个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 目录下 3intro 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();// StandardContexts 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 i0; ilength; 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监听器2org.apache.catalina.startrup.Catalina类是启动类使用Digester对象来解析server.xml文件将其中的XML 元素 转换为 java对象Catalina类定义了 createStartDigester方法来为 Digester对象添加规则。createStartDigester方法中的其中一行如下所示digester.addRuleSet(new HostRuleSet(Server/Service/Engine/));对以上代码的分析AnalysisHostRuleSet 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));//......}对以上代码的分析AnalysisA1当 在 server.xml 文件中遇到符合 Server/Service/Engine/Host 模式的标签时会创建 org.apache.catalina.startup.HostConfig 类的一个实例并将其添加到 Host实例中作为生命周期监听器 A2thats to say, HostConfig 类会处理 StandardHost.start()方法 和 stop()方法的触发事件 ?xml version1.0 encodingutf-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 port8005 shutdownSHUTDOWN!-- Security listener. Documentation at /docs/config/listeners.htmlListener classNameorg.apache.catalina.security.SecurityListener /--!--APR library loader. Documentation at /docs/apr.html --Listener classNameorg.apache.catalina.core.AprLifecycleListener SSLEngineon /!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html --Listener classNameorg.apache.catalina.core.JasperListener /!-- Prevent memory leaks due to use of particular java/javax APIs--Listener classNameorg.apache.catalina.core.JreMemoryLeakPreventionListener /Listener classNameorg.apache.catalina.mbeans.GlobalResourcesLifecycleListener /Listener classNameorg.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 nameUserDatabase authContainertypeorg.apache.catalina.UserDatabasedescriptionUser database that can be updated and savedfactoryorg.apache.catalina.users.MemoryUserDatabaseFactorypathnameconf/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 nameCatalina!--The connectors can use a shared executor, you can define one or more named thread pools--!--Executor nametomcatThreadPool namePrefixcatalina-exec-maxThreads150 minSpareThreads4/--!-- 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 port8888 protocolHTTP/1.1connectionTimeout20000redirectPort8443 /!-- A Connector using the shared thread pool--!--Connector executortomcatThreadPoolport8080 protocolHTTP/1.1connectionTimeout20000redirectPort8443 /--!-- 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 port8443 protocolHTTP/1.1 SSLEnabledtruemaxThreads150 schemehttps securetrueclientAuthfalse sslProtocolTLS /--!-- Define an AJP 1.3 Connector on port 8009 --Connector port8009 protocolAJP/1.3 redirectPort8443 /!-- 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 nameCatalina defaultHostlocalhost jvmRoutejvm1--Engine nameCatalina defaultHostlocalhost!--For clustering, please take a look at documentation at:/docs/cluster-howto.html (simple how to)/docs/config/cluster.html (reference documentation) --!--Cluster classNameorg.apache.catalina.ha.tcp.SimpleTcpCluster/--!-- Use the LockOutRealm to prevent attempts to guess user passwordsvia a brute-force attack --Realm classNameorg.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 classNameorg.apache.catalina.realm.UserDatabaseRealmresourceNameUserDatabase//RealmHost namelocalhost appBasewebappsunpackWARstrue autoDeploytrue!-- SingleSignOn valve, share authentication between web applicationsDocumentation at: /docs/config/valve.html --!--Valve classNameorg.apache.catalina.authenticator.SingleSignOn /--!-- Access log processes all example.Documentation at: /docs/config/valve.htmlNote: The pattern used is equivalent to using patterncommon --Valve classNameorg.apache.catalina.valves.AccessLogValve directorylogsprefixlocalhost_access_log. suffix.txtpattern%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 的代码你就懂的。3HostConfig.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方法A1setDeployXML方法指明了Host实例是否需要部署一个 Context实例的描述符文件 public void setDeployXML(boolean deployXML) {this.deployXML deployXML;} A2setLiveDeploy方法指明了Host实例 是否需要周期性检查一个新的 部署 public void setLiveDeploy(boolean liveDeploy) {this.liveDeploy liveDeploy;} A3setUnpackWARs方法指定是要将WAR 文件形式的web 应用程序解压缩 public void setUnpackWARs(boolean unpackWARs) {this.unpackWARs unpackWARs;} 4lifecycleEvent方法 调用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();} 对以上代码的分析AnalysisA1若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()方法 5deployApps方法源代码如下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.}对以上代码的分析AnalysisA1该方法会获取host的实例的appBase属性的值默认为 webapps 的值参见 server.xml部署进程会将 %CATALINA_HOME%/webapps 目录下的所有目录都看做是 web 应用程序的目录来执行部署工作。此外该目录中所有的WAR 文件和描述符文件也都会进行部署干货——想想以前总是要把项目打个war 包放到webapps 目录下我在这里找到了答案。 A2deployApps()方法会调用其他3个方法deployDescriptors方法deployWARs方法deployDirectories方法 A2.1deployDescriptors方法 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.2deployWARs方法 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.3deployDirectories方法 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/admindebug0 privilegedtrue!-- 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 classNameorg.apache.catalina.valves.RemoteAddrValveallow127.0.0.1/--Logger classNameorg.apache.catalina.logger.FileLoggerprefixlocalhost_admin_log. suffix.txttimestamptrue/
/Context
!-- tomcat5下的 manager.xml 文件(\container\webapps\manager --
Context docBase${catalina.home}/server/webapps/managerprivilegedtrue antiResourceLockingfalse antiJARLockingfalse useHttpOnlytrue!-- Link to the user database we will get roles from --ResourceLink nameusers globalUserDatabasetypeorg.apache.catalina.UserDatabase/
/ContextAttention这两个描述符都有一个Context 元素。Context元素中的 docBase属性的值分别为 %CATALINA_HOME%/server/webapps/admin and %CATALINA_HOME%/server/webapps/manager这表明admin 应用程序和manager应用程序并没有部署到默认的地方2HostConfig类使用了 deployDescriptor()方法来部署XML 文件。在tomcat4中 这些文件位于 %CATALINA_HOME%/webapps 目录下在tomcat5中 位于 %CATALINA_HOME%/server/webapps 子目录下【1.2】部署一个WAR文件1intro可以将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】部署一个目录1intro可以直接将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】动态部署1introStandardHost实例使用HostConfig对象作为生命周期监听器当StandardHost对象启动时它的start()方法会触发一个START事件2为了响应START 事件HostConfig.lifecycleEvent()方法和 HostConfig中的事件处理事件调用 start()方法3在tomcat4中在start()方法的最后一行当isliveDeploytrue时default case下该属性为truestart()方法会调用 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();}}4threadStart()方法会派生一个新线程并调用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);}对以上代码的分析AnalysisthreadSleep()方法会使该线程休眠一段时间protected void threadSleep() {try {Thread.sleep(checkInterval * 1000L);} // ......}5在tomcat5中HostConfig 类没有再使用专用线程来执行检查工作而是由StandardHost.backgroundProcess()方法周期性地触发一个 check事件AttentionbackgroundProcess()方法会由一个专门的线程来周期性地调用用来执行容器中所有的后台处理工作 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 A1check方法会调用 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);} A2check()方法还会调用checkContextLastModified方法后者遍历所有已经部署的Context检查web.xml 文件的 时间戳以及每个Context中 WEB-INF 目录下的内容如果某个检查的资源被修改了会重新启动相应的Context实例。此外该方法还会检查所有已经部署的WAR文件的时间戳如果某个应用程序的WAR 文件被修改了会重新对该应用程序进行部署 【2】Deployer接口1intro部署器是 org.apache.catalina.Deployer 接口的实例2StandardHost类实现了 Deployer 接口 所以StandardHost 实例也是一个部署器它也是一个容器web 应用可以部署到其中或从其中取消部署public class StandardHost extends ContainerBase implements Deployer, Host {
//......3Deployer接口的源代码如下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.StandardHostDeployer1intro to org.apache.catalina.core.StandardHostDeployer该类是一个辅助类帮助完成将web 应用程序部署到StandardHost 实例的工作。StandardHostDeployer实例由 StandardHost 对象来调用在其构造函数中会传入 StandardHost 类的一个实例public StandardHostDeployer(StandardHost host) {super();this.host host;}【3.1】安装一个描述符1intro 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 文件或目录 1org.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实例1org.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实例1org.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);}}ConclusionC1部署器是用来部署和安装web 应用程序的组件是org.apache.catalina.Deployer接口的实例 C2StandardHost是Deployer接口的一个实例使其成为一个 可以向其中部署web 应用程序的特殊容器 C3StandardHost类会将部署和安装web 应用程序的任务委托给其辅助类 org.apache.catalina.core.StandardHostDeployer类完成。 C4StandardHostDeployer类提供了部署和安装web 应用程序以及启动/ 关闭 Context实例的代码