UML/简介

- Configuration:负责XML配置文件、配置项kv的相关操作
- 加载:加载配置文件到内存
- 解析:解析XML语法获取配置项kv
- 合并:多个配置文件合并,相同的配置项覆盖
- 扩展:如${hadoop.tmp.dir}/tmp格式路径,转化为对应的实际路径
- 存储:配置文件到resources,配置项到properties等
- Resource:Configuration的内部类,封装XML配置文件,类型有:Path、File、InputStream、字符串url
- Configurable:实现类可绑定Configuration实例,获取其中的配置项v
- Configured:Configurable Base(基础实现类)
- Reconfigurable:实现类可修改运行时可变配置项
- Writable:实现类可进行hadoop序列化
流程
配置文件读取
- 对于conf目录下的文件,调用addResource()添加XML配置文件到resources(XML配置文件列表)中,static块中调用addDefaultResource()添加默认XML配置文件到defaultResources中
- 调用reloadConfiguration()重新加载配置项(lazy,实际是清空properties和final配置项k,等到调用get()时再用loadResources方法加载)
- loadResources()从XML配置文件列表resources和defaultResources加载所有配置项到properties:调用loadResource()使用dom解析XML配置文件,得到配置项写入properties,并将解析后的properties更新到resources;调用subsituteVars()使用正则匹配${...}并使用System类获取环境变量进行属性扩展
配置实例使用
- 反射创建Configurable实现类实例
- Configurable实现类实例调用setConf()设置Configuration实例。之后即可调用getConf()获取Configuration实例,设置/获取配置项
核心代码(最后的main test,可直接运行)
测试文件(resources文件夹下)
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>build/test</value>
<description>A base for other temporary directories.</description>
</property>
<property>
<name>site</name>
<value>${hadoop.tmp.dir}/s3.name</value>
<description>The name of the s3 file system for testing.</description>
<final>true</final>
</property>
</configuration>
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>build/test</value>
<description>A base for other temporary directories.</description>
</property>
<property>
<name>default</name>
<value>s3:///</value>
<description>The name of the s3 file system for testing.</description>
</property>
</configuration>
配置文件读写
import org.w3c.dom.*;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.net.URL;
import java.nio.file.Path;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Configuration {
private ArrayList<Resource> resources = new ArrayList<>();
private boolean loadDefaults = true;
private static final WeakHashMap<Configuration, Object> REGISTRY = new WeakHashMap<>();
private static final CopyOnWriteArrayList<String> defaultResources = new CopyOnWriteArrayList<>();
static{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null){
cl = Configuration.class.getClassLoader();
}
addDefaultResource("core-default.xml");
}
private Set<String> finalParameters = new HashSet<>();
private Properties properties;
private Properties overlay;
private ClassLoader classLoader;
{
classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null){
classLoader = Configuration.class.getClassLoader();
}
}
public Configuration(){
this.loadDefaults = true;
synchronized (Configuration.class){
REGISTRY.put(this, null);
}
}
private static class Resource{
private final Object resource;
private final String name;
public Resource(Object resource){
this(resource, resource.toString());
}
public Resource(Object resource, String name){
this.resource = resource;
this.name = name;
}
public Object getResource() {
return resource;
}
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
}
public void addResource(String name){
addResourceObject(new Resource(name));
}
public void addResource(Path path){
addResourceObject(new Resource(path));
}
private void addResourceObject(Resource resource){
resources.add(resource);
reloadConfiguration();
}
private synchronized void reloadConfiguration(){
properties = null;
finalParameters.clear();
}
public static synchronized void addDefaultResource(String name){
if(! defaultResources.contains(name)){
defaultResources.add(name);
for (Configuration conf : REGISTRY.keySet()){
if (conf.loadDefaults){
conf.reloadConfiguration();
}
}
}
}
public String get(String name){
return substituteVars(getProps().getProperty(name));
}
protected synchronized Properties getProps(){
if (properties == null) {
properties = new Properties();
loadResources(properties, resources);
if (overlay != null) {
properties.putAll(overlay);
}
}
return properties;
}
private void loadResources(Properties properties, ArrayList<Resource> resources){
if (loadDefaults){
for (String resource : defaultResources){
loadResource(properties, new Resource(resource));
}
}
for (int i=0; i< resources.size(); i++){
resources.set(i, loadResource(properties, resources.get(i)));
}
}
private Resource loadResource(Properties properties, Resource wrapper){
try{
Object resource = wrapper.getResource();
String name = wrapper.getName();
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setIgnoringComments(true);
docBuilderFactory.setNamespaceAware(true);
docBuilderFactory.setXIncludeAware(true);
DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
Document doc = null;
Element root;
if (resource instanceof String){
URL url = classLoader.getResource((String) resource);
doc = builder.parse(url.openStream(), url.toString());
}
if (doc == null){
throw new RuntimeException("parse failed...");
}
root = doc.getDocumentElement();
Properties toAddTo = properties;
NodeList props = root.getChildNodes();
for (int i=0; i<props.getLength(); i++){
Node propNode = props.item(i);
if (! (propNode instanceof Element)){
continue;
}
Element prop = (Element) propNode;
NodeList fields = prop.getChildNodes();
String attr = null;
String value = null;
boolean finalParameter = false;
for (int j=0; j<fields.getLength(); j++) {
Node fieldNode = fields.item(j);
if (! (fieldNode instanceof Element))
continue;
Element field = (Element)fieldNode;
if ("name".equals(field.getTagName()) && field.hasChildNodes())
attr = ((Text)field.getFirstChild()).getData().trim();
if ("value".equals(field.getTagName()) && field.hasChildNodes())
value = ((Text)field.getFirstChild()).getData();
if ("final".equals(field.getTagName()) && field.hasChildNodes())
finalParameter = "true".equals(((Text)field.getFirstChild()).getData());
}
loadProperty(toAddTo, attr, value, finalParameter);
}
overlay(properties, toAddTo);
return new Resource(toAddTo, name);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void loadProperty(Properties properties, String attr, String value, boolean finalParameter) {
if (! finalParameters.contains(attr)) {
properties.setProperty(attr, value);
}
if (finalParameter) {
finalParameters.add(attr);
}
}
private void overlay(Properties to, Properties from){
for (Map.Entry<Object, Object> entry : from.entrySet()){
to.put(entry.getKey(), entry.getValue());
}
}
private static Pattern varPat = Pattern.compile("\$\{[^\}\$\u0020]+\}");
private static int MAX_SUBST = 20;
private String substituteVars(String expr) {
Matcher match = varPat.matcher("");
String eval = expr;
for(int s=0; s<MAX_SUBST; s++) {
match.reset(eval);
if (! match.find()) {
return eval;
}
String var = match.group();
var = var.substring(2, var.length()-1);
String val = null;
try {
val = System.getProperty(var);
} catch(SecurityException ignored) {
}
if (val == null) {
val = getRaw(var);
}
if (val == null) {
return eval;
}
eval = eval.substring(0, match.start()) + val + eval.substring(match.end());
}
return null;
}
private String getRaw(String name){
return getProps().getProperty(name);
}
public void set(String name, String value){
getOverlay().setProperty(name, value);
getProps().setProperty(name, value);
}
private synchronized Properties getOverlay() {
if (overlay==null){
overlay=new Properties();
}
return overlay;
}
public static void main(String[] args) {
Configuration conf = new Configuration();
System.out.println(defaultResources);
conf.addResource("core-site.xml");
System.out.println(conf.resources);
System.out.println(conf.properties);
System.out.println(conf.get("site"));
System.out.println(conf.properties);
conf.set("hadoop.tmp.dir", "set/test");
System.out.println(conf.get("hadoop.tmp.dir"));
}
}
配置实例使用(对外提供接口,简单实现)
public interface Configurable {
void setConf(Configuration conf);
Configuration getConf();
}
public class Configured implements Configurable{
Configuration conf;
public Configured(){
this(null);
}
public Configured(Configuration conf){
setConf(conf);
}
@Override
public void setConf(Configuration conf) {
this.conf = conf;
}
@Override
public Configuration getConf() {
return conf;
}
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.addResource("core-site.xml");
Configured cfed = new Configured(conf);
System.out.println(cfed.getConf().get("hadoop.tmp.dir"));
}
}