Tomcat war包部署
开篇
这篇文章主要针对Tomcat针对War包部署进行一些常用的逻辑判断,这部分的核心逻辑主要回答了以下几个问题(这部分其实说实话我也不是特别明白)。
1、Context对象的创建。
2、配置变更重新部署的前置处理。
3、核心逻辑在于Context启动过程,但是这部分后面由单独的文章进行说明。
部署过程
-
1、对于Host的appBase目录下所有符合条件的war包,由线程池完成部署。
-
2、针对每个war包,按照如下逻辑进行判断创建StandardContext对象:
-
条件判断一:如果Host的deployXML属性为True,且在War包的同名目录下存在META-INF/context.xml文件,同时Context的copyXML属性为false,则用该context.xml创建Context实例。
-
条件判断二:如果Host的deployXML属性为True,且在War包压缩文件下存在META-INF/context.xml文件,则使用该文件创建Context对象。
-
条件判断三:如果deployXML属性为true,且在War包压缩文件下存在META-INF/context.xml文件,则构造FailedContext对象。
-
条件判断四:其他情况下以contextClass属性指定的类型创建Context对象,不指定则用org.apache.catalina.core.StandardContext来进行创建。
-
-
3、为Context实例添加ContextConfig生命周期监听器。
-
4、通过Host的addChild()方法将Context实例添加到Host。该方法会判断Host是否启动,如果已启动则直接启动Context对象。
-
5、将Context描述文件、War包及web.xml等资源添加到守护资源,以便文件发生变更时重新部署及加载Web应用。
源码解析
public class HostConfig implements LifecycleListener {
protected void deployWAR(ContextName cn, File war) {
// 在War包的同名目录下的META-INF/context.xml文件
File xml = new File(host.getAppBaseFile(),
cn.getBaseName() + "/" + Constants.ApplicationContextXml);
File warTracker = new File(host.getAppBaseFile(), cn.getBaseName() + Constants.WarTracker);
boolean xmlInWar = false;
try (JarFile jar = new JarFile(war)) {
JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
if (entry != null) {
xmlInWar = true;
}
} catch (IOException e) {
}
// 判断在War包的同名目录下存在META-INF/context.xml文件
boolean useXml = false;
if (xml.exists() && unpackWARs &&
(!warTracker.exists() || warTracker.lastModified() == war.lastModified())) {
useXml = true;
}
Context context = null;
boolean deployThisXML = isDeployThisXML(war, cn);
// 按照如下逻辑进行判断创建StandardContext对象
try {
// 使用War包的同名目录下存在META-INF/context.xml文件创建StandardContext对象
if (deployThisXML && useXml && !copyXML) {
synchronized (digesterLock) {
try {
context = (Context) digester.parse(xml);
} catch (Exception e) {
} finally {
digester.reset();
if (context == null) {
context = new FailedContext();
}
}
}
context.setConfigFile(xml.toURI().toURL());
} else if (deployThisXML && xmlInWar) {
// // 使用War包内的META-INF/context.xml文件创建StandardContext对象
synchronized (digesterLock) {
try (JarFile jar = new JarFile(war)) {
JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
try (InputStream istream = jar.getInputStream(entry)) {
context = (Context) digester.parse(istream);
}
} catch (Exception e) {
} finally {
digester.reset();
if (context == null) {
context = new FailedContext();
}
context.setConfigFile(
UriUtil.buildJarUrl(war, Constants.ApplicationContextXml));
}
}
} else if (!deployThisXML && xmlInWar) {
} else {
// 使用contextClass属性指定的类型创建Context对象,
// 不指定则用org.apache.catalina.core.StandardContext来进行创建。
context = (Context) Class.forName(contextClass).getConstructor().newInstance();
}
} catch (Throwable t) {
} finally {
if (context == null) {
context = new FailedContext();
}
}
boolean copyThisXml = false;
if (deployThisXML) {
if (host instanceof StandardHost) {
copyThisXml = ((StandardHost) host).isCopyXML();
}
if (!copyThisXml && context instanceof StandardContext) {
copyThisXml = ((StandardContext) context).getCopyXML();
}
if (xmlInWar && copyThisXml) {
// Change location of XML file to config base
xml = new File(host.getConfigBaseFile(),
cn.getBaseName() + ".xml");
try (JarFile jar = new JarFile(war)) {
JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
try (InputStream istream = jar.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(xml);
BufferedOutputStream ostream = new BufferedOutputStream(fos, 1024)) {
byte buffer[] = new byte[1024];
while (true) {
int n = istream.read(buffer);
if (n < 0) {
break;
}
ostream.write(buffer, 0, n);
}
ostream.flush();
}
} catch (IOException e) {
/* Ignore */
}
}
}
DeployedApplication deployedApp = new DeployedApplication(cn.getName(),
xml.exists() && deployThisXML && copyThisXml);
try {
deployedApp.redeployResources.put
(war.getAbsolutePath(), Long.valueOf(war.lastModified()));
if (deployThisXML && xml.exists() && copyThisXml) {
deployedApp.redeployResources.put(xml.getAbsolutePath(),
Long.valueOf(xml.lastModified()));
} else {
// In case an XML file is added to the config base later
deployedApp.redeployResources.put(
(new File(host.getConfigBaseFile(),
cn.getBaseName() + ".xml")).getAbsolutePath(),
Long.valueOf(0));
}
// 通过Host的addChild()方法将Context实例添加到Host
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
context.addLifecycleListener(listener);
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName() + ".war");
// 添加Context对象到StandardHost对象当中,
// 如果Host已启动那么同事启动Context对象。
host.addChild(context);
} catch (Throwable t) {
} finally {
// 将Context描述文件、War包及web.xml等资源添加到守护资源,
// 以便文件发生变更时重新部署及加载Web应用。
boolean unpackWAR = unpackWARs;
if (unpackWAR && context instanceof StandardContext) {
unpackWAR = ((StandardContext) context).getUnpackWAR();
}
if (unpackWAR && context.getDocBase() != null) {
File docBase = new File(host.getAppBaseFile(), cn.getBaseName());
deployedApp.redeployResources.put(docBase.getAbsolutePath(),
Long.valueOf(docBase.lastModified()));
addWatchedResources(deployedApp, docBase.getAbsolutePath(),
context);
if (deployThisXML && !copyThisXml && (xmlInWar || xml.exists())) {
deployedApp.redeployResources.put(xml.getAbsolutePath(),
Long.valueOf(xml.lastModified()));
}
} else {
addWatchedResources(deployedApp, null, context);
}
addGlobalRedeployResources(deployedApp);
}
deployed.put(cn.getName(), deployedApp);
}
}