org.rythmengine.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 org.rythmengine.conf;
import org.rythmengine.Rythm;
import org.rythmengine._Rythm;
import org.rythmengine.cache.CacheServiceFactory;
import org.rythmengine.cache.NoCacheService;
import org.rythmengine.exception.ConfigurationException;
import org.rythmengine.extension.ICodeType;
import org.rythmengine.extension.IDurationParser;
import org.rythmengine.extension.II18nMessageResolver;
import org.rythmengine.logger.ILogger;
import org.rythmengine.logger.JDKLogger;
import org.rythmengine.logger.Logger;
import org.rythmengine.sandbox.SandboxThreadFactory;
import org.rythmengine.utils.S;
import java.io.File;
import java.lang.reflect.Array;
import java.net.URL;
import java.util.*;
/**
* {@link org.rythmengine.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 org.rythmengine.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 org.rythmengine.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 org.rythmengine.extension.ICacheService cache service}. When this
* setting is turned off, the {@link #CACHE_SERVICE_IMPL} will be set to
* {@link org.rythmengine.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 org.rythmengine.extension.ICacheService cache service} implementation
*
* Default value: {@link org.rythmengine.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 org.rythmengine.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);
if (!cacheEnabled) {
return NoCacheService.INSTANCE;
}
return CacheServiceFactory.INSTANCE.get();
}
},
/**
* "cache.duration_parser.impl": set {@link org.rythmengine.extension.IDurationParser duration parser} implementation.
*
* Default value: {@link org.rythmengine.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 org.rythmengine.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 org.rythmengine.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 org.rythmengine.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 org.rythmengine.extension.ICodeType code type}
*
* Default value: {@link org.rythmengine.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 org.rythmengine.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 org.rythmengine.Rythm.Mode mode} of rythm engine
* Default value: {@link org.rythmengine.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 org.rythmengine.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 org.rythmengine.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.gae.enabled": Enable/disable google application engine. This option is used by rythm to check if
* if it is running in a GAE environment. If this option is enabled, then file write is always disabled
* regardless the setting of {@link #ENGINE_FILE_WRITE_ENABLED}
*
* Default value: false
*/
ENGINE_GAE_ENABLED("engine.gae.enabled", false),
/**
* "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 org.rythmengine.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(s) of template files. This configuration is used when the {@link #RESOURCE_LOADER_IMPLS}
* is not configured, therefore the {@link org.rythmengine.resource.TemplateResourceManager} will
* try to load {@link org.rythmengine.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");
}
@Override
public T getConfiguration(Map configuration) {
Object defVal = getDefVal(configuration);
String key = getKey();
Object o = getValFromAliases(configuration, getKey(), "dir", defVal);
List fl = new ArrayList();
if (o instanceof File) {
fl.add((File) o);
} else if (o instanceof List) {
for (Object el: (List)o) {
if (null == el) {
continue;
}
if (el instanceof File) {
fl.add((File) el);
} else {
fl.add(asFile(el.toString(), key));
}
}
} else if (o.getClass().isArray()) {
int len = Array.getLength(o);
for (int i = 0; i < len; ++i) {
Object el = Array.get(o, i);
if (el instanceof File) {
fl.add((File) el);
} else {
fl.add(asFile(el.toString(), key));
}
}
} else {
String s = o.toString();
for (String el: s.split("[,;]")) {
fl.add(asFile(el.trim(), key));
}
}
return (T) fl;
}
},
/**
* "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 org.rythmengine.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) {
String tmp = System.getProperty("java.io.tmpdir");
File f = new File(tmp, "__rythm");
if (!f.exists() && !f.mkdirs()) {
f = new File(tmp);
}
return f;
}
},
/**
* "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 org.rythmengine.extension.II18nMessageResolver}
* interface. Default value: {@link org.rythmengine.extension.II18nMessageResolver.DefaultImpl#INSTANCE}, which delegate
* to {@link org.rythmengine.utils.S#i18n(org.rythmengine.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 org.rythmengine.extension.ILoggerFactory logger factory} implementation.
* When this configuration is not set, then a {@link org.rythmengine.logger.JDKLogger.Factory} instance
* is used to create the logger
*
* Default value: org.rythmengine.logger.JDKLogger.Factory
*/
LOG_FACTORY_IMPL("log.factory.impl") {
@Override
protected Object getDefVal(Map configuration) {
try {
// if commons logging exists then use it
Class.forName("org.apache.commons.logging.Log");
Class> logFactCls = Class.forName("org.rythmengine.logger.CommonsLoggerFactory");
return logFactCls;
} catch (Exception e) {
return new JDKLogger.Factory();
}
}
},
/**
* "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 org.rythmengine.logger.ILogger#debug(String, Object...)}
*
* Default value: false
*/
LOG_TIME_RENDER_ENABLED("log.time.render.enabled", false),
/**
* "render.listener.impl": Set {@link org.rythmengine.extension.IRythmListener tag
* invocation listener} implementation.
* Default value: null
*/
RENDER_LISTENER("render.listener.impl"),
/**
* "render.exception_handler.impl": Set {@link org.rythmengine.extension.IRenderExceptionHandler
* render exception handler} implementation.
* Default value: null
*/
RENDER_EXCEPTION_HANDLER("render.exception_handler.impl"),
/**
* "resource.loader.impls": Set one or more {@link org.rythmengine.extension.ITemplateResourceLoader resource loader}
* implementation, should be a list of class names separated by ",", or list of resource loader instance
* Default value: null
. If this is not configured, try templates will be loaded as
* {@link org.rythmengine.resource.FileTemplateResource file template resource} first and if
* still not found then try to load as
* {@link org.rythmengine.resource.ClasspathTemplateResource classpath resource}.
*
* @see #HOME_TEMPLATE
*/
RESOURCE_LOADER_IMPLS("resource.loader.impls"),
/**
* "resource.def_loader.enabled": Enable/Disable default resource loader. When this option is disabled and
* there is no user configured {@link #RESOURCE_LOADER_IMPLS resource loader}, then the default file system resource
* loader will not initialized
*
* default value: true
*/
RESOURCE_DEF_LOADER_ENABLED("resource.loader.def.enabled", true),
/**
* "resource.name.suffix": does resource name has special rythm suffix attached?
* E.g. .rythm or .rtl etc. Default is empty string
*
* Note, do not use regular file extensions for this setting, like ".html", ".js" etc
* as they can be used to identify the code type of the template
*/
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;
}
},
/**
* "resource.autoScan": when set to true, then resource manager call resource loaders to auto
* scan resources.
*
* Default value: false
*/
RESOURCE_AUTO_SCAN("resource.autoScan", false),
/**
* "sandbox.security_manager.impl": Set the security manager to be used when running a template in
* {@link org.rythmengine.Sandbox sandbox} mode.
* Default value: null
. When no security manager is configured, when the sandbox mode is running, an
* instance of {@link org.rythmengine.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 org.rythmengine.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: 2000
*/
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 org.rythmengine.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 org.rythmengine.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:
*
*
* org.rythmengine.Rythm;
* org.rythmengine.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
*
* file.encoding
* file.separator
* line.separator
* java.home
* java.protocol.handler.pkgs
* java.vm.name
* path.separator
* sun.timezone.ids.oldmapping
* suppressRawWhenUnchecked
* user.country
* user.dir
* user.language
* user.region
* user.timezone
*
*/
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,user.language,user.region,user.timezone,user.country,java.home,sun.timezone.ids.oldmapping,file.separator,path.separator"),
/**
* "sandbox.thread_factory.impl": Configure the thread factory to be used by the sandbox executing service.
* Default value: {@link org.rythmengine.sandbox.SandboxThreadFactory}
*/
SANBOX_THREAD_FACTORY_IMPL("sandbox.thread_factory.impl", new SandboxThreadFactory()),
/**
* "sandbox.tmp_dir.io.enabled": enable or disable tmp dir IO in sandbox mode
* When tmp_dir io is enabled, template running in sandbox mode can read/write files
* in "java.io.tmpdir"
* Default value: true
*/
SANDBOX_TEMP_IO_ENABLED("sandbox.tmp_dir.io.enabled", true),
/**
* "sandbox.secure_mode": used in sandbox security manager to turn on/off
* secure zone
* Default value: UUID.randomUUID().toString()
*/
SANDBOX_SECURE_CODE("sandbox.secure_code", UUID.randomUUID().toString()),
/**
* "ext.transformer": User defined transformers, should be a list of class names separated by ",". If configured
* then {@link org.rythmengine.RythmEngine#registerTransformer(Class[]) RythmEngine.registerTransformer} will
* be called to register these user defined transformer classes. Default value: null
*/
EXT_TRANSFORMER_IMPLS("ext.transformer.impls"),
/**
* "ext.prop_accessor": User defined property accessors, should be a list of class names separated by ",". If configured
* then {@link org.rythmengine.RythmEngine#registerPropertyAccessor(org.rythmengine.extension.IPropertyAccessor...)} RythmEngine.registerPropertyAccessor} will
* be called to register these user defined property accessor classes. Default value: null
*/
EXT_PROP_ACCESSOR_IMPLS("ext.prop_accessor.impls");
private String key;
private Object defVal;
private static ILogger logger = Logger.get(RythmConfigurationKey.class);
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 static Object getValFromAliases(Map configuration, String key, String suffix, Object defVal) {
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 = defVal;
}
}
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 static Boolean getEnabled(String key, Map configuration, Object defVal) {
Object v = getValFromAliases(configuration, key, "enabled", defVal);
if (null == v) {
v = getValFromAliases(configuration, key, "disabled", defVal);
return !toBoolean(v);
}
return toBoolean(v);
}
private static T getImpl(String key, Map configuration, Object defVal) {
Object v = getValFromAliases(configuration, key, "impl", defVal);
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 static T newInstance(String key, Class c, Class expectedClass) {
if (!expectedClass.isAssignableFrom(c)) {
logger.warn("Mismatched type found for configuration %s", key);
return null;
}
try {
return (T)c.newInstance();
} catch (Exception e) {
logger.warn(e, "Cannot create new instance for configuration %s", key);
return null;
}
}
public static List getImplList(String key, Map configuration, Class c) {
final Object v = getValFromAliases(configuration, key, "impls", null);
if (null == v) return Collections.EMPTY_LIST;
final boolean needClass = (Class.class.isAssignableFrom(c));
final List l = new ArrayList();
final Class vc = v.getClass();
if (c.isAssignableFrom(vc)) {
l.add((T)v);
return l;
}
if (v instanceof Class) {
if (needClass) {
l.add((T)v);
} else {
T inst = newInstance(key, (Class) v, c);
if (null != inst) {
l.add(inst);
}
}
return l;
}
if (vc.isArray()) {
int len = Array.getLength(v);
for (int i = 0; i < len; ++i) {
Object el = Array.get(v, i);
if (null == el) {
continue;
}
Class elc = el.getClass();
if (c.isAssignableFrom(elc)) {
l.add((T)el);
} else if (el instanceof Class) {
if (needClass) {
l.add((T)el);
} else {
T inst = newInstance(key, (Class) el, c);
if (null != inst) {
l.add(inst);
}
}
} else {
try {
elc = Class.forName(el.toString());
if (needClass) {
l.add((T)elc);
} else {
T inst = newInstance(key, elc, c);
if (null != inst) {
l.add(inst);
}
}
} catch (Exception e) {
logger.warn(e, "Error getting impl class out from %s for configuration %s", el, key);
}
}
}
return l;
} else if (Collection.class.isAssignableFrom(vc)) {
Collection col = (Collection)v;
for (Object el : col) {
if (null == el) {
continue;
}
Class elc = el.getClass();
if (c.isAssignableFrom(elc)) {
l.add((T)el);
} else if (el instanceof Class) {
if (needClass) {
l.add((T)el);
} else {
T inst = newInstance(key, (Class) el, c);
if (null != inst) {
l.add(inst);
}
}
} else {
try {
elc = Class.forName(el.toString());
if (needClass) {
l.add((T) elc);
} else {
T inst = newInstance(key, elc, c);
if (null != inst) {
l.add(inst);
}
}
} catch (Exception e) {
logger.warn(e, "Error getting impl class out from %s for configuration %s", el, key);
}
}
}
return l;
}
for (String s: v.toString().split("[ \t,;]+")) {
try {
Class ec = Class.forName(s);
if (needClass) {
l.add((T) ec);
} else {
T inst = newInstance(key, ec, c);
if (null != inst) {
l.add(inst);
}
}
} catch (Exception e) {
logger.warn(e, "Error getting impl class out from %s for configuration %s", s, key);
}
}
return l;
}
private static File asFile(String s, String key) {
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 {
if (s.startsWith("..")) {
URL url = Thread.currentThread().getContextClassLoader().getResource(".");
String path = url.getPath();
if (path.endsWith("/")) path = path + s;
else path = path + "/" + s;
return new File(path);
} else {
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);
}
}
private static File getFile(String key, Map configuration, Object defVal) {
Object v = getValFromAliases(configuration, key, "dir", defVal);
if (null == v) return null;
if (v instanceof File) {
return (File) v;
}
String s = v.toString();
return asFile(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;
Object defVal = getDefVal(configuration);
if (key.endsWith(".enabled")) {
return (T) getEnabled(key, configuration, defVal);
}
if (key.endsWith(".impl")) {
return getImpl(key, configuration, defVal);
}
if (key.endsWith(".dir")) {
return (T) getFile(key, configuration, defVal);
}
return (T) getValFromAliases(configuration, key, null, defVal);
}
/**
* 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());
}
}