SeamFramework.orgCommunity Documentation

Capitolo 9. Eventi

9.1. Osservatori di eventi
9.2. Produttori di eventi
9.3. Registering observers dynamically
9.4. Event bindings with members
9.5. Multiple event bindings
9.6. Osservatori transazionali

Il sistema di notifica a eventi di Web Beans consente a Web Beans di interagire in maniera totalmente disaccoppiata. I produttori di eventi sollevano eventi che vengono consegnati agli osservatori di eventi tramite il manager Web Bean. Lo schema base può suonare simile al familiare pattern observer/observable, ma ci sono un paio di differenze:

Un metodo osservatore è un metodo di un Web Bean con un parametro annotato @Observes.

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

Il parametro annotato viene chiamato parametro evento. Il tipo di parametro evento è il tipo evento osservato. I metodi osservatori possono anche specificare dei "selettori", che sono solo istanze di tipi di binding di Web Beans. Quando un tipo di binding viene usato come selettore di eventi viene chiamato tipo binding di evento.

@BindingType

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

Specifichiamo i binding di evento del metodo osservatore annotando il parametro evento:

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

Un metodo osservatore non ha bisogno di specificare alcun binding di evento—in questo caso è interessato a tutti gli eventi di un particolare tipo. Se specifica dei binding di evento, è solamente interessato agli eventi che hanno anche gli stessi binding di evento.

Il metodo osservatore può avere parametri addizionali che vengono iniettati secondo la solita semantica di iniezione del parametro del metodo Web Beans.

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

The event producer may obtain an event notifier object by injection:

@Observable Event<Document

> documentEvent

The @Observable annotation implicitly defines a Web Bean with scope @Dependent and deployment type @Standard, with an implementation provided by the Web Bean manager.

A producer raises events by calling the fire() method of the Event interface, passing an event object:

documentEvent.fire(document);

An event object may be an instance of any Java class that has no type variables or wildcard type parameters. The event will be delivered to every observer method that:

The Web Bean manager simply calls all the observer methods, passing the event object as the value of the event parameter. If any observer method throws an exception, the Web Bean manager stops calling observer methods, and the exception is rethrown by the fire() method.

To specify a "selector", the event producer may pass an instance of the event binding type to the fire() method:

documentEvent.fire( document, new AnnotationLiteral<Updated

>(){} );

The helper class AnnotationLiteral makes it possible to instantiate binding types inline, since this is otherwise difficult to do in Java.

The event will be delivered to every observer method that:

Alternatively, event bindings may be specified by annotating the event notifier injection point:

@Observable @Updated Event<Document

> documentUpdatedEvent

Then every event fired via this instance of Event has the annotated event binding. The event will be delivered to every observer method that:

It's often useful to register an event observer dynamically. The application may implement the Observer interface and register an instance with an event notifier by calling the observe() method.

documentEvent.observe( new Observer<Document

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

Event binding types may be specified by the event notifier injection point or by passing event binding type instances to the observe() method:

documentEvent.observe( new Observer<Document

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

An event binding type may have annotation members:

@BindingType

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

The member value is used to narrow the messages delivered to the observer:

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

Event binding type members may be specified statically by the event producer, via annotations at the event notifier injection point:

@Observable @Role(ADMIN) Event<LoggedIn

> LoggedInEvent;}}

Alternatively, the value of the event binding type member may be determined dynamically by the event producer. We start by writing an abstract subclass of AnnotationLiteral:

abstract class RoleBinding 

    extends AnnotationLiteral<Role
> 
    implements Role {}

The event producer passes an instance of this class to fire():

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

Event binding types may be combined, for example:

@Observable @Blog Event<Document

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

When this event occurs, all of the following observer methods will be notified:

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) { ... }}}

Transactional observers receive their event notifications during the before or after completion phase of the transaction in which the event was raised. For example, the following observer method needs to refresh a query result set that is cached in the application context, but only when transactions that update the Category tree succeed:

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

There are three kinds of transactional observers:

Transactional observers are very important in a stateful object model like Web Beans, because state is often held for longer than a single atomic transaction.

Si immagini di avere cachato un risultato di query JPA nello scope di applicazione:

@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;
    }
    
}

Di tanto in tanto un Product viene creato o cancellato. Quando questo avviene occorre aggiornare il catalogo del Product. Ma si dovrebbe aspettare che la transazione abbia completato con successo prima di eseguire l'aggiornamento!

The Web Bean that creates and deletes Products could raise events, for example:

@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
>(){});
    }
    
    ...
    
}

And now Catalog can observe the events after successful completion of the transaction:

@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);
    }
    
}