Writing an Eclipse plugin
This is an introductory article to writing plugins for the Eclipse IDE, with the Eclipse IDE.
We write an Eclipse plugin that will execute external commands, integrated with Eclipse(logging will be written to the eclipse console,…), from the project root right click menu.
Eclipse version used is 3.5(Helios).
Our plugin
We will add a menu like this to the project root element of our projects in Eclipse:
Note that some of the tasks in this list can be done with other, better integrated plugins. For example, running a maven clean install can be done through “Run as> Maven clean install” in my IDE, because I have a third party Maven eclipse plugin installed. The only thing our plugin will do is provide shortcuts to commands that could be defined in the regular Eclipse External Tools feature anyway.
I chose a use case for this tutorial that might actually be useful to some people, but the goal of this tutorial is still just to give an introduction to eclipse plugin development that goes a bit further than printing the standard hello message.
Starting out
Eclipse has a specific type of project for creating plugins: the aptly named Plug-in Project. Choose New>Project>Plug-in Project from the File menu in Eclipse.
A wizard will be started, which will configure the project and already provide us with all the code needed for a hello world message plugin.
In the first screen of the wizard, we choose a name for the project. Typically, the Java package name in which your plugin classes will reside is used as the plugin project name. The other options we leave at their default values.
In the next screen, we enter things like ID, version, name and provider. Again, we leave the rest at the default values. We ask the wizard to generate an activator – more about activators later – and we also tell the wizard that this plugin will make contributions to the ui.
In the final screen, we choose to create a plugin based on the Hello World template. You could choose to not use a template and start with an empty plugin project, but then you will have to write the plugin.xml – more about the plugin.xml later – yourself.
The two most defining files for an Eclipse plugin: the Activator and the plugin.xml
If you followed the wizard above, these two files will already be present in the project you just created.
The Activator
public class Activator extends AbstractUIPlugin { // The plug-in ID public static final String PLUGIN_ID = "com.integratingstuff.externaltools"; // The shared instance private static Activator plugin; public void start(BundleContext context) throws Exception { super.start(context); plugin = this; } public void stop(BundleContext context) throws Exception { plugin = null; super.stop(context); } public static Activator getDefault() { return plugin; } public static ImageDescriptor getImageDescriptor(String path) { return imageDescriptorFromPlugin(PLUGIN_ID, path); } }
This is the class representing the Plugin. It extends (usually indirectly) from org.eclipse.core.runtime.Plugin. This class manages the lifecycle of the plugin. The method getDefault() for example will be called by Eclipse to return the plugin instance. Since it makes no sense to have more than one instance of a plugin, this instance is pretty much always maintained as a singleton class. The Activator class generated by the wizard is no exception to this rule.
The plugin.xml
In the plugin.xml everything about the plugin is declared. Usually, some extension point is defined. Eclipse supports many extension points. Some common ones are the popupMenus(org.eclipse.ui.popupMenus) and Menus and Toolbar(org.eclipse.ui.actionSets).
We open the plugin.xml and go to its source(the plugin.xml tab in the list of tabs underneath the overview):
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.5"?> <plugin> <extension point="org.eclipse.ui.actionSets"> <actionSet label="Sample Action Set" visible="true" id="com.integratingstuff.externaltools.actionSet"> <menu label="Sample &Menu" id="sampleMenu"> <separator name="sampleGroup"/> </menu> <action label="&Sample Action" icon="icons/sample.gif" class="com.integratingstuff.externaltools.actions.SampleAction" tooltip="Hello, Eclipse world" menubarPath="sampleMenu/sampleGroup" toolbarPath="sampleGroup" id="com.integratingstuff.externaltools.actions.SampleAction"> </action> </actionSet> </extension> </plugin>
Our generated plugin.xml defines one extension point.
The line
<extension point="org.eclipse.ui.actionSets">
can be read as:
“We are going to add an ActionSet(collection of actions linked to Menus and Toolbar)”.
Then we define the actual actionSet, and include a menu element, which we link with only one action. The Action was generated as well after completion of the wizard. The most interesting part of this SampleAction class is probably the run method:
public void run(IAction action) { MessageDialog.openInformation(window.getShell(), "Externaltools", "Hello, Eclipse world"); }
This method will print the typical Hello World Message in a dialog when our one menu item is clicked.
Testing the plugin
So, after the completion of the wizard, we ended up with a runnable plugin. But how to test it and see what it does?
Let us click on the plugin.xml file again, but this time, we are not clicking to the source, but staying in the overview. In the previous section, we wanted to see the technical details, but now we know those, this overview, combined with the other tabs, is handy to manage the further development of the plugin. In the overview we see a testing panel in the right column:
When we click on “Launch an Eclipse application” there, a new instance of Eclipse will be launched, which will have our new plugin installed.
And indeed, in the new instance we see our “Sample Menu” in the top menu bar:
And when we click its one element, the expected dialog box is displayed:
Installing the plugin into Eclipse
We close the test Eclipse instance and return to our Overview. Right next to the Testing Panel, there is the Exporting Panel. To package our plugin, so we can deploy it to regular Eclipse installations, we launch the Export Wizard from the last line in that panel. We enter the directory we want the package to be generated in. After we click finish, the jar will be in the selected directory:
The only thing left to do is drop the jar in the dropins folder in your eclipse installation, and when your eclipse installation is restarted, it will pick up the plugin automatically and the Sample menu will appear in the top menu bar.
Adding our own logic
So far, we have not written a single line of code. We generated a plugin with the new Project Wizard and saw how to test it and how to port it to an Eclipse installation. In this section we are going to add our own custom logic, and make sure the plugin does what we want it to do.
A custom action
First, we are going to implement our custom action:
package com.integratingstuff.externaltools.actions; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.ui.externaltools.internal.model.IExternalToolConstants; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IObjectActionDelegate; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; public class ExternalToolAction implements IObjectActionDelegate { private Shell shell; String name = "Maven clean install"; String command = "/home/sluypaer/development/apache-maven-2.2.1/bin/mvn"; String params = "clean install"; @Override public void setActivePart(IAction action, IWorkbenchPart targetPart) { shell = targetPart.getSite().getShell(); } @Override public void selectionChanged(IAction action, ISelection selection) { } @Override public void run(IAction action) { try { IWorkbench workbench = PlatformUI.getWorkbench(); IWorkbenchPage page= workbench.getActiveWorkbenchWindow().getActivePage(); TreeSelection selection= (TreeSelection) page.getSelection(); IJavaProject javaProject = (IJavaProject) selection.getFirstElement(); ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType programType = manager.getLaunchConfigurationType(IExternalToolConstants.ID_PROGRAM_LAUNCH_CONFIGURATION_TYPE); ILaunchConfiguration cfg = programType.newInstance(null, name); ILaunchConfigurationWorkingCopy wc = cfg.getWorkingCopy(); wc.setAttribute(IExternalToolConstants.ATTR_LOCATION,command); wc.setAttribute(IExternalToolConstants.ATTR_WORKING_DIRECTORY, "${workspace_loc:" + javaProject.getPath().toOSString() +"}"); wc.setAttribute(IExternalToolConstants.ATTR_TOOL_ARGUMENTS, params); cfg = wc.doSave(); cfg.launch(ILaunchManager.RUN_MODE, null, false, true); cfg.delete(); } catch (CoreException e) { //TODO: log error e.printStackTrace(); } } }
First, we get to the selected element, which will always be a IJavaProject – more on this later – which’ root dir we will use as the working directory. Then we build a launch configuration, which will launch the given command from within eclipse. This mainly means the output of the launch will be written to the Eclipse console.
The above is not the best code. I started from my usecase, consulted the Eclipse docs looking for a solution, and this shady org.eclipse.debug.core was the only package I found to launch external commands from.
What is worse, I hardcoded the command and its params in the action. These properties would have to be externalized to some properties file, or even better, be added to some section in the Eclipse Preferences for this specific plugin. For illustration purposes, I think the above code is fine though.
Usually, I do not include the imports in code snippets, but in this one I wanted to illustrate that it will probably be necessary to import extra eclipse jars the project will depend on. You can easily add them through the plugin.xml Dependencies tab. Just start typing the package name and the possibilities will narrow as you go. In our case, add org.eclipse.jdt.core, org.eclipse.ui.externaltools en org.eclipse.debug.core.
plugin.xml revisited
We have our custom action now. The only thing left to do is add the option to execute it to our project root menu.
Our plugin.xml uses another extension point this time:
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.5"?> <plugin> <extension point="org.eclipse.ui.popupMenus"> <objectContribution objectClass="org.eclipse.jdt.core.IJavaProject" id="com.integratingstuff.externaltools.contribution"> <menu label="Integrated Stuff" path="additions" id="com.integratingstuff.externaltools.menu"> <separator name="group"> </separator> </menu> <action label="Maven clean install" class="com.integratingstuff.externaltools.actions.ExternalToolAction" menubarPath="com.integratingstuff.externaltools.menu/group" enablesFor="1" id="com.integratingstuff.externaltools.actions.ExternalToolAction"> </action> </objectContribution> </extension> </plugin>
This time we are extending the popup (right click) menus. Instead of an actionSet, we now define an objectContribution, which decides on which elements’ popup menus the item will be added. In our case, this will be all elements of the IJavaProject type(note that it is often hard to find a catchall objectClass – different project root class types dont necessarily have a common denominator class). Within the contribution, we add an “Integrated Stuff” menu, to which we attach our custom action.
We can easily keep adding actions like this, and eventually, we get a custom external tools menu like in the screenshot:
I’ve been searching for this plugin ability for a while (executing external tools as sub-menu), but I can’t get your example working. Mainly, my system doesn’t recognize the org.eclipse.debug.*, org.eclipse.ui.externaltools.*, or org.eclipse.jdt.* imports. How do I set these up to be able to run this example? Were these from Eclipse source code?
Was this answer helpful?
LikeDislikeHi, did you import the plugin dependencies as described in the last paragraph and image of the “A custom action” section of this article?
Was this answer helpful?
LikeDislikeNice article! I am tryng to create a custom action to run an swing aplication…
Do you have an sample code?
The plugin have an app.JAR(swing) in the claspath(/lib)