Custom touchpoints in p2

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.

Background

When you use p2 to install software it goes through a number of steps.

  1. A provisioning plan is computed. Based on what you already have installed and the install action you’re taking, p2 computes a plan. The plan includes operands such as: remove foo, upgrade bar, install baz, etc..
  2. Artifacts are fetched and checked. Once a plan is constructed, the plan is executed on the p2 engine. The first step is fetching all the artifacts and the trust checking (checksum validation)
  3. Unconfigure / Uninstall. If there are any units being removed, they are given a chance to ‘unconfigure’ themsevles. Once this is done, they are uninstalled.
  4. Install / Configure. If there are any units being added, they are first installed and then they are given a chance to configure themselves.

For installable units being upgraded, this is essentially an unconfigure / uninstall followed by an instal / configure.

The Problem

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 unzippedcopied 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.

Touchpoint Actions

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.

Screen Shot 2013-05-22 at 3.43.23 PMAll 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 ;-).

MetaRequirements

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.

Invoking the Touchpoint

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);

Putting it all Together

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.

Screen Shot 2013-05-22 at 3.12.34 PM

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.

Screen Shot 2013-05-22 at 3.13.54 PM

If you’re interested in the code examples I’ve used here, I’ve pushed them all to GitHub.