guyuehhh 发表于 2015-8-2 13:39:13

Apache Nutch 1.3 学习笔记十(插件机制分析)

  
1. 一些对象说明

[*]PluginRepository:这是一个用于存储所有插件描述对象(PluginDescriptor),插件扩展点(ExtensionPoint)和被激活的插件。
[*]PluginDescriptor:用于描述单个扩展插件的元信息,它的内容主要是从plugin.xml中得到。
[*]Plugin: 用于描述插件的一个抽象,其中包括了一个插件描述符,它们是一对一的关系。
[*]ExtensionPoint: 这个扩展点主要是一个面象对象中的接口的意思,就是说可以有多个扩展来实现这个接口,一个或者多个扩展点实际上就是一个插件,如nutch-extensionpoints.
[*]Extension: 扩展是对于扩展点的实现,一个插件可以包含多个扩展。
[*]PluginManifestParser: 主要是用于解析插件目录下的plugin.xml文件,生成相应的PluginDescriptor对象。
[*]PluginClassLoader: 它继承自URLClassLoader,用来根据urls动态生成相应的插件实现对象
2. 插件仓库初始化流程
  PluginRepository的生成有两种方法,一个是直接new一个相应的对象,另一个是调用PluginRepository的静态的get方法,从Cache中得到相应的PluginRepository,在Nutch的流程中,一般是通用使用第二种方法来得到PluginRepository,这样可以保证资源在多个流程中得到共享。
  

2.1 PluginRepostory的初始化在其ctr函数中进行
  源代码如下:
  

[*]
fActivatedPlugins = new HashMap();
[*]
      fExtensionPoints = new HashMap();
[*]
      this.conf = conf;
[*]
      // 当被配置为过滤(即不加载),但是又被其他插件依赖的时候,是否自动启动,缺省为 true
[*]
      this.auto = conf.getBoolean("plugin.auto-activation", true);
[*]
      // 插件的目录名,可以是多个目录
[*]
      String[] pluginFolders = conf.getStrings("plugin.folders");
[*]
      PluginManifestParser manifestParser = new PluginManifestParser(conf, this);
[*]
      Map allPlugins = manifestParser.parsePluginFolder(pluginFolders);
[*]
      // 要排除的插件名称列表,支持正则表达式方式定义
[*]
      Pattern excludes = Pattern.compile(conf.get("plugin.excludes", ""));
[*]
      // 要包含的插件名称列表,支持正则表达式方式定义
[*]
      Pattern includes = Pattern.compile(conf.get("plugin.includes", ""));
[*]
      // 对不使用的插件进行过滤,返回过滤后的插件
[*]
      Map filterfilteredPlugins = filter(excludes, includes,allPlugins);
[*]
      // 对插件的依赖关系进行检查
[*]
      fRegisteredPlugins = getDependencyCheckedPlugins(filteredPlugins,this.auto ? allPlugins : filteredPlugins);
[*]
      // 安装扩展点,主要是针对nutch-extensionpoints这个插件的
[*]
      installExtensionPoints(fRegisteredPlugins);
[*]
      try {
[*]
            // 安装特定扩展点的相应扩展集
[*]
            // NOTE:其实这边的扩展点与扩展都是以插件的形式表现的
[*]
            installExtensions(fRegisteredPlugins);
[*]
      } catch (PluginRuntimeException e) {
[*]
            LOG.fatal(e.toString());
[*]
            throw new RuntimeException(e.getMessage());
[*]
      }
[*]
      displayStatus();
   2.2 下面分析一个插件描述符的生成
  插件描述符的生成主要是通用调用PluginManifestParser这个对象的parsePluginFolder这个方法生成的,源代码如下:
  

[*]
/**
[*]
            * Returns a list of all found plugin descriptors.
[*]
            *   
[*]
            * @param pluginFolders
[*]
            *          folders to search plugins from
[*]
            * @return A {@link Map} of all found {@link PluginDescriptor}s.
[*]
            */
[*]
            public Map parsePluginFolder(String[] pluginFolders) {
[*]
      Map map = new HashMap();
[*]
[*]
[*]
      if (pluginFolders == null) {
[*]
                throw new IllegalArgumentException("plugin.folders is not defined");
[*]
      }
[*]
[*]
[*]
      for (String name : pluginFolders) {
[*]
            // 遍历所有插件目录,这里的getPluginFolder方法解析一个资源的相对路径的问题
[*]
                File directory = getPluginFolder(name);
[*]
                if (directory == null) {
[*]
                continue;
[*]
                }
[*]
[*]
            LOG.info("Plugins: looking in: " + directory.getAbsolutePath());
[*]
      // 遍历所有子插件目录中的插件
[*]
            for (File oneSubFolder : directory.listFiles()) {
[*]
            if (oneSubFolder.isDirectory()) {
[*]
                  String manifestPath = oneSubFolder.getAbsolutePath() + File.separator
[*]
                  + "plugin.xml";
[*]
                try {
[*]
            LOG.debug("parsing: " + manifestPath);
[*]
            // 分析plugin.xml文件
[*]
            PluginDescriptor p = parseManifestFile(manifestPath);
[*]
            map.put(p.getPluginId(), p);
[*]
                } catch (MalformedURLException e) {
[*]
            LOG.warn(e.toString());
[*]
            } catch (SAXException e) {
[*]
         LOG.warn(e.toString());
[*]
            } catch (IOException e) {
[*]
         LOG.warn(e.toString());
[*]
            } catch (ParserConfigurationException e) {
[*]
         LOG.warn(e.toString());
[*]
            }
[*]
      }
[*]
      }
[*]
    }
[*]
    return map;
[*]
    }
  
  

[*]
private PluginDescriptor parseManifestFile(String pManifestPath)
[*]
      throws MalformedURLException, SAXException, IOException,
[*]
      ParserConfigurationException {
[*]
    // 解析xml文件,生成Document对象
[*]
      Document document = parseXML(new File(pManifestPath).toURL());
[*]
      String pPath = new File(pManifestPath).getParent();
[*]
// 对xml进行分析
[*]
      return parsePlugin(document, pPath);
[*]
    }
  
  

[*]
private PluginDescriptor parsePlugin(Document pDocument, String pPath)
[*]
            throws MalformedURLException {
[*]
   Element rootElement = pDocument.getDocumentElement();
[*]
// 这里是解析xml中的如下信息
[*]
//
[*]
   String id = rootElement.getAttribute(ATTR_ID);
[*]
   String name = rootElement.getAttribute(ATTR_NAME);
[*]
   String version = rootElement.getAttribute("version");
[*]
   String providerName = rootElement.getAttribute("provider-name");
[*]
// 插件类属性,不过这里好像没有用到过
[*]
   String pluginClazz = null;
[*]
   if (rootElement.getAttribute(ATTR_CLASS).trim().length() > 0) {
[*]
   pluginClazz = rootElement.getAttribute(ATTR_CLASS);
[*]
   }
[*]
// 生成插件描述符对象
[*]
   PluginDescriptor pluginDescriptor = new PluginDescriptor(id, version, name,
[*]
       providerName, pluginClazz, pPath, this.conf);
[*]
   LOG.debug("plugin: id=" + id + " name=" + name + " version=" + version
[*]
         + " provider=" + providerName + "class=" + pluginClazz);
[*]
// 这里是解析如下内容
[*]
//
[*]
   //   
[*]
    //
[*]
   parseExtension(rootElement, pluginDescriptor);
[*]
// 这里主要是解析nutch-extensionPoints这个插件,xml内容如下
[*]
//
[*]
   //
[*]
   //
[*]
   parseExtensionPoints(rootElement, pluginDescriptor);
[*]
// 这里主要是解析插件的动态库与插件所使用的第三方库,xml内容如下
[*]
//
[*]
   //
[*]
   //   
[*]
   //
[*]
   //
[*]
   //
[*]
   //
[*]
   //
[*]
//
[*]
   parseLibraries(rootElement, pluginDescriptor);
[*]
// 这里解析插件依赖的插件库,xml内容如下
[*]
//
[*]
   //   
[*]
   //
[*]
   //
[*]
   parseRequires(rootElement, pluginDescriptor);
[*]
   return pluginDescriptor;
[*]
}
  
要注意的是这个PluginManifestParser就是用来解析相应的plugin.xml文件,生成PluginRepository对象的,这个有一个很奇怪的概念就是一个插件描述符(PluginDescriptor)可以包含多个可扩展点或者可扩展点的实现,这里为什么不把可扩展点分离出来,PluginDescriptor就只包含一个或者多个可扩展点的实现。而可扩展点就是插件的接口定义。
2.3 插件依赖关系的检查
这个依赖关系的检查很有趣,主要是根据plugin.auto-activation这个参数来定的,部分源代码如下:
  

[*]
/**
[*]
* @param filtered
[*]
*          is the list of plugin filtred
[*]
* @param all
[*]
*          is the list of all plugins found.
[*]
* @return List
[*]
*/
[*]
private List getDependencyCheckedPlugins(
[*]
   Map filtered, Map all) {
[*]
   if (filtered == null) {
[*]
   return null;
[*]
   }
[*]
   Map checked = new HashMap();
[*]
[*]
[*]
// 遍历所有过滤后的插件
[*]
   for (PluginDescriptor plugin : filtered.values()) {
[*]
   try {
[*]
      // 保存当前插件的依赖插件描述符
[*]
       checked.putAll(getPluginCheckedDependencies(plugin, all));
[*]
    // 保存当前插件描述符
[*]
       checked.put(plugin.getPluginId(), plugin);
[*]
   } catch (MissingDependencyException mde) {
[*]
       // Log exception and ignore plugin
[*]
       LOG.warn(mde.getMessage());
[*]
   } catch (CircularDependencyException cde) {
[*]
[*]
// Simply ignore this plugin
[*]
       LOG.warn(cde.getMessage());
[*]
   }
[*]
   }
[*]
   return new ArrayList(checked.values());
[*]
}
  
3. 插件调用流程
插件调用流程主要分成如下几步:

[*]根据扩展点ID号从插件仓库中得到相应的扩展点对象
[*]根据扩展点对象得到相应的扩展集
[*]遍历扩展集,从扩展对象中实例化出相应的扩展来,实例化的过滤就是调用PluginClassLoader
  下面是生成URLFilter插件的部分代码:
  

[*]
   (1)
[*]
ExtensionPoint point = PluginRepository.get(conf).getExtensionPoint(URLFilter.X_POINT_ID);
[*]
if (point == null)
[*]
throw new RuntimeException(URLFilter.X_POINT_ID + " not found.");
[*]
   (2)
[*]
Extension[] extensions = point.getExtensions();
[*]
Map filterMap = new HashMap();
[*]
for (int i = 0; i
页: [1]
查看完整版本: Apache Nutch 1.3 学习笔记十(插件机制分析)