com.greenlaw110.rythm.conf.RythmConfigurationKey Maven / Gradle / Ivy
Show all versions of rythm-engine Show documentation
/*
* Copyright (C) 2013 The Rythm Engine project
* Gelin Luo
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.greenlaw110.rythm.conf;
import com.greenlaw110.rythm.Rythm;
import com.greenlaw110.rythm._Rythm;
import com.greenlaw110.rythm.cache.NoCacheService;
import com.greenlaw110.rythm.cache.SimpleCacheService;
import com.greenlaw110.rythm.exception.ConfigurationException;
import com.greenlaw110.rythm.extension.ICodeType;
import com.greenlaw110.rythm.extension.IDurationParser;
import com.greenlaw110.rythm.extension.II18nMessageResolver;
import com.greenlaw110.rythm.logger.JDKLogger;
import com.greenlaw110.rythm.utils.S;
import java.io.File;
import java.net.URL;
import java.util.*;
/**
* {@link com.greenlaw110.rythm.RythmEngine} configuration keys. General rules:
*
*
* - When a key is ended with
.enabled
, then you should be able to set
* the setting without .enabled
or replace it with .disabled
* but the value will be inverted. For example, built_in.transformer.enabled
* is equal to built_in.transformer
and invert to
* built_in.transformer.disabled
* - When a key is ended with
.impl
, then you can either put an instance into
* the configuration map or a string of the class name
*
*/
public enum RythmConfigurationKey {
/**
* "built_in.code_type.enabled": Enable built-in {@link com.greenlaw110.rythm.extension.ICodeType code type} implementations
*
* Default value: true
*/
BUILT_IN_CODE_TYPE_ENABLED("built_in.code_type.enabled", true),
/**
* "built_in.transformer.enabled": Enable built-in {@link com.greenlaw110.rythm.extension.Transformer transformer} implementations
*
* Default value: true
*
* @see #FEATURE_TRANSFORM_ENABLED
*/
BUILT_IN_TRANSFORMER_ENABLED("built_in.transformer.enabled", true),
/**
* "cache.enabled": Enable disable {@link com.greenlaw110.rythm.extension.ICacheService cache service}. When this
* setting is turned off, the {@link #CACHE_SERVICE_IMPL} will be set to
* {@link com.greenlaw110.rythm.cache.NoCacheService} without regarding to it's configuration
*
* Default value: false
*
* TODO: add link to cache service reference
*/
CACHE_ENABLED("cache.enabled", false),
/**
* "cache.service.impl": Set {@link com.greenlaw110.rythm.extension.ICacheService cache service} implementation
*
* Default value: {@link com.greenlaw110.rythm.cache.SimpleCacheService}
*
* Note when {@link #CACHE_ENABLED} is set to false
, then this setting
* will be ignored, and the service impl will be set to {@link com.greenlaw110.rythm.cache.NoCacheService}
* anyway
*
* TODO: add link to cache service reference
*/
CACHE_SERVICE_IMPL("cache.service.impl") {
@Override
protected Object getDefVal(Map configuration) {
Boolean cacheEnabled = CACHE_ENABLED.getConfiguration(configuration);
return cacheEnabled ? SimpleCacheService.INSTANCE : NoCacheService.INSTANCE;
}
},
/**
* "cache.duration_parser.impl": set {@link com.greenlaw110.rythm.extension.IDurationParser duration parser} implementation.
*
* Default value: {@link com.greenlaw110.rythm.extension.IDurationParser#DEFAULT_PARSER}
*/
CACHE_DURATION_PARSER_IMPL("cache.duration_parser.impl", IDurationParser.DEFAULT_PARSER),
/**
* "cache.prod_only.enabled": Turn on/off cache at
* {@link com.greenlaw110.rythm.Rythm.Mode#dev dev} mode. When
* this setting is turned on, then cache will not effect at dev mode
*
* Default value: true
*/
CACHE_PROD_ONLY_ENABLED("cache.prod_only.enabled", true),
/**
* "codegen.compact.enabled": Enable/disable compact redundant space and lines
*
* Default value: true
*/
CODEGEN_COMPACT_ENABLED("codegen.compact.enabled", true),
/**
* "codegen.source_code_enhancer.impl": Set template
* {@link com.greenlaw110.rythm.extension.ISourceCodeEnhancer source code enhancer}
* implementation.
* Default value: null
*/
CODEGEN_SOURCE_CODE_ENHANCER("codegen.source_code_enhancer.impl"),
/**
* "codegen.byte_code_enhancer.impl": Set template
* {@link com.greenlaw110.rythm.extension.IByteCodeEnhancer byte code enhancer} implementation.
* Default value: null
*/
CODEGEN_BYTE_CODE_ENHANCER("codegen.byte_code_enhancer.impl"),
/**
* "default.code_type.impl": Set default {@link com.greenlaw110.rythm.extension.ICodeType code type}
*
* Default value: {@link com.greenlaw110.rythm.extension.ICodeType.DefImpl#RAW raw}
*
* TODO: what if {@link #BUILT_IN_CODE_TYPE_ENABLED} is false
*/
DEFAULT_CODE_TYPE_IMPL("default.code_type.impl", ICodeType.DefImpl.RAW),
/**
* "default.cache_ttl": Set default {@link com.greenlaw110.rythm.extension.ICacheService cache} ttl
* in second
*
* Default value: 60 * 60(1hr
*/
DEFAULT_CACHE_TTL("default.cache_ttl") {
public T getConfiguration(Map configuration) {
String k = getKey();
Object v = configuration.get(k);
if (null == v) {
return (T) (Number) (60 * 60);
}
if (v instanceof Number) {
return (T) v;
}
return (T) (Integer.valueOf(v.toString()));
}
},
/**
* "engine.mode": Set the {@link com.greenlaw110.rythm.Rythm.Mode mode} of rythm engine
* Default value: {@link com.greenlaw110.rythm.Rythm.Mode#prod}
*/
ENGINE_MODE("engine.mode") {
@Override
public T getConfiguration(Map configuration) {
String k = getKey();
Object v = configuration.get(k);
if (null == v) {
return (T) Rythm.Mode.prod;
} else {
if (v instanceof Rythm.Mode) {
return (T) v;
} else {
return (T) Rythm.Mode.valueOf(v.toString());
}
}
}
},
/**
* "engine.id": Set the ID of rythm engine instance
* Default value: "re-" plus a random String with 3 chars
*/
ENGINE_ID("engine.id") {
@Override
protected Object getDefVal(Map configuration) {
return "re-" + S.random(3);
}
},
/**
* "engine.class_loader.parent.impl": Set the {@link ClassLoader#getParent() parent} class loader of the rythm
* template class loader
* Default value: first try to use {@link Thread#getContextClassLoader() current thread's context class loader}
* if the context classloader is null
, then use the class loader which loads the Rythm.class
*/
ENGINE_CLASS_LOADER_PARENT_IMPL("engine.class_loader.parent.impl") {
@Override
protected Object getDefVal(Map configuration) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (null == cl) {
cl = Rythm.class.getClassLoader();
}
return cl;
}
},
/**
* "engine.class_loader.byte_code_helper.impl": Set the {@link com.greenlaw110.rythm.extension.IByteCodeHelper bytecode helper}
* implementation
* Default value: null
*/
ENGINE_CLASS_LOADER_BYTE_CODE_HELPER_IMPL("engine.class_loader.byte_code_helper.impl"),
/**
* "engine.load_precompiled.enabled": Set the flag so that Rythm will load precompiled template class directly from
* bytecode cached in the {@link #HOME_PRECOMPILED precompiled root} when running
* in {@link com.greenlaw110.rythm.Rythm.Mode#prod prod} mode.
* Default value: false
*/
ENGINE_LOAD_PRECOMPILED_ENABLED("engine.load_precompiled.enabled", false),
/**
* "engine.file_write.enabled": Enable/disable write to file system. This option is used by rythm to check if
* it should write template class bytecode cache to disk or not. In some cases
* you want to disable file write due the limit of the runtime environment, e.g.
* on GAE platform
*
* Default value: true
*/
ENGINE_FILE_WRITE_ENABLED("engine.file_write.enabled", true),
/**
* "engine.precompile_mode.enabled": Set/unset precompile mode. This option is used by play-rythm plugin (could also
* be other plugin) to notify rythm that is is doing a precompile. User application
* should not use this option
*
* Default value: false
*/
ENGINE_PRECOMPILE_MODE("engine.precompile_mode.enabled") {
@Override
public T getConfiguration(Map configuration) {
String k = getKey();
Object v = configuration.get(k);
if (null == v) {
return (T) Boolean.FALSE;
} else {
if (v instanceof Boolean) {
return (T) v;
} else {
return (T) Boolean.valueOf(v.toString());
}
}
}
},
ENGINE_OUTPUT_JAVA_SOURCE_ENABLED("engine.debug_java_source.enabled", false),
/**
* "engine.playframework.enabled": A special flag used when Rythm is working with rythm-plugin for Play!Framework. Usually
* you should not touch this setting.
*
* Default value: false
*/
ENGINE_PLAYFRAMEWORK("engine.playframework.enabled", false),
/**
* "engine.plugin.version": Set by plugin of certain framework, e.g. play!framework. Used to determine
* whether it needs to refresh the cached template class bytecode. Default value: ""
(empty string)
*/
ENGINE_PLUGIN_VERSION("engine.plugin.version", ""),
/**
* "feature.transform.enabled": Enable disable {@link com.greenlaw110.rythm.extension.Transformer transformer}
*
* Default value: true
*/
FEATURE_TRANSFORM_ENABLED("feature.transform.enabled", true),
/**
* "feature.type_inference.enabled": Enable disable type inference. TODO add link to type inference reference page
*
* Default value: false
*/
FEATURE_TYPE_INFERENCE_ENABLED("feature.type_inference.enabled", false),
/**
* "feature.smart_escape.enabled": Enable disable smart escape. TODO: add link to smart escape reference page
*
* Default value: true
*/
FEATURE_SMART_ESCAPE_ENABLED("feature.smart_escape.enabled", true),
/**
* "feature.natural_template.enabled": Enable disable natural template. TODO: add reference link to natural template
*
* Default value: true
*/
FEATURE_NATURAL_TEMPLATE_ENABLED("feature.natural_template.enabled", false),
/**
* "home.template.dir": Set the home dir of template files. This configuration is used when the {@link #RESOURCE_LOADER_IMPL}
* is not configured, therefore the {@link com.greenlaw110.rythm.resource.TemplateResourceManager} will
* try to load {@link com.greenlaw110.rythm.resource.FileTemplateResource} from this template home dir
* configured.
*
* Default value: a file created with the following logic
*
* new File(Thread.currentThread().getContextClassLoader().getResource("rythm").getFile())
*/
HOME_TEMPLATE("home.template.dir") {
@Override
protected Object getDefVal(Map configuration) {
URL url = Thread.currentThread().getContextClassLoader().getResource("rythm");
if (null != url) return new File(url.getPath());
return new File("rythm");
}
},
/**
* "home.tmp.dir": Set the rythm tmp dir. The tmp dir is to where Rythm write compiled template class bytecode
* when running in the {@link com.greenlaw110.rythm.Rythm.Mode#dev dev} mode.
*
* Default value: a file created with the following logic
*
* new File(System.__getProperty("java.io.tmpdir"), "__rythm")
*/
HOME_TMP("home.tmp.dir") {
@Override
protected Object getDefVal(Map configuration) {
return new File(System.getProperty("java.io.tmpdir"), "__rythm");
}
},
/**
* "home.precompiled.dir": Set the dir root of the precompiled template bytecodes. Default value: null
*
* @see #ENGINE_LOAD_PRECOMPILED_ENABLED
*/
HOME_PRECOMPILED("home.precompiled.dir") {
@Override
protected Object getDefVal(Map configuration) {
return null;
}
},
/**
* "i18n.locale": the locale for the rythm runtime environment. This configuration
* return the {@link java.util.Locale} type of instance.
*
* Default value: java.util.Locale.getDefault()
*
* @see Java Locale
*/
I18N_LOCALE("i18n.locale") {
@Override
public T getConfiguration(Map configuration) {
String k = getKey();
Object o = configuration.get(k);
if (o instanceof Locale) {
return (T)o;
}
// check lang_REGION style
String s = S.str(o);
if (S.empty(s)) {
return (T)Locale.getDefault();
}
Locale retval;
String[] sa = s.split("_");
String lang = sa[0];
if (sa.length > 1) {
String region = sa[1];
retval = new Locale(lang, region);
} else {
retval = new Locale(lang);
}
return (T)retval;
}
},
/**
* "i18n.message.sources": Set message sources. Should be a String of message (resource bundle) properties
* file names separated by ",", E.g. "format,exception,windows".
* Default value: message
*
* @see [Spring]Internationalization using MessageSource
*/
I18N_MESSAGE_SOURCES("i18n.message.sources", "messages"),
/**
* "i18n.message.resolver.impl": Set i18n message resolver. Should implement {@link com.greenlaw110.rythm.extension.II18nMessageResolver}
* interface. Default value: {@link com.greenlaw110.rythm.extension.II18nMessageResolver.DefaultImpl#INSTANCE}, which delegate
* to {@link com.greenlaw110.rythm.utils.S#i18n(com.greenlaw110.rythm.template.ITemplate, String, Object...)} method
*/
I18N_MESSAGE_RESOLVER("i18n.message.resolver.impl", II18nMessageResolver.DefaultImpl.INSTANCE),
/**
* "log.enabled": Enable disable log in Rythm. Default value: true
*/
LOG_ENABLED("log.enabled", true),
/**
* "log.factory.impl": Configure the {@link com.greenlaw110.rythm.extension.ILoggerFactory logger factory} implementation.
* When this configuration is not set, then a {@link com.greenlaw110.rythm.logger.JDKLogger.Factory} instance
* is used to create the logger
*
* Default value: com.greenlaw110.rythm.logger.JDKLogger.Factory
*/
LOG_FACTORY_IMPL("log.factory.impl", JDKLogger.Factory.class),
/**
* "log.source.java.enabled": Print out relevant java source lines when exception encountered
*
* Default value: true
*/
LOG_SOURCE_JAVA_ENABLED("log.source.java.enabled", true),
/**
* "log.source.template.enabled": Print out relevant template source lines when exception encountered
*
* Default value: true
*/
LOG_SOURCE_TEMPLATE_ENABLED("log.source.template.enabled", true),
/**
* "log.time.render.enabled": Log time spent executing a template. The level used to log the time logRenderTime
* is {@link com.greenlaw110.rythm.logger.ILogger#debug(String, Object...)}
*
* Default value: false
*/
LOG_TIME_RENDER_ENABLED("log.time.render.enabled", false),
/**
* "render.listener.impl": Set {@link com.greenlaw110.rythm.extension.IRythmListener tag
* invocation listener} implementation.
* Default value: null
*/
RENDER_LISTENER("render.listener.impl"),
/**
* "render.exception_handler.impl": Set {@link com.greenlaw110.rythm.extension.IRenderExceptionHandler
* render exception handler} implementation.
* Default value: null
*/
RENDER_EXCEPTION_HANDLER("render.exception_handler.impl"),
/**
* "resource.loader.impl": The {@link com.greenlaw110.rythm.extension.ITemplateResourceLoader resource loader}
* implementation
* Default value: null
. But if this is not configured, try templates will be loaded as
* {@link com.greenlaw110.rythm.resource.FileTemplateResource file template resource} first and if
* still not found then try to load as
* {@link com.greenlaw110.rythm.resource.ClasspathTemplateResource classpath resource}.
*
* @see #HOME_TEMPLATE
*/
RESOURCE_LOADER_IMPL("resource.loader.impl"),
/**
* "resource.name.suffix": does resource name has special rythm suffix attached?
* E.g. .rythm or .rtl etc. Default is empty string
*/
RESOURCE_NAME_SUFFIX("resource.name.suffix", "") {
@Override
public T getConfiguration(Map configuration) {
String s = super.getConfiguration(configuration);
if (S.empty(s)) return (T)"";
if (!s.startsWith(".")) s = "." + s;
return (T)s;
}
},
/**
* "sandbox.security_manager.impl": Set the security manager to be used when running a template in
* {@link com.greenlaw110.rythm.Sandbox sandbox} mode.
* Default value: null
. When no security manager is configured, when the sandbox mode is running, an
* instance of {@link com.greenlaw110.rythm.sandbox.RythmSecurityManager} will be initiated to supervise the
* execution. Usually you should NOT set this configuration and allow Rythm to run it's SecurityManager
* implementation.
*/
SANDBOX_SECURITY_MANAGER_IMPL("sandbox.security_manager.impl"),
/**
* "sandbox.timeout": Set the timeout of a {@link com.greenlaw110.rythm.Sandbox sandbox} execution in milliseconds.
* If the execution failed to return after timeout, then Rythm will interrupt the execution thread and force it
* to return. This setting prevent infinite loop in untrusted template.
* Default value: 1000
*/
SANDBOX_TIMEOUT("sandbox.timeout") {
@Override
public T getConfiguration(Map configuration) {
String k = getKey();
Object v = configuration.get(k);
if (null == v) {
return (T) (Integer) 2000;
}
if (v instanceof Number) {
return (T) v;
}
return (T) Integer.valueOf(v.toString());
}
},
/**
* "sandbox.pool.size": Set the thread pool size of {@link com.greenlaw110.rythm.Sandbox sandbox} executors.
* Default value: 10
*/
SANDBOX_POOL_SIZE("sandbox.pool.size") {
@Override
public T getConfiguration(Map configuration) {
String k = getKey();
Object v = configuration.get(k);
if (null == v) {
return (T) (Integer) 10;
}
if (v instanceof Number) {
return (T) v;
}
return (T) Integer.valueOf(v.toString());
}
},
/**
* "sandbox.restricted_class": Set restricted classes for {@link com.greenlaw110.rythm.Sandbox sandbox} execution.
* The value should be full name of the classes or packages separated by ;
. For example,
* "foo.bar.Employee;foo.secure;...".
*
* If a class or package name is presented in this setting, then the sandbox executor will raise a
* {@link SecurityException} when the template trying to access the class. Note whatever this setting is
* configured, Rythm will prevent the access to the following classes/packages:
*
*
* com.greenlaw110.rythm.Rythm;
* com.greenlaw110.rythm.RythmEngine;
* java.io;
* java.nio;
* java.security;
* java.rmi;
* java.net;
* java.awt;
* java.applet
*
* Default value: ""
*/
SANDBOX_RESTRICTED_CLASS("sandbox.restricted_class", ""),
/**
* "sandbox.allowed_system_properties": Set allowed system properties in string separated by ,.
*
* By default the following properties are allowed to access by sandbox thread
*
* user.dir
* line.separator
* java.vm.name
* java.protocol.handler.pkgs
*
*/
SANDBOX_ALLOWED_SYSTEM_PROPERTIES("sandbox.allowed_system_properties",
"java.io.tmpdir,file.encoding,user.dir,line.separator,java.vm.name,java.protocol.handler.pkgs,suppressRawWhenUnchecked"),
/**
* "sandbox.thread_factory.impl": Configure the thread factory to be used by the sandbox executing service.
* Note this configuration should be very rare used as it is create to support rythmfiddle implementation.
* Should you really need to configure this item, make sure it is configured as an instance of {@link com.greenlaw110.rythm.sandbox.SandboxThreadFactory}
* Default value: null
*/
SANBOX_THREAD_FACTORY_IMPL("sandbox.thread_factory.impl", null),
/**
* "transformer.udt": User defined transformers, should be a list of class names separated by ",". If configured
* then {@link com.greenlaw110.rythm.RythmEngine#registerTransformer(Class[]) RythmEngine.registerTransformer} will
* be called to register these user defined transformer classes. Default value: null
*/
TRANSFORMER_UDT("transformer.udt");
private String key;
private Object defVal;
private RythmConfigurationKey(String key) {
this(key, null);
}
private RythmConfigurationKey(String key, Object defVal) {
this.key = key;
this.defVal = defVal;
}
/**
* Return the key string
*
* @return the key of the configuration
*/
public String getKey() {
return key;
}
/**
* Return default value of this setting. The configuration data map
* is passed in in case the default value be variable depending on
* another setting. For example, the default value of {@link #HOME_TMP tmp dir}
* setting depend on the value of {@link #ENGINE_MODE mode} setting
*
* @param configuration
* @return return the default value
*/
protected Object getDefVal(Map configuration) {
return defVal;
}
/**
* Calling to this method is equals to calling {@link #getKey()}
*
* @return key of the configuration
*/
@Override
public String toString() {
return key;
}
private static List aliases(String key, String suffix) {
List l = new ArrayList();
l.add("rythm." + key);
l.add(key);
if (S.notEmpty(suffix)) {
String k0 = key.replace("." + suffix, "");
l.add("rythm." + k0);
l.add(k0);
}
return l;
}
private Object getValFromAliases(Map configuration, String key, String suffix) {
Object v = configuration.get(key);
if (null == v) {
for (String k0 : aliases(key, suffix)) {
v = configuration.get(k0);
if (null != v) break;
}
if (null == v) {
// still not found, load default value
v = getDefVal(configuration);
}
}
return v;
}
private static boolean toBoolean(Object v) {
if (null == v) return false;
if (v instanceof Boolean) return (Boolean)v;
return Boolean.parseBoolean(v.toString());
}
private Boolean getEnabled(String key, Map configuration) {
Object v = getValFromAliases(configuration, key, "enabled");
if (null == v) {
v = getValFromAliases(configuration, key, "disabled");
return !toBoolean(v);
}
return toBoolean(v);
}
private T getImpl(String key, Map configuration) {
Object v = getValFromAliases(configuration, key, "impl");
if (null == v) return null;
if (v instanceof Class) {
try {
return (T) ((Class) v).newInstance();
} catch (Exception e) {
throw new ConfigurationException(e, "Error getting implementation configuration: %s", key);
}
}
if (!(v instanceof String)) return (T)v;
String clsName = (String)v;
try {
return (T) Class.forName(clsName).newInstance();
} catch (Exception e) {
// try to evaluate the string
try {
Object o = _Rythm.eval(clsName);
if (o instanceof Class) {
return (T) ((Class) o).newInstance();
} else {
return (T) o;
}
} catch (Exception e1) {
throw new ConfigurationException(e, "Error getting implementation configuration: %s", key);
}
}
}
private File getFile(String key, Map configuration) {
Object v = getValFromAliases(configuration, key, "dir");
if (null == v) return null;
if (v instanceof File) {
return (File) v;
}
String s = v.toString();
boolean isAbsolute = false;
if (s.startsWith("/") || s.startsWith(File.separator)) {
isAbsolute = true;
} else if (s.matches("^[a-zA-Z]:.*")) {
isAbsolute = true;
}
if (isAbsolute) return new File(s);
try {
URL url = Thread.currentThread().getContextClassLoader().getResource(s);
return new File(url.getPath());
} catch (Exception e) {
throw new ConfigurationException(e, "Error reading file configuration %s", key);
}
}
/**
* Return configuration value from the configuration data map using the {@link #key}
* of this {@link RythmConfigurationKey setting} instance
*
* @param configuration
* @param
* @return return the configuration
*/
public T getConfiguration(Map configuration) {
String key = this.key;
if (key.endsWith(".enabled")) {
return (T) getEnabled(key, configuration);
}
if (key.endsWith(".impl")) {
return getImpl(key, configuration);
}
if (key.endsWith(".dir")) {
return (T) getFile(key, configuration);
}
return (T) getValFromAliases(configuration, key, null);
}
/**
* Return default configuration of this item
*
* @param
* @return default configuration for this item
*/
public T getDefaultConfiguration() {
return (T)getConfiguration((Map)Collections.emptyMap());
}
private static Map lookup = new HashMap(50); static {
for (RythmConfigurationKey k : values()) {
lookup.put(k.getKey().toLowerCase(), k);
}
}
/**
* Return key enum instance from the string in case insensitive mode
*
* @param s
* @return configuration key from the string
*/
public static RythmConfigurationKey valueOfIgnoreCase(String s) {
if (S.empty(s)) throw new IllegalArgumentException();
return lookup.get(s.trim().toLowerCase());
}
}