SwingTasks - SwingWorker on Steroids

In Swing gibt es zwei verschiedene Muster von Aufgaben im Zusammenhang mit Hintergrundtasks, die immer wieder auftreten:

[ul]
[li]Langwierige Berechnungen in einen Hintergrundthread auslagern[/li][li]Einen bestimmten Task immer wieder ausführen, und diese Ausführung mit „Start/Pause/Stop“ kontrollieren[/li][/ul]

Für ersteres gibt es natürlich den SwingWorker, aber um vieles „drumherum“ muss man sich noch selbst kümmern. Im speziellen z.B. darum, dass während der Berechnung ein Dialog angezeigt wird. Da bietet der ProgressMonitor zwar auch schon einige Möglichkeiten, allerdings ist der nicht leicht zu konfigurieren, und der Teufel steckt auch dort oft im Detail…

Um das ganze etwas zu vereinfachen, habe ich ein paar Utility-Klassen erstellt. Das ganze liegt unter GitHub - javagl/SwingTasks: Utility classes for task execution in Swing

Die „Readme“ beschreibt schon die grobe Idee dahinter, und es gibt ein paar Beispiele, die zeigen, wie man das ganze verwenden kann. Darum hier nur kurz: Wenn man in Swing z.B. auf einen Buttonklick hin eine Methode ausführen will, die potentiell lange rechnet, und man während dieser Berechnung gerne einen modalen Dialog anzeigen möchte (was für mich der „Standardfall“ ist), dann ist das mit
SwingTaskExecutors.create(SomeClass::someMethod).build().execute();
erledigt: Der SwingTaskExecutor kümmert sich dann um alles :slight_smile:

Für letzteres (das wiederholte Ausführen eines Tasks, mit Start/Pause/Stop usw) habe ich auch ein paar Helferlein erstellt. Grob gesagt gibt es ein Task-Interface, das man implementiert, und einen TaskRunner übergibt, den man dann mit den Buttons auf einen TaskRunnerControlPanel steuern kann, um den Task zu starten, wiederholt auszuführen, oder zu stoppen. (Beispiele dazu folgen ggf. noch - im Moment gibt es einen Test, der die grundsätzliche Verwendung schonmal zeigt).

Feedback (insbesondere negatives, im Sinne von Verbesserungsvorschlägen :D) ist immer willkommen :slight_smile:

Auf den ersten Blick habe ich mich sehr über den Ordner src/test gefreut. Auf den zweiten Blick vermisse ich darin die UnitTests.

Der Sinn der Klasse DefaultSwingTaskView erschließt sich mir nicht, die ist ein reiner Durchlauferhitzer für die Klasse SwingTaskDialog. Wozu soll man die benötigen?
Außerdem kann man die Default-View nicht ohne den (echten) JDialog darin nicht instanziieren, was das Testen schwierig macht. Auch sehe ich nicht, das eine SwingTaskView zwingend ein Dialog sein muss.

Dann ehe ich nichtssagende Kommentare wie
/**

  • The optional parent component
    */
    private final Component parentComponent;

wenn das Ding optionalParent heißen würde wäre das auch gut.

Und das dieser Kommentar am Konstruktor wiederholt wird macht’s nicht besser…

Soweit erst mal, die Anderen wollen ja auch noch was zum meckern haben…

;o)

bye
TT

Wie man so etwas mit Unit-Tests abdecken soll (da es ja schon sehr darauf ausgerichtet ist, dass man z.B. auf einen Button klickt und dann (nach einer gewissen Zeit) ein Dialog erscheint, in dem sich (im Lauf der Zeit) eine Message ändert) weiß ich nicht. Falls es für sowas (z.B. auch die Frage „Ist der aufgehende Dialog wirklich modal?“) irgendwelche Standard-Unit-Test-Patterns gibt, sind mir die bisher entgangen.

Durch die vorhandenen Tests habe ich aber schon versucht, möglichst viele Fälle abzudecken…

Der Sinn der Klasse DefaultSwingTaskView erschließt sich mir nicht, die ist ein reiner Durchlauferhitzer für die Klasse SwingTaskDialog. Wozu soll man die benötigen?

Ich hatte befürchtet, dass diese Frage aufkommen könnte (aber ehrlich gesagt nicht so schnell damit gerechnet :D), und zugegeben: Ich hatte auch mit mir gehadert, ob ich diese Teile hätte rausnehmen sollen. (Auch sowas wie die Config, für die es ein Interface gibt, obwohl sie ein POJO sein könnte…). Die Intention dahinter war, dass die „View“ für so eine Task Execution ja nicht unbedingt ein Dialog sein muss. Ich dachte da an Dinge, ähnlich wie die „Task-Liste“ in Eclipse, wo die laufenden Tasks einfach untereinander stehen. Man könnte also z.B. eine eigene View-Component erstellen, wo nur eine Liste der aktuell laufenden Tasks angezeigt wird.

(Ja, der Vorwurf des YAGNI-Verstoßes ist nicht ganz ungerechtfertigt: Solange ich das nicht selbst ausprobiert habe, könnte man das als eine Gedankenspielerei abtun, und es besteht die Gefahr, dass das durch die bisherigen Strukturen gar nicht wirklich abgebildet werden kann (auch wenn es das sollte)).

Dann ehe ich nichtssagende Kommentare wie

Auch da widerspreche ich nicht: Solche Kommentare gehen gelegentlich (!) in Richtung „Noise“. Und manche meinen auch, sie wären NUR Noise, und „verteufeln“ sie geradezu. Aber ich will mich nicht auf eine Diskussion einlassen, welche Variable es denn „wert“ ist, (wie) kommentiert zu werden. Ich schreibe hin, was ich für eine geeignete Beschreibung der Variablen halte, und gelegentlich (wenn ich zu müde bin, um über etwas richtiges nachzudenken), erweitere ich das dann gelegentlich mit Informationen, von denen ich glaube, dass sie für jemanden, der aus dem Nichts heraus in den Code reingeworfen wird, hilfreich sein könnten:
**
The optional parent component. The window of this
component may become the parent window of a
dialog…
**
…oder so.

Auch wenn ich dafür jetzt etwa 3 Stunden gebraucht habe, und ich davon ausgehen muss, dass durch das rumgefrickel mit zwei Github-Accounds, zwei SSH-Keys und zwei Sonatype-Logins irgendwas so kaputtkonfiguriert ist, dass das nächste JOCL-release daraus besteht, 3 Stunden lang nochmal das gleiche zu machen:


<dependency>
  <groupId>de.javagl</groupId>
  <artifactId>swing-tasks</artifactId>
  <version>0.0.1</version>
</dependency>

sollte bald in der Central liegen.

Es gibt ein Update


<dependency>
  <groupId>de.javagl</groupId>
  <artifactId>swing-tasks</artifactId>
  <version>0.0.2</version>
</dependency>

Darin ist jetzt ein “executors”-Package enthalten, mit einem ObservableExecutorService und einem ObservableExecutorPanel. Man kann so einen executor service erstellen, ihm dann wie üblich irgendwelche Tasks hinwerfen, und im entsprechenden Panel zuschauen, wie die Tasks abgearbeitet werden: