阅读更多
我们知道tomcat启动 会带动我们自己的应用工程运行,有时候我们的应用工程却是以war形式存在的,那么tomcat 是怎么解析war工程,下面就是我对tomcat源码解析war工程的一些见解
1,我们知道tomcat有自己的一套运行周期,他的运行周期如图,

而 tomcat运行中,则通过LifecycleSupport类添加各种监听器 其中这些监听器以实现LifecycleListener接口的子类 下图是LifecyceListener的一些子类
我在这里重点讲述HostConfig 和ContextConfig
HostConfig对war工程前期的校验 而ContextConfig对war包的真正解析
LifecycleListener 代码如下
package org.apache.catalina;
public interface LifecycleListener {
public void lifecycleEvent(LifecycleEvent event);
}
2, HostConfig中lifecycleEvent方法如下
public void lifecycleEvent(LifecycleEvent event) {
// Identify the host we are associated with
try {
host = (Host) event.getLifecycle();
if (host instanceof StandardHost) {
setCopyXML(((StandardHost) host).isCopyXML());
setDeployXML(((StandardHost) host).isDeployXML());
setUnpackWARs(((StandardHost) host).isUnpackWARs());
setContextClass(((StandardHost) host).getContextClass());
}
} catch (ClassCastException e) {
log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
check();
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
start();
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
stop();
}
}
此方法中有三个状态 PERIODIC_EVENT START_EVENT STOP_EVENT 而我们知道war工程的解析不可能出现在结束状态 所以只能出现在 PERIODIC_EVENT START_EVENT 即check和start方法
下面请看两方法
protected void check() {
if (host.getAutoDeploy()) {
// Check for resources modification to trigger redeployment
DeployedApplication[] apps =
deployed.values().toArray(new DeployedApplication[0]);
for (int i = 0; i < apps.length; i++) {
if (!isServiced(apps[i].name))
checkResources(apps[i]);
}
// Check for old versions of applications that can now be undeployed
if (host.getUndeployOldVersions()) {
checkUndeploy();
}
// Hotdeploy applications
deployApps();
}
}
public void start() {
if (log.isDebugEnabled())
log.debug(sm.getString("hostConfig.start"));
try {
ObjectName hostON = host.getObjectName();
oname = new ObjectName
(hostON.getDomain() + ":type=Deployer,host=" + host.getName());
Registry.getRegistry(null, null).registerComponent
(this, oname, this.getClass().getName());
} catch (Exception e) {
log.error(sm.getString("hostConfig.jmx.register", oname), e);
}
if (host.getCreateDirs()) {
//创建目录中appBase属性下的目录(webapps)和conf/engine/host-name/ 这个目录个
File[] dirs = new File[] {appBase(),configBase()};
for (int i=0; i 有两个方法可知他们其实最终运行到同一方法上 deployApps方法 代码如下
protected void deployApps() {
//appBase属性是Server.xml中host节点的appBase属性 默认是webapps下
File appBase = appBase();
//F:\source\TOMCAT_7_0_57\output\build\conf\Catalina\localhost
File configBase = configBase();
//通过appBase.list()可以罗列出host所对应的appBase属性所指的目录下工程
//过滤appBase属性所指目录下的工程
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
//添加部署conf/Catalina/hostName/Context.xml这个context这个
deployDescriptors(configBase, configBase.list());
// Deploy WARs
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
/**
* Deploy WAR files.
*/
protected void deployWARs(File appBase, String[] files) {
if (files == null)
return;
ExecutorService es = host.getStartStopExecutor();
List> results = new ArrayList>();
for (int i = 0; i < files.length; i++) {
/**
* 排除webapps 下WEB-INF 和META-INF这种的文件夹
*/
if (files[i].equalsIgnoreCase("META-INF"))
continue;
if (files[i].equalsIgnoreCase("WEB-INF"))
continue;
File war = new File(appBase, files[i]);
//查看webapps下是否有.war结束的文件
if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") &&
war.isFile() && !invalidWars.contains(files[i]) ) {
ContextName cn = new ContextName(files[i], true);
if (isServiced(cn.getName())) {
continue;
}
//简单校验一下war工程
if (deploymentExists(cn.getName())) {
DeployedApplication app = deployed.get(cn.getName());
if (!unpackWARs && app != null) {
// Need to check for a directory that should not be
// there
File dir = new File(appBase, cn.getBaseName());
if (dir.exists()) {
if (!app.loggedDirWarning) {
log.warn(sm.getString(
"hostConfig.deployWar.hiddenDir",
dir.getAbsoluteFile(),
war.getAbsoluteFile()));
app.loggedDirWarning = true;
}
} else {
app.loggedDirWarning = false;
}
}
continue;
}
// Check for WARs with /../ /./ or similar sequences in the name
if (!validateContextPath(appBase, cn.getBaseName())) {
log.error(sm.getString(
"hostConfig.illegalWarName", files[i]));
invalidWars.add(files[i]);
continue;
}
//执行DeployWar中的run方法
results.add(es.submit(new DeployWar(this, cn, war)));
}
}
for (Future> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString(
"hostConfig.deployWar.threaded.error"), e);
}
}
}
最终解析war工程 到了 DeployWar(this, cn, war))这个类中的run方法中
private static class DeployWar implements Runnable {
private HostConfig config;
private ContextName cn;
private File war;
public DeployWar(HostConfig config, ContextName cn, File war) {
this.config = config;
this.cn = cn;
this.war = war;
}
@Override
public void run() {
//执行HostConfig类中的deyloyWar
config.deployWAR(cn, war);
}
}
又回到 HostConfig的deployWAR方法中
/**
* @param cn
* @param war
* 解压war包
* 最终真正解压war包的却是ContextConfig
*/
protected void deployWAR(ContextName cn, File war) {
// Checking for a nested /META-INF/context.xml
JarFile jar = null;
InputStream istream = null;
FileOutputStream fos = null;
BufferedOutputStream ostream = null;
//检查War工程下是否有此文件
File xml = new File(appBase(),
cn.getBaseName() + "/META-INF/context.xml");
boolean xmlInWar = false;
JarEntry entry = null;
/**
* 以jar的形式访问War工程
*/
try {
jar = new JarFile(war);
entry = jar.getJarEntry(Constants.ApplicationContextXml);
if (entry != null) {
xmlInWar = true;
}
} catch (IOException e) {
/* Ignore */
} finally {
entry = null;
if (jar != null) {
try {
jar.close();
} catch (IOException ioe) {
// Ignore;
}
jar = null;
}
}
Context context = null;
//解析Context.xml文件中Context节点生成Context接口的类
/**
* 正常情况生成StandardContext类 但是也可能出现失败情况 则生成FailedContext类
*/
try {
if (deployXML && xml.exists() && !copyXML) {
synchronized (digesterLock) {
try {
context = (Context) digester.parse(xml);
} catch (Exception e) {
log.error(sm.getString(
"hostConfig.deployDescriptor.error",
war.getAbsolutePath()), e);
} finally {
if (context == null) {
context = new FailedContext();
}
digester.reset();
}
}
context.setConfigFile(xml.toURI().toURL());
} else if (deployXML && xmlInWar) {
synchronized (digesterLock) {
try {
jar = new JarFile(war);
entry =
jar.getJarEntry(Constants.ApplicationContextXml);
istream = jar.getInputStream(entry);
context = (Context) digester.parse(istream);
} catch (Exception e) {
log.error(sm.getString(
"hostConfig.deployDescriptor.error",
war.getAbsolutePath()), e);
} finally {
if (context == null) {
context = new FailedContext();
}
context.setConfigFile(new URL("jar:" +
war.toURI().toString() + "!/" +
Constants.ApplicationContextXml));
if (istream != null) {
try {
istream.close();
} catch (IOException e) {
/* Ignore */
}
istream = null;
}
entry = null;
if (jar != null) {
try {
jar.close();
} catch (IOException e) {
/* Ignore */
}
jar = null;
}
digester.reset();
}
}
} else if (!deployXML && xmlInWar) {
// Block deployment as META-INF/context.xml may contain security
// configuration necessary for a secure deployment.
log.error(sm.getString("hostConfig.deployDescriptor.blocked",
cn.getPath(), Constants.ApplicationContextXml,
new File(configBase(), cn.getBaseName() + ".xml")));
} else {
context = (Context) Class.forName(contextClass).newInstance();
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("hostConfig.deployWar.error",
war.getAbsolutePath()), t);
} finally {
if (context == null) {
context = new FailedContext();
}
}
boolean copyThisXml = false;
if (deployXML) {
if (host instanceof StandardHost) {
copyThisXml = ((StandardHost) host).isCopyXML();
}
// If Host is using default value Context can override it.
if (!copyThisXml && context instanceof StandardContext) {
copyThisXml = ((StandardContext) context).getCopyXML();
}
if (xmlInWar && copyThisXml) {
// Change location of XML file to config base
xml = new File(configBase(), cn.getBaseName() + ".xml");
entry = null;
try {
jar = new JarFile(war);
entry =
jar.getJarEntry(Constants.ApplicationContextXml);
istream = jar.getInputStream(entry);
fos = new FileOutputStream(xml);
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 */
} finally {
if (ostream != null) {
try {
ostream.close();
} catch (IOException ioe) {
// Ignore
}
ostream = null;
}
if (fos != null) {
try {
fos.close();
} catch (IOException ioe) {
// Ignore
}
fos = null;
}
if (istream != null) {
try {
istream.close();
} catch (IOException ioe) {
// Ignore
}
istream = null;
}
if (jar != null) {
try {
jar.close();
} catch (IOException ioe) {
// Ignore;
}
jar = null;
}
}
}
}
DeployedApplication deployedApp = new DeployedApplication(cn.getName(),
xml.exists() && deployXML && copyThisXml);
long startTime = 0;
// Deploy the application in this WAR file
//开始解压war包 并且交给ContextConfig
//因为Context 才是真正的应用节点动 Host只是在这里校验 检测路径等 xml文件预设是否完善的等工作的了解
if(log.isInfoEnabled()) {
startTime = System.currentTimeMillis();
log.info(sm.getString("hostConfig.deployWar",
war.getAbsolutePath()));
}
try {
// Populate redeploy resources with the WAR file
deployedApp.redeployResources.put
(war.getAbsolutePath(), Long.valueOf(war.lastModified()));
if (deployXML && 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(configBase(),
cn.getBaseName() + ".xml")).getAbsolutePath(),
Long.valueOf(0));
}
//class org.apache.catalina.startup.ContextConfig
//添加ContextConfig这个监听器
Class> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
context.addLifecycleListener(listener);
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName() + ".war");
//往Host节点中添加Context节点(从conf/server.xml 我们可知Context是host的一个子节点 这里正常情况下StandardContext)
host.addChild(context);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("hostConfig.deployWar.error",
war.getAbsolutePath()), t);
} finally {
// If we're unpacking WARs, the docBase will be mutated after
// starting the context
if (unpackWARs && context != null && context.getDocBase() != null) {
File docBase = new File(appBase(), cn.getBaseName());
deployedApp.redeployResources.put(docBase.getAbsolutePath(),
Long.valueOf(docBase.lastModified()));
addWatchedResources(deployedApp, docBase.getAbsolutePath(),
context);
if (deployXML && !copyThisXml && (xmlInWar || xml.exists())) {
deployedApp.redeployResources.put(xml.getAbsolutePath(),
Long.valueOf(xml.lastModified()));
}
} else {
// Passing null for docBase means that no resources will be
// watched. This will be logged at debug level.
addWatchedResources(deployedApp, null, context);
}
// Add the global redeploy resources (which are never deleted) at
// the end so they don't interfere with the deletion process
addGlobalRedeployResources(deployedApp);
}
deployed.put(cn.getName(), deployedApp);
if (log.isInfoEnabled()) {
log.info(sm.getString("hostConfig.deployWar.finished",
war.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime)));
}
}
至此HostConfig对War解析完成,但是我们也没看到war解压的那一下啊,上面只是对war工程路径,名称,里面包含的Context.xml校验 以及Context节点叫生成,下面请看ContextConfig对象运行流程
他跟HostConfig一样 我们先看lifecycleEvent方法
@Override
public void lifecycleEvent(LifecycleEvent event) {
// Identify the context we are associated with
try {
context = (Context) event.getLifecycle();
} catch (ClassCastException e) {
log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
//===================3=========================
configureStart();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
//===================2=========================
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
} else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
configureStop();
} else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
//===================1=========================
init();
} else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
destroy();
}
}
我们从上述状态中可以知道 只有三个可能 init(); beforeStart(); configureStart(); 按照顺序找 我们找到before_start才是解析war的关键所在
protected synchronized void beforeStart() {
try {
//整理server.xml中context下这个的docBase这个文件中的目录
fixDocBase();
} catch (IOException e) {
log.error(sm.getString(
"contextConfig.fixDocBase", context.getName()), e);
}
antiLocking();
}
/**
* Adjust docBase.
*/
protected void fixDocBase()
throws IOException {
Host host = (Host) context.getParent();
String appBase = host.getAppBase();
File canonicalAppBase = new File(appBase);
//检查server.xml中中AppBase属性的值是不是代表一个绝对路径
if (canonicalAppBase.isAbsolute()) {
canonicalAppBase = canonicalAppBase.getCanonicalFile();
} else {
canonicalAppBase =
new File(getBaseDir(), appBase)
.getCanonicalFile();
}
//找到server.xml的context中docBase是否存在
String docBase = context.getDocBase();
if (docBase == null) {
//docBase不存在
/*如果path不存在或者"/" 或者"/ROOT"这样的形式
* 那么docBase就为ROOT文件加下
* 如果path存在就是path文件加下
*/
// Trying to guess the docBase according to the path
String path = context.getPath();
if (path == null) {
return;
}
ContextName cn = new ContextName(path, context.getWebappVersion());
docBase = cn.getBaseName();
}
File file = new File(docBase);
if (!file.isAbsolute()) {
docBase = (new File(canonicalAppBase, docBase)).getPath();
} else {
docBase = file.getCanonicalPath();
}
file = new File(docBase);
String origDocBase = docBase;
ContextName cn = new ContextName(context.getPath(),
context.getWebappVersion());
String pathName = cn.getBaseName();
boolean unpackWARs = true;
if (host instanceof StandardHost) {
unpackWARs = ((StandardHost) host).isUnpackWARs();
if (unpackWARs && context instanceof StandardContext) {
unpackWARs = ((StandardContext) context).getUnpackWAR();
}
}
/**
* 解压war工程
*/
if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) {
if (unpackWARs) {
URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/");
//正式对xxx.war进行分析生成xxx工程
docBase = ExpandWar.expand(host, war, pathName);
file = new File(docBase);
docBase = file.getCanonicalPath();
if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(origDocBase);
}
} else {
URL war =
new URL("jar:" + (new File (docBase)).toURI().toURL() + "!/");
ExpandWar.validate(host, war, pathName);
}
} else {
File docDir = new File(docBase);
if (!docDir.exists()) {
File warFile = new File(docBase + ".war");
if (warFile.exists()) {
URL war =
new URL("jar:" + warFile.toURI().toURL() + "!/");
if (unpackWARs) {
docBase = ExpandWar.expand(host, war, pathName);
file = new File(docBase);
docBase = file.getCanonicalPath();
} else {
docBase = warFile.getCanonicalPath();
ExpandWar.validate(host, war, pathName);
}
}
if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(origDocBase);
}
}
}
if (docBase.startsWith(canonicalAppBase.getPath() + File.separatorChar)) {
docBase = docBase.substring(canonicalAppBase.getPath().length());
docBase = docBase.replace(File.separatorChar, '/');
if (docBase.startsWith("/")) {
docBase = docBase.substring(1);
}
} else {
docBase = docBase.replace(File.separatorChar, '/');
}
context.setDocBase(docBase);
}
//正式对xxx.war进行分析生成xxx工程
docBase = ExpandWar.expand(host, war, pathName); 这段中开始生成真正的工程
代码如下
public static String expand(Host host, URL war, String pathname)
throws IOException {
// Make sure that there is no such directory already existing
File appBase = new File(host.getAppBase());
if (!appBase.isAbsolute()) {
appBase = new File(System.getProperty(Globals.CATALINA_BASE_PROP),
host.getAppBase());
}
if (!appBase.exists() || !appBase.isDirectory()) {
throw new IOException
(sm.getString("hostConfig.appBase",
appBase.getAbsolutePath()));
}
//工程路径存在吗 存在说明已经解压好了
File docBase = new File(appBase, pathname);
if (docBase.exists()) {
// War file is already installed
return (docBase.getAbsolutePath());
}
//不存在则创建 工程路径,一般以war文件名创建路径
// Create the new document base directory
if(!docBase.mkdir() && !docBase.isDirectory())
throw new IOException(sm.getString("expandWar.createFailed", docBase));
// Expand the WAR into the new document base directory
String canonicalDocBasePrefix = docBase.getCanonicalPath();
if (!canonicalDocBasePrefix.endsWith(File.separator)) {
canonicalDocBasePrefix += File.separator;
}
/**
* 下面是以jar方式访问war工程中的文件
* 然后将war工程xxx内部的文件全部复制到工程xxx目录下
*/
JarURLConnection juc = (JarURLConnection) war.openConnection();
juc.setUseCaches(false);
JarFile jarFile = null;
InputStream input = null;
boolean success = false;
try {
jarFile = juc.getJarFile();
Enumeration jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String name = jarEntry.getName();
File expandedFile = new File(docBase, name);
if (!expandedFile.getCanonicalPath().startsWith(
canonicalDocBasePrefix)) {
// Trying to expand outside the docBase
// Throw an exception to stop the deployment
throw new IllegalArgumentException(
sm.getString("expandWar.illegalPath",war, name,
expandedFile.getCanonicalPath(),
canonicalDocBasePrefix));
}
int last = name.lastIndexOf('/');
if (last >= 0) {
File parent = new File(docBase,
name.substring(0, last));
if (!parent.mkdirs() && !parent.isDirectory()) {
throw new IOException(
sm.getString("expandWar.createFailed", parent));
}
}
if (name.endsWith("/")) {
continue;
}
input = jarFile.getInputStream(jarEntry);
if(null == input)
throw new ZipException(sm.getString("expandWar.missingJarEntry", jarEntry.getName()));
// Bugzilla 33636
expand(input, expandedFile);
long lastModified = jarEntry.getTime();
if ((lastModified != -1) && (lastModified != 0)) {
expandedFile.setLastModified(lastModified);
}
input.close();
input = null;
}
success = true;
} catch (IOException e) {
throw e;
} finally {
if (!success) {
// If something went wrong, delete expanded dir to keep things
// clean
deleteDir(docBase);
}
if (input != null) {
try {
input.close();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
input = null;
}
if (jarFile != null) {
try {
jarFile.close();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
jarFile = null;
}
}
// Return the absolute path to our new document base directory
return (docBase.getAbsolutePath());
}
上述代码我们可以知道war这样的工程解压是,通过jar形式的api访问war内部文件 然后复制到工程文件夹一种解析
2, 大家有没有注意到 ,平时我们启动了tomat以后 只要修改工程里的文件 马上他就会重新部署这一现象,这是因为tomat 会检测webapps 下工程改动变换,从而调整做出新的部署
代码如下ContainerBase 中threadStart 方法
protected void threadStart() {
if (thread != null)
return;
if (backgroundProcessorDelay <= 0)
return;
threadDone = false;
String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
thread = new Thread(new ContainerBackgroundProcessor(), threadName);
thread.setDaemon(true);
thread.start();
}
ContainerBackgroundProcessor启动容器守护线程 做这一件事,以每隔backgroundProcessorDelay秒的执行
protected class ContainerBackgroundProcessor implements Runnable {
@Override
public void run() {
Throwable t = null;
String unexpectedDeathMessage = sm.getString(
"containerBase.backgroundProcess.unexpectedThreadDeath",
Thread.currentThread().getName());
try {
while (!threadDone) {
try {
Thread.sleep(backgroundProcessorDelay * 1000L);
} catch (InterruptedException e) {
// Ignore
}
if (!threadDone) {
Container parent = (Container) getMappingObject();
ClassLoader cl =
Thread.currentThread().getContextClassLoader();
if (parent.getLoader() != null) {
cl = parent.getLoader().getClassLoader();
}
processChildren(parent, cl);
}
}
} catch (RuntimeException e) {
t = e;
throw e;
} catch (Error e) {
t = e;
throw e;
} finally {
if (!threadDone) {
log.error(unexpectedDeathMessage, t);
}
protected void processChildren(Container container, ClassLoader cl) {
try {
if (container.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(container.getLoader().getClassLoader());
}
//这里container是StandardHost
container.backgroundProcess();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("Exception invoking periodic operation: ", t);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i].getBackgroundProcessorDelay() <= 0) {
processChildren(children[i], cl);
}
}
}
}
StandardHost 方法可知
backgroundProcess
public void backgroundProcess() {
if (!getState().isAvailable())
return;
if (cluster != null) {
try {
cluster.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e);
}
}
if (loader != null) {
try {
loader.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e);
}
}
if (manager != null) {
try {
manager.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e);
}
}
Realm realm = getRealmInternal();
if (realm != null) {
try {
realm.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e);
}
}
Valve current = pipeline.getFirst();
while (current != null) {
try {
current.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e);
}
current = current.getNext();
}
fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
}
激活状态的 Lifecycle.PERIODIC_EVENT 代码如: fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
此状态运行 我们在前面代码HostConfig中可以知道
HostConfig中lifecycleEvent方法如下
public void lifecycleEvent(LifecycleEvent event) {
// Identify the host we are associated with
try {
host = (Host) event.getLifecycle();
if (host instanceof StandardHost) {
setCopyXML(((StandardHost) host).isCopyXML());
setDeployXML(((StandardHost) host).isDeployXML());
setUnpackWARs(((StandardHost) host).isUnpackWARs());
setContextClass(((StandardHost) host).getContextClass());
}
} catch (ClassCastException e) {
log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
check();
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
start();
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
stop();
}
}
综上可以 tomcat定时的去扫描webapps目录下工程的变化

- 大小: 18.3 KB

- 大小: 135.9 KB