在将hbase的configuration移植到业务平台处理过程中碰到的一个问题
问题描述:
新的业务平台需要使用一些配置,这些配置项放在odin_site.xml文件中,其中odin_site.xml的路径通过参数的方式传递过来。其它的config解析移植hadoop的configuration. 其代码如下:
publicstaticConfiguration create(String envhome) {
Configuration conf = newConfiguration();
// In case HBaseConfiguration is loaded from a different classloader than
// Configuration, conf needs to be set with appropriate class loader to resolve
// HBase resources.
conf.setClassLoader(OdinMessageConfig.class.getClassLoader());
String xmlpath = envhome + "//odin-site.xml";
conf.addResource(xmlpath );
returnconf;
}
但是发现一个问题odin-site.xml总是无法被解析,取出来的值 总是为空。
问题分析:
查看Configuration的addResource代码,可以看到:
privatesynchronizedvoidaddResourceObject(Resource resource) {
resources.add(resource); // add to resources, 其中resources为一个list的链表
reloadConfiguration();
}
publicsynchronizedvoidreloadConfiguration() {
properties = null; // trigger reload
finalParameters.clear(); // clear site-limits
}
从这里可以看到,configuration对于我们的配置文件根本就没有进行解析。 那只有可能在查询的时候进行解析。后面的代码验证这一点。
当我们再调用conf.get()查询的时候,可以看到
publicString get(String name) {
String[] names = handleDeprecation(name);
String result = null;
for(String n : names) {
result = substituteVars(getProps().getProperty(n)); //此时会调用getProps(),这里就开始进行解析了
}
returnresult;
}
再查看getProps()的代码
protectedsynchronizedProperties getProps() {
//之前在addresource的时候,properties的值已经被置空了
if(properties == null) {
properties = newProperties();
HashMap<String, String[]> backup = newHashMap<String, String[]>(updatingResource);
//重新加载xml的值
loadResources(properties, resources, quietmode);
if(overlay != null) {
properties.putAll(overlay);
for(Map.Entry<Object, Object> item : overlay.entrySet()) {
String key = (String) item.getKey();
updatingResource.put(key, backup.get(key));
}
}
}
returnproperties;
}
再次查看loadResources的值
privateResource loadResource(Properties properties, Resource wrapper, booleanquiet) {
String name = UNKNOWN_RESOURCE;
try{
Object resource = wrapper.getResource();
... ...
//从这里可以看到对于解析的文件,有很多方法,总体来说有URL、本地文件解析以及输入流的方式
//因为我们输入的是字符串,重点看字符串
if(resource instanceofURL) { // an URL resource
doc = parse(builder, (URL) resource);
} elseif(resource instanceofString) { // a CLASSPATH resource
URL url = getResource((String) resource);
doc = parse(builder, url);
} elseif(resource instanceofPath) { // a file resource
File file = newFile(((Path) resource).toUri().getPath()).getAbsoluteFile();
if(file.exists()) {
if(!quiet) {
LOG.info("parsing File "+ file);
}
doc = parse(builder, newBufferedInputStream(newFileInputStream(file)),
((Path) resource).toString());
}
} elseif(resource instanceofInputStream) {
... ...
}
...
}
查看getResource的代码,可以看到:
publicURL getResource(String name) {
returnclassLoader.getResource(name);
}
它是通过ClassLoader来获取资源信息,而classloader它会从classpath中查找,而我们输入的本地文件在classpath中找到,因此返回的URL就为空,因此最终解析了来的也就为空了。
解决方法:
从上面我们看到如果addResource()的是为Path类型,解析时候它就创建本地输入 流。因此,修改代码
publicstaticConfiguration create(String envhome) {
Configuration conf = newConfiguration();
// In case HBaseConfiguration is loaded from a different classloader than
// Configuration, conf needs to be set with appropriate class loader to resolve
// HBase resources.
conf.setClassLoader(OdinMessageConfig.class.getClassLoader());
String xmlpath = envhome + "//odin-site.xml";
Path path = new
Path(xmlpath); //创建一个Path对象,将这个Path对象add到resource中去
conf.addResource(path);
returnconf;
}
从这里我们也可以看到,hadoop, hbase都是将它们的conf的目录加入到它们的classpath路径中去。然后就可直接add了