Eclipse RCP Schulung
15. - 19. März 2010
iPhone Schulung
26. - 29. April 2010
git Schulung
27. - 28. Mai 2010
24.10.2009

Tutorial: p2 updates for Eclipse RCP applications

In this tutorial I’ll show how to use the Equinox p2 provisioning system for updating Eclipse RCP applications. I’ll use the RCP mail example as starting point and:

I assume you’re familiar with OSGi and Eclipse RCP development.

This tutorial is part of the updated training materials for my upcoming Eclipse RCP course in Berlin (in German).

Creating the example projects

  1. Create a new plug-in project com.example.mail. Choose to create a rich client application and select the RCP Mail Template:

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

  3. Create a new feature com.example.mail.core. This feature will contain all the main application plug-ins. Add the plug-in com.example.mail to this feature:

  4. Add org.eclipse.rcp to the list of included features:

  5. Open the product and change it to a feature based configuration:

  6. Add the feature com.example.mail.core to the product dependency list:

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

  8. The application should start now:

  9. Export the product using File > Export > Eclipse product:

  10. Launch the exported application from the generated folder:

Adding the p2 UI to the application

p2 comes with a fully functional UI that you can use out of the box for RCP applications. This is the same UI you probably know from installing updates to the Eclipse IDE. This UI might not be suitable for end users of business applications, but let’s use it as a starting point for the moment.

  1. All the plug-ins required for the p2 UI are grouped together in the feature org.eclipse.equinox.p2.user.ui. Add this feature to the list of included features for the com.example.mail.core feature.

  2. org.eclipse.equinox.p2.user.ui contains all required bundles for updating with p2, except org.apache.commons.logging. I guess this was done intentionally so you can choose the logging bundle for your application yourself. Because of this, you have to add org.apache.commons.logging as required plug-in to the feature as well. Otherwise you will get an error when starting the application:

    Missing requirement: Apache Commons Httpclient 3.1.0.v20080605-1935
    (org.apache.commons.httpclient 3.1.0.v20080605-1935) requires
    'package org.apache.commons.logging [1.0.4,2.0.0)' but it could not be found
  3. Run the application from the IDE and check that you have the two additional p2 menu items in the help menu. These are added automatically by the p2 UI bundles:

  4. Click on one of the update menu items. The application will let you know that it’s not configured for software updates. This is because installations started from the IDE are not managed by p2:

  5. Export your product again. To stay away from troubles caused by stale files, please always use a fresh folder as export destination.

Installing additions using p2

In the next step, I’ll show how to allow the users of your application to install additions. This is quite simple and serves as first step into the world of provisioning with p2.

As 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 our e-mail client application can install these features additionally.

  1. Create a new bundle com.example.mail.spamfilter.

  2. Do some arbitrary contribution to the UI of the mail client so you can see that the plug-in was activated after installing it. You can also implement a fully functional spam filter for the RCP sample application, but that’s left as additional homework. For example, just contribute a menu item to the file menu:

    <extension point="org.eclipse.ui.commands">
        <command id="com.example.mail.spamfilter.activate"
            name="Activate SPAM filter"/>
    </extension>
    <extension point="org.eclipse.ui.menus">
        <menuContribution locationURI="menu:file">
            <command commandId="com.example.mail.spamfilter.activate" style="push"/>
        </menuContribution>
    </extension>
    
  3. Add the spam filter plug-in to your run configuration and check that the UI contribution works as expected:

  4. The p2 update UI is intended to be used to install and update features. So we have to create a feature that contains the additional bundle. So, create a new feature com.example.mail.protection. Add the spamfilter bundle to the feature:

    Features to be installed by the user can be grouped into categories. 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). Because of this you would have to uncheck Group items by category in the update dialog to see the feature:

    Categories are assigned in a definition file that can be created using File > New > Other > Category Definition. This is just a definition file that is used by the PDE tooling to assign categories to the exported features, it is not to be included in the exported features. You can create a category definition in your application bundle, for example:

  5. Export the com.example.mail.protection feature using File > Export > Deployable features. Check Generate metadata repository so a repository from which the features can be installed is generated. Also select Categorize repository and specify your category definition file:

    Have a look at the exported folder. This is a repository that can be used to install updates and new features. It contains the binary feature and plug-in jars and metadata about the repository in artifacts.jar and content.jar:

    I recommend configuring a web server that serves the repository files so you can try installing from the repository like a real user would. This is optional, you can also install and update from a local folder. Configuring a real web server is left as an exercise to the reader, I used an apache2 to serve the repository at http://localhost:1234/:

  6. Start the exported mail application. Choose Help > Install New Software... and enter the repository URL (you can also enter a file: URL to point p2 to a local repository folder):

  7. You can install the protection feature to the application now. You will be asked to restart the application:

    Depending on the installed features a restart might be neccessary 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 a installable unit (IU). These installable units are contained in so called metadata repositories. So when you exported your feature, a metadata repository was created, containing all these installable units.

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.bundle' 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 bundles. 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.bundle' name='com.example.mail.spamfilter' version='1.0.1.200910242136'/>
        <provided namespace='org.eclipse.equinox.p2.eclipse.type' name='bundle' version='1.0.0'/>
    </provides>
    <requires size='2'>
        <required namespace='osgi.bundle' name='org.eclipse.ui' range='0.0.0'/>
        <required namespace='osgi.bundle' name='org.eclipse.core.runtime' range='0.0.0'/>
    </requires>
    <artifacts size='1'>
        <artifact classifier='osgi.bundle' 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 bundle 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 (Chris Aniszczyk blogged about this recently: Reverting Changes in an Eclipse Installation using p2)

Updating

Let’s assume we beefed up the spam filter bundle to combat the latest developments from the spam industry and want to provide our users with an update:

  1. Increment the version number of the bundle and the feature. We might follow the Eclipse version numbering scheme or just use 1.0.1.

  2. Export the feature again into the same repository folder. This will add the new bundle/feature to the existing repository. You could also delete the old one, but I recommend keeping track of all published versions in one repository (you might even track the repository using a version control system so you have an archive of all versions ever published).

  3. Start the application and click Help > Check for Update. You can install available updates from all known repository locations:

Add self-update to 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. Have a look at the exported product folder. You’ll see that a repository was generated for the product as well. This can be used to update the application. We just need to tell p2 where to get the updates from. Repository locations can be configured using p2 metadata. We can make the PDE export such metadata by adding a file p2.inf to the bundle that contains the product. We can use the addRepository property to configure default repository locations:

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

    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 (beautiful, isn’t it ;). You can read more about these p2 metadata instructions here: p2 Touchpoint Instructions.

  2. Delete the old exported folder and export the deployable product again. Start the application and check for updates. It should tell you there are no updates available.

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

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

  5. Keep the old exported application folder, but rename it to something like mail_old. Also keep the repository. Export the deployable product so that the repository folder is updated with the artifacts.

  6. Start the newly exported application and check that it works.

  7. Start the old version of the application from mail_old. Use Help > Check for updates. It should offer you to ínstall the new version:

Updating on application startup

The Eclipse wiki states “Sometimes the simplest UI is no UI” and gives a helpful example of how to automatically update the application on startup (see Adding Self-Update to an RCP Application). There is a P2Util class in the Eclipse p2 examples which does everything necessary to update a p2 application in a headless fashion.

Unfortunately, Eclipse bug #282333 puts a spoke in our wheels here. I had to disable progress monitoring to use the P2Util class with Eclipse 3.5. I copied the example code into a separate bundle and provided it on github so you can use the code out of the box for this tutorial.

  1. Download org.eclipse.equinox.p2.autoupdate. Import the bundle into your workspace using File > Import > General > Existing Projects into workspace.

  2. Add a dependency to org.eclipse.equinox.p2.autoupdate in the manifest of com.example.mail.

  3. Activate automatic update in the ApplicationWorkbenchAdvisor of the mail application:

    public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {
    
        // ...
    
        @Override
        public void preStartup() {
            // Check for updates before starting up.
            // If an update is performed, restart.
            if (P2Util.checkForUpdates())
                PlatformUI.getWorkbench().restart();
        }
    
    }
    
  4. Increment the version numbers, export the product as before and install the update manually one last time using Help > Check for updates. You should see the progress monitor when the application is restarted:

  5. Increment the version numbers and export a new version again. Restart the application - the product should be updated automatically now.

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 Franklin McCourt for providing the very helpful Adding Self-Update to an RCP Application wiki page and example code that was used as a starting point for this tutorial!

More information

Martin Dilger, 25. Oktober, 19:41 Uhr

Thank you very much for this tutorial, very very helpful!!
See you on W-Jax?

Bye

Martin

Ralf Ebert, 25. Oktober, 21:24 Uhr

Thanks, I'm glad to hear you like the tutorial.
Sorry, I decided to visit ESE and DEVOXX conference this year...

Matthias, 27. Oktober, 15:20 Uhr

Thanks, once more a very understandable tutorial.
while autoupdating on startup i encounter the problem, that a "File that was copied to backup could not be deleted: C:\Temp\deploy\eclipse\jre\lib\jce.jar"
is there a way to configure that the update process doesn't include the deletion of the JRE?
Bye, Matthias

Philipp Kursawe, 28. Oktober, 09:32 Uhr

Thanks Ralf for de-mystifying the horrors of p2 a bit more.
Now, why the p2.inf use "type:0" and "type:1" instead of "type:content" and "type:artifact" or "type:c" and "type:a" and this awful cryptic colon is totally beyond me. Ok, if PDE provides tooling for it in the future. But bad design anyway. I also not get why they keep mixing formats. Why not use XML consistently inside Eclipse? The p2.inf seems like it tries to be a script file. Why not simply:
repository.content=http://
repository.artifact=http://
even multiple times possible by using a counter.
Well, I guess, p2 was never meant to obey: KISS.

Hussein MHANNA, 28. Oktober, 13:09 Uhr

Thank you very much for this tutorial

but i have some problems when i add the org.eclipse.equinox.p2.user.ui feature and the org.apache.commons.logging plugin the two additional p2 menu items in the help menu does not displayed

Hussein

Joachim, 02. November, 08:12 Uhr

Hi.

First of all: great tutorial.
Maybe it's me or it's Windows 7, but I tried your tutorial under Vista 64bit and it worked just like it should. Then I switched to Windows 7 and now I can't get the automatic update to work. I first have to insert an update site manually (after first start of app) and on the second start, the program tries to autoconnect to the update site on startup. Any ideas?

Ralf Ebert, 02. November, 11:37 Uhr

Maybe you could try to do a fresh export, duplicate the folder and test it under Windows XP and Windows 7 with a fresh installation both times. If it works in XP and not in 7, can you please file a bug in bugs.eclipse.org attaching the example product?

Joachim, 02. November, 12:48 Uhr

I tried the following:
1. exported product under XP ... works under XP and Windows 7
2. exported exact same product (just copied the workspace) under Windows 7 ... neither worked under Windows 7 nor XP (tried it on a colleagues Windows 7 PC and it didn't work either)
As far as my humble knowledge goes, there should be some *.prefs files under <EclipseProductDir>/eclipse/p2/org.eclipse.equinox.p2.engine/profileRegistry/profile.profile/.data/.settings, but there is nothing until I manually add an update site.

=> will try to open a bug on bugs.eclipse.org

Noam, 04. November, 15:58 Uhr

This tutorial helped me so much...3 days I am struggling with building a featured based product with an updater, and after reading this, everything is clear ! Thanks !!

Gergo, 08. November, 05:07 Uhr

Wow, this looks useful - just scared away by the length a bit..
One thing I might have missed though: how do you run P2 standalone from the command line?
Also, is it true that you can't just dump bundle jars (and use -clean) to the plugin dir - what exactly do I run to pick them up?

So the tutorial seems doing a good job make P2 as understable as possible. Still, I agree with Philipp, the system looks too convoluted, indirect, and overengineered (almost like TPTP:) Any modern Linux distribution has a powerful yet intuitive package management system . Let's say Eclipse update support can't be as simple as Firefoxes, but does it need to be more complex than an OS?

Anyway, thanks for making it clearer!

Chris Lewold, 23. November, 15:47 Uhr

First of all thanks for this nice tutorial - really helps a lot!

Do you possibly have a tip on how to provide staging logic? To be more concrete:
we have several stages (like dev, test, production, ... ). We cannot rebuild the original client / update site everytime we move the code from one stage to the next.

Once the client for a given stage is installed it should always go to the correct install site. (e.g. dev clients should always go to our dev update site). Once the downloadable client code is moved from e.g. Stage "Dev" to "Test" it should refer to another update site ("test" in this case).
So we have to manage multiple clients (one for each stage), which refer to their correct update site.

The main problem I see is, that the update site is provided already during the build process.
Is there a way to change it once the build is done (during the staging process e.g. by updating a .property file, or as command line argument to the client).

We need this to be totally seamless - updates to the clients should happen automatically without any kind of user interaction.

Any tip would be great!
Chris

Ralf Ebert, 24. November, 00:27 Uhr

If the application knows by itself in which stage it is running in, you might be able to configure this using an API (not sure if there is one). Maybe there is also a way to change this for an already built p2 site - I'm not sure if the URL goes in the built jars or just in the p2 metadata. You might also ask in the 'eclipse.technology.equinox' newsgroup about this.

Chris Lewold, 24. November, 10:17 Uhr

Hi Ralf,
Yes, our application knows its stage - it also knows which the correct update site URL would be. It knows this both at "staging time" and at runtime of the application itself.

I simply don't know how to set it. Right now my best idea would be to patch the "org.eclipse.equinox.p2.*.repository.prefs" file in the "p2/org.eclipse.qeuinox.p2.engine/profileRegistry/profile.profile/.data/.settings" folder .... but somehow I don't have a good feeling doing that.

Another idea would be to use the "director" application for startup, as you can specify repositories as command line arguments there. I simply didn't use the director right now, and as the whole build/deploy/update process for RCP is rocket science with tons of pitfalls, I would like to avoid yet another new component in this area.

Thanks again for your tutorial - really helps in this area,
Chris

Tom, 27. November, 08:32 Uhr

Hello Ralf,
thanks a lot for this great tutorial!
I put the p2.inf file into the feature (not product) com.example.mail.core. But when I tried to export the product I got the error: no action found for: addRepository
To make it work I had to use the fully qualified action "org.eclipse.equinox.p2.touchpoint.eclipse.addRepository":

instructions.configure=\
org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:0,location:file${#58}///C${#58}/tom_local/repository/);\
org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:1,location:file${#58}///C${#58}/tom_local/repository/);

Thanks again!
Tom

Vitaly, 27. November, 13:35 Uhr

Thanks for the great tutorial, Ralf!

A quick question: what is the best source to of bundles required by your org.eclipse.equinox.p2.autoupdate plugin-in?

My product was based on RCP Runtime binaries package, and now I need to upgrade it in order to support headless auto-update.
Full "Eclipse for RCP/Plug-in Developers" package contains all that I need and does solve the problem, but in my opinion it is a bit too large and polluted with dependencies to be used as a target platform :)
Almost all of the required plug-ins can be found in equinox-p2-agent (available from
http://download.eclipse.org/equinox/drops/R-3.5.1-200909170800/index.php) but couple of them is still missing: org.eclipse.equinox.p2.extensionlocation and org.eclipse.equinox.p2.updatesite.

Is there some small pre-packaged set of plugins (other than full IDE) required by autoupdate?

Vitaly, 27. November, 13:41 Uhr

Tom,

I had the same issue:

Check the slash symbols in your config. My file that worked:

instructions.configure=\
org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:0,location:file${#58}/C${#58}/myProduct/);\
org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:1,location:file${#58}/C${#58}/myProduct/);

Also please make sure that p2.inf file is next to your .product file in the file system.

karthik, 01. Dezember, 09:07 Uhr

Thanks for the great tutorial made of the Eclipse Users...

I had problem when i am trying to launch the i cant get succeed even though i added required plug ins and all.. The error says "java.lang.ClassNotFoundException: org.eclipse.core.runtime.adaptor.EclipseStarter " like this.

Can you help me out in this?
And if your posting the sample on this it will be very useful for me and Eclipse users Pls...

Raja, 06. Dezember, 19:58 Uhr

Very good tutorial!

Is there any difference between creating an update site using Update Site Project and creating it using the Generate metadata repository?

Thanks.

Ralf Ebert, 07. Dezember, 10:56 Uhr

@Raja, Thanks. Update Site Project works as well, you get a p2 metadata repository generated when you build the site.

Marco, 14. Januar, 13:05 Uhr

Thank you for this excellent tutorial!

Matthias, 26. Januar, 15:06 Uhr

first, thanks for another great tutorial.
i'm facing a problem while updating the application on startup. nothing is updated automatically. when i try to manually update through "Check for updates" it displays the updates under "Available Updates" but i cant see an update running.
So, i tried to activate the ProgressBar to see if there is any progress running. Now i receive an error that the repositories containing the plugins can't be found.
I don't understand why i get a list of the available updates in the specified repositories and
simultaneously can't update from that repositories.

Peter Maurer, 30. Januar, 10:34 Uhr

Hi, somehow self-updating isn't working on mac os (using Eclipse 3.5.1).
Here are the steps I performed:

- exported the deployable product (installing & updating additions works)
- incremented version in plugin.xml, .product and feature.xml to 1.0.1
- readded the feature to the .product Dependencies (because of the version change of the feature)
- renamed the exported application folder (left the repository untouched)
- exported the product again
- started the product, still says "nothing to update"

Any hints?

I'm looking forward to your comments:

Schulungen

Eclipse RCP

Ralf Ebert | Blog | Eclipse RCP | Tutorial: p2 updates for Eclipse RCP applications