Tomcat 的部署器
要使用一个Web应用程序,必须要将表示该应用程序的Context实例部署到一个Host实例中,在Tomcat中,Context实例可以用WAR文件的形式来部署,也可以将整个WEB应用程序复制到Tomcat安装目录下的webapp下。对于部署的每个应用程序,可以在其中包含一个描述符文件,该文件包含Context实例的配置信息,描述符文件也采用XML文档格式。
下面会讨论如何使用部署器来部署一个web应用程序,在Tomcat 中,部署器 是org.apahce.catalina.Deployer接口的实例,部署器与一个Host实例相关联,用来安装Context实例,安装Context实例的意思是,创建一个StandardContext实例,并将该实例添加到Host实例中,创建的Context实例会随其父容器 ---Host实例一起启动,因此 除了Wrapper实例类,容器的实例总会是调用其子容器的start方法,但是 部署器也可以用来单独签订和关闭Context实例。
部署一个Web应用程序
在原来我们学习Host实例时,使用如下代码来实例化StandardHost实例,并将一个Context实例作为子容器添加到Host实例中。
Context context = new StandardContext();
context.setPath("/app1");
conntext.setDocBase("app1");
LifecycleListener listener = new ContextConfig();
(Lifecycle(context)).addLifecycleListener(listener);
Host host = new StandardHost();
host.addChild(context);
这是之前部署应用程序的方法,但是在Tomcat中并没有这样部署应用程序,拿在实际部署环境中Context实例是如何被添加到Host容器中的呢?答案在于StandardHost实例中使用的org.apache.catalina.startup.HostConfig类型的声明周期监听器。
当调用StandardHost实例的start方法时,它会触发START事件,HostConfig实例会对该事件进行响应,并调用其自身的start方法,该方法会逐个部署并安装指定目录中的所有web应用程序,下面对其中的细节进行讲解。
回忆一下之前如何使用Digester对象来解析XML文档的内容,但是它并没有涉及Digester对象中的所有规则,其中被忽略掉的一个主题就是部署器。
在Tomcat中,org.apahce.catalina.startup.Catalina类时启动类,使用Digester对象来解析server.xml文件,将其中的XML元素转换为java对象,Catalina类定义了createStartDigester方法 来为Digester对象来添加规则,createStarDigester方法的器其中一行代码如下:
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
org.apahce.catalina.strtup.HostRuleSet类继承自org.apache.commons.digester.RuleSetBase类(该类在前面介绍过,)。作为RuleSetBase类的一个子类,HostRuleSet类必须提供addRuleInstances方法的实现,需要在该方法中定义RuleSet的规则,
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",
"hostConfigClass"));
当在Server.xml文件中遇到符合 Server/Service/Engine/Host 模式的标签时,会创建 org.apache.catalina.startup.HostConfig的一个实例。并将其添加到Host实例中,作为声明周期监听器,换句话说,HostConfig类会处理StandardHost实例的start方法和 stop方法触发的事件,
下面给出了HostConfig实例的 lifecycleEvent方法的实现,该方法是一个事件处理程序,因为HostConfig的实例是StandardHost对象的声明周期事件监听器,每当StandardHost实例启动或者关闭时,都会调用HostConfig的lifecycleEvent方法
/**
* 处理与该对象关联的Host对象的 声明周期监听事件
*
* @param event
* 发生的事件
*/
public void lifecycleEvent(LifecycleEvent event) { // 确定与我们关联的Host
try {
host = (Host) event.getLifecycle();
if (host instanceof StandardHost) {
int hostDebug = ((StandardHost) host).getDebug();
if (hostDebug > this.debug) {
this.debug = hostDebug;
}
// 设置 Host实例是否要部署一个Context实例的描述文件,默认情况 标志位true;
setDeployXML(((StandardHost) host).isDeployXML());
// 设置Host实例是否要周期性的检查一个新的部署的标志
setLiveDeploy(((StandardHost) host).getLiveDeploy());
// 设置 是否要将WAR文件形式的Web应用程序解压缩标志
setUnpackWARs(((StandardHost) host).isUnpackWARs());
}
} catch (ClassCastException e) {
log(sm.getString("hostConfig.cce", event.getLifecycle()), e);
return;
} // 处理已经发生的事件
if (event.getType().equals(Lifecycle.START_EVENT))
start();
else if (event.getType().equals(Lifecycle.STOP_EVENT))
stop(); }
如果变量host指向的对象是org.apahce.catalina.core.StandardHost类的实例,就会调用,setDeployXML、setLiveDeploye、setUnpackWARS方法,,
StandardHost类的 isDeployXML方法指明了host实例是否需要部署一个Context实例的描述符文件,默认情况下 deployXML属性的值为true,liveDeploy属性指明了Host实例是否要周期性的检查一个新的部署。unpackWARS属性指明是要将WAR文件形式的Web应用程序解压缩,
在收到START事件通知后,HostConfig对象的lifecycleEvent方法会调用start方法来部署web应用程序,
/**
*
* 处理开始事件
*/
protected void start() { if (debug >= 1)
log(sm.getString("hostConfig.start")); // 获取host的是否需要自动部署Web应用程序的标志,默认值为true
if (host.getAutoDeploy()) {
// 获取Host实例的 appBase属性的值,默认为“webapps”
// server.xml 中
/**
* <Host name="localhost" debug="0" appBase="webapps" unpackWARs=
* "true" autoDeploy="true">
*
*/
// 部署进程会将%CATALINA_HOME%/webapps目录下的所有目录都看做web应用程序的目录来执行部署工作,该目录中的所有WAR文件和描述符文件也都会进行部署
deployApps();
}
/**
* HostConfig作为一个声明周期监听器。当StandardHost对象启动时,它的start方法会触发start事件。
* 为了响应start事件。HostConfig中的LifcycleEvent 方法 和 HostConfig 的start方法。
* 当LiveDeploye的值为true时,会调用threadStart方法
*/
// 如果 需要周期性的检查部署 动态部署
if (isLiveDeploy()) {
threadStart();
} }
当autoDeploy的属性值是true时,默认情况下该值为true,start方法会调用deployApps方法,此外 如果 liveDeploy属性的值为true,它还会调用threadStart方法来派生一个新线程来动态部署web应用。
deployApps方法 会获取Host实例的appBase属性的值,默认为webapps的值(可以参考下Tomcat的server.xml文件),部署进程会将%CATALINA_HOME%/webapps目录下的所有目录都看作为Web应用程序的目录来执行部署工作,此外该目录中的所有的WAR文件和描述符文件也都会进行部署。
/**
* 为在我们的“应用程序根目录”中找到的任何目录或war文件部署应用程序。
*/
protected void deployApps() { if (!(host instanceof Deployer))
return;
if (debug >= 1)
log(sm.getString("hostConfig.deploying"));
// 返回了host 的根目录File引用
File appBase = appBase();
// 如果这个目录不存在 或者 这个目录 不是一个文件夹引用 直接返回
if (!appBase.exists() || !appBase.isDirectory())
return;
// 获取host根目录下的所有文件名
String files[] = appBase.list();
// 部署描述文件
deployDescriptors(appBase, files);
deployWARs(appBase, files);
deployDirectories(appBase, files); }
deployApps方法会调用其他三个方法,deployDescriptions、deployeWARs、deployDirectories方法,deployApps方法会将FIle类型的变量appBase和webApps目录下的所有的文件名数组形式传递给这3个方法,Context实例是通过它的路径来标识的,每个部署的Context是都必须有一条唯一的路径,已经部署的Context实例的文件名 也就是去掉 /的路径标识会添加到存储已经部署Context的文件名的ArrayList中,因此,在部署一个Context实例之前,deployDescriptions、deployWARs、deployDirectories方法必须保证已经部署的ArrayList中没有具有相同路径的Context实例,
HostConfig类使用deployDescriptions来部署XML文件
/**
* Deploy XML context descriptors.
*/
protected void deployDescriptors(File appBase, String[] files) {
// 如果部署描述器标志 为false 则直接返回
if (!deployXML)
return;
// 迭代 host根目录下的所有文件名
for (int i = 0; i < files.length; i++) {
// 如果文件名为 "META-INF" 则不作处理 继续迭代
if (files[i].equalsIgnoreCase("META-INF"))
continue;
// 如果文件名为 "WEB-INF" 则不作处理 继续迭代
if (files[i].equalsIgnoreCase("WEB-INF"))
continue;
// 如果 存储部署好的 Context 里已经包含这个这个文件名了 则 不继续做处理 继续迭代
if (deployed.contains(files[i]))
continue;
// 拼接完整的 文件目录 并实例一个File对象
File dir = new File(appBase, files[i]);
// 如果文件末尾以xml结尾
if (files[i].toLowerCase().endsWith(".xml")) {
// 将文件名 添加到已经部署好的集合中
deployed.add(files[i]); // 将文件名 .xml去掉 例如:admin.xml file = admin
String file = files[i].substring(0, files[i].length() - 4);
// Context的路径 = /admin
String contextPath = "/" + file;
// 如果 file 名为ROOT 则 context 的路径为host的appBase路径
if (file.equals("ROOT")) {
contextPath = "";
}
// 如果 host中 有这个路径Context 就不作任何处理 继续迭代
if (host.findChild(contextPath) != null) {
continue;
} // 假设这是一个配置描述符,然后部署它
log(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);
} } } }
第二:部署一个WAR文件
/**
* Deploy WAR files. 可以将Web应用程序以一个WAR形式的文件来部署,
*/
protected void deployWARs(File appBase, String[] files) {
// 开始迭代 host根目录下的文件名
for (int i = 0; i < files.length; i++) {
// 如果文件名是 META-INF 则不作处理
if (files[i].equalsIgnoreCase("META-INF"))
continue;
// 如果文件名是WEB-INF 则不做处理
if (files[i].equalsIgnoreCase("WEB-INF"))
continue;
// 如果这个文件名 已经属于被被部署了 则不做处理 继续迭代
if (deployed.contains(files[i]))
continue;
// 将这个文件全名 拼接完成 并实例一个FIle、引用
File dir = new File(appBase, files[i]);
// 如果这个文件是一个 war文件
if (files[i].toLowerCase().endsWith(".war")) {
// 将其添加到已经部署集合中
deployed.add(files[i]); // 计算上下文路径并确保其唯一
// 拼接一个context路径
String contextPath = "/" + files[i];
// 如果这个路径还有.
int period = contextPath.lastIndexOf(".");
if (period >= 0)
// 截取.之前的路径字符串
contextPath = contextPath.substring(0, period);
// 如果context路径等于/ROOT
if (contextPath.equals("/ROOT"))
contextPath = "";
// 如果这个host的子容器中已经存在这个路径的Context了 则不需要继续了
if (host.findChild(contextPath) != null)
continue;
// 如果允许将WAR问价 解压缩的话
if (isUnpackWARs()) { // 将此应用程序扩展并部署为目录
log(sm.getString("hostConfig.expand", files[i]));
try {
URL url = new URL("jar:file:" + dir.getCanonicalPath() + "!/");
// 解压缩后的目录文件名
String path = expand(url); url = new URL("file:" + path);
((Deployer) host).install(contextPath, url);
} catch (Throwable t) {
log(sm.getString("hostConfig.expand.error", files[i]), t);
} } else { // 如果不允许解压缩 则把WAR文件作为JAR文件部署
log(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);
} } } } }
部署一个目录,可以直接将Web应用程序的整个目录复制到%CATALINA_HOME%/webapps目录下来完成web应用程序的部署
/**
* 部署一个目录 可以直接将Web应用程序的整个目录复制到 %CATALINA_HOME%webapps目录下来完成web应用程序的部署,
*/
protected void deployDirectories(File appBase, String[] files) { // 迭代host根目录下的所有文件名
for (int i = 0; i < files.length; i++) {
// 如果 文件名为 META-INF则 不做任何处理 继续跌倒
if (files[i].equalsIgnoreCase("META-INF"))
continue;
// 如果文件名为 WEB-INF则不做任何处理继续迭代
if (files[i].equalsIgnoreCase("WEB-INF"))
continue;
// 如果这个文件已经被部署了 则继续迭代 不做任何处理了
if (deployed.contains(files[i]))
continue;
// 引用这个文件的FIle引用
File dir = new File(appBase, files[i]);
// 如果这个文件是一个 文件夹
if (dir.isDirectory()) {
// 将文件添加到已经部署好的集合了
deployed.add(files[i]); // 确保存在应用程序配置目录。如果上下文AppBase与Web服务器文档根目录相同,则需要此目录,以确保仅部署Web应用程序,而不部署Web空间目录。.
// 只部署其web-inf目录
// 找到 dir目录下的 /WEB-INF文件夹
File webInf = new File(dir, "/WEB-INF");
// 如果不存在则 不做任何处理 继续迭代
if (!webInf.exists() || !webInf.isDirectory() || !webInf.canRead())
continue; // 将文件名前加上一个/
String contextPath = "/" + files[i];
// 如果 文件名为ROOT 则host的appBase的目录为 context的目录
if (files[i].equals("ROOT"))
contextPath = "";
// 如果 host的子容器中已经存在这个路径的Context 则继续迭代 不继续处理了
if (host.findChild(contextPath) != null)
continue; // 在此目录中部署应用程序
log(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);
} } } }
动态部署,正如前面提到的 StandardHost实例会使用HostConfig对象作为一个生命周期事件监听器,当StandardHost对象被启动时,它的start方法会触发一个START事件。为了响应START事件,HostConfig中lifecycleEvent方法和HostConfig中的事件处理程序会调用start方法,在start方法的最后一行,当liveDeploy属性为true时,start方法会调用threadStart方法,
threadStart方法会派生一个新线程并调用run方法,(本身HostConfig对象就继承了Runnable接口)所以会吧当前HostConfig对象传入,HostConfig对象的run方法会定期检查是否有新的应用要部署,或者已经部署的Web应用程序的web.xml文件是否已经被修改,
/**
*
* 派生一个新的线程并调用run方法,run方法会定期检查是否有新的应用要部署,或已经部署的Web应用程序的web.xml是否有修改。
*
* @exception IllegalStateException
* 如果我们不能启动这个线程
*/
protected void threadStart() { // 这个后台线程是否已经被启动了
if (thread != null)
return; // 启动一个线程
if (debug >= 1)
log(" Starting background thread");
threadDone = false;
threadName = "HostConfig[" + host.getName() + "]";
thread = new Thread(this, threadName);
// 设置为守护线程
thread.setDaemon(true);
thread.start(); }
这个守护线程被启动后 会调用HostConfig的run方法
/**
*
* 周期性检查Web应用程序自动部署和对web.xml配置的更改的后台线程。
*/
public void run() { if (debug >= 1)
log("BACKGROUND THREAD Starting"); // 循环直到终止变量被设置
while (!threadDone) { // 等待我们的检查间隔
threadSleep(); // 如果主机允许自动部署,则部署应用程序
deployApps(); // 检查web.xml文件的事假戳
checkWebXmlLastModified(); } if (debug >= 1)
log("BACKGROUND THREAD Stopping"); }
threadSleep方法会使该线程休眠一段时间,该时间的长短由属性checkInterval指定,该属性默认为15s,即每隔15秒检查一次,
看下检查web.xml文件的时间戳的方法
/**
* 检查部署描述符的上次修改日期。
*/
protected void checkWebXmlLastModified() { if (!(host instanceof Deployer))
return; Deployer deployer = (Deployer) host; String[] contextNames = deployer.findDeployedApps(); for (int i = 0; i < contextNames.length; i++) { String contextName = contextNames[i];
Context context = deployer.findDeployedApp(contextName); if (!(context instanceof Lifecycle))
continue; try {
DirContext resources = context.getResources();
if (resources == null) {
// This can happen if there was an error initializing
// the context
continue;
}
// 如果旧的时间戳 和新的 时间戳不一致
ResourceAttributes webXmlAttributes = (ResourceAttributes) resources.getAttributes("/WEB-INF/web.xml");
long newLastModified = webXmlAttributes.getLastModified();
Long lastModified = (Long) webXmlLastModified.get(contextName);
if (lastModified == null) {
webXmlLastModified.put(contextName, new Long(newLastModified));
} else {
if (lastModified.longValue() != newLastModified) {
webXmlLastModified.remove(contextName);
// 先将cotext停止
((Lifecycle) context).stop();
// Context 已停止,将引发生命周期异常,并且Context将不会重新启动。
// 在重新启动 更细 现有的web.xml进行重新配置
((Lifecycle) context).start();
}
}
} catch (LifecycleException e) {
; // Ignore
} catch (NamingException e) {
; // Ignore
} } }
如果发现web.xml文件的时间戳已经发生改变则 将Context关闭 并重新启动,在重新启动时会重新读取web.xml的配置
下面来说一下 重中之重,部署器
Deployer接口
部署器是org.apache.catalina.Deployer接口的实例,StandardHost类实现了 Deployer接口,因此,StandardHost实例也是一个部署器,它是一个容器,web应用程序可以部署到其中,或者从中取消部署、
package org.apache.catalina; import java.io.IOException;
import java.net.URL; /**
*
* <p>
* <b>Title:Deployer.java</b>
* </p>
* <p>
* Copyright:ChenDong 2019
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述: 部署程序是一个专门的容器,可以在其中部署和取消部署Web应用程序。这样的容器将为每个部署的应用程序创建和安装Context实例。
* 每个Web应用程序的唯一键是其附加到Context路径。
* </p>
*
* @author * @date 2019年1月2日 下午10:03:45
* @version 1.0
*/
/* public interface Deployer extends Container { */
public interface Deployer { // ----------------------------------------------------- Manifest Constants /**
* <code>install()</code>安装新应用程序时,在启动前发送的ContainerEvent事件类型。
*/
public static final String PRE_INSTALL_EVENT = "pre-install"; /**
* 启动新应用程序后,<code>install()</code>安装新应用程序时发送的ContainerEvent事件类型
*/
public static final String INSTALL_EVENT = "install"; /**
* <code>remove()</code>.移除现有应用程序时发送的ContainerEvent事件类型。
*/
public static final String REMOVE_EVENT = "remove"; // --------------------------------------------------------- Public Methods /**
* 返回与此部署程序关联的容器的名称
*/
public String getName(); /**
*
*
* <p>
* Title: install
* </p>
*
* @date 2019年1月2日 下午10:18:43
*
* <p>
* 功能描述:使用指定的Context 路径将其标识的 Web应用程序部署到 指定URL位置上。此容器的根应用程序应使用“”(
* 空字符串)的Context路径。否则,Context路径必须以斜线开头。
*
*
*
* 如果成功安装此应用程序,将向所有注册的侦听器发送install_event类型的containerEvent,
* 并将新创建的Context作为参数。
* </p>
*
* @param contextPath
* @param war
* @throws IOException
*/
public void install(String contextPath, URL war) throws IOException; /**
*
*
* <p>
* Title: install
* </p>
*
* @date 2019年1月2日 下午10:27:36
*
* <p>
* 功能描述:安装新的Web应用程序,Context 配置描述XML文件(由<context>元素组成) 部署到 指定URL。
*
*
*
* 如果成功安装此应用程序,将向所有注册的侦听器发送install_event类型的containerEvent,
* 并将新创建的Context作为参数
* </p>
*
* @param config
* @param war
* @throws IOException
*/
public void install(URL config, URL war) throws IOException; /**
*
*
* <p>
* Title: findDeployedApp
* </p>
*
* @date 2019年1月2日 下午10:30:03
*
* <p>
* 功能描述:返回与指定Context路径关联的已部署应用程序的Context(如果有);否则返回空值。
* </p>
*
* @param contextPath
* context路径
* @return
*/
public Context findDeployedApp(String contextPath); /**
*
*
* <p>
* Title: findDeployedApps
* </p>
*
* @date 2019年1月2日 下午10:30:55
*
* <p>
* 功能描述:返回此容器中所有已部署Web应用程序的Context 路径。如果没有部署的应用程序,则返回零长度数组。
* </p>
*
* @return
*/
public String[] findDeployedApps(); /**
*
*
* <p>
* Title: remove
* </p>
*
* @date 2019年1月2日 下午10:31:41
*
* <p>
* 功能描述:删除附加到指定Context 路径的现有Web应用程序。如果成功删除此应用程序,
* 将向所有注册的侦听器发送一个remove_event类型的containerEvent,并将删除的Context作为参数。
* </p>
*
* @param contextPath
* @throws IOException
*/
public void remove(String contextPath) throws IOException; /**
*
*
* <p>
* Title: start
* </p>
*
* @date 2019年1月2日 下午10:32:26
*
* <p>
* 功能描述:启动附加到指定Context路径的现有Web应用程序。仅当Web应用程序不运行时才启动它。
* </p>
*
* @param contextPath
* @throws IOException
*/
public void start(String contextPath) throws IOException; /**
*
*
* <p>
* Title: stop
* </p>
*
* @date 2019年1月2日 下午10:32:53
*
* <p>
* 功能描述:关闭附加到指定Context 路径的现有Web应用程序。仅在Web应用程序运行时才关闭。
* </p>
*
* @param contextPath
* @throws IOException
*/
public void stop(String contextPath) throws IOException; }
StandardHost类使用了一个辅助类(org.apahce.catalina.core.StandardHostDeployer)来完成部署与安装Web应用程序的相关任务,下面是StandardHost类的部分代码片段,展示了StandardHost对象是如何将部署任务委托给StandardHostDeployer实例来完成的,
/**
* 我们将应用程序部署请求委托给的<code>Deployer</code>
*/
private Deployer deployer = new StandardHostDeployer(this);
public 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 start(String contextPath) throws IOException { deployer.start(contextPath); } public void stop(String contextPath) throws IOException { deployer.stop(contextPath); }
下面介绍一下StandardHostDeployer
StandardHostDeployer类
org.apahce.catalina.core.StandardHostDeployer类是一个辅助类,帮助完成将Web应用程序部署到StandardHost实例的工作,StandardHostDeployer实例由StandardHost对象调用,在其构造函数中,会传入StandardHost的一个实例对象
public StandardHostDeployer(StandardHost host) { super();
this.host = host; }
安装一个描述符
StandardHostDeployer类有两个install方法,下面介绍第一个install方法,它用来安装一个描述符,下面的代码给出的install方法用来安装描述符,当HostConfig对象的deployDescriptors方法调用了install方法后,StandardHost实例调用该install方法:
public synchronized void install(URL config, URL war) throws IOException { // 验证参数的格式和状态
if (config == null)
throw new IllegalArgumentException(sm.getString("standardHost.configRequired")); if (!host.isDeployXML())
throw new IllegalArgumentException(sm.getString("standardHost.configNotAllowed")); // 计算新Web应用程序的文档库(如果需要)
String docBase = null; // 配置文件中值的可选重写
if (war != null) {
String url = war.toString();
host.log(sm.getString("standardHost.installingWAR", url));
// 计算war文件的绝对路径名
if (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);
else
throw new IllegalArgumentException(sm.getString("standardHost.warURL", url)); } // 安装新的Web应用程序
this.context = null;
this.overrideDocBase = docBase;
InputStream stream = null;
try {
stream = config.openStream();
// 老话常谈了 这个就是自定义了一些Rule 有空自己在单独看吧
Digester digester = createDigester();
digester.setDebug(host.getDebug());
digester.clear();
// Digester将 StandardHostDeployer放到 栈中的第一个位置
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) {
;
}
}
} }
安装一个WAR文件或者目录
第二个 install方法接收一个表示 Context路径的字符串 和一个表示WAR文件的URL,代码清单如下:
public synchronized void install(String contextPath, URL war) throws IOException { // 验证参数的格式和状态
if (contextPath == null)
throw new IllegalArgumentException(sm.getString("standardHost.pathRequired"));
if (!contextPath.equals("") && !contextPath.startsWith("/"))
throw new IllegalArgumentException(sm.getString("standardHost.pathFormat", contextPath));
// 先看看这个Context 是否已经被部署了
if (findDeployedApp(contextPath) != null)
throw new IllegalStateException(sm.getString("standardHost.pathUsed", contextPath));
if (war == null)
throw new IllegalArgumentException(sm.getString("standardHost.warRequired")); // 计算新Web应用程序的文档库
host.log(sm.getString("standardHost.installing", contextPath, war.toString()));
String url = war.toString();
String docBase = null;
if (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);
else
throw new IllegalArgumentException(sm.getString("standardHost.warURL", url)); // 安装新的Web应用程序
try {
Class clazz = Class.forName(host.getContextClass());
Context context = (Context) clazz.newInstance();
context.setPath(contextPath);
// 重点在TM这里 就是将web库设置给新实例化的Context
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);
//安装完这个Context之后 就会被添加到Host实例当中
host.addChild(context);
host.fireContainerEvent(INSTALL_EVENT, context);
} catch (Exception e) {
host.log(sm.getString("standardHost.installError", contextPath), e);
throw new IOException(e.toString());
} }
启动Context实例
StandardHostDeployer类中的start方法用于启动刚刚被安装的Context实例:
public void start(String contextPath) throws IOException {
// 验证参数的有效性
if (contextPath == null)
throw new IllegalArgumentException(sm.getString("standardHost.pathRequired"));
if (!contextPath.equals("") && !contextPath.startsWith("/"))
throw new IllegalArgumentException(sm.getString("standardHost.pathFormat", contextPath));
// 根据 contextpath 找到 被安装的Context实例
Context context = findDeployedApp(contextPath);
if (context == null)
throw new IllegalArgumentException(sm.getString("standardHost.pathMissing", contextPath));
host.log("standardHost.start " + contextPath);
try {
// 启动Context
((Lifecycle) context).start();
} catch (LifecycleException e) {
host.log("standardHost.start " + contextPath + ": ", e);
throw new IllegalStateException("standardHost.start " + contextPath + ": " + e);
}
}
那同样 有启动 就会有 停止
停止一个Context实例
StandardHostDeployer类的stop方法可以用来通知一个Context实例,代码清单如下:
public void stop(String contextPath) throws IOException { // 验证参数的有效性
if (contextPath == null)
throw new IllegalArgumentException(sm.getString("standardHost.pathRequired"));
if (!contextPath.equals("") && !contextPath.startsWith("/"))
throw new IllegalArgumentException(sm.getString("standardHost.pathFormat", contextPath));
// 根据contextPath 来找到 被安装的Context实例
Context context = findDeployedApp(contextPath);
if (context == null)
throw new IllegalArgumentException(sm.getString("standardHost.pathMissing", contextPath));
host.log("standardHost.stop " + contextPath);
// 关闭这个Context
try {
((Lifecycle) context).stop();
} catch (LifecycleException e) {
host.log("standardHost.stop " + contextPath + ": ", e);
throw new IllegalStateException("standardHost.stop " + contextPath + ": " + e);
} }
部署器是用来部署和安装Web应用程序的组件,是org.apache.catalina.core.Deployer接口的实例,StandHost类实现了Deployer接口,使其成为一个可以向其中部署web应用程序的特殊容器。StandardHost类会将部署和安装web应用程序的任务委托给StandardHostDeployer来完成,StandardHostDeployer类提供了部署和安装Web应用程序以及启动、关闭Context实例的具体实现。
Tomcat 的部署器的更多相关文章
- tomcat源码阅读之部署器
我们知道web应用是用Context实例表示的,而Context是部署到Host实例中的,因此tomcat的部署器是关联的Host实例.Context实例可以用WAR文件部署,也可以把整个web应用的 ...
- tomcat源码解读(1)–tomcat热部署实现原理
tomcat的热部署实现原理:tomcat启动的时候会有启动一个线程每隔一段时间会去判断应用中加载的类是否发生变法(类总数的变化,类的修改),如果发生了变化就会把应用的启动的线程停止掉,清除引用,并且 ...
- 【JVM】linux上tomcat中部署的web服务,时好时坏,莫名其妙宕机,报错:There is insufficient memory for the Java Runtime Environment to continue.
=========================================================================================== 环境: linu ...
- Tomcat的类加载器
看完了Java类装载器,我们再来看看应用服务器(Tomcat)对类加载器的使用,每个应用服务器都有一套自己的类加载器体系,从而与Java的类加载器区别开以达到自己与应用程序隔离的目的.Tomcat的类 ...
- Tomcat热部署的实现原理
Tomcat热部署机制 对于Java应用程序来说,热部署就是在运行时更新Java类文件.在基于Java的应用服务器实现热部署的过程中,类装入器扮演着重要的角色.大多数基于Java的应用服务器,包括EJ ...
- JFinal 项目 在tomcat下部署
原文:http://my.oschina.net/jfinal/blog/353062 首先明确一下 JFinal 项目是标准的 java web 项目,其部署方式与普通 java web 项目没有任 ...
- Intellij IDEA 创建Web项目并在Tomcat中部署运行(不使用maven)【转载】
原文链接:http://www.thinksaas.cn/topics/0/350/350000.html 一.创建Web项目 1.File -> New Module,进入创建项目窗口 2.选 ...
- 在tomcat下部署工程
xx系统第一期工程完成,今天老大要我去部署系统,从来就没有在tomcat下部署过,一直都是在myeclipse下部署.启动.运行即可,所以这次遇到了几个问题,记录下来. tomcat启动 在安装tom ...
- [Linux]Linux下安装和配置solr/tomcat/IK分词器 详细实例二.
为了更好的排版, 所以将IK分词器的安装重启了一篇博文, 大家可以接上solr的安装一同查看.[Linux]Linux下安装和配置solr/tomcat/IK分词器 详细实例一: http://ww ...
随机推荐
- Leetcode题目152.乘积最大子序列(动态规划-中等)
题目描述: 给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数). 示例 1: 输入: [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6 ...
- Remote Ubuntu VM from Windows
Need to install the xrdp tool on Ubuntu. To do this, open a Terminal window (Ctrl + Alt + T) and ent ...
- 数据库 | Oracle数据库查表空间使用情况
平时在使用Oracle的时候,如果业务中的数据量激增.数据量变大,很有可能就会有表空间不足的情况,需要重点关注.今天我们分享下如何查看表空间的使用情况. 一.如何查看使用状况 我们废话不说,先直接贴上 ...
- [转]Nginx实现高并发的原理
Nginx 首先要明白,Nginx 采用的是多进程(单线程) & 多路IO复用模型.使用了 I/O 多路复用技术的 Nginx,就成了”并发事件驱动“的服务器. 异步非阻塞(AIO)的详解ht ...
- Python多进程库multiprocessing创建进程以及进程池Pool类的使用
问题起因最近要将一个文本分割成好几个topic,每个topic设计一个regressor,各regressor是相互独立的,最后汇总所有topic的regressor得到总得预测结果.没错!类似bag ...
- 机器学习之Xgboost算法
知识点 """ xgboost:是一种提升算法,串行的决策树 过程: 第一棵树:目标值:1000 ,预测值:950 第二颗树:目标值:1000-950=50(残差作为输入 ...
- Highcharts的常用属性及导出SVG
做了好几个项目都用到了Highcharts,好用自然不用多说,这里总结一下每次作图经常用的一些配置,方便以后查看. 引入Highcharts非常简单,只需引入对应的js,页面中写入存放表格的div,j ...
- vue 面试题(文章末尾还有其他链接)
https://segmentfault.com/a/1190000016344599
- Linux系统管理_主题01 :初识Linux_1.5 与Linux进行交互_echo_nano_Tab_whoami
[root@izkfv3zmvcl0omz ~]# 其中,'root'为登录用户名,'izkfv3zmvcl0omz'为登录主机名,’~’ 表示当前用户正处在 root 用户的 家目录中,’#’则表示 ...
- 代码格式化工具 AStyle
Astyle是一个用来对C/C++代码进行格式化的工具,在windows或者linux都有对应的版本,下面介绍几个本人比较常用的参数 --style=linux 个人比较喜欢linux风格,即函数的 ...