HostConfig.deployApps() //在监听到start事件类型,也就是StandardHost调用startInternal protected void deployApps() { File appBase = host.getAppBaseFile(); //这个值是在触发before_start时间时生成的,默认是tomcat安装目录+engine名+host名 File configBase = host.getConfigBaseFile(); //获取host上配置的webapp下的所有文件,默认是webapps目录下的所有文件 String[] filteredAppPaths = filterAppPaths(appBase.list()); // Deploy XML descriptors from configBase 发布xml描述文件 deployDescriptors(configBase, configBase.list()); // Deploy WARs deployWARs(appBase, filteredAppPaths); // Deploy expanded folders deployDirectories(appBase, filteredAppPaths); } deployWARs protected void deployWARs(File appBase, String[] files) { if (files == null) return; ExecutorService es = host.getStartStopExecutor(); List<Future<?>> results = new ArrayList<>(); for (int i = 0; i < files.length; i++) { if (files[i].equalsIgnoreCase("META-INF")) continue; if (files[i].equalsIgnoreCase("WEB-INF")) continue; File war = new File(appBase, files[i]); //处理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; } //如果已经发布 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(); } //如果不需要解压,并且已经发布。那么判断对应的docBase是否已经存在 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; } } 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; } 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); } } } HostConfig.deployWAR(cn, war); protected void deployWAR(ContextName cn, File war) { // Constants.ApplicationContextXml == 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); //判断是否有context.xml在jar包里面 if (entry != null) { xmlInWar = true; } } catch (IOException e) { /* Ignore */ } // If there is an expanded directory then any xml in that directory // should only be used if the directory is not out of date and // unpackWARs is true. Note the code below may apply further limits boolean useXml = false; // If the xml file exists then expandedDir must exists so no need to // test that here //如果对应xml存在,并且允许解压,也就是已经解压了,这个war包没有修改过 if (xml.exists() && unpackWARs && (!warTracker.exists() || warTracker.lastModified() == war.lastModified())) { useXml = true; } Context context = null; //是否需要发布这个context.xml,通过校验 boolean deployThisXML = isDeployThisXML(war, cn); try { //允许发布这个xml并且仅仅是使用xml并且不允许copyxml到配置目录,如果允许copy到配置目录,那么这个xml可能不是最新的。 if (deployThisXML && useXml && !copyXML) { synchronized (digesterLock) { try { context = (Context) digester.parse(xml); } catch (Exception e) { log.error(sm.getString( "hostConfig.deployDescriptor.error", war.getAbsolutePath()), e); } finally { digester.reset(); if (context == null) { context = new FailedContext(); } } } //设置context.xml配置文件 context.setConfigFile(xml.toURI().toURL()); //允许发布这个context.xml并且这个xml在war包里面 } else if (deployThisXML && xmlInWar) { 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) { log.error(sm.getString( "hostConfig.deployDescriptor.error", war.getAbsolutePath()), e); } finally { digester.reset(); if (context == null) { context = new FailedContext(); } context.setConfigFile( UriUtil.buildJarUrl(war, Constants.ApplicationContextXml)); } } //不允许发布这个context.xml并且这个xml在war包里面的,那么就直接new出一个StandardContext } else if (!deployThisXML && 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(host.getConfigBaseFile(), cn.getBaseName() + ".xml"))); } else { context = (Context) Class.forName(contextClass).getConstructor().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;//是否需要复制xml到配置文件夹中 if (deployThisXML) {//允许发布 if (host instanceof StandardHost) { copyThisXml = ((StandardHost) host).isCopyXML(); } // If Host is using default value Context can override it. context指定的copyXml属性可以覆盖host级别的 if (!copyThisXml && context instanceof StandardContext) { copyThisXml = ((StandardContext) context).getCopyXML(); } //如果context.xml在war中,并且允许复制这个xml到配置路径,那么就将此context.xml复制到配置文件夹中 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 */ } } } //下面这部分代码基本和deployDescriptors差不多 DeployedApplication deployedApp = new DeployedApplication(cn.getName(), xml.exists() && deployThisXML && copyThisXml); long startTime = 0; // Deploy the application in this WAR file 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 (deployThisXML && xml.exists() && copyThisXml) { //这个xml被复制到配置目录 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)); } 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"); host.addChild(context);//与deployDescriptors一样的操作 } 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 boolean unpackWAR = unpackWARs; if (unpackWAR && context instanceof StandardContext) { unpackWAR = ((StandardContext) context).getUnpackWAR(); } //上面不是已经添加了docBase资源的监控了吗?这里是因为tomcat在解压了war之后会重新定义它的docBase目录,所以需要添加,不管原始的还是新的都要进行监控 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 { // 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))); } }
分类:
tomcat
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?