Sunday, November 17, 2013

Self updating Eclipse RCP application with p2

We recently moved to self updating for our RCP application.
There is a nice wiki page on self-updating with several ways to achieve it.
We opted for the headless one as we wanted no user interaction at all.
Simple requirement:
During startup look for updates, if found, update.

Unfortunately the P2Util class in the wiki didn't work right away for our case.
So we made some modifications to make it work for us.

public class P2Util {
    private static final String JUSTUPDATED = "justUpdated";
    private final static org.apache.log4j.Logger mLogger = org.apache.log4j.Logger
.getLogger(P2Util.class.getName());

    public static void autoupdate(long millisDelay) {
        final IProvisioningAgent agent = (IProvisioningAgent) ServiceHelper.getService(Activator.getDefault()
                .getBundle().getBundleContext(), IProvisioningAgent.SERVICE_NAME);
        if (agent == null) {
            LogHelper.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
                    "No provisioning agent found.  This application is not set up for updates."));
        }
        // XXX if we're restarting after updating, don't check again.
        final IPreferenceStore prefStore = Activator.getDefault().getPreferenceStore();
        if (prefStore.getBoolean(JUSTUPDATED)) {
            prefStore.setValue(JUSTUPDATED, false);
            return;
        }
        /*
         * We create a Job to run the update operation with a cancellable
         * dialog.
         */
        final Job job = new Job("Application Update") {

            @Override
            protected IStatus run(IProgressMonitor monitor) {
                IStatus updateStatus = update(agent, monitor);
                return updateStatus;
            }
        };
        job.setUser(true);
        /*
         * Hook after-update todos as a listener.
         */
        job.addJobChangeListener(new JobChangeAdapter() {
            public void done(IJobChangeEvent event) {
                IStatus updateStatus = event.getResult();
                if (updateStatus.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) {
                    // do nothing..
                }
                else if (updateStatus.getSeverity() != IStatus.ERROR) {
                    prefStore.setValue(JUSTUPDATED, true);
                    Display.getDefault().syncExec(new Runnable() {

                        @Override
                        public void run() {
                            PlatformUI.getWorkbench().restart();
                        }
                    });
                }
                else {
                    mLogger.debug(updateStatus.getMessage());
                }
            }
        });
        job.schedule(millisDelay);

    }

    private static IStatus update(IProvisioningAgent agent, IProgressMonitor monitor) {
        UpdateOperation operation = null;
        /*
         * Any update has to be done against a profile. We'll use the first
         * profile in profile registry under which all the installable units are
         * registered. It's a self hosting profile.
         */
        IProfileRegistry registry = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME);
        IProfile[] profiles = registry.getProfiles();
        IProfile profile = null;
        if (profiles.length > 0) {
            profile = profiles[0];
        }
        else {
            return new Status(IStatus.ERROR, "", "No profile found to update!");
        }

        /*
         * Query to find all installable units in the profile. These will be
         * searched in the update site for updates.
         */
        Collection<iinstallableunit> toUpdate = profile.query(
QueryUtil.createIUAnyQuery(),
            monitor).toSet();
        ProvisioningSession provisioningSession = new ProvisioningSession(agent);
        operation = new UpdateOperation(provisioningSession, toUpdate);
        operation.setProfileId(profile.getProfileId());
        SubMonitor sub = SubMonitor.convert(monitor, "Checking for Application updates...", 200);
        IStatus status = operation.resolveModal(sub.newChild(100));
        if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) {
            return status;
        }
        if (status.getSeverity() == IStatus.CANCEL)
            throw new OperationCanceledException();

        if (status.getSeverity() != IStatus.ERROR) {
            // More complex status handling might include showing the user what
            // updates
            // are available if there are multiples, differentiating patches vs.
            // updates, etc.
            // We simply update as suggested by the operation without any user
            // interaction.
            sub.setTaskName("Updating Application to latest version available..");
            ProvisioningJob job = operation.getProvisioningJob(sub);
            status = job.runModal(sub.newChild(100));
            if (status.getSeverity() == IStatus.CANCEL)
                throw new OperationCanceledException();
        }
        return status;

    }

}
We add our update site to p2.inf file of the feature.

instructions.configure=\
org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:0,location:http${#58}//pc0020:8080/uploads/,name:Update Site );\
org.eclipse.equinox.p2.touchpoint.eclipse.addRepository(type:1,location:http${#58}//pc0020:8080/uploads/,name:Update Site);\

No comments:

Post a Comment