Running Task asynchronously in Eclipse RCP and JFace

These code snippets show how it is possible to executes task in Eclipse by showing a progress dialog, which is possible to hide

This is the code to start the job:

// create action to be called after the action is completed
// this action shows a success dialog if the job executed without
// an exception, otherwise it shows an error dialog
final CompletionAction completionAction = new 
        CompletionAction(AdminClientActivator.PLUGIN_ID, parent.getShell(), view);
completionAction.setOkTitle("Job sucess");
completionAction.setOkMsg("Sucessfully executed job");
completionAction.setFailTitle("Job failed");
completionAction.setFailMsg("There was an exception while executing job");

RSPClientJob job = new ClientJob("Long job...", completionAction);

// if short action, otherwise is long Job.LONG
job.setPriority(Job.SHORT);
// show a dialog immediately
job.setUser(true);
// start as soon as possible
job.schedule();

This snippet is the Job class:

/**
 * Abstract {@link Job} class for executing tasks with a progress dialog
 * @author Robert von Burg
 */
public class ClientJob extends Job {
  private CompletionAction completedAction;

  /**
   * @param name
   * @param completedAction
   */
  public ClientJob(String name, CompletionAction completedAction) {
    super(name);
    this.completedAction = completedAction;
  }

  protected IStatus run(IProgressMonitor monitor) {
    // activate the progress bar with an unknown amount of task work
    monitor.beginTask("Loading Hibernate configuration", IProgressMonitor.UNKNOWN);
    // perform the job
    try {
      // execute task work...
      Thread.sleep(10000);
      // at the end of the successfully ended work, set the completion
      // task to be ok
      completionAction.setOk(true);
    } catch (Exception e1) {
      logger.error(e1, e1);
      // if the work failed then set the completion
      // task to be NOT ok and set the exception so it
      // can be shown to the user
      completionAction.setThrowable(e1);
      completionAction.setOk(false);
    }
    // stop the monitor
    monitor.done();
    // execute the completion task
    complete();
    return Status.OK_STATUS;
  }

  /**
   * completes the task by showing the user a dialog about the execution 
   * state of the job
   */
  protected void complete() {
    setProperty(IProgressConstants.ICON_PROPERTY, ImageFactory.
                getImg(ImageFactory.IMG_OK));
    Boolean isModal = (Boolean) this.getProperty(
                IProgressConstants.PROPERTY_IN_DIALOG);
    if (isModal != null && isModal.booleanValue()) {
      // The progress dialog is still open so
      // just open the message
      showResults(completedAction);
    } else {
      setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE);
      setProperty(IProgressConstants.ACTION_PROPERTY, completedAction);
    }
  }

  /**
   * Asynchronous execution of an {@link Action}
   * @param action
   */
  protected static void showResults(final Action action) {
    Display.getDefault().asyncExec(new Runnable() {
      public void run() {
        action.run();
      }
    });
  }
}

And since we want to notify the caller about task completion we use this interface:

/**
 * Interface for notifying objects to be refreshed
 * @author robertb
 */
public interface Refreshable {
  public void refresh();
}

And task completion is handled with this Action:

/**
 * The completion task, which either shows a success dialog, or an error dialog, 
 * depending on the ok state set and then notifies the {@link Refreshable} to 
 * refresh its contents
 * @author robertb
 */
public class CompletionAction extends Action {
  private boolean ok;
  private String okTitle;
  private String okMsg;
  private String failMsg;
  private String failTitle;
  private Throwable throwable;

  private Refreshable refreshable;
  private Shell shell;
  private String pluginId;

  /**
   * @param pluginId
   * @param shell
   * @param refreshable
   */
  public CompletionAction(String pluginId, Shell shell, Refreshable refreshable) {
    this.pluginId = pluginId;
    this.shell = shell;
    this.refreshable = refreshable;
  }

  public void setOk(boolean ok) {
    this.ok = ok;
  }

  public void setOkTitle(String okTitle) {
    this.okTitle = okTitle;
  }

  public void setOkMsg(String okMsg) {
    this.okMsg = okMsg;
  }

  public void setFailMsg(String failMsg) {
    this.failMsg = failMsg;
  }

  public void setFailTitle(String failTitle) {
    this.failTitle = failTitle;
  }

  public void setThrowable(Throwable throwable) {
    this.throwable = throwable;
  }

  public void run() {
    // first refresh
    refreshable.refresh();
    // then show the dialog
    if (ok) {
      MessageDialog.openInformation(shell, okTitle, okMsg);
    } else {
      Status status = new Status(IStatus.ERROR, pluginId, 
                throwable.getLocalizedMessage(), throwable);
      ErrorDialog.openError(shell, failTitle, failMsg, status);
    }
  }
}

I put this together with help from the following site:
Job Concurrency

Thanks
eitch

One Comment

Comments are closed.