views:

37

answers:

2

I have a personal Eclipse RCP product (com.example.product) based on one personal feature (com.example.feature) which is composed of one personal plugin (com.example.plugin) and a bunch of others from Eclipse Helios (3.6). I want the app to check for updates and update itself if necessary from a p2 site. I want it to be headless, ie the user does not interact in the update process, but may see progress in a dialog.

I based my implementation for the updates on the RCP Mail application. I changed the P2Util.checkForUpdates method a bit to include some logging so I can see what, if anything, is going wrong there:

    static IStatus checkForUpdates(IProvisioningAgent agent,
        IProgressMonitor monitor) throws OperationCanceledException,
        InvocationTargetException {
    ProvisioningSession session = new ProvisioningSession(agent);
    UpdateOperation operation = new UpdateOperation(session);
    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) {
        try {
            logger.info( "Status is " + status );
            Update[] updates = operation.getPossibleUpdates();              
            for( Update u : updates){
                logger.info( "Update is " + u );
            }               
            ProvisioningJob job = operation.getProvisioningJob(null);
            if( job == null ){
                logger.error( "Provisioning Job is null" );
            }
            status = job.runModal(sub.newChild(100));
            if (status.getSeverity() == IStatus.CANCEL) {
                throw new OperationCanceledException();
            }
        } catch ( Exception e ){
            logger.error( "Exception while trying to get updates", e);
        }
    }
    return status;
}

I have a p2.inf file in my feature at the same level as my example.product file. It contains:

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

I build the product with plugin, feature and product IDs set to 1.0.0. I can export and run my product from eclipse using the product export wizard. I tick generate metadata repository when I do this.

I create my update site using the Create an Update Site Project option in the Feature Manfiest Editor. I add my `com.example.feature' and build it. Just to see if it works I try browsing it via eclipse IDE Install New Software option and I can see the feature there.

I build the update site with all 3 IDs changed to 1.0.1. When I start the app it says there are no updates to install, there are no errors in the logs.

I don't know why it doesn't update from the update site, but things that have crossed my mind are:

1) I may need more info in the p2.inf file, but I'm not sure what, maybe something like namespace, name and range, but I can't find a good practical example.

2) In the checkForUpdates method I may need to do something with profiles to change what installable units are being updated. Again, I only found comments hinting at this and not any example code that shows how.

Any hints or ideas are much appreciated here, this is eating a lot of time.

+1  A: 

Look at this code. Rebuild your prduct with a new product version and try to setup a http server. It didnt work with file repo for me. Just publishing the feature will not work.

final IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {

sub = SubMonitor.convert(monitor, Messages.getString("UpdateManager.searchforupdates"), 200); //$NON-NLS-1$
final Update update = getUpdate(profile, provisioningContext, engine, context);

status = operation.resolveModal(sub.newChild(100));
LogHelper.log(status);
if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) {
 status = null;
 return;
}
if (status.getSeverity() == IStatus.CANCEL)
 throw new OperationCanceledException();
if (status.getSeverity() != IStatus.ERROR) {
 log(IStatus.INFO, "Checking for available update matches", null); //$NON-NLS-1$
 Update[] selected = new Update[1];
 operation.setSelectedUpdates(new Update[0]);
 for (Update available : operation.getPossibleUpdates()) {
  if (available.equals(update)) {
   log(IStatus.INFO, "Update matches available: " + update, null); //$NON-NLS-1$
   selected[0] = available;
   operation.setSelectedUpdates(selected);
  }
 }
 if (selected[0] == null) {
  status = null;
  monitor.setCanceled(true);

  log(IStatus.WARNING, "No Update matches selected", null); //$NON-NLS-1$
  return;
 }
 ProvisioningJob job = operation.getProvisioningJob(monitor);
 if (job != null) {
  status = job.runModal(sub.newChild(100));
  if (status.getSeverity() != IStatus.ERROR) {
   prefStore.setValue(JUSTUPDATED, true);
   Display.getDefault().syncExec(new Runnable() {
    public void run() {
     PlatformUI.getWorkbench().restart();
    }
   });

  } else {
   LogHelper.log(status);
  }

 } else {
  log(IStatus.INFO, "getJob returned null", null); //$NON-NLS-1$
  status = null;
 }
 if (status != null && status.getSeverity() == IStatus.CANCEL)
  throw new OperationCanceledException();
 }
 }

};

  Display.getDefault().asyncExec(new Runnable() {
public void run() {
try {
 new ProgressMonitorDialog(null).run(true, true, runnable);
} catch (InvocationTargetException x) {
 log(IStatus.ERROR, "Runnable failure", x); //$NON-NLS-1$
} catch (InterruptedException e) {
}

} });

Eiswind
@user473284 thanks for the help, when you say just publishing the feature won't work, how do I publish the product instead. Do you mean to use the repository produced when exporting the product instead of the creating an update site from the feature manifest editor?
Alb
@user473284 hi again, I've set up a local web server and have my p2.inf and the update url for the feature pointing at that now. I've tried to add your code but I don't have the implementation for getUpdate(profile, provisioningContext, engine, context); or know where these arguments came from? Is this from another example or something?
Alb
A: 

@user473284's answer had some suggestions that I used but I don't know if they were definite requirements

1) using a local web server instead of trying to point to a file 2) Incrementing the product version and using the update repository generated by the export product wizard.

I never did find the implementation for the getUpdate method referenced from the code sample so I couldn't make use of the snippet.

After the above changes I was still left with the app detecting no updates on startup. Debugging showed that my repository was not showing up in the session. I had to explicitly add the update url in the code, despite having it in the p2.inf and in set in the feature manifest editor form field. Here's the code for this:

    public static void addUpdateSite(IProvisioningAgent provisioningAgent)
        throws InvocationTargetException {
    // Load repository manager
    IMetadataRepositoryManager metadataManager = (IMetadataRepositoryManager) provisioningAgent
            .getService(IMetadataRepositoryManager.SERVICE_NAME);
    if (metadataManager == null) {
        logger.error( "Metadata manager was null");
         Throwable throwable = new
         Throwable("Could not load Metadata Repository Manager");
         throwable.fillInStackTrace();
         throw new InvocationTargetException(throwable);
    }

    // Load artifact manager
    IArtifactRepositoryManager artifactManager = (IArtifactRepositoryManager) provisioningAgent
            .getService(IArtifactRepositoryManager.SERVICE_NAME);
    if (artifactManager == null) {
        logger.error( "Artifact manager was null");
        Throwable throwable = new Throwable(
                "Could not load Artifact Repository Manager");
        throwable.fillInStackTrace();
        throw new InvocationTargetException(throwable);
    }

    // Load repo
    try {
        URI repoLocation = new URI("http://localhost/respository");
        logger.info( "Adding repository " + repoLocation );
        metadataManager.loadRepository(repoLocation, null);
        artifactManager.loadRepository(repoLocation, null);
    } catch (ProvisionException pe) {
        logger.error( "Caught provisioning exception " + pe.getMessage(), pe);
        throw new InvocationTargetException(pe);
    } catch (URISyntaxException e) {
        logger.error( "Caught URI syntax exception " + e.getMessage(), e);
        throw new InvocationTargetException(e);
    }
}

I now call this first thing in the checkForUpdates method from the original question. After this change my app at least now sees the update and attempts to install it. I'm still having problem but that deserves a separate question of its own which I've created at http://stackoverflow.com/questions/3944953/error-during-p2-eclipse-rcp-app-headless-update

Alb