我们知道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()) {
//创建目录<host>中appBase属性下的目录(webapps)和conf/engine/host-name/ 这个目录个
File[] dirs = new File[] {appBase(),configBase()};
for (int i=0; i<dirs.length; i++) {
if (!dirs[i].mkdirs() && !dirs[i].isDirectory()) {
log.error(sm.getString("hostConfig.createDirs",dirs[i]));
}
}
}
if (!appBase().isDirectory()) {
log.error(sm.getString(
"hostConfig.appBase", host.getName(), appBase().getPath()));
host.setDeployOnStartup(false);
host.setAutoDeploy(false);
}
if (host.getDeployOnStartup())
deployApps();
}
有两个方法可知他们其实最终运行到同一方法上 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<Future<?>> results = new ArrayList<Future<?>>();
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中<host>中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<JarEntry> 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
分享到:
相关推荐
【描述】"这是一个Eclipse的Tomcat源码工程,适合于查看Tomcat源码"意味着你可以通过Eclipse IDE高效地探索Tomcat的内部工作原理。Eclipse作为一款强大的Java开发工具,拥有优秀的代码导航、调试和重构功能,对于...
源码中的`Host`和`Context`类展示了如何解析WAR文件并加载应用到服务器。 5. **JMX(Java Management Extensions)**:Tomcat支持JMX,允许管理员监控和管理服务器状态。查看`jmx`目录下的源码可以了解如何使用JMX...
一、Tomcat7源码结构解析 Tomcat7的源代码结构清晰,主要包含以下几个核心模块: 1. catalina:这是Tomcat的核心模块,负责处理Servlet容器的主要功能,如Servlet和Context的生命周期管理,请求处理等。 2. ...
7. **部署与热部署**:Tomcat支持自动部署和热部署,只需将WAR文件放入webapps目录,Tomcat会自动解压并部署应用。修改应用后,无需重启服务器,Tomcat可以检测到变化并自动更新。 8. **安全性**:Tomcat提供多种...
【标题】"Tomcat源码分析" 在深入探讨Tomcat源码之前,首先需要了解Tomcat是什么。Tomcat是一款开源的、基于Java的Web应用服务器,由Apache软件基金会开发。它实现了Java Servlet和JavaServer Pages(JSP)规范,...
【Tomcat源码详解】 Tomcat,作为Apache...深入研究Tomcat源码,不仅能够帮助开发者理解Web服务器的工作原理,还能为优化应用性能、排查问题提供极大的便利。对于Java Web开发者而言,这是一个不可或缺的学习资源。
通过深入研究Apache Tomcat 8.0的源码,开发者可以了解Web服务器的工作原理,定制自己的容器,优化性能,甚至修复bug或开发新的功能。对于Java Web开发人员来说,这是一次宝贵的学习机会,有助于提升专业技能。
总的来说,通过研究Tomcat 6.0的源码,我们可以深入理解Web服务器的工作原理,这对于开发高性能、高可用性的Web应用至关重要。同时,源码分析也能提升我们在遇到问题时的诊断和解决能力,为日常运维带来便利。因此,...
总之,Apache Tomcat的源码解析是一次深入学习Java Web技术的宝贵机会,它可以帮助你更好地理解Servlet和JSP的运行机制,提升你的开发和调试技能。通过实际操作编译源码,你可以进一步掌握Maven和Java构建流程,为...
**Tomcat源码编译详解** Tomcat,作为Apache软件基金会的一个开源项目,是Java Servlet、JavaServer Pages(JSP)以及Java Expression Language(EL)的实现,是世界上最流行的Java应用服务器之一。深入理解并编译...
深入学习Tomcat源码,有助于我们掌握以下关键知识点: 1. **Servlet生命周期**:理解Servlet的初始化、服务、销毁过程,以及Tomcat如何管理和调度Servlet实例。 2. **容器结构**:Tomcat的容器模型包括Engine...
在这个项目中,开发者可能已经配置了相关的Maven插件和目标,以便于编译、测试和打包Tomcat源码。 【标签】: 1. **Tomcat8**:Tomcat 8是Tomcat服务器的第8个主要版本,它支持Java Servlet 3.1、JavaServer Pages...
二、Tomcat源码解析 1. **启动过程**:从`bin/catalina.sh`或`catalina.bat`开始,通过`Bootstrap`类启动`Catalina`,接着加载`Server.xml`配置,初始化各个组件。 2. **请求处理**:当请求到达时,Coyote连接器...
通过阅读和研究Tomcat 9.0.12的源码,开发者可以学习到如何设计和实现一个高效、可扩展的Web服务器,理解HTTP协议的处理流程,以及Java EE规范的实现细节。这对于提升Java服务器端开发能力非常有帮助。同时,源码也...
在【压缩包子文件的文件名称列表】中,"Tomcat源码"很可能是包含了一系列文件,如Java源代码、配置文件、文档等,这些内容将帮助学习者逐步解析Tomcat的架构和工作流程。 以下是基于这些信息可能会涵盖的一些Tomcat...
源码中,ClassLoader的实现(例如`org.apache.catalina.loader.WebappClassLoaderBase`)值得深入研究。 5. **部署和配置**:Tomcat支持多种方式部署Web应用,如WAR文件、目录结构或者通过管理接口。源码中,你可以...
《深入剖析Tomcat》这本书是Java开发者们探索Tomcat服务器内部机制的重要参考资料,它带领读者逐步揭开Tomcat的...通过学习和研究Tomcat源码,我们可以提升技术水平,解决实际问题,甚至为Tomcat社区贡献自己的力量。
《深入剖析Tomcat3:源码解析与运行机制》 Tomcat3作为Apache Tomcat的早期版本,虽然相比后来的6、7、8等版本在功能上显得较为简单,但正是这种简洁,使得它成为初学者研究Web服务器和Java Servlet容器理想的起点...
4. **部署和生命周期管理**:了解Tomcat如何解析并部署WAR文件,以及Web应用的启动、停止和更新过程。 5. **安全管理**:Tomcat提供了多种安全特性,如角色基的安全认证,理解这些机制有助于构建更安全的Web服务。 ...
通过研究Tomcat源码,开发者可以了解到以下内容: 1. **线程模型**:了解Tomcat如何管理线程池,处理并发请求,以及如何在高负载下保持性能。 2. **请求处理流程**:从接收到HTTP请求到返回响应的整个过程,包括...