ProgressWorker is a SwingWorker extension, providing a fluent API for constructing background task workers for a variety of task types.

All handlers get called on the EventDispatchThread.

Note
Like SwingWorker, ProgressWorker instances can not be reused. Tasks, on the other hand, can be made stateful and reusable if required.

1. Task

// A non-progress aware task, producing no result
ProgressWorker.Task task = () -> {
  // Perform the task
};

ProgressWorker.builder(task)
        .onException(exception ->
                exceptionDialog()
                        .owner(applicationFrame)
                        .show(exception))
        .execute();

2. ResultTask

// A non-progress aware task, producing a result
ProgressWorker.ResultTask<String> task = () -> {
  // Perform the task
  return "Result";
};

ProgressWorker.builder(task)
        .onResult(result ->
                showMessageDialog(applicationFrame, result))
        .onException(exception ->
                exceptionDialog()
                        .owner(applicationFrame)
                        .show(exception))
        .execute();

3. ProgressTask

// A progress aware task, producing no result
ProgressWorker.ProgressTask<String> task = progressReporter -> {
  // Perform the task
  progressReporter.report(42);
  progressReporter.publish("Message");
};

ProgressWorker.builder(task)
        .onProgress(progress ->
                System.out.println("Progress: " + progress))
        .onPublish(message ->
                showMessageDialog(applicationFrame, message))
        .onException(exception ->
                exceptionDialog()
                        .owner(applicationFrame)
                        .show(exception))
        .execute();

4. ProgressResultTask

// A reusable, cancellable task, producing a result.
// Displays a progress bar in a dialog while running.
var task = new DemoProgressResultTask();

ProgressWorker.builder(task.prepare(142))
        .onStarted(task::started)
        .onProgress(task::progress)
        .onPublish(task::publish)
        .onDone(task::done)
        .onCancelled(task::cancelled)
        .onException(task::failed)
        .onResult(task::finished)
        .execute();
static final class DemoProgressResultTask implements ProgressResultTask<Integer, String> {

  private final JProgressBar progressBar = progressBar()
          .indeterminate(false)
          .stringPainted(true)
          .string("")
          .build();
  // Indicates whether the task has been cancelled
  private final AtomicBoolean cancelled = new AtomicBoolean();
  // A Control for setting the cancelled state
  private final Control cancel = Control.builder()
          .command(() -> cancelled.set(true))
          .caption("Cancel")
          .mnemonic('C')
          .build();
  // A panel containing the progress bar and cancel button
  private final JPanel progressPanel = borderLayoutPanel()
          .centerComponent(progressBar)
          .eastComponent(button(cancel).build())
          .build();
  // The dialog displaying the progress panel
  private final JDialog dialog = componentDialog(progressPanel)
          .owner(applicationFrame)
          // Trigger the cancel control with the Escape key
          .keyEvent(KeyEvents.builder(VK_ESCAPE)
                  .action(cancel))
          // Prevent the dialog from closing on Escape
          .disposeOnEscape(false)
          .build();

  private int taskSize;

  @Override
  public int maximumProgress() {
    return taskSize;
  }

  @Override
  public Integer execute(ProgressReporter<String> progressReporter) throws Exception {
    List<Integer> result = new ArrayList<>();
    for (int i = 0; i < taskSize; i++) {
      Thread.sleep(50);
      if (cancelled.get()) {
        throw new CancelException();
      }
      result.add(i);
      reportProgress(progressReporter, i);
    }

    return result.stream()
            .mapToInt(Integer::intValue)
            .sum();
  }

  // Makes this task reusable by resetting the internal state
  private DemoProgressResultTask prepare(int taskSize) {
    this.taskSize = taskSize;
    progressBar.getModel().setMaximum(taskSize);
    cancelled.set(false);

    return this;
  }

  private void reportProgress(ProgressReporter<String> reporter, int progress) {
    reporter.report(progress);
    if (progress < taskSize * 0.5) {
      reporter.publish("Going strong");
    }
    else if (progress > taskSize * 0.5 && progress < taskSize * 0.85) {
      reporter.publish("Half way there");
    }
    else if (progress > taskSize * 0.85) {
      reporter.publish("Almost done");
    }
  }

  private void started() {
    dialog.setVisible(true);
  }

  private void progress(int progress) {
    progressBar.setValue(progress);
  }

  private void publish(List<String> strings) {
    progressBar.setString(strings.get(0));
  }

  private void done() {
    dialog.setVisible(false);
  }

  private void cancelled() {
    showMessageDialog(applicationFrame, "Cancelled");
  }

  private void failed(Exception exception) {
    exceptionDialog()
            .owner(applicationFrame)
            .show(exception);
  }

  private void finished(Integer result) {
    showMessageDialog(applicationFrame, "Result : " + result);
  }
}