Ian is an Eclipse committer and EclipseSource Distinguished Engineer with a passion for developer productivity.
He leads the J2V8 project and has served on several …
For most people, p2 is simply the Install Software Dialog in Eclipse. While this is certainly true (or at least the Install Software Dialog is built with p2), p2 is much more than this. The Eclipse Provisioning Platform (p2) is a general purpose platform for provisioning everything and nothing in particular.
Most of the out-of-the-box p2 tools are related to provisioning OSGi bundles (or Eclipse plug-ins, if you prefer), but there is nothing in the core that limits us to Eclipse or OSGi. In fact, you can extend p2 to install whatever you want. In this example I’ll show you how to create a custom Install Action (touchpoint) that executes an arbitrary binary during installation.
When you use p2 to install software it goes through a number of steps.
For installable units being upgraded, this is essentially an unconfigure / uninstall followed by an instal / configure.
p2 has a number of actions you can invoke when an installable unit (IU) is installed, configured, unconfigured or uninstalled. For example, artifacts can be unzipped**,** copied or the permissions can be set. These actions are called touchpoint actions. Eclipse has two sets of touchpoint actions, native and OSGi. Native ones are general (such as move, copy, mkdir) while the OSGi ones are Eclipse or OSGi specific (install bundle, set VM Args, etc…).
While these are a good start (and enough to provision an Eclipse system), often you need custom actions if you are provisioning a highly specific system. Also, not only do you need custom actions, you need them to be installed before they can be used.
Let’s consider the case where we want to ship and run an arbitrary executable during the installation of a plugin.
Custom touchpoint actions can be specified using the org.eclipse.equinox.p2.engine.action extension point. Touchpoint Actions are tied to a touchpoint, the controller responsible for executing the action. You can either write your own custom touchpoint, or simply tie your action to an existing one. In our case, we will tie our touchpoint action to the OSGi touchpoint.
All touchpoint actions extend ProvisioningAction and provide two methods execute and undo. Execute is used whenever the provisioning action is invoked. Undo is used if the action needs to be rolled back. In our case we will implement execute to read a parameter and call Runtime#exec on the value of that parameter.
public IStatus execute(Map<String, Object> parameters) {
String artifact = null;
if ( parameters.containsKey(ARTIFACT) ) {
// If an artifact is specified, look it up
}
String program = (String)parameters.get(PROGRAM);
String fullPath = computeProgramPath(artifact, program);
try {
Runtime.getRuntime().exec(fullPath);
} catch (IOException e) {
e.printStackTrace();
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Cannot execute " + fullPath);
}
return Status.OK_STATUS;
}
The undo method (that is, the inverse of Runtime#exec) has been left as an exercise to the reader ;-).
In order to ensure that your touchpoints get installed before the components that need them, you can either invoke two updates (update the installer and then update the application) or you can use MetaRequirements. MetaRequirements invoke a two stage plan, ensuring that the touchpoints are fetched and installed before they are invoked.
MetaRequirements can be declared in a p2.inf file as follows.
metaRequirements.0.namespace=org.eclipse.equinox.p2.iu metaRequirements.0.name=com.ianbull.p2.touchpoint.exec metaRequirements.0.range=[1,2)
In this case we’ve specified a metaRequirement on the Installable Unit com.ianbull.p2.touchpoint.exec version [1.0,2.0). This Installable Unit must exist before our IU can be installed. For OSGi bundles, the p2.inf file should be placed next to the MANIFEST.MF file. In our case, it’s placed in a bundle called com.ianbull.p2.touchpoint.payload.
To summarize, before ‘payload’ can be installed, the touchpoint ’exec’ must be installed and available. Since it’s declared as a metaRequirement, p2 will handle this by using a two-stage plan.
Now that we’ve created the touchpoint, all we need to do is invoke it. This is done by the Installable Unit which declares the metaRequirement. In our case, we will ship the executable as part of our ‘payload’ plugin.
We will use two touchpoints, one to set the permission (755) and then our custom touchpoint to launch the application.
instructions.install = \ chmod(targetDir:@artifact,targetFile:payload/someProgram,permissions:755); instructions.install.import= \ org.eclipse.equinox.p2.touchpoint.eclipse.chmod instructions.configure = com.ianbull.p2.touchpoint.exec.Execute(artifact:@artifact, program:payload/someProgram);
Once we have these two plugins (the ’exec’ touchpoint and the ‘payload’) we create a repository that contains them both.
With the repository in place, we simply invoke the director, or better yet, the p2 Install Software Dialog to install the payload.
The payload doesn’t actually have any dependency on the ’exec’ touchpoint, except for the MetaRequirement. This MetaRequirement will cause the ’exec’ touchpoint to first be installed, and then the touchpoint can be invoked.
And now during installation, our custom application is started. While this particular application doesn’t do anything useful, it could be an installer for another tool, or it may simply invoke an existing service such as Apache HTTPD.
If you’re interested in the code examples I’ve used here, I’ve pushed them all to GitHub.
Ian is an Eclipse committer and EclipseSource Distinguished Engineer with a passion for developer productivity.
He leads the J2V8 project and has served on several …