The importance of SwingUtilities

After some experiments and coding using an extended SwingWorker and wrapping this in an Action. I find that this is an easy way to code long running operations. Why I think so? First a long running operation is triggered by an event from the user. In most cases this is a button click, mouse click or key press on a component. In all cases these events can be converted into ActionEvents for an Action implementation. So in my case I extended SwingWorker and delegated important methods to base asynchronous Action implementation. After that I can subclass this one pretty easy and implement the long running operation in the background method.

So how does a user work with the application. First he wants to see some objects in a list where he can selects the one he wants to work with. Then he clicks a button or presses a key to start editing the object. This would trigger the execution of an action. The action starts the SwingWorker in a background thread which again delegates to the action.

In the background method it might be important to check whether someone else has changed the object under change before letting the user change it and in the case of a change present a warning message. How can this be accomplished? It is quite simple by using SwingUtilities#invokeAndWait(Runnable) method. Inside the Runnable object the dialog is displayed. If the user clicks on Cancel the SwingWorker is canceled, otherwise it continues normally. The same with the editor dialog for the object. Again use SwingUtilities#invokeAndWait(Runnable) to display the dialog and wait until the user made the change or cancels. What if someone in between also edited the object under change and the server throws an exception? If the user should be able to overwrite any made change show an error dialog in the background method using again SwingUtilities#invokeAndWait(Runnable). Based on the outcome the object can be saved or discarded. So how can it look like:

public class MyAction extends AbstractAsyncAction {
...
@Override
protected Activity background(final Task<Activity> worker,
    final ActionEvent event) throws Exception {
  // get the selected object
  final Activity selected = getModel().getSelectedActivity();
  // retrieve the entity from the repository
  final Activity entity = activityRepository.findByOid(selected.getOid());
  // identify changes between selected and retrieved entity (optimistic locking)
  ...
  // retrieve more required data
  final List<String> categories = activityRepository.findAllCategories();
  // allow user to edit the activity
  SwingUtilities.invokeAndWait(new Runnable() {
    @Override
    public void run() {
      // if the user cancels editing the activity, cancel the worker
      JComponent source = (JComponent) event.getSource();
      boolean canceled = openActivityEditor(
          (Frame) SwingUtilities.getWindowAncestor(source), entity, categories);
      if (canceled) {
        worker.cancel(false);
      }
    }
  });
  if (!worker.isCancelled()) {
    // update the activity if the worker is not canceled
    activityRepository.save(entity);
  }
  return entity;
}
}

This method is part of a base Action implementation and part of the delegated methods from SwingWorker or Task. Other methods are before(Task, ActionEvent) which is called before the background operation starts. Then like in Swing Application Framework there are methods like suceeded(Task, ActionEvent, Object), failed(Task, ActionEvent, Throwable), interrupted(Task, ActionEvent, InterruptedException), canceled(Task, ActionEvent, CancellationException), intermediate(Task, ActionEvent, List) and finished(Task, ActionEvent) which correspond to the methods from Task.

Also the action implementation handles the input blocking by blocking the input before the task is executed and unblocking when the task is finished.

Schlagwörter: ,

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: