All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zodiac.template.velocity.impl.VelocityConfigurationImpl Maven / Gradle / Ivy

The newest version!
package org.zodiac.template.velocity.impl;

import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.zodiac.sdk.toolkit.constants.StringPool;
import org.zodiac.sdk.toolkit.util.AssertUtil;
import org.zodiac.sdk.toolkit.util.ToStringBuilder.MapBuilder;
import org.zodiac.sdk.toolkit.util.collection.CollUtil;
import org.zodiac.sdk.toolkit.util.file.FileToolUtil;
import org.zodiac.sdk.toolkit.util.lang.ArrayUtil;
import org.zodiac.sdk.toolkit.util.lang.ObjUtil;
import org.zodiac.sdk.toolkit.util.lang.StrUtil;
import org.apache.velocity.app.event.EventHandler;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.util.ExtProperties;
import org.slf4j.Logger;
import org.springframework.core.io.ContextResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.zodiac.template.velocity.VelocityConfiguration;
import org.zodiac.template.velocity.VelocityPlugin;
import org.zodiac.template.velocity.constants.VelocityTemplateConstants;
import org.zodiac.template.velocity.support.RenderableHandler;

/**
 * Velocity engine的配置。
 *
 */
public class VelocityConfigurationImpl implements VelocityConfiguration {

    private final Logger log;
    private final ExtProperties properties = new ExtProperties();
    private final Map preloadedResources = CollUtil.map();
    private final CloneableEventCartridge eventCartridge = new CloneableEventCartridge();
    private Object[] plugins;
    private ResourceLoader loader;
    private boolean productionMode = true;

    /*For resource loader.*/
    private String path;
    private boolean cacheEnabled = true;
    private int modificationCheckInterval = VelocityTemplateConstants.DEFAULT_MODIFICATION_CHECK_SECONDS;

    /*Strict ref.*/
    private boolean strictReference = true;

    /*Template charset encoding.*/
    private Charset charset;

    private int parserPoolSize = VelocityTemplateConstants.DEFAULT_PARSER_POOL_SIZE;

    private boolean logWhenFoundTemplate = false;

    private boolean exceptionWrongArgs = true;

    private boolean inlineLocalScope = true;

    /*Global macros.*/
    private String[] macros;

    public VelocityConfigurationImpl(Logger log) {
        this.log = AssertUtil.assertNotNull(log, "log");
    }

    public ExtProperties getProperties() {
        return properties;
    }

    public CloneableEventCartridge getEventCartridge() {
        return eventCartridge;
    }

    public ResourceLoader getResourceLoader() {
        return loader;
    }

    public void setResourceLoader(ResourceLoader loader) {
        this.loader = loader;
    }

    public boolean isProductionMode() {
        return productionMode;
    }

    /**
     * 设置生产模式。默认为true。
     * 
     * @param productionMode 是否生产模式
     */
    public void setProductionMode(boolean productionMode) {
        this.productionMode = productionMode;
    }

    /**
     * 设置搜索模板的根目录。默认为classpath:/templates。
     * 
     * @param path 路径
     */
    public void setPath(String path) {
        this.path = StrUtil.trimToNull(path);
    }

    /**
     * 是否开启模板缓存。在生产模式下,该模式将被强行开启。
     * 
     * @param cacheEnabled 是否开启缓存
     */
    public void setCacheEnabled(boolean cacheEnabled) {
        this.cacheEnabled = cacheEnabled;
    }

    /**
     * 设置检查模板被修改的间隔秒数。默认为2。
     * 
     * @param modificationCheckInterval 秒数
     */
    public void setModificationCheckInterval(int modificationCheckInterval) {
        this.modificationCheckInterval = modificationCheckInterval;
    }

    /**
     * 设置strict reference模式。默认为true。
     * 
     * @param strictReference strict reference
     */
    public void setStrictReference(boolean strictReference) {
        this.strictReference = strictReference;
    }

    public void setTemplateEncoding(Charset charset) {
        this.charset = charset;
    }

    public void setParserPoolSize(int parserPoolSize) {
        this.parserPoolSize = parserPoolSize;
    }

    public void setLogWhenFoundTemplate(boolean logWhenFoundTemplate) {
        this.logWhenFoundTemplate = logWhenFoundTemplate;
    }

    public void setExceptionWrongArgs(boolean exceptionWrongArgs) {
        this.exceptionWrongArgs = exceptionWrongArgs;
    }

    public void setInlineLocalScope(boolean inlineLocalScope) {
        this.inlineLocalScope = inlineLocalScope;
    }

    /**
     * 设置全局宏的名称,可包含通配符。 
     * 
     * @param macros 宏名称数组
     */
    public void setGlobalMacros(String[] macros) {
        this.macros = macros;
    }

    public void setPlugins(Object[] plugins) {
        this.plugins = plugins;
    }

    /**
     * 设置高级配置。
     * 
     * @param properties 属性
     */
    public void setAdvancedProperties(Map properties) {
        this.properties.clear();
        for (Map.Entry entry : properties.entrySet()) {
            this.properties.setProperty(entry.getKey(), entry.getValue());
        }
    }

    public void addPreloadedResource(String name, Resource resource) {
        String _name = StrUtil.trimToNull(name);
        if (null != _name && null != resource) {
            this.preloadedResources.put(_name, resource);
        }
    }

    public void addPreloadedResources(Map resources) {
        if (CollUtil.isNotEmptyMap(resources)) {
            this.preloadedResources.putAll(resources);
        }
    }

    /**
     * 初始化configuration。 
     * 
     * @throws Exception Exception
     */
    public void init() throws Exception {
        AssertUtil.assertNotNull(loader, "resourceLoader");
        removeReservedProperties();
        initPlugins();
        initLogger();
        initMacros();
        /*依赖于initMacros的结果。*/
        initResourceLoader();
        initEventHandlers();
        initMiscs();
    }

    private void addHandler(EventHandler handler) {
        AssertUtil.assertTrue(eventCartridge.addEventHandler(handler), "Unknown event handler type: %s", handler.getClass());
    }

    /** 删除保留的properties,这些properties用户不能修改。 */
    private void removeReservedProperties() {
        Set keysToRemove = CollUtil.set();

        /*Remove resource loader settings*/
        keysToRemove.add(RuntimeConstants.RESOURCE_LOADERS);

        for (Iterator i = properties.getKeys(); i.hasNext();) {
            Object key = i.next();

            if (key instanceof String && ((String)key).contains(RuntimeConstants.RESOURCE_LOADER)) {
                keysToRemove.add((String)key);
            }
        }

        /*Remove log settings.*/
        keysToRemove.add(RuntimeConstants.RUNTIME_LOG_INSTANCE);
        keysToRemove.add(RuntimeConstants.RUNTIME_LOG_NAME);
        keysToRemove.add(RuntimeConstants.RUNTIME_LOG_REFERENCE_LOG_INVALID);
        keysToRemove.add(RuntimeConstants.RUNTIME_LOG_METHOD_CALL_LOG_INVALID);
        keysToRemove.add(RuntimeConstants.RUNTIME_LOG_TRACK_LOCATION);

        /*Remove macros.*/
        keysToRemove.add(RuntimeConstants.VM_LIBRARY);

        /*Remove event handlers: 仅移除eventhandler.xxx.class,保留其它参数。*/
        for (Iterator i = properties.getKeys(); i.hasNext();) {
            Object key = i.next();

            if (key instanceof String && ((String)key).startsWith("eventhandler.")
                && ((String)key).endsWith(".class")) {
                keysToRemove.add((String)key);
            }
        }

        /*Remove others.*/
        keysToRemove.add(RuntimeConstants.INPUT_ENCODING);
        keysToRemove.add(RuntimeConstants.VM_LIBRARY_AUTORELOAD);
        keysToRemove.add(RuntimeConstants.RUNTIME_REFERENCES_STRICT);

        /*Do removing.*/
        for (String key : keysToRemove) {
            if (properties.containsKey(key)) {
                log.warn("Removed reserved property: {} = {}", key, properties.get(key));
                properties.clearProperty(key);
            }
        }
    }

    /* 初始化plugins。 */
    private void initPlugins() throws Exception {
        if (plugins != null) {
            for (Object plugin : plugins) {
                if (plugin instanceof VelocityPlugin) {
                    ((VelocityPlugin)plugin).init(this);
                }
            }
        }
    }

    /**
     * 初始化resource loader。
     * 

* 固定使用ResourceLoadingService/Spring ResourceLoader来装载资源,由于以上机制已经包含足够的灵活性,所以不再允许用户在velocity层面配置resource loader。 *

*/ private void initResourceLoader() { // path = defaultIfNull(path, "/templates"); path = ObjUtil.defaultIfNull(path, VelocityTemplateConstants.DEFAULT_RESOURCE_LOADER_PATH); if (productionMode) { cacheEnabled = true; } properties.setProperty(RuntimeConstants.RESOURCE_LOADERS, SpringResourceLoaderAdapter.RESOURCE_LOADER_NAME); String prefix = null; /*Spring resource loader.*/ //prefix = "spring." + RESOURCE_LOADER + "."; prefix = String.format("%s.%s.", RuntimeConstants.RESOURCE_LOADER, SpringResourceLoaderAdapter.RESOURCE_LOADER_NAME); properties.setProperty(prefix + "description", "Spring Resource Loader Adapter"); properties.setProperty(prefix + "class", SpringResourceLoaderAdapter.class.getName()); properties.setProperty(prefix + "path", path); properties.setProperty(prefix + "cache", String.valueOf(cacheEnabled)); //properties.setProperty(prefix + "modificationCheckInterval", String.valueOf(modificationCheckInterval)); properties.setProperty(prefix + RuntimeConstants.RESOURCE_LOADER_CHECK_INTERVAL, String.valueOf(modificationCheckInterval)); /*Preloaded resource loader.*/ //prefix = "preloaded." + RESOURCE_LOADER + "."; prefix = String.format("%s.%s.", RuntimeConstants.RESOURCE_LOADER, PreloadedResourceLoader.RESOURCE_LOADER_NAME); properties.setProperty(prefix + "description", "Preloaded Resource Loader"); properties.setProperty(prefix + "class", PreloadedResourceLoader.class.getName()); properties.setProperty(prefix + "cache", String.valueOf(cacheEnabled)); //properties.setProperty(prefix + "modificationCheckInterval", String.valueOf(modificationCheckInterval)); properties.setProperty(prefix + RuntimeConstants.RESOURCE_LOADER_CHECK_INTERVAL, String.valueOf(modificationCheckInterval)); properties.setProperty(prefix + PreloadedResourceLoader.PRELOADED_RESOURCES_KEY, preloadedResources); if (!preloadedResources.isEmpty()) { properties.addProperty(RuntimeConstants.RESOURCE_LOADER, PreloadedResourceLoader.RESOURCE_LOADER_NAME); } } /** 初始化日志系统。 */ private void initLogger() {} /* 查找所有全局macros。 */ private void initMacros() throws Exception { ResourcePatternResolver resolver; if (loader instanceof ResourcePatternResolver) { resolver = (ResourcePatternResolver)loader; } else { resolver = new PathMatchingResourcePatternResolver(loader); } if (macros != null) { for (String macro : macros) { resolveMacro(resolver, macro); } } /*Velocity default: VM_global_library.vm*/ resolveMacro(resolver, RuntimeConstants.VM_LIBRARY_DEFAULT); /*Plugin macros.*/ if (plugins != null) { for (Object plugin : plugins) { if (plugin instanceof VelocityPlugin) { addMacroResources(null, ((VelocityPlugin)plugin).getMacros()); } } } if (!properties.containsKey(RuntimeConstants.VM_LIBRARY)) { properties.setProperty(RuntimeConstants.VM_LIBRARY, StringPool.EMPTY); } } private void resolveMacro(ResourcePatternResolver resolver, String macro) { String path = FileToolUtil.normalizeAbsolutePath(this.path + "/"); String pattern = FileToolUtil.normalizeAbsolutePath(path + macro); Resource[] resources; try { resources = resolver.getResources(pattern); } catch (IOException e) { resources = null; } addMacroResources(path, resources); } private void addMacroResources(String path, Resource[] resources) { if (resources != null) { /*必须用vector,否则VelocimacroFactory老代码读不到值。*/ @SuppressWarnings("unchecked") Set macros = CollUtil.set(properties.getVector(RuntimeConstants.VM_LIBRARY)); for (Resource resource : resources) { if (resource.exists()) { String templateName = null; /*对于多数resource,如ServletResource,ResourceAdapter等,都可以从中取得原始的resourceName。*/ if (path != null && resource instanceof ContextResource) { String resourceName = ((ContextResource)resource).getPathWithinContext(); if (resourceName.startsWith(path)) { templateName = resourceName.substring(path.length()); } } /*对于不可取得resourceName的,使用特殊的装载机制。*/ if (templateName == null) { templateName = getTemplateNameOfPreloadedResource(resource); } if (!macros.contains(templateName)) { properties.addProperty(RuntimeConstants.VM_LIBRARY, templateName); macros.add(templateName); } } } } } private String getTemplateNameOfPreloadedResource(Resource resource) { URL url; try { url = resource.getURL(); } catch (IOException e) { url = null; } String templateNameBase; if (url != null) { //templateNameBase = "globalVMs/" + StringUtils.getFilename(url.getPath()); templateNameBase = "globalVMs/" + FileToolUtil.getFileName(url.getPath()); } else { templateNameBase = "globalVMs/globalVM.vm"; } String templateName = templateNameBase; /*防止加入重复的resource对象。*/ for (int i = 1; preloadedResources.containsKey(templateName) && !resource.equals(preloadedResources.get(templateName)); i++) { templateName = templateNameBase + i; } preloadedResources.put(templateName, resource); return templateName; } private void initEventHandlers() { /*准备eventCartridge,并设置默认的handler。*/ boolean hasRenderableHandler = false; if (ArrayUtil.isNotEmptyArray(plugins)) { for (Object plugin : plugins) { if (plugin instanceof RenderableHandler) { hasRenderableHandler = true; break; } } } if (!hasRenderableHandler) { addHandler(new RenderableHandler()); } if (ArrayUtil.isNotEmptyArray(plugins)) { for (Object plugin : plugins) { if (plugin instanceof EventHandler) { addHandler((EventHandler)plugin); } } } } /* 初始化杂项。 */ private void initMiscs() { if (charset == null) { charset = VelocityTemplateConstants.DEFAULT_ENCODING; } setDefaultProperty(RuntimeConstants.RESOURCE_MANAGER_LOGWHENFOUND, String.valueOf(logWhenFoundTemplate)); setDefaultProperty(RuntimeConstants.INPUT_ENCODING, charset); setDefaultProperty(VelocityTemplateConstants.OUTPUT_ENCODING, charset); setDefaultProperty(RuntimeConstants.PARSER_POOL_SIZE, String.valueOf(parserPoolSize > 0 ? parserPoolSize : VelocityTemplateConstants.DEFAULT_PARSER_POOL_SIZE)); setDefaultProperty(RuntimeConstants.UBERSPECT_CLASSNAME, CustomizedUberspectImpl.class.getName()); setDefaultProperty(RuntimeConstants.VM_ARGUMENTS_STRICT, String.valueOf(exceptionWrongArgs)); setDefaultProperty(RuntimeConstants.VM_PERM_INLINE_LOCAL, String.valueOf(inlineLocalScope)); /*Auto-reload macros.*/ properties.setProperty(RuntimeConstants.VM_LIBRARY_AUTORELOAD, productionMode ? StringPool.FALSE : StringPool.TRUE); /*Strict ref.*/ properties.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, String.valueOf(strictReference)); } /* 设置默认值。如果值已存在,则不覆盖。 */ private void setDefaultProperty(String key, Object value) { if (!properties.containsKey(key)) { properties.setProperty(key, value); } } @Override public String toString() { return new MapBuilder().setSortKeys(true).setPrintCount(true).appendAll(properties).toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy