SeamFramework.orgCommunity Documentation

Kapitel 9. Ereignisse

9.1. Ereignis-Observer
9.2. Ereignis-Producer
9.3. Dynamische Registrierung von Observern
9.4. Ereignis-Bindings mit Mitgliedern
9.5. Multiple Ereignis-Bindings
9.6. Transaktionale Observer

Die Web Beans Ereignisbenachrichtigungs-Facility gestattet es Web Beans auf eine völlig abgekoppelte Weise zu interagieren. Ereignis Producer bringen Ereignisse auf, die dann vom Web Bean Manager an Ereignis Observer geliefert werden. Dieses schlichte Schema klingt zwar etwas nach dem bekannten Observer/observierbar Muster, aber es gibt ein paar Überraschungen:

Eine Observer-Methode ist eine Methode eines Web Beans mit einem Parameter, der @Observes annotiert ist.

public void onAnyDocumentEvent(@Observes Document document) { ... }

Der annotierte Parameter heißt Ereignisparameter. Der Typ des Ereignisparameter ist der beobachtete Ereignistyp. Observer-Methoden können auch "Selektoren" festlegen, die nur Instanzen von von Web Beans Binding-Typen sind. Wird ein Binding-Typ als Ereignis-Selektor verwendet, so wird dies als Ereignis Binding-Typ.

@BindingType

@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface Updated { ... }

Wir legen die Ereignis-Bindings der Observer-Methode durch Annotation des Ereignisparameters fest:

public void afterDocumentUpdate(@Observes @Updated Document document) { ... }

Eine Observer-Methode muss keine Ereignis-Bindings — festlegen, in diesem Fall interessiert sie sich für alle Ereignisse eines bestimmten Typs. Legt sie Ereignis-Bindings fest, so interessiert sie sich nur für Ereignisse, die diese Ereignis-Bindings besitzen.

Die Observer-Methode kann zusätzliche Parameter besitzen, die gemäß der üblichen Einspeisungssemantik Web Beans Methodenparameter eingespeist werden:

public void afterDocumentUpdate(@Observes @Updated Document document, User user) { ... }

Der Ereignis-Producer kann ein Ereignisbenachrichtigungs-Objekt durch Einspeisung erhalten:

@Observable Event<Document

> documentEvent

Die @Observable-Annotation definiert implizit ein Web Bean mit Geltungsbereich @Dependent und Deployment-Typ @Standard mit einer durch den Web Bean Manager bereitgestellten Implementierung.

Ein Producer bringt durch Aufruf der fire()-Methode des Event-Interface Ereignisse auf, wobei ein Ereignisobjekt weitergegeben wird:

documentEvent.fire(document);

Ein Ereignis-Objekt kann eine Instanz einer beliebigen Java-Klasse sein, die keine Typ-Variablen oder Platzhalter-Typenparameter besitzt. Das Ereignis wird an jede Observer-Methode geliefert, die:

Der Web Bean Manager ruft einfach alle Observer-Methoden auf und gibt das Ereignis-Objekt als den Wert des Ereignisparameters weiter. Meldet eine Observer-Methode eine Ausnahme, so stoppt der Web Bean Manager den Aufruf von Observer-Methoden und die Ausnahme wird durch die fire()-Methode erneut gemeldet.

Um einen "Selektor" festzulegen kann der Ereignis-Producer eine Instanz des Ereignis-Binding-Typs an die fire()-Methode weitergeben:

documentEvent.fire( document, new AnnotationLiteral<Updated

>(){} );

Die Helferklasse AnnotationLiteral ermöglicht die Instanziierung der Binding-Typen inline, da es andernfalls schwierig ist, die in Java zu tun.

Das Ereignis wird an jede Observer-Methode geliefert, die:

Alternativ können Ereignis-Bindings durch Festlegen des Einspeisungspunkts der Ereignisbenachrichtigung festgelegt werden:

@Observable @Updated Event<Document

> documentUpdatedEvent

Dann besitzt jedes über diese Instanz abgegebene Ereignis das Event annotierte Ereignis-Binding. Das Ereignis wird an jede Observer-Methode geliefert, die:

Es ist oft hilfreich, einen Ereignis-Observer dynamisch zu registrieren. Die Anwendung kann das Observer-Interface implementieren und eine Instanz mit einer Ereignisbenachrichtigung registrieren, indem die observe()-Methode aufgerufen wird.

documentEvent.observe( new Observer<Document

>() { public void notify(Document doc) { ... } } );

Typen von Ereignis-Bindings können durch den Einspeisungspunkt für Ereignisbenachrichtigungen oder Weitergabe von Instanzen von Typen von Ereignis-Bindings an die observe()-Methode festgelegt werden:

documentEvent.observe( new Observer<Document

>() { public void notify(Document doc) { ... } }, 
                                                new AnnotationLiteral<Updated
>(){} );

Ein Ereignis-Binding-Typ kann Annotationsmitglieder besitzen:

@BindingType

@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface Role {
    RoleType value();
}

Der Mitgliederwert dient der Eingrenzung von an den Observer gelieferten Nachrichten:

public void adminLoggedIn(@Observes @Role(ADMIN) LoggedIn event) { ... }

Typenmitglieder von Ereignis-Bindings können durch den Ereignis-Producer statisch festgelegt werden - dies erfolgt über Annotationen am Einspeisungspunkt der Ereignisbenachrichtigungen:

@Observable @Role(ADMIN) Event<LoggedIn

> LoggedInEvent;}}

Alternativ kann der Wert des Typenmitglieds des Ereignis-Bindings dynamisch durch den Ereignis-Producer bestimmt werden. Wir beginnen durch Schreiben einer abstrakten Unterklasse von AnnotationLiteral:

abstract class RoleBinding 

    extends AnnotationLiteral<Role
> 
    implements Role {}

Der Ereignis-Producer gibt eine Instanz dieser Klasse an fire() weiter:

documentEvent.fire( document, new RoleBinding() { public void value() { return user.getRole(); } } );

Typen von Ereignis-Bindings können kombiniert werden, zum Beispiel:

@Observable @Blog Event<Document

> blogEvent;
...
if (document.isBlog()) blogEvent.fire(document, new AnnotationLiteral<Updated
>(){});

Findet dieses Ereignis statt, so werden alle folgenden Observer-Methoden benachrichtigt:

public void afterBlogUpdate(@Observes @Updated @Blog Document document) { ... }
public void afterDocumentUpdate(@Observes @Updated Document document) { ... }
public void onAnyBlogEvent(@Observes @Blog Document document) { ... }
public void onAnyDocumentEvent(@Observes Document document) { ... }}}

Transaktionale Observers erhalten ihre Ereignisbenachrichtigungen vor oder nach der Abschlussphase der Transaktion während derer das Ereignis aufgegekommen ist. Zum Beispiel muss die folgende Observer-Methode einen Satz von Abfrageergebnissen neu laden, der im Applikationskontext gecacht ist, jedoch nur dann, wenn die den Category-Baum aktualisierenden Transaktionen erfolgreich sind:

public void refreshCategoryTree(@AfterTransactionSuccess @Observes CategoryUpdateEvent event) { ... }

Es gibt drei Arten von transaktionalen Observern:

Transaktional Observer sind in einem "stateful" Objektmodell wie Web Beans sehr wichtig, da der Status oft länger bestehen bleibt als eine einzelne atomare Transaktion.

Stellen wir uns vor, wir besitzen einen gecachten Satz von JPA-Abfrageergebnissen im Geltungsbereich der Anwendung:

@ApplicationScoped @Singleton

public class Catalog {
    @PersistenceContext EntityManager em;
    
    List<Product
> products;
    @Produces @Catalog 
    List<Product
> getCatalog() {
        if (products==null) {
            products = em.createQuery("select p from Product p where p.deleted = false")
                .getResultList();
        }
        return products;
    }
    
}

Von Zeit zu Zeit wird ein Product erstellt oder gelöscht. Ist dies der Fall, so müssen wir den Product-Katalog neu laden. Wir sollten damit aber bis nach dem erfolgreichen Abschluss der Transaktion warten!

Das Web Bean, das Products erstellt oder löscht könnte Ereignisse aufrufen, zum Beispiel:

@Stateless

public class ProductManager {
    @PersistenceContext EntityManager em;
    @Observable Event<Product
> productEvent;
    public void delete(Product product) {
        em.delete(product);
        productEvent.fire(product, new AnnotationLiteral<Deleted
>(){});
    }
    
    public void persist(Product product) {
        em.persist(product);
        productEvent.fire(product, new AnnotationLiteral<Created
>(){});
    }
    
    ...
    
}

Und jetzt kann Catalog die Ereignisse nach erfolgreichem Abschluss der Transaktion beobachten:

@ApplicationScoped @Singleton

public class Catalog {
    ...
    
    void addProduct(@AfterTransactionSuccess @Observes @Created Product product) {
        products.add(product);
    }
    
    void addProduct(@AfterTransactionSuccess @Observes @Deleted Product product) {
        products.remove(product);
    }
    
}