package com.wangyin.ak47.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import org.yaml.snakeyaml.Yaml;
import com.wangyin.ak47.core.exception.Ak47RuntimeException;
/**
*
* Config loader will automatically load the AK47 or user's config file,
* formated in YAML, give the prefix such as 'ak47.yml'.
*
* Loading strategy (assume the prefix is 'xxx'): 1. Check whether there
* {class_path}/xxx-test.yml, may be within the jar package. If exists, load
* xxx-test.yml, if not, load {class_path}/xxx.yml, if both are absent, throw an
* exception. 2. Check whether there {$AK47_HOME}/conf/xxx.yml, load it if
* exists. 3. Check whether there {$HOME}/.ak47/xxx.yml, load it if exists.
*
* NOTICE: If a config value both in two conf file, the after loaded will cover
* the before loaded.
*
* WARNING: {class_path}/xxx-test.yml just be used in beta test, should be
* invisible in official release.
*
* 配置加载类
*
* 加载顺序(后面覆盖前面):
*
* 1、 首先,检查是否有 getClassLoader()/xxx-test.yml,有可能在jar包内。 若有,则加载
* xxx-test.yml,若没有,则加载 xxx.yml,若两者都无,则抛异常。
*
* 2、 其次,检查是否有 $APP/conf/xxx.yml,若有则加载,若无则不加载。
*
* 3、 最后,检查是否有 $HOME/.ak47/xxx.yml,若有则加载,若无则不加载。
*
* 注:对 xxx-test.yml 的加载策略只是用于AK47开发内测,正式发布版应该是看不见的。
*
* @author hannyu
*
*/
public class ConfigLoader {
private static final Logger log = new Logger(ConfigLoader.class);
private static final String POSTFIX_YML = ".yml";
private static final String POSTFIX_TEST_YML = "-test.yml";
private static final ClassLoader CLASS_LOADER = ConfigLoader.class
.getClassLoader();
/**
* loading
*
* 加载
*
* @param prefix
* prefix of configuration file, like {prefix}.yml
* @return map of configuration items
* @throws IOException
* i/o wrong
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> load(String prefix) throws IOException {
// init map
Map<String, Object> omap;
Yaml yaml = new Yaml();
// in jar ( xxx-test.yml or xxx.yml )
String testYmlName = prefix + POSTFIX_TEST_YML;
String ymlName = prefix + POSTFIX_YML;
System.out.println(CLASS_LOADER.getResource(""));
InputStream testYmlIs = CLASS_LOADER.getResourceAsStream(testYmlName);
if (null == testYmlIs) {
InputStream ymlIs = CLASS_LOADER.getResourceAsStream(ymlName);
if (null == ymlIs) {
log.error("NOT found {} and {}.", testYmlName, ymlName);
throw new Ak47RuntimeException(testYmlName + " and " + ymlName
+ " both NOT found.");
}
try {
omap = (Map<String, Object>) yaml.load(ymlIs);
} catch (Exception e) {
log.error("load {} fail.", ymlName);
throw new Ak47RuntimeException("load " + ymlName + " fail.", e);
} finally {
ymlIs.close();
}
log.info("load {} success.", ymlName);
} else {
try {
omap = (Map<String, Object>) yaml.load(testYmlIs);
} catch (Exception e) {
log.error("load {} fail.", testYmlName);
throw new Ak47RuntimeException(
"load " + testYmlName + " fail.", e);
} finally {
testYmlIs.close();
}
log.info("load {} success.", testYmlName);
}
if (omap == null || omap.size() == 0) {
throw new Ak47RuntimeException("load " + testYmlName + " or "
+ ymlName + " fail.");
}
// app conf dir ( ak47/conf/xxx.yml )
String appConfYmlPath = Ak47Env.AK47_HOME_CONF_DIR + File.separator
+ ymlName;
File appConfYmlFile = new File(appConfYmlPath);
if (!appConfYmlFile.exists()) {
log.info("NOT found {}.", appConfYmlPath);
} else if (!appConfYmlFile.canRead()) {
log.warn("Can NOT read {}.", appConfYmlPath);
} else {
FileInputStream fis = new FileInputStream(appConfYmlFile);
Map<String, Object> newMap;
try {
newMap = (Map<String, Object>) yaml.load(fis);
} catch (Exception e) {
log.error("load {} fail.", appConfYmlPath);
throw new Ak47RuntimeException("load " + appConfYmlPath
+ " fail.", e);
} finally {
fis.close();
}
if (null == newMap) {
log.error("load {} fail.", appConfYmlPath);
throw new Ak47RuntimeException("load " + appConfYmlPath
+ " fail. result is null.");
}
log.info("load {} success.", appConfYmlPath);
newCoverOld(omap, newMap);
}
// local home share dir ( /home/wy/.ak47/xxx.yml )
String homeYmlPath = Ak47Env.AK47_LOCAL_SHARE_DIR + File.separator
+ ymlName;
File homeYmlFile = new File(homeYmlPath);
if (!homeYmlFile.exists()) {
log.info("NOT found {}.", homeYmlPath);
} else if (!homeYmlFile.canRead()) {
log.warn("can NOT read {}.", homeYmlPath);
} else {
FileInputStream fis = new FileInputStream(homeYmlFile);
Map<String, Object> newMap;
try {
newMap = (Map<String, Object>) yaml.load(fis);
} catch (Exception e) {
log.error("load {} fail.", homeYmlPath);
throw new Ak47RuntimeException(
"load " + homeYmlPath + " fail.", e);
} finally {
fis.close();
}
if (null == newMap) {
log.error("load {} fail.", homeYmlPath);
throw new Ak47RuntimeException("load " + homeYmlPath
+ " fail. result is null.");
}
log.info("load {} success.", homeYmlPath);
newCoverOld(omap, newMap);
}
// return
return omap;
}
/**
* New cover old After cover before
*
* @param oldMap
* @param newMap
*/
private static void newCoverOld(Map<String, Object> oldMap,
Map<String, Object> newMap) {
for (String key : newMap.keySet()) {
Object value = newMap.get(key);
if (oldMap.containsKey(key)) {
log.info("cover key[{}] from {} to {}.", key, oldMap.get(key),
value);
} else {
log.info("added key[{}] with {}.", key, value);
}
oldMap.put(key, value);
}
}
}