美文网首页WebTomcat
Tomcat web应用加载流程解析

Tomcat web应用加载流程解析

作者: 晴天哥_王志 | 来源:发表于2018-10-27 07:16 被阅读85次

    开篇

     本篇文章主要是是想从源码角度梳理下web应用加载的全过程,注意这里重点是梳理应用加载的整体过程,对于真正执行web应用加载的细节部分后面会有专门的文章进行梳理。

     下图中我们只关注标记的红色边框里面的内容,这部分其实也就是war包的加载过程,希望能够讲明白这部分即可。

     整个web应用的加载过程是容器StandardHost的start过程,所以整体加载过程包括StandardHost的启动Web应用加载两个部分。

     另外需要理解的是配置文件Server.xml当中关于Host的配置在Tomcat整体的init过程中生成HostConfig对象,所以才有StandardHost->ContainerBase->HostConfig的调用关系链,之前在HostConfig的生成当中纠结了很久最后才饶出来

    Web应用加载过程

    下图是StandardHost的类图,之所以把类图放这里是为了方便后面大家对代码的理解。


    StandardHost类关系图

    Web应用加载过程 - StandardHost

    • 1、StandardHost的启动过程通过startInternal()方法开始并调用父类startInternal()方法。
    • 2、super.startInternal()调用ContainerBase父类的方法。
    public class StandardHost extends ContainerBase implements Host {
    
        protected synchronized void startInternal() throws LifecycleException {
    
            // 省略一些核心代码
           
            super.startInternal();
        }
    }
    

    Web应用加载过程 - ContainerBase

    • 1、StandHost执行父类ContainerBase的startInternal() 执行一系列对象启动过程。
    • 2、startInternal()方法的setState(LifecycleState.STARTING)设置启动状态。
    public abstract class ContainerBase extends LifecycleMBeanBase
            implements Container {
    
        protected synchronized void startInternal() throws LifecycleException {
    
            // Start our subordinate components, if any
            logger = null;
            getLogger();
            Cluster cluster = getClusterInternal();
            if (cluster instanceof Lifecycle) {
                ((Lifecycle) cluster).start();
            }
            Realm realm = getRealmInternal();
            if (realm instanceof Lifecycle) {
                ((Lifecycle) realm).start();
            }
    
            // Start our child containers, if any
            Container children[] = findChildren();
            List<Future<Void>> results = new ArrayList<>();
            for (int i = 0; i < children.length; i++) {
                results.add(startStopExecutor.submit(new StartChild(children[i])));
            }
    
            MultiThrowable multiThrowable = new MultiThrowable();
    
            for (Future<Void> result : results) {
                try {
                    result.get();
                } catch (Throwable e) {
                    log.error(sm.getString("containerBase.threadedStartFailed"), e);
                    multiThrowable.add(e);
                }
    
            }
    
            if (pipeline instanceof Lifecycle) {
                ((Lifecycle) pipeline).start();
            }
    
            // 设置启动状态
            setState(LifecycleState.STARTING);
    
            // Start our thread
            threadStart();
        }
    }
    

    Web应用加载过程 - LifecycleBase

    • 1、LifecycleBase的setState()方法传入LifecycleState.STARTING参数依次执行。
    • 2、fireLifecycleEvent()内部执行listener.lifecycleEvent(event)中listener为HostConfig
    public abstract class LifecycleBase implements Lifecycle {
    
        protected synchronized void setState(LifecycleState state)
                throws LifecycleException {
            setStateInternal(state, null, true);
        }
    
        private synchronized void setStateInternal(LifecycleState state,
                Object data, boolean check) throws LifecycleException {
            // 省略核心代码
    
            this.state = state;
            String lifecycleEvent = state.getLifecycleEvent();
            if (lifecycleEvent != null) {
                fireLifecycleEvent(lifecycleEvent, data);
            }
        }
    
        protected void fireLifecycleEvent(String type, Object data) {
            LifecycleEvent event = new LifecycleEvent(this, type, data);
    
            // HostConfig对象
            for (LifecycleListener listener : lifecycleListeners) {
                listener.lifecycleEvent(event);
            }
        }
    }
    

    Web应用加载过程 - HostConfig

    • 1、HostConfig的lifecycleEvent()方法内部通过start()方法启动web应用的部署。
    • 2、start()方法内部执行deployApps()方法部署web应用。
    • 3、deployApps()内部执行不同类型的web应用的部署。
    public class HostConfig implements LifecycleListener {
    
        public void lifecycleEvent(LifecycleEvent event) {
    
            // 省略相关代码
    
            // 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();
            }
        }
    
    
    
        public void start() {
    
           // 省略相关代码
            if (host.getDeployOnStartup())
                deployApps();
        }
    
    
        protected void deployApps() {
    
            File appBase = host.getAppBaseFile();
            File configBase = host.getConfigBaseFile();
            // todo filteredAppPaths是指已经过滤的文件
            String[] filteredAppPaths = filterAppPaths(appBase.list());
            // Deploy XML descriptors from configBase
            deployDescriptors(configBase, configBase.list());
            // todo 部署War包 Deploy WARs
            deployWARs(appBase, filteredAppPaths);
            // todo 部署展开的目录 Deploy expanded folders
            deployDirectories(appBase, filteredAppPaths);
        }
    }
    
    • 4、deployWARs方法内部提交DeployWar任务进行war包部署
    • 5、deployDirectories内部提交DeployDirectory任务进行目录部署
    • 6、deployDescriptors内部提交DeployDescriptor任务进行部署。
    • 7、DeployWar、DeployDirectory、DeployDescriptor都实现Runnable接口。
    public class HostConfig implements LifecycleListener {
    
        protected void deployDescriptors(File configBase, String[] files) {
    
            if (files == null)
                return;
    
            ExecutorService es = host.getStartStopExecutor();
            List<Future<?>> results = new ArrayList<>();
    
            for (int i = 0; i < files.length; i++) {
                File contextXml = new File(configBase, files[i]);
    
                if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
                    ContextName cn = new ContextName(files[i], true);
    
                    if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
                        continue;
    
                    results.add(
                            es.submit(new DeployDescriptor(this, cn, contextXml)));
                }
            }
    
            for (Future<?> result : results) {
                try {
                    result.get();
                } catch (Exception e) {
                    log.error(sm.getString(
                            "hostConfig.deployDescriptor.threaded.error"), e);
                }
            }
        }
    
        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]);
               
                // 省略相关代码
                results.add(es.submit(new DeployWar(this, cn, war)));
            }
    
            for (Future<?> result : results) {
                try {
                    result.get();
                } catch (Exception e) {}
            }
        }
    
    
    
       protected void deployDirectories(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 dir = new File(appBase, files[i]);
                if (dir.isDirectory()) {
                    ContextName cn = new ContextName(files[i], false);
                    results.add(es.submit(new DeployDirectory(this, cn, dir)));
                }
            }
    
            for (Future<?> result : results) {
                try {
                    result.get();
                } catch (Exception e) {}
            }
        }
    
    • 8、DeployDescriptor的run()方法通过执行deployDescriptor进行部署
    • 9、DeployWar的run()方法通过执行deployDescriptor进行部署
    • 10、DeployDirectory的run()方法通过执行deployDirectory进行部署
        private static class DeployDescriptor implements Runnable {
    
            private HostConfig config;
            private ContextName cn;
            private File descriptor;
    
            public DeployDescriptor(HostConfig config, ContextName cn,
                    File descriptor) {
                this.config = config;
                this.cn = cn;
                this.descriptor= descriptor;
            }
    
            @Override
            public void run() {
                config.deployDescriptor(cn, descriptor);
            }
        }
    
    
    
        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() {
                config.deployWAR(cn, war);
            }
        }
    
    
    
        private static class DeployDirectory implements Runnable {
    
            private HostConfig config;
            private ContextName cn;
            private File dir;
    
            public DeployDirectory(HostConfig config, ContextName cn, File dir) {
                this.config = config;
                this.cn = cn;
                this.dir = dir;
            }
    
            @Override
            public void run() {
                config.deployDirectory(cn, dir);
            }
        }
    }
    

    Web应用加载过程 - 目录形式的项目加载

    • 1、Directory类型的项目真正加载实现的过程。
    • 2、这部分后面单独进行一篇文章进行讲解。
    public class HostConfig implements LifecycleListener {
    
        protected void deployDirectory(ContextName cn, File dir) {
    
    
            long startTime = 0;
            // Deploy the application in this directory
            if( log.isInfoEnabled() ) {
                startTime = System.currentTimeMillis();
                log.info(sm.getString("hostConfig.deployDir",
                        dir.getAbsolutePath()));
            }
    
            Context context = null;
            File xml = new File(dir, Constants.ApplicationContextXml);
            File xmlCopy =
                    new File(host.getConfigBaseFile(), cn.getBaseName() + ".xml");
    
    
            DeployedApplication deployedApp;
            boolean copyThisXml = isCopyXML();
            boolean deployThisXML = isDeployThisXML(dir, cn);
    
            try {
                if (deployThisXML && xml.exists()) {
                    synchronized (digesterLock) {
                        try {
                            context = (Context) digester.parse(xml);
                        } catch (Exception e) {
                            log.error(sm.getString(
                                    "hostConfig.deployDescriptor.error",
                                    xml), e);
                            context = new FailedContext();
                        } finally {
                            digester.reset();
                            if (context == null) {
                                context = new FailedContext();
                            }
                        }
                    }
    
                    if (copyThisXml == false && context instanceof StandardContext) {
                        // Host is using default value. Context may override it.
                        copyThisXml = ((StandardContext) context).getCopyXML();
                    }
    
                    if (copyThisXml) {
                        Files.copy(xml.toPath(), xmlCopy.toPath());
                        context.setConfigFile(xmlCopy.toURI().toURL());
                    } else {
                        context.setConfigFile(xml.toURI().toURL());
                    }
                } else if (!deployThisXML && xml.exists()) {
                    // 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(), xml, xmlCopy));
                    context = new FailedContext();
                } else {
                    context = (Context) Class.forName(contextClass).getConstructor().newInstance();
                }
    
                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());
                host.addChild(context);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("hostConfig.deployDir.error",
                        dir.getAbsolutePath()), t);
            } finally {
                deployedApp = new DeployedApplication(cn.getName(),
                        xml.exists() && deployThisXML && copyThisXml);
    
                // Fake re-deploy resource to detect if a WAR is added at a later
                // point
                deployedApp.redeployResources.put(dir.getAbsolutePath() + ".war",
                        Long.valueOf(0));
                deployedApp.redeployResources.put(dir.getAbsolutePath(),
                        Long.valueOf(dir.lastModified()));
                if (deployThisXML && xml.exists()) {
                    if (copyThisXml) {
                        deployedApp.redeployResources.put(
                                xmlCopy.getAbsolutePath(),
                                Long.valueOf(xmlCopy.lastModified()));
                    } else {
                        deployedApp.redeployResources.put(
                                xml.getAbsolutePath(),
                                Long.valueOf(xml.lastModified()));
                        // Fake re-deploy resource to detect if a context.xml file is
                        // added at a later point
                        deployedApp.redeployResources.put(
                                xmlCopy.getAbsolutePath(),
                                Long.valueOf(0));
                    }
                } else {
                    // Fake re-deploy resource to detect if a context.xml file is
                    // added at a later point
                    deployedApp.redeployResources.put(
                            xmlCopy.getAbsolutePath(),
                            Long.valueOf(0));
                    if (!xml.exists()) {
                        deployedApp.redeployResources.put(
                                xml.getAbsolutePath(),
                                Long.valueOf(0));
                    }
                }
                addWatchedResources(deployedApp, dir.getAbsolutePath(), 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.deployDir.finished",
                        dir.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime)));
            }
        }
    }
    

    Web应用加载过程 - War包形式的项目加载

    • 1、War类型的项目真正加载实现的过程。
    • 2、这部分后面单独进行一篇文章进行讲解。
    public class HostConfig implements LifecycleListener {
    
        protected void deployWAR(ContextName cn, File war) {
    
            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) {
                /* 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
            if (xml.exists() && unpackWARs &&
                    (!warTracker.exists() || warTracker.lastModified() == war.lastModified())) {
                useXml = true;
            }
    
            Context context = null;
            boolean deployThisXML = isDeployThisXML(war, cn);
    
            try {
                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.setConfigFile(xml.toURI().toURL());
                } 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));
                        }
                    }
                } 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;
            if (deployThisXML) {
                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(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);
    
            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) {
                    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);
            } 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();
                }
                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)));
            }
        }
    }
    

    Web应用加载过程 - Descriptor形式的项目加载

    • 1、Descriptor类型的项目真正加载实现的过程。
    • 2、这部分后面单独进行一篇文章进行讲解。
    public class HostConfig implements LifecycleListener {
    
        protected void deployDescriptor(ContextName cn, File contextXml) {
    
            DeployedApplication deployedApp =
                    new DeployedApplication(cn.getName(), true);
    
            long startTime = 0;
            // Assume this is a configuration descriptor and deploy it
            if(log.isInfoEnabled()) {
               startTime = System.currentTimeMillis();
               log.info(sm.getString("hostConfig.deployDescriptor",
                        contextXml.getAbsolutePath()));
            }
    
            Context context = null;
            boolean isExternalWar = false;
            boolean isExternal = false;
            File expandedDocBase = null;
    
            try (FileInputStream fis = new FileInputStream(contextXml)) {
                synchronized (digesterLock) {
                    try {
                        context = (Context) digester.parse(fis);
                    } catch (Exception e) {
                        log.error(sm.getString(
                                "hostConfig.deployDescriptor.error",
                                contextXml.getAbsolutePath()), e);
                    } finally {
                        digester.reset();
                        if (context == null) {
                            context = new FailedContext();
                        }
                    }
                }
    
                Class<?> clazz = Class.forName(host.getConfigClass());
                LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
                context.addLifecycleListener(listener);
    
                context.setConfigFile(contextXml.toURI().toURL());
                context.setName(cn.getName());
                context.setPath(cn.getPath());
                context.setWebappVersion(cn.getVersion());
                // Add the associated docBase to the redeployed list if it's a WAR
                if (context.getDocBase() != null) {
                    File docBase = new File(context.getDocBase());
                    if (!docBase.isAbsolute()) {
                        docBase = new File(host.getAppBaseFile(), context.getDocBase());
                    }
                    // If external docBase, register .xml as redeploy first
                    if (!docBase.getCanonicalPath().startsWith(
                            host.getAppBaseFile().getAbsolutePath() + File.separator)) {
                        isExternal = true;
                        deployedApp.redeployResources.put(
                                contextXml.getAbsolutePath(),
                                Long.valueOf(contextXml.lastModified()));
                        deployedApp.redeployResources.put(docBase.getAbsolutePath(),
                                Long.valueOf(docBase.lastModified()));
                        if (docBase.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                            isExternalWar = true;
                        }
                    } else {
                        log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified",
                                 docBase));
                        // Ignore specified docBase
                        context.setDocBase(null);
                    }
                }
    
                host.addChild(context);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("hostConfig.deployDescriptor.error",
                                       contextXml.getAbsolutePath()), t);
            } finally {
                // Get paths for WAR and expanded WAR in appBase
    
                // default to appBase dir + name
                expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName());
                if (context.getDocBase() != null
                        && !context.getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                    // first assume docBase is absolute
                    expandedDocBase = new File(context.getDocBase());
                    if (!expandedDocBase.isAbsolute()) {
                        // if docBase specified and relative, it must be relative to appBase
                        expandedDocBase = new File(host.getAppBaseFile(), context.getDocBase());
                    }
                }
    
                boolean unpackWAR = unpackWARs;
                if (unpackWAR && context instanceof StandardContext) {
                    unpackWAR = ((StandardContext) context).getUnpackWAR();
                }
    
                // Add the eventual unpacked WAR and all the resources which will be
                // watched inside it
                if (isExternalWar) {
                    if (unpackWAR) {
                        deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
                                Long.valueOf(expandedDocBase.lastModified()));
                        addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context);
                    } else {
                        addWatchedResources(deployedApp, null, context);
                    }
                } else {
                    // Find an existing matching war and expanded folder
                    if (!isExternal) {
                        File warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war");
                        if (warDocBase.exists()) {
                            deployedApp.redeployResources.put(warDocBase.getAbsolutePath(),
                                    Long.valueOf(warDocBase.lastModified()));
                        } else {
                            // Trigger a redeploy if a WAR is added
                            deployedApp.redeployResources.put(
                                    warDocBase.getAbsolutePath(),
                                    Long.valueOf(0));
                        }
                    }
                    if (unpackWAR) {
                        deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
                                Long.valueOf(expandedDocBase.lastModified()));
                        addWatchedResources(deployedApp,
                                expandedDocBase.getAbsolutePath(), context);
                    } else {
                        addWatchedResources(deployedApp, null, context);
                    }
                    if (!isExternal) {
                        // For external docBases, the context.xml will have been
                        // added above.
                        deployedApp.redeployResources.put(
                                contextXml.getAbsolutePath(),
                                Long.valueOf(contextXml.lastModified()));
                    }
                }
                // Add the global redeploy resources (which are never deleted) at
                // the end so they don't interfere with the deletion process
                addGlobalRedeployResources(deployedApp);
            }
    
            if (host.findChild(context.getName()) != null) {
                deployed.put(context.getName(), deployedApp);
            }
    
            if (log.isInfoEnabled()) {
                log.info(sm.getString("hostConfig.deployDescriptor.finished",
                    contextXml.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime)));
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:Tomcat web应用加载流程解析

        本文链接:https://www.haomeiwen.com/subject/lzrhtqtx.html