编程语言
首页 > 编程语言> > tomcat源码研究:tomcat自动重载改变的类

tomcat源码研究:tomcat自动重载改变的类

作者:互联网

在tomcat6源码中,检查文件是否发生改变的任务由org.apache.catalina.core.StandardContext 

类中的backgroundProcess()方法来执行。这个方法周期性地被org.apache.catalina.core.ContainerBase类中一个专门的线程调用,该类中的内部类ContainerBackgroundProcessor实现了Runnable接口。代码如下:

/**
     * Private thread class to invoke the backgroundProcess method 
     * of this container and its children after a fixed delay.
     */
    protected class ContainerBackgroundProcessor implements Runnable {


        public void run() {
            while (!threadDone) {
                try {
                    Thread.sleep(backgroundProcessorDelay * 1000L);
                } catch (InterruptedException e) {
                    ;
                }
                if (!threadDone) {
                    Container parent = (Container) getMappingObject();
                    ClassLoader cl = 
                        Thread.currentThread().getContextClassLoader();
                    if (parent.getLoader() != null) {
                        cl = parent.getLoader().getClassLoader();
                    }
                    processChildren(parent, cl);
                }
            }
        }


        protected void processChildren(Container container, ClassLoader cl) {
            try {
                if (container.getLoader() != null) {
                    Thread.currentThread().setContextClassLoader
                        (container.getLoader().getClassLoader());
                }
                container.backgroundProcess();
            } catch (Throwable 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);
                }
            }
        }
    }

  }

这个线程的run()方法里面有个while循环,该循环有三个作用:

以backgroundProcessorDelay为周期周期性地睡眠;

通过调用WebappLoader类的modified()方法检查加载的类是否发生改变,如果改变,通知和这个servlet容器关联的加载器重新加载。

java.org.apache.catalina.core.StandardWrapper.java

public void backgroundProcess() {
        super.backgroundProcess();
        
        if (!started)
            return;
        
        if (getServlet() != null && (getServlet() instanceof PeriodicEventListener)) {
            ((PeriodicEventListener) getServlet()).periodicEvent();
        }
    }


public void periodicEvent() {
        rctxt.checkCompile();
    }

java.org.apache.jasper.servlet.JspServlet.java

 public void periodicEvent() {
        rctxt.checkCompile();
    }

java.org.apache.jasper.compiler.JspRuntimeContexxt.java

 public void checkCompile() {


        if (lastCheck < 0) {
            // Checking was disabled
            return;
        }
        long now = System.currentTimeMillis();
        if (now > (lastCheck + (options.getCheckInterval() * 1000L))) {
            lastCheck = now;
        } else {
            return;
        }
        
        Object [] wrappers = jsps.values().toArray();
        for (int i = 0; i < wrappers.length; i++ ) {
            JspServletWrapper jsw = (JspServletWrapper)wrappers[i];
            JspCompilationContext ctxt = jsw.getJspEngineContext();
            // JspServletWrapper also synchronizes on this when
            // it detects it has to do a reload
            synchronized(jsw) {
                try {
                    ctxt.compile();
                } catch (FileNotFoundException ex) {
                    ctxt.incrementRemoved();
                } catch (Throwable t) {
                    jsw.getServletContext().log("Background compile failed",
t);
                }
            }
        }

java.org.apache.jasper.JspCompilationContext.java
public void compile() throws JasperException, FileNotFoundException {
        createCompiler();
        if (isPackagedTagFile || jspCompiler.isOutDated()) {
            try {
                jspCompiler.removeGeneratedFiles();
                jspLoader = null;
                jspCompiler.compile();
                jsw.setReload(true);
                jsw.setCompilationException(null);
            } catch (JasperException ex) {
                // Cache compilation exception
                jsw.setCompilationException(ex);
                throw ex;
            } catch (Exception ex) {
                JasperException je = new JasperException(
                            Localizer.getMessage("jsp.error.unable.compile"),
                            ex);
                // Cache compilation exception
                jsw.setCompilationException(je);
                throw je;
            }
        }
    }


/**
     * Create a "Compiler" object based on some init param data. This
     * is not done yet. Right now we're just hardcoding the actual
     * compilers that are created. 
     */
    public Compiler createCompiler() throws JasperException {
        if (jspCompiler != null ) {
            return jspCompiler;
        }
        jspCompiler = null;
        if (options.getCompilerClassName() != null) {
            jspCompiler = createCompiler(options.getCompilerClassName());
        } else {
            if (options.getCompiler() == null) {
                jspCompiler = createCompiler("org.apache.jasper.compiler.JDTCompiler");
                if (jspCompiler == null) {
                    jspCompiler = createCompiler("org.apache.jasper.compiler.AntCompiler");
                }
            } else {
                jspCompiler = createCompiler("org.apache.jasper.compiler.AntCompiler");
                if (jspCompiler == null) {
                    jspCompiler = createCompiler("org.apache.jasper.compiler.JDTCompiler");
                }
            }
        }
        if (jspCompiler == null) {
            throw new IllegalStateException(Localizer.getMessage("jsp.error.compiler"));
        }
        jspCompiler.init(this, jsw);
        return jspCompiler;
    }

最后附上编译类的抽象类,大家可以参考研究

java.org.apache.jasper.Compiler.Compiler.java

/**
     * Compile the jsp file from the current engine context. As an side- effect,
     * tag files that are referenced by this page are also compiled.
     * 
     * @param compileClass
     *            If true, generate both .java and .class file If false,
     *            generate only .java file
     * @param jspcMode
     *            true if invoked from JspC, false otherwise
     */
    /*
 * Copyright 1999,2004-2006 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.apache.jasper.compiler;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.List;


import org.apache.jasper.JasperException;
import org.apache.jasper.JspCompilationContext;
import org.apache.jasper.Options;
import org.apache.jasper.servlet.JspServletWrapper;


/**
 * Main JSP compiler class. This class uses Ant for compiling.
 * 
 * @author Anil K. Vijendran
 * @author Mandar Raje
 * @author Pierre Delisle
 * @author Kin-man Chung
 * @author Remy Maucherat
 * @author Mark Roth
 */
public abstract class Compiler {
    
    protected org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
            .getLog(Compiler.class);


    // ----------------------------------------------------- Instance Variables


    protected JspCompilationContext ctxt;


    protected ErrorDispatcher errDispatcher;


    protected PageInfo pageInfo;


    protected JspServletWrapper jsw;


    protected TagFileProcessor tfp;


    protected Options options;


    protected Node.Nodes pageNodes;


    // ------------------------------------------------------------ Constructor


    public void init(JspCompilationContext ctxt, JspServletWrapper jsw) {
        this.jsw = jsw;
        this.ctxt = ctxt;
        this.options = ctxt.getOptions();
    }


    // --------------------------------------------------------- Public Methods


    /**
     * <p>
     * Retrieves the parsed nodes of the JSP page, if they are available. May
     * return null. Used in development mode for generating detailed error
     * messages. http://issues.apache.org/bugzilla/show_bug.cgi?id=37062.
     * </p>
     */
    public Node.Nodes getPageNodes() {
        return this.pageNodes;
    }


    /**
     * Compile the jsp file into equivalent servlet in .java file
     * 
     * @return a smap for the current JSP page, if one is generated, null
     *         otherwise
     */
    protected String[] generateJava() throws Exception {


        String[] smapStr = null;


        long t1, t2, t3, t4;


        t1 = t2 = t3 = t4 = 0;


        if (log.isDebugEnabled()) {
            t1 = System.currentTimeMillis();
        }


        // Setup page info area
        pageInfo = new PageInfo(new BeanRepository(ctxt.getClassLoader(),
                errDispatcher), ctxt.getJspFile());


        JspConfig jspConfig = options.getJspConfig();
        JspConfig.JspProperty jspProperty = jspConfig.findJspProperty(ctxt
                .getJspFile());


        /*
         * If the current uri is matched by a pattern specified in a
         * jsp-property-group in web.xml, initialize pageInfo with those
         * properties.
         */
        if (jspProperty.isELIgnored() != null) {
            pageInfo.setELIgnored(JspUtil.booleanValue(jspProperty
                    .isELIgnored()));
        }
        if (jspProperty.isScriptingInvalid() != null) {
            pageInfo.setScriptingInvalid(JspUtil.booleanValue(jspProperty
                    .isScriptingInvalid()));
        }
        if (jspProperty.getIncludePrelude() != null) {
            pageInfo.setIncludePrelude(jspProperty.getIncludePrelude());
        }
        if (jspProperty.getIncludeCoda() != null) {
            pageInfo.setIncludeCoda(jspProperty.getIncludeCoda());
        }
        if (jspProperty.isDeferedSyntaxAllowedAsLiteral() != null) {
            pageInfo.setDeferredSyntaxAllowedAsLiteral(JspUtil.booleanValue(jspProperty
                    .isDeferedSyntaxAllowedAsLiteral()));
        }
        if (jspProperty.isTrimDirectiveWhitespaces() != null) {
            pageInfo.setTrimDirectiveWhitespaces(JspUtil.booleanValue(jspProperty
                    .isTrimDirectiveWhitespaces()));
        }


        ctxt.checkOutputDir();
        String javaFileName = ctxt.getServletJavaFileName();
        ServletWriter writer = null;


        try {
            // Setup the ServletWriter
            String javaEncoding = ctxt.getOptions().getJavaEncoding();
            OutputStreamWriter osw = null;


            try {
                osw = new OutputStreamWriter(
                        new FileOutputStream(javaFileName), javaEncoding);
            } catch (UnsupportedEncodingException ex) {
                errDispatcher.jspError("jsp.error.needAlternateJavaEncoding",
                        javaEncoding);
            }


            writer = new ServletWriter(new PrintWriter(osw));
            ctxt.setWriter(writer);


            // Reset the temporary variable counter for the generator.
            JspUtil.resetTemporaryVariableName();


            // Parse the file
            ParserController parserCtl = new ParserController(ctxt, this);
            pageNodes = parserCtl.parse(ctxt.getJspFile());


            if (ctxt.isPrototypeMode()) {
                // generate prototype .java file for the tag file
                Generator.generate(writer, this, pageNodes);
                writer.close();
                writer = null;
                return null;
            }


            // Validate and process attributes
            Validator.validate(this, pageNodes);


            if (log.isDebugEnabled()) {
                t2 = System.currentTimeMillis();
            }


            // Collect page info
            Collector.collect(this, pageNodes);


            // Compile (if necessary) and load the tag files referenced in
            // this compilation unit.
            tfp = new TagFileProcessor();
            tfp.loadTagFiles(this, pageNodes);


            if (log.isDebugEnabled()) {
                t3 = System.currentTimeMillis();
            }


            // Determine which custom tag needs to declare which scripting vars
            ScriptingVariabler.set(pageNodes, errDispatcher);


            // Optimizations by Tag Plugins
            TagPluginManager tagPluginManager = options.getTagPluginManager();
            tagPluginManager.apply(pageNodes, errDispatcher, pageInfo);


            // Optimization: concatenate contiguous template texts.
            TextOptimizer.concatenate(this, pageNodes);


            // Generate static function mapper codes.
            ELFunctionMapper.map(this, pageNodes);


            // generate servlet .java file
            Generator.generate(writer, this, pageNodes);
            writer.close();
            writer = null;


            // The writer is only used during the compile, dereference
            // it in the JspCompilationContext when done to allow it
            // to be GC'd and save memory.
            ctxt.setWriter(null);


            if (log.isDebugEnabled()) {
                t4 = System.currentTimeMillis();
                log.debug("Generated " + javaFileName + " total=" + (t4 - t1)
                        + " generate=" + (t4 - t3) + " validate=" + (t2 - t1));
            }


        } catch (Exception e) {
            if (writer != null) {
                try {
                    writer.close();
                    writer = null;
                } catch (Exception e1) {
                    // do nothing
                }
            }
            // Remove the generated .java file
            new File(javaFileName).delete();
            throw e;
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (Exception e2) {
                    // do nothing
                }
            }
        }


        // JSR45 Support
        if (!options.isSmapSuppressed()) {
            smapStr = SmapUtil.generateSmap(ctxt, pageNodes);
        }


        // If any proto type .java and .class files was generated,
        // the prototype .java may have been replaced by the current
        // compilation (if the tag file is self referencing), but the
        // .class file need to be removed, to make sure that javac would
        // generate .class again from the new .java file just generated.
        tfp.removeProtoTypeFiles(ctxt.getClassFileName());


        return smapStr;
    }


    /**
     * Compile the servlet from .java file to .class file
     */
    protected abstract void generateClass(String[] smap)
            throws FileNotFoundException, JasperException, Exception;


    /**
     * Compile the jsp file from the current engine context
     */
    public void compile() throws FileNotFoundException, JasperException,
            Exception {
        compile(true);
    }


    /**
     * Compile the jsp file from the current engine context. As an side- effect,
     * tag files that are referenced by this page are also compiled.
     * 
     * @param compileClass
     *            If true, generate both .java and .class file If false,
     *            generate only .java file
     */
    public void compile(boolean compileClass) throws FileNotFoundException,
            JasperException, Exception {
        compile(compileClass, false);
    }


    /**
     * Compile the jsp file from the current engine context. As an side- effect,
     * tag files that are referenced by this page are also compiled.
     * 
     * @param compileClass
     *            If true, generate both .java and .class file If false,
     *            generate only .java file
     * @param jspcMode
     *            true if invoked from JspC, false otherwise
     */
    public void compile(boolean compileClass, boolean jspcMode)
            throws FileNotFoundException, JasperException, Exception {
        if (errDispatcher == null) {
            this.errDispatcher = new ErrorDispatcher(jspcMode);
        }


        try {
            String[] smap = generateJava();
            if (compileClass) {
                generateClass(smap);
            }
        } finally {
            if (tfp != null) {
                tfp.removeProtoTypeFiles(null);
            }
            // Make sure these object which are only used during the
            // generation and compilation of the JSP page get
            // dereferenced so that they can be GC'd and reduce the
            // memory footprint.
            tfp = null;
            errDispatcher = null;
            pageInfo = null;


            // Only get rid of the pageNodes if in production.
            // In development mode, they are used for detailed
            // error messages.
            // http://issues.apache.org/bugzilla/show_bug.cgi?id=37062
            if (!this.options.getDevelopment()) {
                pageNodes = null;
            }


            if (ctxt.getWriter() != null) {
                ctxt.getWriter().close();
                ctxt.setWriter(null);
            }
        }
    }


    /**
     * This is a protected method intended to be overridden by subclasses of
     * Compiler. This is used by the compile method to do all the compilation.
     */
    public boolean isOutDated() {
        return isOutDated(true);
    }


    /**
     * Determine if a compilation is necessary by checking the time stamp of the
     * JSP page with that of the corresponding .class or .java file. If the page
     * has dependencies, the check is also extended to its dependeants, and so
     * on. This method can by overidden by a subclasses of Compiler.
     * 
     * @param checkClass
     *            If true, check against .class file, if false, check against
     *            .java file.
     */
    public boolean isOutDated(boolean checkClass) {


        String jsp = ctxt.getJspFile();


        if (jsw != null
                && (ctxt.getOptions().getModificationTestInterval() > 0)) {


            if (jsw.getLastModificationTest()
                    + (ctxt.getOptions().getModificationTestInterval() * 1000) > System
                    .currentTimeMillis()) {
                return false;
            } else {
                jsw.setLastModificationTest(System.currentTimeMillis());
            }
        }


        long jspRealLastModified = 0;
        try {
            URL jspUrl = ctxt.getResource(jsp);
            if (jspUrl == null) {
                ctxt.incrementRemoved();
                return false;
            }
            URLConnection uc = jspUrl.openConnection();
            jspRealLastModified = uc.getLastModified();
            uc.getInputStream().close();
        } catch (Exception e) {
            return true;
        }


        long targetLastModified = 0;
        File targetFile;


        if (checkClass) {
            targetFile = new File(ctxt.getClassFileName());
        } else {
            targetFile = new File(ctxt.getServletJavaFileName());
        }


        if (!targetFile.exists()) {
            return true;
        }


        targetLastModified = targetFile.lastModified();
        if (checkClass && jsw != null) {
            jsw.setServletClassLastModifiedTime(targetLastModified);
        }
        if (targetLastModified < jspRealLastModified) {
            if (log.isDebugEnabled()) {
                log.debug("Compiler: outdated: " + targetFile + " "
                        + targetLastModified);
            }
            return true;
        }


        // determine if source dependent files (e.g. includes using include
        // directives) have been changed.
        if (jsw == null) {
            return false;
        }


        List depends = jsw.getDependants();
        if (depends == null) {
            return false;
        }


        Iterator it = depends.iterator();
        while (it.hasNext()) {
            String include = (String) it.next();
            try {
                URL includeUrl = ctxt.getResource(include);
                if (includeUrl == null) {
                    return true;
                }


                URLConnection includeUconn = includeUrl.openConnection();
                long includeLastModified = includeUconn.getLastModified();
                includeUconn.getInputStream().close();


                if (includeLastModified > targetLastModified) {
                    return true;
                }
            } catch (Exception e) {
                return true;
            }
        }


        return false;


    }


    /**
     * Gets the error dispatcher.
     */
    public ErrorDispatcher getErrorDispatcher() {
        return errDispatcher;
    }


    /**
     * Gets the info about the page under compilation
     */
    public PageInfo getPageInfo() {
        return pageInfo;
    }


    public JspCompilationContext getCompilationContext() {
        return ctxt;
    }


    /**
     * Remove generated files
     */
    public void removeGeneratedFiles() {
        try {
            String classFileName = ctxt.getClassFileName();
            if (classFileName != null) {
                File classFile = new File(classFileName);
                if (log.isDebugEnabled())
                    log.debug("Deleting " + classFile);
                classFile.delete();
            }
        } catch (Exception e) {
            // Remove as much as possible, ignore possible exceptions
        }
        try {
            String javaFileName = ctxt.getServletJavaFileName();
            if (javaFileName != null) {
                File javaFile = new File(javaFileName);
                if (log.isDebugEnabled())
                    log.debug("Deleting " + javaFile);
                javaFile.delete();
            }
        } catch (Exception e) {
            // Remove as much as possible, ignore possible exceptions
        }
    }


    public void removeGeneratedClassFiles() {
        try {
            String classFileName = ctxt.getClassFileName();
            if (classFileName != null) {
                File classFile = new File(classFileName);
                if (log.isDebugEnabled())
                    log.debug("Deleting " + classFile);
                classFile.delete();
            }
        } catch (Exception e) {
            // Remove as much as possible, ignore possible exceptions
        }
    }
}



标签:java,tomcat,apache,源码,file,重载,return,null,ctxt
来源: https://blog.51cto.com/u_15095774/2718564