Blog

Freundliche Eindringlinge: CDI seams to let EJB spring (JSR 299) – Teil 3

Nachdem im ersten Teil der Blogserie „Freundliche Eindringlinge: CDI seams to let EJB spring (JSR 299)“ ein wenig Theorie zu CDI vermittelt wurde, die Dependeny Injection (DI) vorgestellt und im zweiten Teil die Werkzeuge Qualifier, Alternativen, Stereotypen, EL-Namen und Scopes vorgestellt wurden, sollen in diesem abschließenden Artikel die Werkzeuge Events, Interzeptor-Bindings, Dekoratoren und Producer eingeführt werden. Mit der Beispielapplikation „Friendly Invaders“ wird die gesamte Werkzeugkiste in der Praxis zum Fliegen gebracht.

Events

Wie auch die Dependency Injection sind Events kein vollkommen neues Konzept für den Java EE Stack. CDI liefert jedoch ein neues Werkzeug, getreu dem Observer Pattern, um Events zu realisieren.

Im Kontext von Events spricht CDI von Producern und Observern. Producer erzeugen Events und Observer „lauschen“, ob Events eingetreten sind. Um Producer und Observer zusammenzubringen bzw. für die Observer festzulegen, welche Events für sie von Interesse sind, werden Qualifier genutzt.

Ergo muss im ersten Schritt ein Qualifier angelegt werden.

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

Im zweiten Schritt wird der Producer in der Bean angelegt, aus welcher heraus wir Events werfen wollen.

@NSS
public class InvasionController implements Serializable{
  ...
  @Inject @Invaded
  private Event blogArticleEvent;
  ...
  public String giveKudos(BlogArticle blogArticle, int amount){
    blogArticleEvent.fire(blogArticle);
    ...
  }
}

Im Codebeispiel tritt ein Event ein, wenn durch die Methode giveKudos(blogArticle, amount) dem Autor eines Blogartikels Anerkennung gezeigt wird. Das Event-Objekt wird mit @Invaded annotiert und kann somit mit entspechenden Observern in Beziehung gesetzt werden. (@NSS ist ein benutzerdefinierter Stereotyp, der die Annotationen @Named & @SessionScoped bündelt und im zweiten Teil der Blogserie eingeführt wurde.)

Fehlen also nur noch Observer, die reagieren, wenn ein Event eingetreten ist.
Zum Beispiel könnte beim Eintreten des Events getwittert werden, wer gerade für welchen Blogartikel Anerkennung bekommen hat.

@RequestScoped
public class InvasionTweeter{
  public void afterBlogIntrude( @Observes @Invaded BlogArticle blogArticle) {
    ...
  }
}

Da nicht jeder Mensch bei Twitter ist und die Autoren sicher gern Bescheid wüssten, sobald sie Anerkennung bekommen, könnte zusätzlich ein Observer Mails an die Autoren versenden.

@RequestScoped
public class InvasionMailer{
  public void afterBlogIntrude( @Observes @Invaded BlogArticle blogArticle) {
    ...
  }
}

Observer Pattern

Die Frage, ob CDI nun getreu dem Observer Pattern agiert, muss mit „Jein“ beantwortet werden. Nicht nur Producer sind von Observern entkoppelt, sondern auch Observer von Producern. Hinzu kommt, dass Observer Events mit Qualifiern filtern und verzögert aufgerufen werden können, z.B. erst, nachdem eine Transaktion abgeschlossen wurde.

Interzeptor-Bindings

Ja, auch Interzeptoren sind nichts Neues, aber mit den Interzeptor-Bindings wird ein mächtiges Werkzeug für des Binden von Interzeptoren an Methoden zur Verfügung gestellt. Auch hier dienen Annotationen als Bindeglied.

Im ersten Schritt wird eine mit @InterceptorBinding annotierte Annotation angelegt, das Interzeptor-Binding LoggerBinding.

@InterceptorBinding
@Retention (RUNTIME)
@Target ({METHOD, TYPE})
public @interface LoggerBinding {}

Im zweiten Schritt wird ein mit dem Interzeptor-Binding annotierter Interzeptor erstellt.

@LoggerBinding
@Interceptor
public class LoggingInterceptor implements Serializable {
  private static final long serialVersionUID = 1L;
  @Inject
  private Logger log;

  @AroundInvoke
  public Object onMethodCall(InvocationContext context)
      throwsException {
    ...
  }
}

Die anderen zwei Annotationen @Interceptor und @AroundInvoke sollten, wenn schon mal Interzeptoren eingesetzt wurden, bekannt sein. @Interceptor markiert die Klasse als Interzeptor, und mit @AroundInvoke wird die Methode gekennzeichnet, die beim Aufruf einer mit dem Interzeptor verbundenen Methode ausgeführt wird.

Im dritten Schritt müssen wir unseren Interzeptor jetzt an eine Bean heften. Dazu wird das Interzeptor-Binding @LoggerBinding an die Methode checkout() des CheckoutController annotiert.

@Stateful @ConversationScoped public class CheckOutServiceBean {
  ...
  @LoggerBinding public void checkout() {
    conversation.end();
  }
}

Interzeptoren müssen konfiguriert werden, damit sie in der Applikation dann auch Anwendung finden. Standardmäßig sind sie deaktiviert. Also muss im letzten Schritt der Interzeptor im CDI Deployment-Deskriptor definiert werden.

Dazu wird die beans.xml um folgende Zeilen erweitert.


    de.holisticon.blog.friendlyInvaders.interceptor.LoggingInterceptor

Jetzt ist der Interzeptor aktiviert und jeder Checkout wird geloggt.

Die mit CDI eingeführten Annotationen ermöglichen es, Interzeptoren an Beans zu binden, ohne dass eine Abhängigkeit zwischen der konkreten Bean und einem konkreten Interzeptor entsteht.

Dekoratoren

Mit Dekoratoren bietet CDI eine saubere Umsetzung des Decorator Pattern.

Der Dekorator und die zu dekorierende Klasse müssen dabei das gleiche Interface implementieren. Der Dekorator wird mit @Decorator annotiert.

@Decorator
public abstract class CheckoutServiceDecorator
    implements CheckoutService, Serializable {
   ...
  @Inject @Delegate
  private CheckoutService checkoutService;

    public void giveKudos( BlogArticle blogArticle, float amount) {
      if (blogArticle.getAuthor().getName().endsWith(" Erck")){
        logger.info("No Kudos in the form of money.");
    }else{
        checkoutService.giveKudos(blogArticle, amount);
    }
  }
}

Jeder Dekorator muss ein mit @Delegate annotiertes Objekt besitzen: das Objekt, das dekoriert werden soll. Alle Methoden, die dekoriert werden sollen, bzw. um Geschäftslogik erweitert, werden vom Dekorator implementiert. Im Codebeispiel soll folglich die Methode giveKudos(blogArticle, amount) des CheckoutServiceController dekoriert werden.

Auch Dekoratoren sind Deployment-spezifisch und müssen in der beans.xml aktiviert werden.


    de.holisticon.blog.friendlyInvaders.decorator.CheckoutServiceDecorator

Dank des Dekorators bekommt jetzt keiner, der meinen Nachnamen trägt, mehr Anerkennung in Form von Centbeträgen.

Interzeptoren vs. Dekoratoren

Da im Artikel ja schon vorgestellt wurde, wie einfach sich Interzeptoren mit den Interzeptor-Bindings an Klassen binden lassen, bleibt die Frage offen, wann Interzeptoren und wann Dekoratoren die richtige Wahl sind.

Ein konkreter Interzeptor lässt sich an beliebige Klassen jedes Java-Typs binden und sollte eingesetzt werden, um technische Aspekte wie z.B. Logging zu realisieren.

Ein konkreter Dekorator lässt sich nur an eine Klasse eines konkreten Java-Typs binden, kennt somit die Semantik und sollte eingesetzt werden, um eine Klasse mit Aspekten der Geschäftslogik zu dekorieren.

Produzenten

Als letztes Werkzeug sollen die Produzenten vorgestellt werden. Sie sind mit @Produces annotierte Methoden, die immer genau dann aufgerufen werden, wenn eine Instanz eines zu produzierenden Objektes verlangt wird und noch keine entsprechende Instanz erzeugt wurde.

Im Beispielcode wird ein Produzent für den Logger erzeugt.

public class LoggerProducer {

  @Produces
  public Logger produceLogger(InjectionPoint injectionPoint){
    return LoggerFactory.getLogger(injectionPoint.getMember()
        .getDeclaringClass().getName());
  }
}

Soll im Kontext einer Klasse ein Logger via Depenedency Injection injiziert werden und wurde für diesen Kontext noch kein Logger erzeugt, so wird er über die Methode produceLogger(injectionPoint) erzeugt. Im Codebeispiel ist zu sehen, dass der Logger in Abhängigkeit des Typs der aufrufenden Klasse erzeugt wird. Wird eine Instanz über den Produzenten erzeugt, so hat sie den Scope @Dependent, d.h., die Instanz nimmt den Scope und somit auch den Kontext der Klasse an, in die sie injiziert wurde.

Einsatzgebiete

Produzenten sind genau dann sinnvoll, wenn der Typ der zu produzierenden Klasse zur Laufzeit variiert bzw., wenn die Initialisierung individuell erfolgt.

Praxis

Friendly Invaders (deutsch: Freundliche Eindringlinge)

Um natürlich nicht einfach nur staubigen Quellcode zu lesen, sondern das Ganze auch in der Praxis zum Fliegen zu bringen, dient die Beispielapplikation „Friendly Invaders„.

Sinn und Unsinn

Die Beispielapplikation ermöglicht Autoren von Blogbeiträgen, einem via RSS angebundenen Blog Anerkennung (Kudos) zu zeigen. Dazu befindet sich unter jedem Blogartikel ein „Give Kudos“ Button.

"Give Kudos" Button

Wurde dieser Button geklickt, wird das Kudos in einen Warenkorb gepackt. Wird auf den Warenkorb, der durch eine Bean im Conversation Scope implementiert wurde, geklickt, so wird ein einstufiger Checkout vollzogen und schließt den Prozess der Anerkennung ab.

cid des Conversation Scope als Parameter

cid des Conversation Scope als Parameter

Interzeptoren, Logger, Dekoratoren und Observer erzeugen Text auf der Console, der ihre Aktivität zweifelsfrei belegen soll.

[STDOUT] Tweet:
Roman Schlömmer: Retrospektive – Prozessmanagement im Branchenfokus
[STDOUT] Mail:
Roman Schlömmer: Retrospektive – Prozessmanagement im Branchenfokus
[de.holisticon.blog.friendlyInvaders.interceptor.LoggingInterceptor]
checkout: Thu May 26 14:11:40 CEST 2011

Lebensraum

Um die Applikation auszuführen, empfehle ich den JBoss 6 Application Server. Soll die Anwendung in Eclipse, mit Serveradapter für den JBoss 6 Application Server und API zum Editieren der beans.xml, zum Laufen gebracht werden, so müssen in Eclipse die JBoss Tools 3.2 installiert werden.

Fazit

Innerhalb der Blogserie wurde die Werkzeugkiste von CDI vorgestellt. Alle Werkzeuge erleichtern den Entwicklern von Applikationen die Arbeit erheblich und helfen, die Basics wie Entkopplung und Separation of Concerns umzusetzen. Das Ziel, ein umfassendes DI Framework im Java EE Standard zu etablieren und eine bessere Verbindung zwischen JSF und EJB bzw. der Präsentationsschicht und Geschäftslogik herzustellen, wurde definitiv erreicht.

Ob CDI mit der Referenzimplementierung Weld in der Praxis in der Lage ist, Frameworks wie Spring oder Seam aus der Welt zu schaffen, wird sich zeigen.

Was ist eure Meinung dazu? Hat CDI das Zeug, Spring oder Seam als DI Frameworks zu verdrängen? Über Kommentare würde ich mich freuen.

Über den Autor

Mr Norman Erck M.Sc. started developing websites as a teen in 1999 driven by his fascination for the possibilities of the rising e-business technologies. He is now a certified ScrumMaster and Enterprise- Software-Architect who has worked on e-business projects for over seven years. He takes the role of a scrum master and architect for IT projects in large companies. He is a speaker on conferences about CDI as well.

2 Kommentare

  1. Klasse Einführung in die CDI Thematik, kompakt und gut verständlich. Wurde mir selber von Kollegen empfohlen, die ihn gut gefunden haben.

    @Weld / Seam / Spring:
    Weld ist nach meinem Verständnis schon ein Teil von Seam 3 bzw. Seam3 ist inzwischen eine Sammlung von Tools rund um Weld, die die Funktionalität von Seam 2 abdecken soll.
    Spring hat noch Vorteile bei der Initialisierung von Beans. Daran arbeitet Weld wohl auch, aber zumindest ich hatte damit Schwierigkeiten. Ausserdem bietet Spring noch viel mehr und ist in vielen Projekten eingesetzt: das wird sich schon halten. An einer Seam 3 / Spring Integration wird ja zur Zeit auch gearbeitet.

Antwort hinterlassen