Package jetbrick.template

Source Code of jetbrick.template.JetTemplate

/**
* jetbrick-template
* http://subchen.github.io/jetbrick-template/
*
* Copyright 2010-2014 Guoqiang Chen. All rights reserved.
* Email: subchen@gmail.com
*
* 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 jetbrick.template;

import java.io.*;
import java.util.Map;
import jetbrick.template.JetConfig.CompileStrategy;
import jetbrick.template.compiler.JavaCompiler;
import jetbrick.template.compiler.JavaSource;
import jetbrick.template.parser.*;
import jetbrick.template.parser.code.Code;
import jetbrick.template.parser.grammer.*;
import jetbrick.template.parser.grammer.JetTemplateParser.TemplateContext;
import jetbrick.template.resource.*;
import jetbrick.template.runtime.*;
import jetbrick.template.utils.ExceptionUtils;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class JetTemplate {
    private static final Logger log = LoggerFactory.getLogger(JetTemplate.class);

    private final JetEngine engine;
    private final Resource resource;
    private final String encoding;
    private final boolean reloadable;
    private final File javaClassFile;
    private long lastCompiledTimestamp;
    private JetPage pageObject;

    protected JetTemplate(JetEngine engine, Resource resource) {
        JetConfig config = engine.getConfig();
        CompileStrategy compileStrategy = config.getCompileStrategy();

        this.engine = engine;
        this.resource = resource;
        this.encoding = config.getOutputEncoding();
        this.reloadable = config.isTemplateReloadable() && compileStrategy != CompileStrategy.none;
        this.javaClassFile = engine.getClassLoader().getGeneratedJavaClassFile(resource.getQualifiedClassName());
        this.lastCompiledTimestamp = javaClassFile.lastModified();

        // compile and load
        switch (compileStrategy) {
        case precompile:
        case always:
            compileAndLoadClass();
            break;
        case auto:
            if (lastCompiledTimestamp > resource.lastModified()) {
                try {
                    loadClassFile();
                } catch (Throwable e) {
                    // 无法 load 的话,尝试重新编译
                    log.warn(e.getClass().getName() + ": " + e.getMessage());
                    log.warn("Try to recompile this template.");
                    compileAndLoadClass();
                }
            } else {
                compileAndLoadClass();
            }
            break;
        case none:
            if (resource instanceof SourceCodeResource) {
                // source code 的情况下必须编译
                compileAndLoadClass();
            } else if (resource instanceof CompiledClassResource) {
                try {
                    loadClassFile();
                } catch (Exception e) {
                    throw ExceptionUtils.uncheck(e);
                }
            } else {
                throw new IllegalStateException("Invalid resource when " + JetConfig.COMPILE_STRATEGY + " is " + compileStrategy);
            }
            break;
        }
    }

    // 检测模板是否已更新
    protected void checkLastModified() {
        if (reloadable && lastCompiledTimestamp < resource.lastModified()) {
            synchronized (this) {
                // double check
                if (lastCompiledTimestamp < resource.lastModified()) {
                    compileAndLoadClass();
                }
            }
        }
    }

    // 从 disk 的缓存文件中读取 class
    private void loadClassFile() throws Exception {
        Class<?> compiledKlass;

        if (resource instanceof CompiledClassResource) {
            log.info("Loading template from classpath: {}.", resource.getName());
            compiledKlass = ((CompiledClassResource) resource).getCompiledClass();
        } else {
            log.info("Loading template class file: {}", javaClassFile.getAbsolutePath());
            compiledKlass = engine.getClassLoader().loadClass(resource.getQualifiedClassName());

            // 判断编码匹配
            if (!encoding.equals(compiledKlass.getDeclaredField("$ENC").get(null))) {
                throw new IllegalStateException("The encoding of last compiled template class is not " + encoding);
            }
        }

        pageObject = (JetPage) compiledKlass.newInstance();
    }

    // 编译 source 为 class, 然后 load class
    private void compileAndLoadClass() {
        boolean notPrecompileThread = !"JetPreCompiler".equals(Thread.currentThread().getName());
        if (notPrecompileThread) {
            log.info("Loading template source file: " + resource.getAbsolutePath());
        }

        // generateJavaSource
        String source = generateJavaSource(resource);
        if (notPrecompileThread && log.isInfoEnabled() && engine.getConfig().isCompileDebug()) {
            File javaSourceFile = engine.getClassLoader().getGeneratedJavaSourceFile(resource.getQualifiedClassName());
            StringBuilder sb = new StringBuilder(source.length() + 128);
            sb.append("generateJavaSource: ");
            sb.append(javaSourceFile.getAbsolutePath());
            sb.append("\n");
            sb.append("===========================================================================\n");
            sb.append(source);
            sb.append("\n");
            sb.append("===========================================================================");
            log.info(sb.toString());
        }

        JavaCompiler javaCompiler = engine.getJavaCompiler();

        // compile
        long ts = System.currentTimeMillis();
        JavaSource javaSource = new JavaSource(resource.getQualifiedClassName(), source, javaCompiler.getOutputdir());
        Class<?> cls = javaCompiler.compile(javaSource);
        if (notPrecompileThread) {
            ts = System.currentTimeMillis() - ts;
            log.info("generateJavaClass: {}, {}ms", javaClassFile.getAbsolutePath(), ts);
        }
        try {
            lastCompiledTimestamp = javaClassFile.lastModified();
            pageObject = (JetPage) cls.newInstance();
        } catch (Exception e) {
            throw ExceptionUtils.uncheck(e);
        }
    }

    private String generateJavaSource(Resource resource) {
        char[] source = resource.getSource();
        ANTLRInputStream is = new ANTLRInputStream(source, source.length);
        is.name = resource.getAbsolutePath(); // set source file name, it will be displayed in error report.

        JetTemplateLexer lexer = new JetTemplateLexer(is);
        lexer.removeErrorListeners(); // remove ConsoleErrorListener
        lexer.addErrorListener(JetTemplateErrorListener.getInstance());

        JetTemplateParser parser = new JetTemplateParser(new CommonTokenStream(lexer));
        parser.removeErrorListeners(); // remove ConsoleErrorListener
        parser.addErrorListener(JetTemplateErrorListener.getInstance());
        parser.setErrorHandler(new JetTemplateErrorStrategy());

        TemplateContext templateParseTree = parser.template();
        JetTemplateCodeVisitor visitor = new JetTemplateCodeVisitor(engine, engine.getVariableResolver(), engine.getSecurityManager(), parser, resource);
        Code code = templateParseTree.accept(visitor);
        return code.toString();
    }

    public void render(Map<String, Object> context, Writer out) {
        JetContext ctx = new JetContext(context);
        JetWriter writer = JetWriter.create(out, encoding);
        render(new JetPageContext(this, ctx, writer));
    }

    public void render(Map<String, Object> context, OutputStream out) {
        JetContext ctx = new JetContext(context);
        JetWriter writer = JetWriter.create(out, encoding);
        render(new JetPageContext(this, ctx, writer));
    }

    public void render(JetContext context, Writer out) {
        if (context == null) {
            context = new JetContext(null);
        } else if (context.isSimpleModel()) {
            // simpleModel 的情况代表是用户自己 new 出来的 JetContext
            // 为了防止 #set 污染 context,这里重新 new 一个新的。
            context = new JetContext(context.getContext());
        }
        JetWriter writer = JetWriter.create(out, encoding);
        render(new JetPageContext(this, context, writer));
    }

    public void render(JetContext context, OutputStream out) {
        if (context == null) {
            context = new JetContext(null);
        } else if (context.isSimpleModel()) {
            // simpleModel 的情况代表是用户自己 new 出来的 JetContext
            // 为了防止 #set 污染 context,这里重新 new 一个新的。
            context = new JetContext(context.getContext());
        }
        JetWriter writer = JetWriter.create(out, encoding);
        render(new JetPageContext(this, context, writer));
    }

    public void render(JetContext context, JetWriter writer) {
        render(new JetPageContext(this, context, writer));
    }

    private void render(JetPageContext ctx) {
        try {
            if (engine.getGlobalVariables() != null) {
                ctx.getContext().setGlobalVariables(engine.getGlobalVariables());
            }
            pageObject.render(ctx);
        } catch (Throwable e) {
            // JSP use response.getWriter() to ignore all io exceptions.
            // I use response.getOutputStream() and only ignore ClientAbortException.
            if ("org.apache.catalina.connector.ClientAbortException".equals(e.getClass().getName())) {
                log.warn(e.toString());
            } else {
                throw ExceptionUtils.uncheck(e);
            }
        }
    }

    public JetEngine getEngine() {
        return engine;
    }

    public String getName() {
        return resource.getName();
    }
}
TOP

Related Classes of jetbrick.template.JetTemplate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.