当前页面: 开发资料首页 → JSP 专题 → 发现 Eclipse 中未解析的插件依赖性
摘要: 发现 Eclipse 中未解析的插件依赖性
<?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>
程序员通常会将第二个地方称为 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 系统中的所有插件
在掌握了以下信息之后,就可以编写一些代码来罗列磁盘上物理存在的所有插件了:
下面是罗列 Eclipse 系统中所有插件的详细步骤:
清单 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.getPluginID(); String versionValue = pd.getPluginVersion(); String pluginPath = pd.getPluginLocation(); pluginData.add(pd); } } int breakpoint=0; return pluginData; }
当用户选择希望解析依赖链的插件之后,我们就需要对用户所选择的插件的 plugin.xml 文件进行分析,从而查看它的依赖性。每个依赖性都会导致检查另外一个 plugin.xml 文件,后者又有自己的依赖性。从用户选择的插件开始,这个依赖链可以迅速导致有很多个 plugin.xml 文件需要进行分析。我们可以编写一个递归函数来遍历这些依赖性,它可以查找某个插件的最新版本(针对在相同的系统中有重复插件的情况)以及它的所有依赖性。
编写这种递归函数需要执行的步骤如下所示,清单 3 给出了这个函数的源代码。递归函数有时对资源的消耗量很大,而且在用户失去耐心之前可能还没有返回结果。另外一种选择是编写一个函数,只获取用户选择的插件的直接依赖性清单。后一种方法请参看样例代码中的 loadImmediateDependencies() 函数。
清单 3. recursivePluginDependencyWalker() 函数
private Vector alreadyNotified = new Vector(); private boolean firstCall = true; private TreeParent root = null; private void recursivePluginDependencyWalker(PluginData pdObject, TreeParent parentNode){ try { String path = pdObject.getPluginLocation(); PluginParser pp = null; File pluginDotXmlFile = new File(path + "/plugin.xml"); if(pluginDotXmlFile.exists()){ pp = new PluginParser(pluginDotXmlFile); }else{ File fragmentDotXmlFile = new File(path + "/fragment.xml"); if(fragmentDotXmlFile.exists()){ pp = new PluginParser(fragmentDotXmlFile); }else{ return;//no plugin.xml or fragment.xml found } } String displayName = pdObject.getDisplayName(); System.out.println("\nPlugin ["+ displayName + "] requires" + "\n"); String requires[] = pp.getDependencyList(); if(0 != requires.length ){ for(int i=0; i<requires.length; i++){ System.out.println("\t" + requires[i] ); PluginData pd[] = getPluginDataObjectsFromPluginID(requires[i]); PluginData nextPlugin = null; switch(pd.length){ case 0: //great, we know there is //something missing nextPlugin = null; break; case 1: //best case, everything will be smooth nextPlugin = pd[0]; break; default: //worst case, there must be more //than 1 plugin with the same id //at different locations. String msgLine1 = "Plugin " + displayName + " requires " + requires[i] + "\n"; String msgLine2 = "Duplicate plug-ins found for ID: \ " " + requires[i] + "\"" + "\n Continuing with higher version... " ; //it is bad to give repeated //reminders, //so remind only once per plugin id. if(! alreadyNotified.contains( new String(requires[i]))){ MessageDialog.openInformation(null, "Dependency Walker", msgLine1 + msgLine2); alreadyNotified.add( new String(requires[i])); } //always take the better //version anyway nextPlugin = getBetterVersionPlugin(pd); break; }//end of switch if( null != nextPlugin ){ TreeParent nextinLine = new TreeParent( nextPlugin.getDisplayName(), nextPlugin.isPlugin LoadedInRegistry() ); parentNode.addChild(nextinLine); recursivePluginDependencyWalker( nextPlugin, nextinLine); }else{ TreeParent nextinLine = new TreeParent( requires[i] + " [This plug-in is missing] ", false); parentNode.addChild(nextinLine); //obviously we can't recurse //into a missing plugin... } }//end of for }else{ System.out.println("\t NOTHING: No further dependency \n" ); //no further dependency } } catch (Exception e) { e.printStackTrace(); } }
在这种情况中,必须要确定从这两个或更多个磁盘副本中选用哪个插件。显然,我们所感兴趣的应该是最新的插件,也就是说,版本较新的插件。我们可以利用现有的一些函数来对 Eclipse 插件的版本进行比较,或者可以基于清单 4 所示的样例代码编写一个简单的函数来对插件版本进行比较。
清单 4. 比较插件版本
private PluginData getBetterVersionPlugin(PluginData pdo[]){ PluginData _pdObjs[] = pdo; int len = pdo.length; if(len==0) return null; Arrays.sort(_pdObjs,new Comparator() { /**Compares its two arguments for order. * Returns a negative integer, zero, or a positive integer * as the first argument is less than, equal to, or greater than * the second. **/ public int compare(Object leftObj, Object riteObj) { String leftPID = ((PluginData)leftObj). getPluginVersion().replace('.', ':'); String ritePID = ((PluginData)riteObj). getPluginVersion().replace('.', ':'); String leftID[] = leftPID.split(":"); String riteID[] = ritePID.split(":"); int maxlen = leftID.length > riteID.length ? leftID.length : riteID.length; for(int i=0; i<maxlen; i++){ int left = 0; int rite = 0; try { left = new Integer(leftID[i]).intValue(); } catch (NullPointerException e) { left = 0; } try { rite = new Integer(riteID[i]).intValue(); } catch (NullPointerException e) { rite = 0; } if(left==rite){ continue; }else{ int bigger = left > rite ? left : rite; if(bigger==left) return 1; if(bigger==rite) return -1; } } return 0; } public boolean equals(Object arg0) { return false; } }); return _pdObjs[len-1]; }