Nutch 1.3 学习笔记 10-3 插件机制分析

Nutch 1.3 学习笔记 10-3 插件机制分析
-------------------------------------

1. 一些对象说明 

  1. PluginRepository:这是一个用于存储所有插件描述对象(PluginDescriptor),插件扩展点(ExtensionPoint)和被激活的插件。
  2. PluginDescriptor:用于描述单个扩展插件的元信息,它的内容主要是从plugin.xml中得到。
  3. Plugin: 用于描述插件的一个抽象,其中包括了一个插件描述符,它们是一对一的关系。
  4. ExtensionPoint: 这个扩展点主要是一个面象对象中的接口的意思,就是说可以有多个扩展来实现这个接口,一个或者多个扩展点实际上就是一个插件,如nutch-extensionpoints.
  5. Extension: 扩展是对于扩展点的实现,一个插件可以包含多个扩展。
  6. PluginManifestParser: 主要是用于解析插件目录下的plugin.xml文件,生成相应的PluginDescriptor对象。
  7. PluginClassLoader: 它继承自URLClassLoader,用来根据urls动态生成相应的插件实现对象

2. 插件仓库初始化流程

PluginRepository的生成有两种方法,一个是直接new一个相应的对象,另一个是调用PluginRepository的静态的get方法,从Cache中得到相应的PluginRepository,在Nutch的流程中,一般是通用使用第二种方法来得到PluginRepository,这样可以保证资源在多个流程中得到共享。



2.1 PluginRepostory的初始化在其ctr函数中进行

源代码如下:
fActivatedPlugins = new HashMap<String, Plugin>();
      	fExtensionPoints = new HashMap<String, ExtensionPoint>();
    	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<String, PluginDescriptor> allPlugins = manifestParser.parsePluginFolder(pluginFolders);
		// 要排除的插件名称列表,支持正则表达式方式定义
    	Pattern excludes = Pattern.compile(conf.get("plugin.excludes", ""));
		// 要包含的插件名称列表,支持正则表达式方式定义
    	Pattern includes = Pattern.compile(conf.get("plugin.includes", ""));
		// 对不使用的插件进行过滤,返回过滤后的插件
    	Map<String, PluginDescriptor> filteredPlugins = 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<String, PluginDescriptor> parsePluginFolder(String[] pluginFolders) {
    		Map<String, PluginDescriptor> map = new HashMap<String, PluginDescriptor>();


    		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中的如下信息
	// <plugin id="index-anchor" name="Anchor Indexing Filter" version="1.0.0" provider-name="nutch.org">
    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);
	// 这里是解析如下内容
	//  <extension id="org.apache.nutch.indexer.anchor" name="Nutch Anchor Indexing Filter" point="org.apache.nutch.indexer.IndexingFilter">
    //	<implementation id="AnchorIndexingFilter"
    //  		class="org.apache.nutch.indexer.anchor.AnchorIndexingFilter" />
  	//	</extension>
    parseExtension(rootElement, pluginDescriptor);
	// 这里主要是解析nutch-extensionPoints这个插件,xml内容如下
	// <extension-point id="org.apache.nutch.indexer.IndexingFilter" name="Nutch Indexing Filter"/>
    // <extension-point id="org.apache.nutch.parse.Parser" name="Nutch Content Parser"/>
    // <extension-point id="org.apache.nutch.parse.HtmlParseFilter" name="HTML Parse Filter"/>
    parseExtensionPoints(rootElement, pluginDescriptor);
	// 这里主要是解析插件的动态库与插件所使用的第三方库,xml内容如下
	// <runtime>
    //  <library name="parse-tika.jar">
    //     <export name="*"/>
    //  </library>
    //  <library name="apache-mime4j-0.6.jar"/>
    //  <library name="asm-3.1.jar"/>
    //  <library name="bcmail-jdk15-1.45.jar"/>
    //  <library name="bcprov-jdk15-1.45.jar"/>
	//  </runtime>
    parseLibraries(rootElement, pluginDescriptor);
	// 这里解析插件依赖的插件库,xml内容如下
	//  <requires>
    //   <import plugin="nutch-extensionpoints"/>
    //  <import plugin="lib-regex-filter"/>
    //  </requires>
    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<PluginDescriptor> getDependencyCheckedPlugins(
      Map<String, PluginDescriptor> filtered, Map<String, PluginDescriptor> all) {
    if (filtered == null) {
      return null;
    }
    Map<String, PluginDescriptor> checked = new HashMap<String, PluginDescriptor>();


	// 遍历所有过滤后的插件
    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<PluginDescriptor>(checked.values());
  }

3. 插件调用流程
  插件调用流程主要分成如下几步:
  1. 根据扩展点ID号从插件仓库中得到相应的扩展点对象
  2. 根据扩展点对象得到相应的扩展集
  3. 遍历扩展集,从扩展对象中实例化出相应的扩展来,实例化的过滤就是调用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<String, URLFilter> filterMap = new HashMap<String, URLFilter>();
        for (int i = 0; i < extensions.length; i++) {
          Extension extension = extensions[i];
           (3)
          URLFilter filter = (URLFilter) extension.getExtensionInstance();
          if (!filterMap.containsKey(filter.getClass().getName())) {
            filterMap.put(filter.getClass().getName(), filter);
          }
        }


4. 总结

Nutch的插件机制还是比较经典的,上面只是做了一个简单的分析,要深入理解还是要多实践。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值