分類: Nutch 2011-09-18 00:19 437人閱讀 評論(0) 收藏 舉報 Nutch 1.3 學習筆記 10-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,這樣可以保證資源在多個流程中得到共享。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZuIWNiV2MxYjN3IjNxMTMfBzLcdTMvwVOwETMwIzLcRnbl1GajFGd0F2LcRXZu5ibkN3YukGavw1LcpDc0RHaiojIsJye.gif)
2.1 PluginRepostory的初始化在其ctr函數中進行
源代碼如下:
[html] view plain copy
- 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這個方法生成的,源代碼如下:
[html] view plain copy
- 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;
- }
[html] view plain copy
- 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);
- }
[html] view plain copy
- 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這個參數來定的,部分源代碼如下:
[html] view plain copy
- 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. 插件調用流程
插件調用流程主要分成如下幾步:
- 根據擴充點ID号從插件倉庫中得到相應的擴充點對象
- 根據擴充點對象得到相應的擴充集
- 周遊擴充集,從擴充對象中執行個體化出相應的擴充來,執行個體化的過濾就是調用PluginClassLoader
下面是生成URLFilter插件的部分代碼:
[html] view plain copy
- (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的插件機制還是比較經典的,上面隻是做了一個簡單的分析,要深入了解還是要多實踐。