Trying to locate an unresolved plug-in dependency is a stressful and time-consuming task. Activating each plug-in relies on many other plug-ins, which are dependent on other plug-ins. If Eclipse cannot load a plug-in in this long chain, it may take more time and effort to manually find out which plug-in is the problem. If you want a way to automate this kind of plug-in dependency detection, please read this article.
problems encountered
Suppose we want to use a specific plug-in in Eclipse and have performed all the necessary actions, include it in the plug-in's manifest file, and declare it as a dependent file. But the system does not load this plugin, so we will be stuck here, software development can not continue to progress.
Does it sound familiar? If so, you may have spent a lot of time and effort looking at a lot of plugin.xml files to find out which specific plug-ins Eclipse might not have loaded. You may also have attempted to use the Plug-in dependencies view provided by the Eclipse PDE project, and you will find that the only work of this view is simply to display plug-ins that have been successfully loaded. Unfortunately, the problematic plug-ins are probably not part of the plug-in that was successfully loaded.
What should we do to make sure that Eclipse does not find or load a specific plug-in? Instead of manually traversing each plugin.xml file, we consider automatically implementing this search function. To automate the search, we need to understand how Eclipse saves its own plug-ins and how to find links to other plug-ins that are saved on disk. Based on this knowledge, we may want to write our own code to create a plug-in dependency traversal program, or use the generic Dependency Walker plugin given in this article. The "Downloads" section of this article gives the source code for this example.
Getting Started: Understanding plug-in dependencies and Eclipse's plug-in chain
Plug-in Dependencies
The Eclipse plug-in is a software module that provides functionality that other plug-ins can use. If plug-in a requires plug-in B to work properly, then we say a depends on B. This dependency also means that plug-in A does not function unless plug-in B has been successfully loaded. Sometimes, plug-in B may also rely on Plug-ins C, D, E, which makes it even more uncomfortable that each of these plug-ins may depend on other plug-ins. This chain of dependence is easy to form hundreds of plug-ins linked together. There is no doubt that if any of the plug-ins in this chain cannot be loaded successfully, the plug-ins that depend on it may be problematic.
The plugin manifest file Plugin.xml describes each plug-in. This XML file contains a section that declares dependencies or requirements for other plug-ins. In Listing 1, this dependency is declared in the section in the Plugin.xml file that is represented in bold.
Listing 1. plugin.xml file
<?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>
Note the < import plugin = "plugin ID" / > declaration embedded in the < requirements > < / requirements > section. The example in Listing 1 shows that the plug-in ID org.eclipse.draw2d depends on the plug-ins with ID org.eclipse.swt and org.eclipse.core.runtime.
Plug-in chain
When we use Java? Technology platform to develop software in eclipse, the system actually compiles the source code according to the selected target platform. You can specify the location of the target platform in Window > Preferences > plug in Development > target platform. This target platform has its own copy of eclipse in < target platform > \ eclipse. To resolve these dependencies for your code, look for the required plug-ins in two places:
Eclipse plug-ins in the folder of < targetplatform > \ eclipse \ plugins
The link plug-in pointed to by the. Link file in the folder < targetplatform > \ eclipse \ links
Programmers often refer to the second place as the links folder. The links folder contains 0 or more files, usually ending with a ". Link" extension. These files contain some link information, which can be used to locate where the link plug-in can be found on the disk.
Each. Link file has a key value pair in the form path = location. (for example, there may be many. Link files in the links folder C: \ eclipse \ links, one of which may be com.ibm.indicator.dependencywalker.link. The only line in this file might look like path = C: \ myplugins \ dependencywalker). This. Link file will boot eclipse to the specified location and find more plug-ins available in the \ eclipse \ plugins folder.
Create your own Eclipse Plug-in dependency traverser
Writing a dependency traversal program is basically divided into two steps: first, listing all plug-ins, and then listing the dependencies of plug-ins selected by users.
The first step is to locate each plug-in that appears in the eclipse system and provide a list of all plug-ins for the end user in a simple user interface (UI) - such as a table. The UI should also provide users with ways to select plug-ins that they want to resolve their dependencies.
The second step is to analyze the plugin.xml file of the plug-in selected by the user, and find the declaration of < import plugin = "plugin ID" / > embedded in the plugin.xml file. This effort obviously requires a recursive search of each plug-in's manifest file to identify the entire chain of dependencies on the plug-in. Tree view is the most appropriate UI to describe the parent-child relationship that this plug-in may depend on other plug-ins. We should also be able to see visually whether an Eclipse Plug-in registry actually loads a physically existing plug-in.
Step 1: list all plug-ins in the eclipse system
After mastering the following information, you can write some code to list all the plug-ins physically existing on the disk:
The plug-ins are mainly in the target platform > \ eclipse \ plugins folder.
Plug ins may also be found in several other folders.
The path to each 'somelinkedpath' can be obtained from the. Link file in the 'targetplatform > \ eclipse \ links' folder.
Here are the detailed steps for listing all plug-ins in the eclipse system:
Find the location of the target platform.
Prepare the path to the links folder. The links folder is in the \ eclipse folder.
Get the list of files in the \ eclipse \ links folder. Refer to the utilities. Getlinkedpaths() function in the source code.
Check each. Link file for the path to the linked Eclipse Plug-in.
Prepare a list of all plug-in root folders (that is, the < targetplatform > \ eclipse \ plugins folder and all possible < somelinkedpath > \ eclipse \ plugins folder).
For each root folder, go to each plug-in directory and get the path of the plugin.xml file.
The plugin.xml file is analyzed to obtain the plug-in ID and plug-in version, and the information is saved in a data structure.
Go back to step 6 and move on to the next plug-in directory.
Listing 2. Prepare a list of all plug-ins that physically exist under the eclipse system
/** * * @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; }
After mastering all plug-ins, we can display the ID, version, location and more information of plug-ins, which can be displayed in a standard widget Toolkit (SWT) table, so as to make these information more intuitive. We can also write some code to sort according to the plug-in ID column, just like our sample code. You should also indicate in a column how many plug-ins were found. The results should be as follows:
Figure 1. All plug-ins in the target platform view
Discover unresolved plug-in dependencies in eclipse - Eclipse SVN plug-ins
Step 2: recursively search the plugin.xml file to traverse the entire dependency chain
After the user selects the plug-in that wants to resolve the dependency chain, we need to analyze the plugin.xml file of the plug-in that the user selects to view its dependency. Each dependency leads to checking another plugin. XML file, which in turn has its own dependency. Starting from the plug-ins selected by users, this dependency chain can quickly lead to many plugin.xml files that need to be analyzed. We can write a recursive function to traverse these dependencies. It can find the latest version of a plug-in (for the case of duplicate plug-ins in the same system) and all its dependencies.
The steps to write this recursive function are as follows, and Listing 3 shows the source code of this function. Recursive functions sometimes consume a lot of resources and may not return results before the user loses patience. Another option is to write a function that only gets the direct dependency list of the plug-in selected by the user. For the latter method, see the loadimmediatedependencies() function in the sample code.
Gets the path of the plug-in selected by the user.
Check if the plugin.xml or fragment.xml file exists at this location.
Analyze the plugin.xml or fragment.xml file to get a list of all the plug-ins needed by the plug-in.
For each plug-in ID in this listing, look for the corresponding plug-in.
If more than one plug-in has the same ID, it is reported only once to the user and automatically determines to use a newer version of the plug-in. How to program to compare plug-in versions and find a newer plug-in, see Figure 4.
Add the plug-in (found in step 4 or 4A) to a tree view, call the same function recursively, and re execute from step 1 again. But this time the user doesn't have to choose the plug-in; Add the plug-in (found in step 4 or 4A) to a tree view, call the same function recursively, and re execute from step 1 again. However, this time the user does not need to select the plug-in; the code corresponding to step 4 or 4A will be responsible for selecting the plug-in.
Listing 3. Recursive plugindependencywalker() function
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(); } }
Sometimes, we encounter situations where plug-ins with the same ID exist in different locations. For example, a plug-in with ID org.eclipse.xsd might appear in the < targetplatform > \ eclipse \ plugins folder and the < somelinkedpath > \ eclipse \ plugins folder at the same time.
In this case, you must decide which plug-in to choose from the two or more copies of the disk. Obviously, we should be interested in the latest plug-ins, that is, the newer versions of plug-ins. We can use some existing functions to compare the versions of eclipse plug-ins, or we can write a simple function based on the sample code shown in Listing 4 to compare the versions of plug-ins.
Listing 4. Comparing plug-in versions
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]; }
After the code traverses the entire link dependency chain, we can use a tree view to express it intuitively. You should also visually indicate (see the red circle in the figure below) which plug-in caused the load failure.
The results of this search should look like the following:
Figure 2. Dependency Walker tree view
Discover unresolved plug-in dependencies in eclipse - Eclipse SVN plug-in installation
Concluding remarks
If we want to locate some unresolved plug-in dependencies (missing plug-ins or eclipse failed to load them for some reason), we can first use the eclipse PDE plug in dependencies view to display the plug-in dependencies. If the plug-in dependencies view does not show our plug-ins, you may want to use the tool described in this article to automate the search of all linked plug-in folders. If you are only interested in a specific plug-in, you can also modify this code to meet your requirements.
The source code for this tool is available in the download section below. To browse the source code, expand the source code package and open the plug-in as an eclipse project. To use this tool, extract the plug-in into the \ eclipse \ plugins folder and do the following:
In eclipse, switch to Window > show View > others > dependencywalker category and select all plugins in target platform view.
This view shows all the plug-ins that appear in the specified target platform. Select a plug-in and double-click it.
Dependencywalkertreeview displays all the dependencies of the plug-in you selected. When you are finished, close this view.