Suche
Kolumne: EnterpriseTales

Asynchrone Events in CDI 2.0

Lars Röwekamp, Arne Limburg

CDI ist seit der Version 1.0 in Java EE 6 der neue Enterprise-Komponentenstandard. Während es mit CDI 1.1 in Java EE 7 nur ein Minor Update gab, kann für Java EE 8 wieder mit einem größeren Entwicklungsschritt gerechnet werden. Die Spezifizierung von CDI 2.0 hat bereits begonnen. Eines der Themen, die auf der Agenda stehen, ist die asynchrone Verarbeitung von Events. Wir werfen in dieser Kolumne einen Blick auf den aktuellen Stand der Spezifikation und geben einen Ausblick darauf, was für Möglichkeiten asynchrone Events bieten.

Der CDI-Eventing-Mechanismus bietet bereits seit CDI 1.0 eine einfache Möglichkeit, das Observer-Pattern zu implementieren und Java-EE-Architekturen zu entkoppeln. Um ein CDI-Event abzufeuern, genügt es dabei, sich das Containerobjekt vom Typ Event injizieren zu lassen und darauf die Methode fire(…) aufzurufen. Der Payload (das Eventobjekt, das tatsächlich verschickt wird) kann dabei ein beliebiges Java-Objekt sein. Möchte eine CDI Bean auf ein solches Event reagieren, muss sie nur eine beliebige Methode zur Verfügung stellen, die über einen Parameter vom Typ des Eventobjekts (oder einer Oberklasse) verfügt, und diesen mit @Observes annotieren (Listing 1).

Wie überall in CDI, funktioniert auch hier das Einschränken über Qualifier. Versieht man den Injection Point für das Event mit Qualifiern, so reagieren auch nur Observer auf das Event, deren @Observes-Parameter mit denselben Qualifiern (oder einem Subset davon) ausgestattet ist. Eventtyp und Qualifier können auch über die Methode select(…) am Event-Objekt weiter eingeschränkt werden. Über Einstellungen an der Observer-Methode können weitere Einschränkungen vorgenommen werden. So kann zum Beispiel festgelegt werden, dass der Observer nur aufgerufen wird, wenn die zugehörige Bean schon existiert (über das @Observes-Attribut notifyObserver), oder es kann festgelegt werden, in welcher Transaktionsphase der Observer aufgerufen werden soll (über das Attribut during). Observer, die hier eine andere Phase als IN_PROGRESS angeben, werden Transactional Observer genannt.

Ab CDI 2.0 wird es über @Priority wahrscheinlich möglich sein, Observer zu priorisieren und so eine Reihenfolge der Event-Observer festzulegen. Auch wenn über die Angabe einer Transaktionsphase das Ausführen des Observers verzögert werden kann (nämlich bis zum Transaktions-Commit), läuft die gesamte Eventverwaltung bisher synchron, d. h. die Observer werden in dem Thread aufgerufen, in dem das Event gefeuert wird. Das soll sich nun mit CDI 2.0 ändern.


public class EventLauncher {
    @Inject
    private Event event;

    public void fireMyEvent() {
        event.fire(new MyEventPayload());
    }
}

public class MyEventObserver {
    public void listenToMyEventPayload(@Observes MyEventPayload payload) {
        // do something
    }
}

Asynchronität beim Feuern

Die CDI-2.0-Spezifikation ist noch nicht final, sodass alles, was wir in dieser Kolumne schreiben, unter dem Vorbehalt zu sehen ist, dass sich das Spezifikationskomitee noch anders entscheiden kann. Wir werden aber hier den aktuellen Stand der Spezifikation zum Thema asynchrone Events vorstellen.

Geplant ist, dass die Asynchronität der Eventverarbeitung beim Abfeuern des Events gesteuert werden kann. Um entscheiden zu können, dass ein Event asynchron verarbeitet werden soll, wird dem containereigenen Eventobjekt eine Methode fireAsync(…) hinzugefügt. Benutzt man diese anstatt der bisherigen Methode fire(…), so wird das abgefeuerte Event asynchron verarbeitet.

Die Methode fireAsync(…) liefert dabei eine Instanz des mit Java 8 neu eingeführten Interfaces CompletionStage zurück, um es zu ermöglichen, dass nach der asynchronen Abarbeitung aller Observer noch Code vom Aufrufer ausgeführt werden kann. Dieser kann der CompletionStage über eine Lambda Expression übergeben werden, und das geht dann sogar „fluent“ (Listing 2).

Listing 2
public class EventLauncher {
    @Inject
    private Event event;

    public void fireMyEvent() {
        event.fireAsync(new MyEventPayload()).thenRun(() -> {
            // this code runs after every event is dispatched
        });
    }
}

Offene Punkte

Heiß diskutiert wird aktuell noch, ob die Observer Einfluss darauf haben sollen, ob sie asynchron ausgeführt werden können oder nicht. Würde man dieses Feature einführen, könnte das bedeuten, dass der aufrufende Code ein Event zwar asynchron feuert, einige Observer aber entscheiden, das Event lieber synchron zu erhalten. Nur Observer, die selbst als asynchron markiert sind, würden das Event auch asynchron erhalten. Damit soll eine bessere Abwärtskompatibilität zu den bisher synchronen Events gewährleistet werden. Würden bisher synchrone Observer auf einmal asynchron verarbeitet, könnte das unerwartete Folgen haben. Zum Beispiel würden sie dann in einer neuen Transaktion ausgeführt, obwohl sie bisher in der Transaktion des Eventinitiators gelaufen sind.

Ein weiterer offener Punkt ist der Umgang mit den oben erwähnten Transactional Observern. Wie bereits erwähnt, laufen solche Observer bisher schon asynchron, allerdings im selben Thread wie der Aufrufer. Das ist auch notwendig, weil sie gemäß Spezifikation im selben Transaktionskontext laufen müssen. Der Transaktionskontext ist in Java EE immer an einen Thread gebunden, was wohl bedeutet, dass sie auch in Zukunft im selben Thread laufen.

Die Behandlung von @Priority soll von der Asynchronität nicht betroffen sein. Auch bei asynchron verarbeiteten Events muss sichergestellt sein, dass sie in der richtigen Reihenfolge abgearbeitet werden, sofern sie eine @Priority-Annotation besitzen.

Fazit

Asynchrone Events sind ein vielversprechender Ansatz, der eine einfach zu bedienende Schnittstelle bietet, um mehr Parallelität in der Ausführung von Enterprise-Anwendungen umzusetzen. Auch wenn noch nicht alle Details geklärt sind, zeichnet sich bereits jetzt ab, dass CDI 2.0 mit asynchronen Events ein wichtiges Feature gelingen wird, um Enterprise-Anwendungen durch mehr Parallelität in der Ausführung zu beschleunigen. Man darf gespannt sein, welche weiteren Features Java EE 8 in diesem Bereich bringt. In diesem Sinne: stay tuned.

Geschrieben von
Lars Röwekamp
Lars Röwekamp
Lars Röwekamp ist Gründer des IT-Beratungs- und Entwicklungsunternehmens open knowledge GmbH, beschäftigt sich im Rahmen seiner Tätigkeit als „CIO New Technologies“ mit der eingehenden Analyse und Bewertung neuer Software- und Technologietrends. Ein besonderer Schwerpunkt seiner Arbeit liegt derzeit in den Bereichen Enterprise und Mobile Computing, wobei neben Design- und Architekturfragen insbesondere die Real-Life-Aspekte im Fokus seiner Betrachtung stehen. Lars Röwekamp, Autor mehrerer Fachartikel und -bücher, beschäftigt sich seit der Geburtsstunde von Java mit dieser Programmiersprache, wobei er einen Großteil seiner praktischen Erfahrungen im Rahmen großer internationaler Projekte sammeln konnte.
Arne Limburg
Arne Limburg
Arne Limburg ist Softwarearchitekt bei der open knowledge GmbH in Oldenburg. Er verfügt über langjährige Erfahrung als Entwickler, Architekt und Consultant im Java-Umfeld und ist auch seit der ersten Stunde im Android-Umfeld aktiv.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.