org.tbwork.anole.loader.core.manager.impl.LocalConfigManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of anole-loader Show documentation
Show all versions of anole-loader Show documentation
An awesome property or configuration loader for java.
The newest version!
package org.tbwork.anole.loader.core.manager.impl;
import org.tbwork.anole.loader.context.Anole;
import org.tbwork.anole.loader.core.manager.ConfigManager;
import org.tbwork.anole.loader.core.model.ConfigItem;
import org.tbwork.anole.loader.exceptions.CircularDependencyException;
import org.tbwork.anole.loader.exceptions.ErrorSyntaxException;
import org.tbwork.anole.loader.types.ConfigType;
import org.tbwork.anole.loader.util.AnoleLogger;
import org.tbwork.anole.loader.util.StringUtil;
import java.net.URL;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* LocalConfigManager provides basic operations
* for local configurations.
* @see #setConfigItem(String, String, ConfigType)
* @see #getConfigItem(String)
* @author Tommy.Tang
*/
public class LocalConfigManager implements ConfigManager{
private AnoleLogger logger;
protected static final Map configMap = new ConcurrentHashMap();
private static final Set unknownConfigSet = new HashSet();
private String currentRunEnvironment;
@Override
public void setConfigItem(String key, String value, ConfigType type){
if(!Anole.initialized)//Add to JVM system properties for other frameworks to read.
System.setProperty(key, value);
ConfigItem cItem = configMap.get(key);
String operation = "New";
if(cItem == null)
cItem = initialConfig(key);
else
operation = "Update";
if(AnoleLogger.anoleLogLevel != null && logger.isDebugEnabled())
logger.debug("{} config found: key = {}, raw value = {}, type = {}", operation, key, value, type);
cItem.setValue(value, type);
}
@Override
public void foreachProcess(Anole.AnoleProcessor anoleProcessor) {
for(Entry entry : configMap.entrySet()){
anoleProcessor.process(entry.getKey(), entry.getValue().strValue());
}
}
@Override
public ConfigItem getConfigItem(String key){
//local first
ConfigItem result = configMap.get(key);
//then system
if(result == null ) {
String sysProperty = System.getProperty(key);
if(sysProperty != null)
{
result = initialConfig(key);
result.setValue(sysProperty, ConfigType.STRING);
}
}
return result;
}
@Override
public void postProcess(){
initializeContext();
recursionBuildConfigMap();
cleanEscapeCharacters();
calculateExpression();
registerProjectInfo();
if(!unknownConfigSet.isEmpty()){
logger.error("There are still some configurations could not be parsed rightly, they are: {} ", unknownConfigSet.toArray().toString() );
}
}
public ConfigItem initialConfig(String key){
ConfigItem cItem = new ConfigItem(key);
configMap.put(key, cItem);
return cItem;
}
/**
* Replace those variable-values with concrete values recursively.
* E.g., a snippet of Anole configuration is as following:
*
* ip=127.0.0.1
* port=80
* connectionString=#{ip}:#{port}
*
* In this case, after calling this method, the connectionString
* would be 127.0.0.1:80
*/
private void recursionBuildConfigMap(){
Set> entrySet = configMap.entrySet();
for(Entry item : entrySet){
rsc(item.getKey());
if(!Anole.initialized)//Add to JVM system properties for other frameworks to read.
System.setProperty(item.getKey(), item.getValue().strValue());
}
}
private String judgeEqual(String key, String expression){
if("true".contains(expression) || "false".contains(expression))
return expression;
String [] subExpressions = expression.split("=");
if(subExpressions.length == 2){
if(subExpressions[0].trim().equals(subExpressions[1].trim())){
return "true";
}
else{
return "false";
}
}
String message = String.format("The right equal-expression should be '${value_1}=${value_2}' while yours is '%s'", expression);
throw new ErrorSyntaxException(key, message);
}
private String parseThreeElementExpression(String key, String value){
String [] firstElements = value.split(" \\? ");
if(firstElements.length == 2){
String firstElement = firstElements[0].trim();
firstElement = judgeEqual(key, firstElement);
String [] secondELements = firstElements[1].split(" : ");
if(secondELements.length == 2){
String secondElement = secondELements[0];
String thirdElement = secondELements[1];
if("true".equals(firstElement)){
return secondElement;
}
else if("false".equals(firstElement)) {
return thirdElement;
}
}
}
String message = String.format("The right three-element-expression should be '${boolean_value} ? ${true_result} : ${false_result}' or '${value_1}=${value_2} ? ${true_result} : ${false_result}' while yours is '%s'", value);
throw new ErrorSyntaxException(key, message);
}
/**
* Anole support the three element expression like `boolean ? a : b`
*/
private void calculateExpression(){
Set> entrySet = configMap.entrySet();
for(Entry item : entrySet){
String value = item.getValue().strValue().trim();
if(value != null && ( value.startsWith("@@@") || value.startsWith("```"))){
item.getValue().setValue(parseThreeElementExpression(item.getKey(), value.substring(3)), item.getValue().getType());
if(!Anole.initialized)//Add to JVM system properties for other frameworks to read.
System.setProperty(item.getKey(), item.getValue().strValue());
}
}
}
/**
* Clear all escape characters in the configuration value.
*/
private void cleanEscapeCharacters(){
Set> entrySet = configMap.entrySet();
for(Entry item : entrySet){
cleanEscapeCharactersFromConfigItem(item.getValue());
}
}
private void cleanEscapeCharactersFromConfigItem(ConfigItem ci){
String strValue = ci.strValue();
ci.setValue(StringUtil.replaceEscapeChars(strValue), ci.getType());
}
/**
* Recursively re-Set Configurations.
*/
private String rsc(String key){
if(unknownConfigSet.contains(key)){
throw new CircularDependencyException(key);
}
unknownConfigSet.add(key);
ConfigItem ci = extendibleGetConfigItem(key);
if(ci == null || ci.strValue() == null) {
String message = String.format("There is no manual-set or default-set value for %s", key);
throw new ErrorSyntaxException(key, message);
}
String [] variblesWithCloth = StringUtil.getVariables(ci.strValue(), key);
for(String str: variblesWithCloth){
String vkey = StringUtil.getVariable(str);
if(vkey==null || vkey.isEmpty())
throw new ErrorSyntaxException(key, str + " must contains a valid variable.");
String realValue = rsc(vkey);
if(realValue == null){
String message = String.format("The config(key=%s) could not be null value because it is a reference (dependency) of config(key=%s)", vkey, key);
throw new ErrorSyntaxException(key, message);
}
if(!realValue.equals(str)){
ci.setValue(ci.strValue().replace(str, realValue), ci.getType());
}
// else: real value is still not found, keep intact and do nothing
}
unknownConfigSet.remove(key);
return ci.strValue();
}
/**
* This open method allow its children classes to
* re-define the configuration retrieving way, for
* example retrieving configurations from a remote
* server or other configuration repositories.
* @param key the key
*/
protected ConfigItem extendibleGetConfigItem(String key){
return configMap.get(key);
}
protected void initializeContext(){
//nothing special for local configuration manager.
}
private void registerProjectInfo() {
// get current classpath which the application running in.
URL url = Thread.currentThread().getContextClassLoader().getResource("/");
if(url != null) {
String classpathString = url.toString();
if(classpathString.contains(".jar")) {// running in jar
}
}
}
}