试图定位无法解析的插件依赖性是件紧张而又耗时的事情。激活每个插件都要依赖于很多其他插件,这些插件又会依赖于其他更多插件。如果 Eclipse 无法加载这个长长的链条中的某个插件,那么手工查找出现问题的到底是哪个插件可能会比原计划所花费的时间和精力都要多。如果您希望有一种方法可以自动实现这种插件依赖性的检测,就请阅读本文。
碰到的问题
假设我们希望在 Eclipse 中使用一个特定的插件,并已经执行了所有必须的操作,将其包含到插件的 manifest 文件中,并将其声明为一个依赖文件。但是系统并没有加载这个插件,这样我们就会被困在这里了,软件开发就无法继续进展下去了。
听起来非常熟悉吗?如果是这样,那么您可能早已花费了很多时间和努力来查看很多 plugin.xml 文件,从而查明 Eclipse 可能没有加载哪个特定的插件。还可能已经尝试使用了 Eclipse PDE 项目提供的 Plug-in Dependencies 视图,此时您会发现这个视图的唯一工作不过是显示已经成功加载的插件而已。不幸的是,有问题的插件很可能并不属于成功加载的插件。
要确定 Eclipse 没有找到或加载哪个特定的插件,我们应该做些什么呢?我们不用手工遍历每个 plugin.xml 文件,而是考虑自动实现这种搜索功能。要自动进行搜索,我们需要了解 Eclipse 是如何保存自己的插件的,以及如何发现到保存在磁盘上的其他插件的链接。基于这些知识,我们可能会希望编写自己的代码来创建一个插件依赖性遍历程序,或者使用在本文中给出的这个通用的 Dependency Walker 插件。本文的 “下载” 一节给出了这个例子的源代码。
开始:理解插件依赖性和 Eclipse 的插件链
插件依赖性
Eclipse 插件是提供了其他插件可以使用的功能的软件模块。如果插件 A 需要插件 B 才能正常工作,那么我们就说 A 依赖于 B。这种依赖性还意味着,除非插件 B 已经成功加载了,否则插件 A 就不能正常工作。有时候,插件 B 可能还会依赖于插件 C、D、E,令人更不爽的是,这些插件每个都可能会依赖于其他插件。这种依赖链很容易形成数百个插件相互链接在一起。毫无疑问,如果这个链条中的任何一个插件不能成功加载,那么依赖它的插件就可能会出现问题。
插件 manifest 文件 plugin.xml 描述了每个插件。这个 XML 文件中有一节声明了对于其他插件的依赖性或需求。在清单 1 中,plugin.xml 文件中使用黑体表示的一节就声明了这种依赖性。
清单 1. plugin.xml 文件
<?xml version="1.0" encoding="UTF-8" ?>
<?eclipse version="3.0"?>
<plugin id="org.eclipse.draw2d" name="Draw2d" version="3.0.0"
provider-name="Eclipse.org">
<runtime>
<library name="draw2d.jar">
<export name="*" />
<packages prefixes="org.eclipse.draw2d" />
</library>
</runtime>
<requires>
<import plugin="org.eclipse.swt" export="true" />
<import plugin="org.eclipse.core.runtime" />
</requires>
</plugin>
注意嵌入在 <requires> </requires> 节中的 <import plugin="plugin id"/> 声明。清单 1 的例子说明这个插件 ID org.eclipse.draw2d 依赖于 ID 为 org.eclipse.swt 和 org.eclipse.core.runtime 的插件。
插件链
当我们在 Eclipse 中使用 Java? 技术平台来开发软件时,系统实际上根据所选择的目标平台对源代码进行编译。可以在 Window > Preferences > Plug-in Development > Target Platform 中指定目标平台的位置。这个目标平台在 <targetPlatform>\eclipse 中有自己的一个 Eclipse 副本。要为代码解析这些依赖性,请从两个地方查找是否存在所需要的插件:
<targetPlatform>\eclipse\plugins 文件夹中的 Eclipse 插件
<targetPlatform>\eclipse\links 文件夹中 .link 文件所指向的链接插件
程序员通常会将第二个地方称为 links 文件夹。这个 links 文件夹中包含 0 个或多个文件,文件名通常都是以 “.link” 扩展名结尾。这些文件中包含了一些链接信息,可以使用这些信息定位在磁盘上哪些地方可以找到链接插件。
每个 .link 文件都有一个关键字-值对,其格式为 path=location。(例如,links 文件夹 C:\eclipse\links 中就可能会有很多 .link 文件,其中一个文件的名字可能为 com.ibm.indiver.dependencywalker.link。这个文件中唯一的一行可能类似于 path=c:\myPlugins\dependencyWalker)。这个 .link 文件会将 Eclipse 引导到指定的位置,并在 \eclipse\plugins 文件夹中寻找更多的可用插件。
创建自己的 Eclipse 插件依赖性遍历程序
编写一个依赖性遍历程序基本上分为两个步骤:首先罗列出所有插件,其次罗列出用户所选择的插件的依赖性。
第一个步骤要负责定位 Eclipse 系统中出现的每个插件,并在一个简单的用户界面(UI)—— 例如表 —— 中为终端用户提供所有插件的清单。这个 UI 还应该为用户提供一些方法来选择希望解析其依赖性的插件。
第二个步骤则要对用户选择的插件的 plugin.xml 文件进行分析,并查找这个 plugin.xml 文件中嵌入的 <import plugin="plugin id"/> 声明。这种努力显然需要对每个插件的 manifest 文件进行递归搜索,从而查明依赖插件的整个链条。对于描述这个插件可能依赖于其他插件的父-兄-子关系,树状视图是最合适的一种 UI。我们还应该可以直观地看出某个 Eclipse 插件注册项是否真正加载了一个物理存在的插件。
步骤 1:罗列 Eclipse 系统中的所有插件
在掌握了以下信息之后,就可以编写一些代码来罗列磁盘上物理存在的所有插件了:
插件主要在 <targetPlatform>\eclipse\plugins 文件夹中。
在其他几个 <someLinkedPath>\eclipse\plugins 文件夹中也可能会找到插件。
从 <targetPlatform>\eclipse\links 文件夹中的 .link 文件中可以获得到每个 <someLinkedPath> 的路径。
下面是罗列 Eclipse 系统中所有插件的详细步骤:
找到目标平台的位置。
准备 links 文件夹的路径。links 文件夹在 \eclipse 文件夹中。
获得 \eclipse\links 文件夹中文件的清单。请参考源代码中的 Utilities.getLinkedPaths() 函数。
查看每个 .link 文件,获取链接 Eclipse 插件的路径。
准备一个所有插件根文件夹的清单(即,<targetPlatform>\eclipse\plugins 文件夹和所有可能的 <someLinkedPath>\eclipse\plugins 文件夹)。
对于每个根文件夹,进入每个插件目录中,并获取 plugin.xml 文件的路径。
对 plugin.xml 文件进行分析,获得插件 ID 和插件版本,并将这些信息保存到一个数据结构中。
回到步骤 6,继续处理下一个插件目录。
清单 2. 准备在 Eclipse 系统下物理存在的所有插件的清单
/**
*
* @return returns a Vector containing PluginData objects.
* Each PluginData object represents a Plugin found under any of the following
* plugin directories
* a. the targetPlatformLocation\eclipse\plugins directory,
* b. other plugin directories as specified by *.link files under
* targetPlatform\eclipse\links directory
**/
public static Vector getPluginsInTargetPlatform(){
/**
//step1: Get path of target platform.
//step2: Prepare path of links folder.
//step3: Get list of files in links folder.
//step4: Parse each link file and get the path of linked Eclipse folder.
//step5: Prepare a list of all plugin root folders
// (Eclipse plugins and linked Eclipse plugins).
//step6: 6a. For each plugin root folder,
// 6b. go to each plugin directory and get path of plugin.xml.
//step7: Parse the plugin.xml file to get plugin id, plugin version,
// and store in vectors, lists, etc.
//step8: Go back to step 6 to continue with next plugin directory.
**/
//step1: Get path of target platform.
//Fall back to Eclipse install location if targetplatform in not set.
URL platFormURL = Platform.getInstallLocation().getURL();
Location location = Platform.getInstallLocation();
IPath eclipsePath = null ;
//Get path of target platform against which the users of this tool
//will compile their code.
IPath targetPlatFormLocation = new Path(getTargetPlatformPath(true));
if(_useTargetPlatform == false)
eclipsePath = new Path(platFormURL.getPath());
else
eclipsePath = targetPlatFormLocation;
showMessage("Considering target platform to be: " +
eclipsePath.toString());
//step2: Prepare path of links folder.
//step3: Get list of files in links folder.
//step4: Parse each link file and get the path of linked Eclipse folder.
IPath linksPath = new Path( eclipsePath.toString() ).append("/links");
String linkedPaths[] = getLinkedPaths(linksPath.toString());
int linkedPathLength = 0;
if(null != linkedPaths){
linkedPathLength = linkedPaths.length;
}
//step5: Prepare a list of all plugin root folders
// (Eclipse plugins and linked Eclipse plugins).
IPath eclipsePluginRootFolders[] = new IPath[linkedPathLength + 1];
eclipsePluginRootFolders[0] =
new Path( eclipsePath.toString() ).append("/plugins");
if(null != linkedPaths){
for(int i=0; i<linkedPaths.length; i++){
eclipsePluginRootFolders[i+1] =
new Path(linkedPaths[i]).append("/eclipse/plugins");
}
}
//step6: 6a. For each plugin root folder,
// 6b. go to each plugin directory and get path of plugin.xml.
//step7: Parse the plugin.xml file to get plugin id, plugin version,
// and store in vectors, lists, etc.
Vector vectorsInThisVector = new Vector();
for(int i=0; i<eclipsePluginRootFolders.length; i++){
System.out.println("\n========plugin IDs and Versions in " +
eclipsePluginRootFolders[i] + "========");
Vector pluginDataObjs =
getPluginDataForAllPlugins(
eclipsePluginRootFolders[i].toString());
vectorsInThisVector.add(pluginDataObjs);
System.out.println(pluginDataObjs);
System.out.println("\n===========|||=== end ===|||===========");
}
Vector pluginData = new Vector();
Iterator outerIterator = vectorsInThisVector.iterator();
while(outerIterator.hasNext()){
Vector pluginDataObjs = (Vector)outerIterator.next();
Iterator innerIterator = pluginDataObjs.iterator();
while(innerIterator.hasNext()){
PluginData pd = (PluginData)innerIterator.next();
String pluginIdKey = pd.ge