在将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了