Erweiterter Event-Mechanismus mit CDI - JAXenter
Aufgepasst hoch zwei!

Erweiterter Event-Mechanismus mit CDI

Lars Röwekamp, Matthias Weßendorf

Dank der bereits in einer vorherigen Ausgabe der Kolumne vorgestellten CDI-Events, können einzelne Blöcke einer Java-EE-Anwendung modular und unabhängig voneinander aufgebaut werden und trotzdem einfach und effizient miteinander kommunizieren. Dabei muss nicht immer ein Event das Mittel der Interaktion sein. Auch POJOs sind erlaubt, die bei Bedarf mittels Qualifier zusätzlich typisiert werden können. Wem das immer noch nicht reicht, der greift auf Conditional-Observer-Methoden zurück.

Ein Blick zurück

Wie bereits in [1] beschrieben, bietet das Event-System von CDI ein elegantes Werkzeug, um auf einfache Art und Weise das bekannte Observer Pattern innerhalb eines Java-EE-Containers zu implementieren. Ein Event Producer erzeugt ein Event Object, das potenziell von einem oder mehreren Event Observern konsumiert werden kann. Die Aufgabe der Vermittlung zwischen Event Producer und Event Observer(n) übernimmt dabei der CDI-Container. So weit, so gut.

Bereits der eben beschriebene, recht einfache Mechanismus erlaubt den Aufbau lose gekoppelter Systeme, in denen Event Observer gezielt und schichtenübergreifend über Änderungen am System informiert werden und bei Bedarf spezifische (Business-)Logik ausführen können. Wie wir gleich sehen werden, sind damit die Möglichkeiten des CDI-basierten Event-Handlings bei Weitem noch nicht erschöpft.

Ein Beispiel – Reloaded

Werfen wir noch einmal einen Blick auf das in [1] vorgestellte Redaktionssystem einer Nachrichtenagentur. Stellt ein Redakteur einen neuen Artikel ein, wird dieses Ereignis potenziellen Interessenten (CDI Observern) mittels eigenem Event (NewArticleEvent) signalisiert (Listing 1):

Listing 1: Einfaches Event-Objekt
public class NewArticleEvent   {   private Article article;   public NewArticleEvent(Article article)   {     this.article = article;   } 
  public Article getArticle()   {
    return article;
  }
}

Es fällt auf, dass die Klasse NewArticleEvent lediglich als Wrapper für einen Artikel dient, um so die Möglichkeit zu schaffen, dessen Eigenschaften, wie z. B. Autor und Titel, bei Bedarf in den verschiedenen Schichten einer Java-EE-Anwendung zu verarbeiten. Gibt es da in Zeiten von CDI keine bessere Lösung, die das Wrapper-Objekt obsolet werden lässt? Und wäre es nicht treffender anstelle einer Observer-Methode mit der Signatur:

public void newArticleOberserverMethod(@Observes NewArticleEvent event) {…}

etwas in der Art, wie

public void newArticleOberserverMethod(@Observes @Created article) {…}

zu haben? Kein Problem. Da die CDI-Spezifikation keine Ableitungshierarchie für CDI-Events vorgibt, kann ohne Probleme jedes beliebige POJO durch die Gegend geschickt werden. Natürlich muss dem Observer dann allerdings, mittels zusätzlichem Qualifier, beigebracht werden, auf welches Objekt genau geachtet werden soll. In unserem Beispiel interessiert sich der Observer nur für neu erstellte Artikel. Dies signalisieren wir durch den selbst implementierten Qualifier @Created:

@Qualifier
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface Created{}

Der Quellcode zum „Feuern“ des Pseudo-Events innerhalb des JSF View Controllers unterscheidet sich kaum von dem bisherigen Ansatz. Der einzige Unterschied besteht darin, dass nun kein extra Event erzeugt und „gefeuert“ wird, sondern das Article-Objekt, ergänzt um dem zusätzlichem Qualifier @Created (Listing 2).

Listing 2: Eine JSF Managed Bean als Event Producer
@Named("createArticle")
@RequestScoped
public class CreateArticleController
{     // Injection des CDI Events
      // nicht:  @Inject Event eventSource; 
  @Inject @Created Event
eventSource; ... public Object createNewArticle() { myService.storeNewArticle(newArticle); // "feuern" des CDI Events // nicht: eventSource.fire(new NewArticleEvent(newArticle)); eventSource.fire(newArticle); return "success"; } }

Was ist nun aber, wenn man zum Zeitpunkt der Injection des Events noch gar nicht weiß, wie der Qualifier aussehen soll und diesen dynamisch zur Laufzeit erzeugen möchte. Auch dieses Szenario sieht CDI – wenn auch mit einer etwas gewöhnungsbedürftigen Syntax – vor:

eventSource.select(new AnnotaionLiteral(){}).fire(newArticle);

Das Problem an dieser Stelle ist, dass wir eine Instanz der @Created-Annotation benötigen, um diese via select()-Methode mit dem CDI-Event zu verbinden. Erhalten können wir die Instanz, indem wir von der Klasse abstrakten AnnotationLiteral ableiten – in unserem Fall mithilfe einer anonymen Klasse.

Geschrieben von
Lars Röwekamp, Matthias Weßendorf
Kommentare

Schreibe einen Kommentar

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