一、概述
Host类中比较重要的类就是HostConfig其它类实现的功能和之前的组件差不多,这里就不多介绍了。
二、阅读源码
1、HostConfig
(1)重要方法
lifecycleEvent:
根据对应的方法设置对应的属性,并调用对应的方法。
@Override
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.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
start();
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
stop();
}
}
beforeStart:
beforeStart创建了如下图的两个目录。
public void beforeStart() {
if (host.getCreateDirs()) {
File[] dirs = new File[] {host.getAppBaseFile(),host.getConfigBaseFile()};
for (File dir : dirs) {
if (!dir.mkdirs() && !dir.isDirectory()) {
log.error(sm.getString("hostConfig.createDirs", dir));
}
}
}
}
start:
start调用了deployApps部署对应的App。
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.warn(sm.getString("hostConfig.jmx.register", oname), e);
}
if (!host.getAppBaseFile().isDirectory()) {
log.error(sm.getString("hostConfig.appBase", host.getName(),
host.getAppBaseFile().getPath()));
host.setDeployOnStartup(false);
host.setAutoDeploy(false);
}
if (host.getDeployOnStartup()) {
deployApps();
}
}
deployApps:
protected void deployApps() {
File appBase = host.getAppBaseFile();
File configBase = host.getConfigBaseFile();
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
deployDescriptors:
根据xml文件部署应用。
protected void deployDescriptors(File configBase, String[] files) {
if (files == null) {
return;
}
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<>();
for (String file : files) {
File contextXml = new File(configBase, file);
if (file.toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
ContextName cn = new ContextName(file, true);
if (tryAddServiced(cn.getName())) {
try {
if (deploymentExists(cn.getName())) {
removeServiced(cn.getName());
continue;
}
// DeployDescriptor will call removeServiced
results.add(es.submit(new DeployDescriptor(this, cn, contextXml)));
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
removeServiced(cn.getName());
throw t;
}
}
}
}
for (Future<?> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("hostConfig.deployDescriptor.threaded.error"), e);
}
}
}
deployWARs:
根据war包部署文件。
protected void deployWARs(File appBase, String[] files) {
if (files == null) {
return;
}
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<>();
for (String file : files) {
if (file.equalsIgnoreCase("META-INF")) {
continue;
}
if (file.equalsIgnoreCase("WEB-INF")) {
continue;
}
File war = new File(appBase, file);
if (file.toLowerCase(Locale.ENGLISH).endsWith(".war") && war.isFile() && !invalidWars.contains(file)) {
ContextName cn = new ContextName(file, true);
if (tryAddServiced(cn.getName())) {
try {
if (deploymentExists(cn.getName())) {
DeployedApplication app = deployed.get(cn.getName());
boolean unpackWAR = unpackWARs;
if (unpackWAR && host.findChild(cn.getName()) instanceof StandardContext) {
unpackWAR = ((StandardContext) host.findChild(cn.getName())).getUnpackWAR();
}
if (!unpackWAR && 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;
}
}
removeServiced(cn.getName());
continue;
}
// Check for WARs with /../ /./ or similar sequences in the name
if (!validateContextPath(appBase, cn.getBaseName())) {
log.error(sm.getString("hostConfig.illegalWarName", file));
invalidWars.add(file);
removeServiced(cn.getName());
continue;
}
// DeployWAR will call removeServiced
results.add(es.submit(new DeployWar(this, cn, war)));
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
removeServiced(cn.getName());
throw t;
}
}
}
}
for (Future<?> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("hostConfig.deployWar.threaded.error"), e);
}
}
}
deployDirectories:
根据文件夹部署文件。
protected void deployDirectories(File appBase, String[] files) {
if (files == null) {
return;
}
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<>();
for (String file : files) {
if (file.equalsIgnoreCase("META-INF")) {
continue;
}
if (file.equalsIgnoreCase("WEB-INF")) {
continue;
}
File dir = new File(appBase, file);
if (dir.isDirectory()) {
ContextName cn = new ContextName(file, false);
if (tryAddServiced(cn.getName())) {
try {
if (deploymentExists(cn.getName())) {
removeServiced(cn.getName());
continue;
}
// DeployDirectory will call removeServiced
results.add(es.submit(new DeployDirectory(this, cn, dir)));
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
removeServiced(cn.getName());
throw t;
}
}
}
}
for (Future<?> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("hostConfig.deployDir.threaded.error"), e);
}
}
}
check :
监听资源是否变动,变动的话就刷新上下文或者重新部署。
protected void check() {
if (host.getAutoDeploy()) {
// Check for resources modification to trigger redeployment
DeployedApplication[] apps = deployed.values().toArray(new DeployedApplication[0]);
for (DeployedApplication app : apps) {
if (tryAddServiced(app.name)) {
try {
checkResources(app, false);
} finally {
removeServiced(app.name);
}
}
}
// Check for old versions of applications that can now be undeployed
if (host.getUndeployOldVersions()) {
checkUndeploy();
}
// Hotdeploy applications
deployApps();
}
}
checkResources:
check方法具体调用的监听方法。
protected synchronized void checkResources(DeployedApplication app,
boolean skipFileModificationResolutionCheck) {
String[] resources =
app.redeployResources.keySet().toArray(new String[0]);
// Offset the current time by the resolution of File.lastModified()
long currentTimeWithResolutionOffset =
System.currentTimeMillis() - FILE_MODIFICATION_RESOLUTION_MS;
for (int i = 0; i < resources.length; i++) {
File resource = new File(resources[i]);
if (log.isDebugEnabled()) {
log.debug("Checking context[" + app.name +
"] redeploy resource " + resource);
}
long lastModified =
app.redeployResources.get(resources[i]).longValue();
if (resource.exists() || lastModified == 0) {
// File.lastModified() has a resolution of 1s (1000ms). The last
// modified time has to be more than 1000ms ago to ensure that
// modifications that take place in the same second are not
// missed. See Bug 57765.
if (resource.lastModified() != lastModified && (!host.getAutoDeploy() ||
resource.lastModified() < currentTimeWithResolutionOffset ||
skipFileModificationResolutionCheck)) {
if (resource.isDirectory()) {
// No action required for modified directory
app.redeployResources.put(resources[i],
Long.valueOf(resource.lastModified()));
} else if (app.hasDescriptor &&
resource.getName().toLowerCase(
Locale.ENGLISH).endsWith(".war")) {
// Modified WAR triggers a reload if there is an XML
// file present
// The only resource that should be deleted is the
// expanded WAR (if any)
Context context = (Context) host.findChild(app.name);
String docBase = context.getDocBase();
if (!docBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
// This is an expanded directory
File docBaseFile = new File(docBase);
if (!docBaseFile.isAbsolute()) {
docBaseFile = new File(host.getAppBaseFile(),
docBase);
}
reload(app, docBaseFile, resource.getAbsolutePath());
} else {
reload(app, null, null);
}
// Update times
app.redeployResources.put(resources[i],
Long.valueOf(resource.lastModified()));
app.timestamp = System.currentTimeMillis();
boolean unpackWAR = unpackWARs;
if (unpackWAR && context instanceof StandardContext) {
unpackWAR = ((StandardContext) context).getUnpackWAR();
}
if (unpackWAR) {
addWatchedResources(app, context.getDocBase(), context);
} else {
addWatchedResources(app, null, context);
}
return;
} else {
// Everything else triggers a redeploy
// (just need to undeploy here, deploy will follow)
undeploy(app);
deleteRedeployResources(app, resources, i, false);
return;
}
}
} else {
// There is a chance the the resource was only missing
// temporarily eg renamed during a text editor save
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
// Ignore
}
// Recheck the resource to see if it was really deleted
if (resource.exists()) {
continue;
}
// Undeploy application
undeploy(app);
deleteRedeployResources(app, resources, i, true);
return;
}
}
resources = app.reloadResources.keySet().toArray(new String[0]);
boolean update = false;
for (String s : resources) {
File resource = new File(s);
if (log.isDebugEnabled()) {
log.debug("Checking context[" + app.name + "] reload resource " + resource);
}
long lastModified = app.reloadResources.get(s).longValue();
// File.lastModified() has a resolution of 1s (1000ms). The last
// modified time has to be more than 1000ms ago to ensure that
// modifications that take place in the same second are not
// missed. See Bug 57765.
if ((resource.lastModified() != lastModified &&
(!host.getAutoDeploy() ||
resource.lastModified() < currentTimeWithResolutionOffset ||
skipFileModificationResolutionCheck)) ||
update) {
if (!update) {
// Reload application
reload(app, null, null);
update = true;
}
// Update times. More than one file may have been updated. We
// don't want to trigger a series of reloads.
app.reloadResources.put(s,
Long.valueOf(resource.lastModified()));
}
app.timestamp = System.currentTimeMillis();
}
}
(2)处理流程
lifecycleEvent调用start()方法,start()方法再调用deployApps()方法,deployApps()方法根据情况分别调用deployDescriptors,deployWARs,deployDirectories创建应用。