Communardo Software GmbH, Kleiststraße 10 a, D-01129 Dresden
0800 1 255 255

Events mit CDI und EJB 3.1

Vor der Einführung von CDI (JSR-299/JSR-330) und EJB 3.1 war die Umsetzung von asyn­chro­nen Prozessen rela­tiv umständ­lich und der Einsatz von JMS meist unum­gäng­lich. Ich möchte hier kurz zei­gen, dass es mit EJB 3.1 eine wei­tere Möglichkeit gibt schnell und effi­zi­ent ein klas­si­sches Beobachterpattern umzu­set­zen ohne auf JMS zurück­grei­fen zu müssen.

Dazu benö­ti­gen wir zunächst einen Service der Ereignisse (Events) erzeu­gen kann (Producer).

import javax.ejb.Stateless;
import javax.enterprise.event.Event;
import javax.inject.Inject;

@Stateless
public class Producer {

  @Inject
  private Event<String> events;

  public void fireEvent() {
    this.events.fire("event");
  }
}

Die Klasse ist vom Typ Stateless der per Dependency Injection ein javax.enterprise.event.Event Object inji­ziert wird. Mit Hilfe die­ser Klasse kön­nen anschlie­ßend Events ver­schickt werden.

Für den Empfang der Events imple­men­tie­ren wir einen wei­te­ren Stateless Service der eine Methode lis­ten besitzt. Der Parameter ist mit @Observes anno­tiert und wird vom System als Empfänger für Events vom Typ String erkannt. Beim Verschicken von Events wer­den alle poten­ti­el­len Empfängermethoden für den Eventtyp aufgerufen.

import javax.ejb.Stateless;
import javax.enterprise.event.Observes;

@Stateless
public class Consumer {

    public void listen(@ObservesString eventMessage) {
        System.out.println(eventMessage);
    }

}

Die Ausführung der lis­ten Methode erfolgt syn­chron und im glei­chen trans­ak­tio­nel­len Kontext wie die fireEvent Methode. Wird bei der Verarbeitung des Events eine Ausnahme gewor­fen so wird die kom­plette Transaktion zurück­ge­rollt (und umge­kehrt). Mit der Angabe der Transaktionsphase für die Eventverarbeitung lässt sich die­ser Zusammenhang steuern:

import javax.ejb.Stateless;
import javax.enterprise.event.Observes;
import javax.enterprise.event.TransactionPhase;

@Stateless
public class Consumer {

    public void listen(@Observes(during = TransactionPhase.AFTER_SUCCESS) String eventMessage) {
        System.out.println(eventMessage);
    }

}

Damit wird das Event von der lis­ten Methode erst nach einer erfolg­rei­chen Transaktion ver­ar­bei­tet. Schlägt die Transaktion fehl wird die Methode nicht aufgerufen.

Für die Eventverarbeitung exis­tie­ren die fol­gen­den Transaktionsphasen:

  • IN_PROGRESS
  • BEFORE_COMPLETION
  • AFTER_COMPLETION
  • AFTER_SUCCESS
  • AFTER_FAILURE

Somit las­sen sich z.B. auch Eventhandler schrei­ben die nur nach fehl­ge­schla­ge­nen Transaktionen auf­ge­ru­fen werden.

Mit der in EJB 3.1 neu ein­ge­führ­ten Annotation @Asynchronous kön­nen die Methoden zur Eventverarbeitung asyn­chron aus­ge­führt wer­den. Die Eventverarbeitung ist damit von der Erzeugung kom­plett entkoppelt:

import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import javax.enterprise.event.Observes;

@Stateless
public class Consumer {

    @Asynchronous
    public void listen(@Observes String eventMessage) {
        System.out.println(eventMessage);
    }

}

Ich habe eine kleine Webapplikation geschrie­ben wel­che den Einsatz von CDI, EJB 3.1 und JSF mit Berücksichtigung der ein­zel­nen Transaktionsphasen demons­triert. Viel Spaß damit.
events-tutorial.zip

Related Posts

Mir sind paar klei­nig­kei­ten mit dei­ner CDI beans aufgefallen.

public void afterFailure(@Observes(during = TransactionPhase.AFTER_FAILURE) String eventMessage) {
syn­chro­ni­zed (this.messages) {
this.messages.add("afterFailure: " + eventMessage);
}
System.out.println(eventMessage);
}

Warun syn­chro­ni­zed ?? Du bist doch in einer stateless bean. In einem stateless bean soll­test du nie auf einen mem­eber Variable schrei­ben zugreifen. 

Aussderdem soll­test du lauf EJB restrik­tion auch keine this. ver­we­den. Für dein bei­piel wäre ein @Singleton geeignet.

Avatar Stefan Dieringer

Vielen Dank für dein Feedback! Ich habe deine Anmerkungen ins Tutorial aufgenommen.

Comments are closed.

Pin It on Pinterest