tomcat 源码研究之war工程解析

   我们知道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目录下工程的变化

猜你喜欢

转载自584431411.iteye.com/blog/2376753