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(task)
.onException(exception ->
Dialogs.exception()
.owner(applicationFrame)
.show(exception))
.execute();
2. TaskHandler
// TaskHandler encapsulates the task and its handlers in a single class.
// Handler interface methods are called first, followed by
// any handlers added via the builder, in the order they were added.
// This enables a layered approach where the handler interface
// handles model-level concerns (logging, state updates) while
// builder handlers handle UI-level concerns (displaying dialogs).
ProgressWorker.TaskHandler task = new TaskHandler() {
@Override
public void execute() throws Exception {
// Perform the task
}
// Called first on exception: log the error (model-level)
@Override
public void onException(Exception exception) {
LOG.log(Level.WARNING, exception.getMessage());
}
};
ProgressWorker.builder()
.task(task)
// Called after the handler's onException: display the error (UI-level)
.onException(exception -> Dialogs.exception()
.owner(applicationFrame)
.show(exception))
.execute();
3. ResultTask
// A non-progress aware task, producing a result
ProgressWorker.ResultTask<String> task = () -> {
// Perform the task
return "Result";
};
ProgressWorker.builder()
.task(task)
.onResult(result ->
showMessageDialog(applicationFrame, result))
.onException(exception ->
Dialogs.exception()
.owner(applicationFrame)
.show(exception))
.execute();
4. ResultTaskHandler
// ResultTaskHandler encapsulates a result-producing task and its handlers.
// The handler's onResult and onException are called first (model-level),
// then the builder's handlers are called after (UI-level).
ResultTaskHandler<String> task = new ResultTaskHandler<String>() {
@Override
public String execute() throws Exception {
// Perform the task
return "Result";
}
// Called first on success: log the result (model-level)
@Override
public void onResult(String result) {
LOG.log(Level.INFO, result);
}
// Called first on exception: log the error (model-level)
@Override
public void onException(Exception exception) {
LOG.log(Level.WARNING, exception.getMessage());
}
};
ProgressWorker.builder()
.task(task)
// Called after the handler's onResult: display the result (UI-level)
.onResult(result -> showMessageDialog(applicationFrame, result))
// Called after the handler's onException: display the error (UI-level)
.onException(exception -> Dialogs.exception()
.owner(applicationFrame)
.show(exception))
.execute();
5. 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(task)
.onProgress(progress ->
System.out.println("Progress: " + progress))
.onPublish(message ->
showMessageDialog(applicationFrame, message))
.onException(exception ->
Dialogs.exception()
.owner(applicationFrame)
.show(exception))
.execute();
6. ProgressTaskHandler
// ProgressTaskHandler encapsulates a progress-aware task and its handlers.
// The handler's methods are called first (model-level),
// then the builder's handlers are called after (UI-level).
ProgressTaskHandler<String> task = new ProgressTaskHandler<String>() {
@Override
public void execute(ProgressReporter<String> progressReporter) throws Exception {
// Perform the task
for (int i = 0; i < maximum(); i++) {
progressReporter.report(i);
progressReporter.publish("Message " + i);
}
}
@Override
public void onProgress(int progress) {
System.out.println("Progress: " + progress);
}
@Override
public void onPublish(List<String> message) {
displayMessage(message);
}
// Called first on exception: log the error (model-level)
@Override
public void onException(Exception exception) {
LOG.log(Level.WARNING, exception.getMessage());
}
};
ProgressWorker.builder()
.task(task)
// Called after the handler's onException: display the error (UI-level)
.onException(exception -> Dialogs.exception()
.owner(applicationFrame)
.show(exception))
.execute();
7. ProgressResultTask
// A reusable, cancellable task, producing a result.
// Displays a progress bar in a dialog while running.
var task = new DemoProgressResultTask();
ProgressWorker.builder()
.task(task.prepare(142))
.execute();
static final class DemoProgressResultTask implements ProgressResultTaskHandler<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()
.center(progressBar)
.east(button()
.control(cancel))
.build();
// The dialog displaying the progress panel
private final JDialog dialog = Dialogs.builder()
.component(progressPanel)
.owner(applicationFrame)
// Trigger the cancel control with the Escape key
.keyEvent(KeyEvents.builder()
.keyCode(VK_ESCAPE)
.action(cancel))
// Prevent the dialog from closing on Escape
.disposeOnEscape(false)
.build();
private int 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();
}
@Override
public int maximum() {
return taskSize;
}
@Override
public void onStarted() {
dialog.setVisible(true);
}
@Override
public void onProgress(int progress) {
progressBar.setValue(progress);
}
@Override
public void onPublish(List<String> strings) {
progressBar.setString(strings.get(0));
}
@Override
public void onDone() {
dialog.setVisible(false);
}
@Override
public void onCancelled() {
showMessageDialog(applicationFrame, "Cancelled");
}
@Override
public void onException(Exception exception) {
Dialogs.exception()
.owner(applicationFrame)
.show(exception);
}
@Override
public void onResult(Integer result) {
showMessageDialog(applicationFrame, "Result : " + result);
}
// 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");
}
}
}