/** * 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.File; import java.lang.annotation.Annotation; import java.util.*; import jetbrick.template.JetConfig.CompileStrategy; import jetbrick.template.compiler.JavaCompiler; import jetbrick.template.compiler.JetTemplateClassLoader; import jetbrick.template.parser.VariableResolver; import jetbrick.template.resource.Resource; import jetbrick.template.resource.SourceCodeResource; import jetbrick.template.resource.loader.CompiledClassResourceLoader; import jetbrick.template.resource.loader.ResourceLoader; import jetbrick.template.utils.*; import jetbrick.template.utils.finder.AnnotationClassLookupUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JetEngine { private static final Logger log = LoggerFactory.getLogger(JetEngine.class); public static final String VERSION = Version.getVersion(JetEngine.class); private JetConfig config; private ResourceLoader resourceLoader; private VariableResolver resolver; private JetTemplateClassLoader classLoader; private ConcurrentResourceCache resourceCache; private ConcurrentTemplateCache templateCache; private JavaCompiler javaCompiler; private JetSecurityManager securityManager; private JetGlobalVariables globalVariables; public static JetEngine create() { return new JetEngine(new JetConfig().loadClasspath(JetConfig.DEFAULT_CONFIG_FILE)); } public static JetEngine create(File configFile) { return new JetEngine(new JetConfig().loadFile(configFile)); } public static JetEngine create(Properties properties) { return new JetEngine(new JetConfig().load(properties)); } // 提供给 JetWebEngine 用 protected JetEngine() { } protected JetEngine(JetConfig config) { load(config); } protected void load(JetConfig config) { this.config = config.build(); this.resolver = createVariableResolver(); this.resourceLoader = createResourceLoader(); this.classLoader = new JetTemplateClassLoader(config); this.resourceCache = new ConcurrentResourceCache(); this.templateCache = new ConcurrentTemplateCache(); this.securityManager = createSecurityManager(); this.globalVariables = createGlobalVariables(); if (config.getCompileStrategy() == CompileStrategy.precompile) { startPreCompileTask(); } } /** * 根据一个绝对路径,判断资源文件是否存在. */ public boolean lookupResource(String name) { name = PathUtils.getStandardizedName(name); return resourceCache.get(name) != null; } /** * 根据一个绝对路径,获取一个资源对象. * * @throws ResourceNotFoundException */ public Resource getResource(String name) throws ResourceNotFoundException { name = PathUtils.getStandardizedName(name); Resource resource = resourceCache.get(name); if (resource == null) { throw new ResourceNotFoundException(name); } return resource; } /** * 根据一个绝对路径,获取一个模板对象. * * @throws ResourceNotFoundException */ public JetTemplate getTemplate(String name) throws ResourceNotFoundException { name = PathUtils.getStandardizedName(name); JetTemplate template = templateCache.get(name); template.checkLastModified(); return template; } /** * 直接从源代码中创建一个新的模板对象. * * <p>返回的对象内部没有缓存,每次都会重新进行解析和编译,如果需要缓存,请在外面直接实现。</p> * * @since 1.1.0 */ public JetTemplate createTemplate(String source) { Resource resource = new SourceCodeResource(source); return new JetTemplate(this, resource); } protected VariableResolver getVariableResolver() { return resolver; } protected JetTemplateClassLoader getClassLoader() { return classLoader; } protected JavaCompiler getJavaCompiler() { if (javaCompiler == null) { // 在 compileStrategy == none 的情况下,采用延迟加载,可以有效避免没有 javax.tools.JavaCompiler 的情况 synchronized (this) { if (javaCompiler == null) { javaCompiler = JavaCompiler.create(this.classLoader, config); } } } return javaCompiler; } public JetSecurityManager getSecurityManager() { return securityManager; } public JetGlobalVariables getGlobalVariables() { return globalVariables; } /** * 获取模板配置. */ public JetConfig getConfig() { return config; } /** * 获取模板引擎的版本号. */ public String getVersion() { return VERSION; } private VariableResolver createVariableResolver() { VariableResolver resolver = new VariableResolver(); for (String pkg : config.getImportPackages()) { resolver.addImportPackage(pkg); } for (String klassName : config.getImportClasses()) { resolver.addImportClass(klassName); } for (String method : config.getImportMethods()) { resolver.addMethodClass(method); } for (String function : config.getImportFunctions()) { resolver.addFunctionClass(function); } for (String tag : config.getImportTags()) { resolver.addTagClass(tag); } for (String variable : config.getImportVariables()) { int pos = variable.lastIndexOf(" "); String defination = variable.substring(0, pos); String id = variable.substring(pos + 1); resolver.addGlobalVariable(defination, id); } if (config.isImportAutoscan()) { log.info("Starting to autoscan the JetMethods, JetFunctions, JetTags implements..."); autoScanClassImplements(resolver); } return resolver; } // 自动扫描 annotation @SuppressWarnings({ "unchecked" }) private void autoScanClassImplements(VariableResolver resolver) { List<String> scanPackages = config.getImportAutoscanPackages(); //@formatter:off Class<? extends Annotation>[] annoClasses = (Class<? extends Annotation>[]) new Class<?>[] { JetAnnotations.Methods.class, JetAnnotations.Functions.class, JetAnnotations.Tags.class, }; //@formatter:on long ts = System.currentTimeMillis(); Set<Class<?>> klasses = AnnotationClassLookupUtils.getClasses(scanPackages, true, annoClasses, config.isImportAutoscanSkiperrors()); ts = System.currentTimeMillis() - ts; log.info("Successfully to find {} classes, cost {} ms.", klasses.size(), ts); for (Class<?> klass : klasses) { for (Annotation anno : klass.getAnnotations()) { if (anno instanceof JetAnnotations.Methods) { resolver.addMethodClass(klass); } else if (anno instanceof JetAnnotations.Functions) { resolver.addFunctionClass(klass); } else if (anno instanceof JetAnnotations.Tags) { resolver.addTagClass(klass); } } } } private ResourceLoader createResourceLoader() { try { ResourceLoader resourceLoader; if (config.getCompileStrategy() == CompileStrategy.none) { // 这种情况下,使用自定义的 ResourceLoader resourceLoader = new CompiledClassResourceLoader(); } else { resourceLoader = (ResourceLoader) config.getTemplateLoader().newInstance(); } resourceLoader.initialize(this, config.getTemplatePath(), config.getInputEncoding()); return resourceLoader; } catch (Exception e) { throw ExceptionUtils.uncheck(e); } } private JetSecurityManager createSecurityManager() { Class<?> klass = config.getSecurityManager(); if (klass == null) { return null; } try { JetSecurityManager manager = (JetSecurityManager) klass.newInstance(); manager.initialize(this); return manager; } catch (Exception e) { throw new RuntimeException(e); } } private JetGlobalVariables createGlobalVariables() { Class<?> klass = config.getGlobalVariables(); if (klass == null) { return null; } try { return (JetGlobalVariables) klass.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } private void startPreCompileTask() { // 启动预编译线程 Thread thread = new Thread() { @Override public void run() { List<String> resources = resourceLoader.loadAll(); log.info("Find {} templates to precompile ...", resources.size()); int succ = 0; int fail = 0; long ts = System.currentTimeMillis(); for (String name : resources) { try { getTemplate(name); succ++; } catch (Exception e) { fail++; log.error("precompile error.", e); } } ts = System.currentTimeMillis() - ts; log.info("Completed precompile templates in {} ms, success = {}, failure = {}.", ts, succ, fail); } }; thread.setName("JetPreCompiler"); thread.setDaemon(true); thread.start(); } private class ConcurrentResourceCache extends ConcurrentCache<String, Resource> { @Override protected Resource doGetValue(String name) { return JetEngine.this.resourceLoader.load(name); } } private class ConcurrentTemplateCache extends ConcurrentCache<String, JetTemplate> { @Override protected JetTemplate doGetValue(String name) { Resource resource = JetEngine.this.getResource(name); return new JetTemplate(JetEngine.this, resource); } } }