Tutorial: p2 updates for Eclipse RCP applications

In this tutorial I’ll show how to make the RCP mail example application updatable using the p2 provisioning platform. This version of the tutorial is for Eclipse 3.6 from the Helios release. I assume you’re familiar with OSGi and Eclipse RCP development.

If you have suggestions or ideas for improving the tutorial and its underlying approach, I’d be very glad to hear from you. This tutorial is part of my Eclipse RCP course (in German). Making it public for everyone is one small way for me to give back to the great Eclipse community.

Creating the example project

  1. Create a new plug-in project com.example.mail. Choose Eclipse version 3.6 and create a rich client application from the RCP Mail Template:

Making the application updateable

  1. Download org.eclipselabs.p2.rcpupdate. This project contains a plug-in and a feature to make RCP applications updateable via p2. It’s mostly extracted from the Eclipse Wiki: Adding Self-Update to an RCP Application and the RCP Cloud example project from Susan F. McCourt. There are plans to include something like this in Eclipse 3.7, see Bug 281226 - RCP Simple Update UI for more information about this.

  2. The archive contains two projects, import both into your workspace using File > Import > Existing Projects into Workspace > Select archive file.

  3. Create a new menu contribution in the plugin.xml of the com.example.mail plug-in and add both the commands org.eclipselabs.p2.rcpupdate.install and org.eclipselabs.p2.rcpupdate.update to the help menu:

    <extension point="org.eclipse.ui.menus">
        <menuContribution locationURI="menu:help">
            <command commandId="org.eclipselabs.p2.rcpupdate.install" style="push"/>
            <command commandId="org.eclipselabs.p2.rcpupdate.update" style="push"/>
        </menuContribution>
    </extension>
  4. Add a package import to org.eclipselabs.p2.rcpupdate.utils to the com.example.mail plug-in:

  5. Include the update check on application startup by calling P2Util.checkForUpdates() in your ApplicationWorkbenchAdvisor class:

    public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {
        
        // ...
        
        @Override
        public void preStartup() {
            P2Util.checkForUpdates();
        }
        
    }

Feature / product setup

  1. p2 will only install features, so create a new feature com.example.mail.app for the mail application. This feature will contain all the main application plug-ins, so add the plug-in com.example.mail to this feature:

  2. Add org.eclipse.rcp and org.eclipselabs.p2.rcpupdate to the list of included features:

  3. Create a new product named mail.product using File > New > Plug-in Development > Product Configuration:

  4. Open the product. Check that it has a version number like ‘1.0.0.qualifier’ and that the ID is empty (an ID will be generated automatically. Please note: If you enter an ID here, make sure it’s different from the product ID!). Change the product to be based on features:

  5. Add the feature com.example.mail.app to the product dependencies and delete the version number (this means it should use the newest version available - this is new in Eclipse 3.6, see Bug 279465 - no feature version should imply “0.0.0”):

  6. Note: We will export the application in a minute. I’ll refer to the folder we will export to as export/. The export will create two subfolders, export/repository/ containing a p2 repository for updating, and one folder with the application. We need to configure p2 to get the application updates from the repository, so you need to plan ahead the location of the repository folder. If you have a HTTP server somewhere, you can upload the repository and use a http:// URL; a local file:// URL will work as well.

  7. Create a p2.inf file in the plug-in project that contains the product and configure either a file:// or a http://-repository. Please make sure that you get the syntax right or copy from http://gist.github.com/551240):

    instructions.configure=\
      addRepository(type:0,location:file${#58}/c:/export/repository/);\
      addRepository(type:1,location:file${#58}/c:/export/repository/);
    
    instructions.configure=\
      addRepository(type:0,location:http${#58}//localhost:1234/repository/);\
      addRepository(type:1,location:http${#58}//localhost:1234/repository/);

    (type configures the repository type, 0 is a metadata repository, 1 is an artifact repository - you need to configure both. ${#58} in the URL expresses a colon. You can read more about the p2 metadata instructions here: p2 Touchpoint Instructions)

  8. Launch the product from the product configuration. If you have started the product already, delete the existing run configuration before launching the product again (the changed dependencies are applied to the run configuration only when a new run configuration is created):

  9. Check that the application launches and that the menu items are shown correctly (it’s normal that they don’t work yet, as applications are not managed by p2 when started using a run configuration from Eclipse - by the way, since Eclipse 3.6 there is an option for that, see Run Configuration > Configuration > Software Installation > Support software installation in the launched application).

  10. Export the product using File > Export > Eclipse product to your export/ folder. Make sure to check Generate metadata repository:

  11. Have a look at the generated export folder - you should find a repository folder and a folder mail. Move mail to mail_user to simulate installing the app at the user’s computer (the folder will cause a conflict upon re-exporting if you don’t rename it because the export will only update the repository, but not the product). Launch the exported application:

Installing additions using p2

Lets try to install additions to the Mail application.

As an example, let’s assume we want to supply users of the mail application with some e-mail protection and safety features (like a spam filter or phising protection). Lets assume we decided that users of the mail application can install these features additionally, they are not included in the default distribution of the Mail application or are even provided by a 3rd party.

  1. Create a new plug-in project com.example.mail.protection.spamfilter. Don’t create a rich client application and choose the template Hello, World Command to create a plug-in that adds a single command to the menu and toolbar of the application.

  2. Add the spam filter plug-in to your run configuration and check that the UI contribution works as expected:

  3. The p2 update UI only works for installing and updating features. So we have to create a feature that contains the additional plug-in. Create a new feature com.example.mail.protection and add the spamfilter plug-in to the feature:

  4. Create a new update site project mail_repository. Optionally click generate a web page listing:

  5. Create a new category Mail Additions and add the com.example.mail.protection feature to it:

    Note: Features to be installed by the user need to have a category. By default, the p2 UI shows only features that have a category assigned. Features without a category are meant to be technical and not to be installed by the user directly (like the RCP feature). If we don’t create a category for the repository, the user would have to uncheck Group items by category in the update dialog to see the feature.

  6. Click Build All to build the feature and add the feature and plug-in to the repository (by the way, we could also use Export > Deployable features to do the export, but the update site project is a bit more convenient).

  7. Have a look at the files in the mail_repository. This is a p2 repository/p2 software site that can be used to install updates and new features. It contains the feature and plug-in jars and metadata about the repository in artifacts.jar and content.jar:

  8. Optional: upload the repository files to some HTTP server so that they are reachable using a http:// URL.

  9. Start the mail application from mail_user. Choose Help > Install New Software... and enter the repository URL or choose a local file using Add...:

  10. Install the Mail Protection feature. You will be asked about installing unsigned bundles and about restarting the application:

    Depending on the installed features a restart might be necessary or not - p2 cannot know. If you just added a menu item, you can count on the dynamic nature of the RCP workbench and just go without a restart. You will see the added menu items immediately:

p2 concepts

What just happened is that you used the p2 UI to install additions from a repository. These additions (or more general, everything that can be installed using p2), is called an installable unit (IU). These installable units are contained in so called metadata repositories which refer to actual files in an artifact repository.

Have a look into the metadata files artifacts.jar and content.jar to learn about the general nature of p2 repositories.

artifacts.xml in artifact.jar is just a list of all the files (artifacts) in the repository and metadata like file size, for example:

<artifact classifier='osgi.plug-in' id='com.example.mail' version='1.0.9.200910242009'>
    <properties size='2'>
        <property name='artifact.size' value='4096'/>
        <property name='download.size' value='103133'/>
    </properties>
</artifact>

content.xml in content.jar is a list of installable units with their name, capabilities and a description of what the unit provides and requires. This is a very general description, as p2 is meant to be able to provision everything, not only features or plug-ins. For example:

<unit id='com.example.mail.spamfilter' version='1.0.1.200910242136'>
    <update id='com.example.mail.spamfilter' range='[0.0.0,1.0.1.200910242136)' severity='0'/>
    <properties size='2'>
        <property name='org.eclipse.equinox.p2.name' value='Spamfilter'/>
        <property name='org.eclipse.equinox.p2.provider' value='EXAMPLE'/>
    </properties>
    <provides size='3'>
        <provided namespace='org.eclipse.equinox.p2.iu' name='com.example.mail.spamfilter' version='1.0.1.200910242136'/>
        <provided namespace='osgi.plug-in' name='com.example.mail.spamfilter' version='1.0.1.200910242136'/>
        <provided namespace='org.eclipse.equinox.p2.eclipse.type' name='plug-in' version='1.0.0'/>
    </provides>
    <requires size='2'>
        <required namespace='osgi.plug-in' name='org.eclipse.ui' range='0.0.0'/>
        <required namespace='osgi.plug-in' name='org.eclipse.core.runtime' range='0.0.0'/>
    </requires>
    <artifacts size='1'>
        <artifact classifier='osgi.plug-in' id='com.example.mail.spamfilter' version='1.0.1.200910242136'/>
    </artifacts>
    <touchpoint id='org.eclipse.equinox.p2.osgi' version='1.0.0'/>
        <touchpointData size='1'>
            <instructions size='1'>
                <instruction key='manifest'>
                    <!-- manifest omitted here -->
                </instruction>
            </instructions>
        </touchpointData>
    </touchpoint>
</unit>

The idea behind these metadata repositories is that p2 can reason (like resolving dependencies) without downloading the actual artifacts. So when PDE exports deployable features or plug-ins, all the plug-in dependencies are written down in the metadata repository in this very general format.

The installation of installable units is a fairly complex process that is conducted by several p2 components. There is a p2 planner component that reasons about the necessary steps to perform a p2 operation like an installation. These steps are carried out by the p2 engine. Planner and engine are directed by the director component. Mostly you can use p2 as a black box, but sometimes it’s required to dig deeper into these concepts - have a look at the p2 concepts to learn more about the general p2 architecture.

This additional complexity yields some interesting features. For example, a p2 installation is capable to revert itself to previous installation states (see Chris Aniszczyks blog post for more information about this: Reverting Changes in an Eclipse Installation using p2)

Updating the feature

Let’s assume we improved the spam filter plug-in to combat the latest developments from the spam industry and want to provide our users with an update:

  1. Do some visible change to the spam filter plug-in, like changing some text.

  2. Increment the version number of the plug-in and the feature. Hint: Have a look at the Eclipse version numbering scheme to learn how version numbers for Eclipse projects are handled.

  3. Add the feature again to the category of the update site project and click Build. This will add the new plug-in/feature to the existing repository (you could also delete the old one, but the general recommendation is to keep track of all published versions in one repository and never delete something that has already been published):

  4. Click Help > Check for Update in the application or restart the application to update to the newest version (you might have to restart because of p2 caching the metadata).

Updating the application

For now we just provided additions and updates for these additions. But how about the main application? Let’s see how this can be updated using p2.

  1. Do some visible change in the mail plug-in so that you can check if the product was updated correctly (like adding some String to a label in the View class).

  2. Increment the version number of your plug-in, the feature and the product (for real products you should organize some strategy that makes sure that version numbers are incremented when doing releases).

  3. Warning: I told you to rename export/mail to mail_user before. This is important now. You should have export/repository, but not export/mail. Keeping the repository is ok, the export will update it, but if the mail application from the previous export would still exist there, you would get a conflict.

  4. Export the deployable product again. The repository will get updated with the new versions.

  5. Click Help > Check for Update in the application or restart the application to update to the newest version.

Checking out the examples

I highly recommend to have a look at the p2 examples. You can check them out from the Eclipse Runtime Project at :pserver:anonymous@dev.eclipse.org:/cvsroot/rt. Just paste the connect string into the CVS repository view and go to org.eclipse.equinox/p2/examples:

Thanks

Thanks to Susan F. McCourt for providing the very helpful Adding Self-Update to an RCP Application wiki page and example code that was used as starting point for this tutorial!

More information

About the author

Ralf Ebert Ralf Ebert is an independent software developer and trainer for Mac OS X and iOS. He makes the Page Layers, TakeOff and Straight ahead apps and conducts iOS trainings and Git trainings in Germany.