NOW Hiring

Quick links

This tutorial describes how to integrate update functionality into Eclipse RCP applications using Eclipse p2.

1. Prerequisites

This tutorial assumes that you have a basic understanding of development for the Eclipse platform.

Please see http://www.vogella.com/tutorials/EclipseRCP/article.html and http://www.vogella.com/tutorials/EclipsePlugin/article.html for an introduction. It also assumes that you know how to create Eclipse feature projects. See http://www.vogella.com/tutorials/EclipseFeatureProject/article.html for an introduction into features.

2. Eclipse application updates with p2

2.1. Eclipse application updates

The Eclipse platform provides an installation and update mechanism called Eclipse p2 (short: p2). This update mechanism allows you to update Eclipse applications and to install new functions.

The update and installation of functions with p2 is typical based on feature projects (short: features). It is possible to update complete products or individual features. In the terminology of p2 these features are installable units.

2.2. Creating p2 update sites

Installable units can be grouped into a p2 repository. A repository is defined via its URI and can point to a local file system or to a web server. A p2 repository is also called update site.

During the export of an Eclipse application you can select the Generate p2 repository option in the Eclipse product export dialog. This option is highlighted in the following screenshot.

Selection in the export wizard for the metadata repository creation

If you select this option, an update site iscreated in a sub-folder called repository of the export directory.

Typically this directory is copied to a web server so users can install new functionality or upgrades using the p2 mechanism.

The Eclipse update mechanism also supports file based update sites. File based update sites are useful for testing the update functionality.

An update site contains an artifact and a metadata repository. == Required plug-ins for updates

The following table lists the core plug-ins and the feature which provide the non-user interface functionality of p2.

Table 1. Table Eclipse p2 plug-ins and features
Plug-in Description

org.eclipse.equinox.p2.core

Core p2 functionality.

org.eclipse.equinox.p2.engine

The engine carries out the provisioning operation.

org.eclipse.equinox.p2.operations

Layer over the core and engine API to describe updates as an atomic install.

org.eclipse.equinox.p2.metadata.repository

Contains the definition of p2 repositories.

org.eclipse.equinox.p2.core.feature

Feature containing the p2 bundles.

To use the Eclipse update API you need to include these plug-ins as dependencies to your manifest file. And you must add the feature to your product configuration file.

When the Eclipse installation is used as target platform, these required plugins should be already available. Otherwise they can be installed or added to a target definition file like this:

install p2

3. Updating Eclipse RCP applications

To update your Eclipse 4 application you use the p2 API. The following code shows an example handler which performs an update of a complete Eclipse RCP application. The method annotated with @Execute configures the update operation and runs it.

package com.example.e4.rcp.todo.handlers;

import java.net.URI;
import java.net.URISyntaxException;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.ui.di.UISynchronize;
import org.eclipse.e4.ui.workbench.IWorkbench;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.operations.ProvisioningJob;
import org.eclipse.equinox.p2.operations.ProvisioningSession;
import org.eclipse.equinox.p2.operations.UpdateOperation;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;

// Require-Bundle: org.eclipse.equinox.p2.core|engine|operation|metadata.repository
// Feature: org.eclipse.equinox.p2.core.feature
//
// !!! do not run from within IDE, the update only works in an exported product !!!
//
public class UpdateHandler {
        private static final String REPOSITORY_LOC =
                        System.getProperty("UpdateHandler.Repo", "http://localhost/repository");

        @Execute
        public void execute(final IProvisioningAgent agent, final Shell shell,
                        final UISynchronize sync, final IWorkbench workbench,
                        final Logger logger) {
                Job j = new Job("Update Job") {
                        @Override
                        protected IStatus run(final IProgressMonitor monitor) {
                                return checkForUpdates(agent, shell, sync, workbench, monitor,
                                                logger);
                        }
                };
                j.schedule();
        }

        private IStatus checkForUpdates(final IProvisioningAgent agent,
                        final Shell shell, final UISynchronize sync,
                        final IWorkbench workbench, IProgressMonitor monitor, Logger logger) {

                // configure update operation
                final ProvisioningSession session = new ProvisioningSession(agent);
                final UpdateOperation operation = new UpdateOperation(session);
                configureUpdate(operation, logger);

                // check for updates, this causes I/O
                final IStatus status = operation.resolveModal(monitor);

                // failed to find updates (inform user and exit)
                if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) {
                        showMessage(shell, sync);
                        return Status.CANCEL_STATUS;
                }

                // run installation
                final ProvisioningJob provisioningJob = operation
                                .getProvisioningJob(monitor);

                // updates cannot run from within Eclipse IDE!!!
                if (provisioningJob == null) {
                        logger.error("Trying to update from the Eclipse IDE? This won't work!");
                        return Status.CANCEL_STATUS;
                }
                configureProvisioningJob(provisioningJob, shell, sync, workbench);

                provisioningJob.schedule();
                return Status.OK_STATUS;

        }

        private void configureProvisioningJob(ProvisioningJob provisioningJob,
                        final Shell shell, final UISynchronize sync,
                        final IWorkbench workbench) {

                // register a job change listener to track
                // installation progress and notify user upon success
                provisioningJob.addJobChangeListener(new JobChangeAdapter() {
                        @Override
                        public void done(IJobChangeEvent event) {
                                if (event.getResult().isOK()) {
                                        sync.syncExec(new Runnable() {

                                                @Override
                                                public void run() {
                                                        boolean restart = MessageDialog
                                                                        .openQuestion(shell,
                                                                                        "Updates installed, restart?",
                                                                                        "Updates have been installe. Do you want to restart?");
                                                        if (restart) {
                                                                workbench.restart();
                                                        }
                                                }
                                        });

                                }
                                super.done(event);
                        }
                });

        }

        private void showMessage(final Shell parent, final UISynchronize sync) {
                sync.syncExec(new Runnable() {

                        @Override
                        public void run() {
                                MessageDialog
                                                .openWarning(parent, "No update",
                                                                "No updates for the current installation have been found.");
                        }
                });
        }

        private UpdateOperation configureUpdate(final UpdateOperation operation,
                        Logger logger) {
                // create uri and check for validity
                URI uri = null;
                try {
                        uri = new URI(REPOSITORY_LOC);
                } catch (final URISyntaxException e) {
                        logger.error(e);
                        return null;
                }

                // set location of artifact and metadata repo
                operation.getProvisioningContext().setArtifactRepositories(
                                new URI[] { uri });
                operation.getProvisioningContext().setMetadataRepositories(
                                new URI[] { uri });
                return operation;
        }
}

4. Exercise: Application update

4.1. Target

Create a sample application, where features can be updated by using an update handler.

4.2. Exercise preparation - Create a update enabled RCP application

Create an Eclipse 4 RCP project called com.example.p2.core based on the Eclipse 4 wizard.

new e4 application wizard

And a feature called com.example.p2.feature which contain the com.example.p2.core project.

new feature wizard

4.3. Ensure the exported product works

Export or run your build system to create a deployable application. Ensure that the exported application starts correctly outside of the Eclipse IDE.

4.4. Add the p2 feature to the product

Add the following features to the list of the Contents tab.

  • com.exmaple.p2.feature

  • org.eclipse.e4.rcp

  • org.eclipse.equinox.p2.core.feature

After adding these feature the Add Required button needs to be clicked so that the required features listed below are available as well.

product features

4.5. Add dependencies

Add the plug-in dependencies from [p2intro_plugins] to your com.example.p2.core plug-in.

app plugin p2 dependencies

4.6. Create a user interface

Create a new Update menu entry in your application. Implement a new handler for this menu entry.

package com.example.p2.core.handlers;

import java.net.URI;
import java.net.URISyntaxException;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.ui.di.UISynchronize;
import org.eclipse.e4.ui.workbench.IWorkbench;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.operations.ProvisioningJob;
import org.eclipse.equinox.p2.operations.ProvisioningSession;
import org.eclipse.equinox.p2.operations.UpdateOperation;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;

public class UpdateHandler {
        private static final String REPOSITORY_LOC = System.getProperty("UpdateHandler.Repo",
                        "file:////home/simon/updatesite/repository");

        @Execute
        public void execute(final IProvisioningAgent agent, final Shell shell, final UISynchronize sync,
                        final IWorkbench workbench) {
                Job updateJob = new Job("Update Job") {
                        @Override
                        protected IStatus run(final IProgressMonitor monitor) {
                                return checkForUpdates(agent, shell, sync, workbench, monitor);
                        }
                };
                updateJob.schedule();
        }

        private IStatus checkForUpdates(final IProvisioningAgent agent, final Shell shell, final UISynchronize sync,
                        final IWorkbench workbench, IProgressMonitor monitor) {

                // configure update operation
                final ProvisioningSession session = new ProvisioningSession(agent);
                final UpdateOperation operation = new UpdateOperation(session);
                configureUpdate(operation);

                // check for updates, this causes I/O
                final IStatus status = operation.resolveModal(monitor);

                // failed to find updates (inform user and exit)
                if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) {
                        showMessage(shell, sync);
                        return Status.CANCEL_STATUS;
                }

                // run installation
                final ProvisioningJob provisioningJob = operation.getProvisioningJob(monitor);

                // updates cannot run from within Eclipse IDE!!!
                if (provisioningJob == null) {
                        System.err.println("Trying to update from the Eclipse IDE? This won't work!");
                        return Status.CANCEL_STATUS;
                }
                configureProvisioningJob(provisioningJob, shell, sync, workbench);

                provisioningJob.schedule();
                return Status.OK_STATUS;

        }

        private void configureProvisioningJob(ProvisioningJob provisioningJob, final Shell shell, final UISynchronize sync,
                        final IWorkbench workbench) {

                // register a job change listener to track
                // installation progress and notify user upon success
                provisioningJob.addJobChangeListener(new JobChangeAdapter() {
                        @Override
                        public void done(IJobChangeEvent event) {
                                if (event.getResult().isOK()) {
                                        sync.syncExec(new Runnable() {

                                                @Override
                                                public void run() {
                                                        boolean restart = MessageDialog.openQuestion(shell, "Updates installed, restart?",
                                                                        "Updates have been installed. Do you want to restart?");
                                                        if (restart) {
                                                                workbench.restart();
                                                        }
                                                }
                                        });

                                }
                                super.done(event);
                        }
                });

        }

        private void showMessage(final Shell parent, final UISynchronize sync) {
                sync.syncExec(new Runnable() {

                        @Override
                        public void run() {
                                MessageDialog.openWarning(parent, "No update",
                                                "No updates for the current installation have been found.");
                        }
                });
        }

        private UpdateOperation configureUpdate(final UpdateOperation operation) {
                // create uri and check for validity
                URI uri = null;
                try {
                        uri = new URI(REPOSITORY_LOC);
                } catch (final URISyntaxException e) {
                        System.err.println(e.getMessage());
                        return null;
                }

                // set location of artifact and metadata repo
                operation.getProvisioningContext().setArtifactRepositories(new URI[] { uri });
                operation.getProvisioningContext().setMetadataRepositories(new URI[] { uri });
                return operation;
        }

}

Ensure to adjust the REPOSITORY_LOC constant in this code to the actual p2 udpate site location on your file system.

4.7. Enter a version in your product configuration file

Ensure that you have entered a version number for the product and to append the ".qualifier" suffix to the product version.

Enter version in the product configuration files

4.8. Create the initial product export

Build your product via the Tycho build system on the command line. If you do not want to use Tycho, you can also export your product via the Eclipse Product export link on the Overview tab of the product file.

Do not choose the REPOSITORY_LOC path as destination, this path is later used to place the update site in.

4.9. Start the exported application and check for updates

Start your exported application from the export directory. Check for updates by invoking your update handler. Your handler should give you the feedback that no updates are available.

4.10. Make a change and export the product again

Change a (visible) label in your application, e.g., remove the generated top trimbar. Increment the product version on the overview page of the product editor to indicate a functional change.

Run the Tycho build again or export your product again via the UI. Use a different export folder than you did before. Y ou don’t want to override the existing exported application. Make sure to select the Generate metadata repository option on the export dialog. Do not export to the REPOSITORY_LOC< path.

In the new export folder you find a sub-folder called repository. Copy this sub-folder to the REPOSITORY_LOC path.

4.11. Update the application

Start your exported application and check again for updates via your menu entry. If everything was implemented correctly, your handler should report that updates are available. Install these updates and restart the application.

TIP:In case your handler does not find the update, restart your application to clear the caches of p2. p2 caches the meta information of an update side via a weak HashMap. So as long as the application is running and it has enough memory the information is cached.

Verify that all updates have been applied.

5. Adding p2 update sites

If you place a p2.inf file beside your product configuration file you can add update sites to your product. The following listing is an example for that.

instructions.configure=\
  addRepository(type:0,location:http${#58}//download.eclipse.org/eclipse/updates/4.5,name:Eclipse Mars Update Site);\
  addRepository(type:1,location:http${#58}//download.eclipse.org/eclipse/updates/4.5,name:Eclipse Mars Update Site);
Add update site via product

6. Custom install actions (touchpoints) in p2

The Eclipse p2 functionality invokes actions (touchpoints) when an installable unit is installed, configured, or uninstalled. For example, it is possible to unzip or copy artifacts or to set file permissions.

The Eclipse p2 API uses install actions for this, called touchpoints. By default, is supports two touchpoint types, native and OSGi types. Native touchpoint actions are not directly related to Eclipse (such as file commands) while the OSGi ones are Eclipse specific.

It is possible to add custom touchpoint actions via the org.eclipse.equinox.p2.engine.action extension point. The class which implements this touchpoint action must implement ProvisioningAction.

p2 must be instructed to run this touchpoint. For this, you need to specify the action as requirement via a MetaRequirements section in the META-INF/p2.inf file of the corresponding plug-in. It must also define how to invoke the execution of the custom touchpoint.

7. p2 composite repositories

It is possible to create p2 composite repositories. Such a repository wraps the information about other repositories. If a client connects to such an update site, the other update sites are contacted and the user can install features from the other update site.

To build such a composite repository you have to create two files. The first file must be called compositeContent.xml. The second file must be called compositeArtifacts.xml. The following example demonstrates the content of these files.

The following listing is an example for an compositeContent.xml file.

<?xml version='1.0' encoding='UTF-8'?>
<?compositeMetadataRepository version='1.0.0'?>
<repository name='&quot;oclipse development environment&quot;'
        type='org.eclipse.equinox.internal.p2.metadata.repository.CompositeMetadataRepository'
        version='1.0.0'>
        <properties size='1'>
                <property name='p2.timestamp' value='1243822502499' />
        </properties>
        <children size='2'>
            <child location='http://download.eclipse.org/releases/luna/'/>
            <child location='http://dl.bintray.com/vogellacompany/eclipse-preference-spy/'/>
        </children>
</repository>

The following listing is an example for a fitting compositeArtifacts.xml file.

<?xml version='1.0' encoding='UTF-8'?>
<?compositeArtifactRepository version='1.0.0'?>
<repository name='&quot;voclipse development environment&quot;'
        type='org.eclipse.equinox.internal.p2.artifact.repository.CompositeArtifactRepository'
        version='1.0.0'>
        <properties size='1'>
                <property name='p2.timestamp' value='1243822502440' />
        </properties>
        <children size='2'>
                <child location='http://download.eclipse.org/releases/luna/' />
                <child location='http://dl.bintray.com/vogellacompany/eclipse-preference-spy/' />
        </children>
</repository>

8. About this website

9. Eclipse p2 update resources

9.1. vogella GmbH training and consulting support

TRAINING SERVICE & SUPPORT

The vogella company provides comprehensive training and education services from experts in the areas of Eclipse RCP, Android, Git, Java, Gradle and Spring. We offer both public and inhouse training. Whichever course you decide to take, you are guaranteed to experience what many before you refer to as “The best IT class I have ever attended”.

The vogella company offers expert consulting services, development support and coaching. Our customers range from Fortune 100 corporations to individual developers.

Copyright © 2012-2016 vogella GmbH. Free use of the software examples is granted under the terms of the EPL License. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.

See Licence.