SeamFramework.orgCommunity Documentation

Capítulo 9. Eventos

9.1. Observadores de evento
9.2. Productores de Evento
9.3. Registro dinámico de observadores
9.4. Enlaces de evento con miembros
9.5. Enlaces de evento múltiples
9.6. Observadores transaccionales

La notificación de eventos de Web Beans permite a Web Beans interactuar de una manera completamente disociada. Los productores crean eventos que son enviados luego a observadores de evento por el administrador de Web Beans. Este esquema básico podría parecerse al patrón conocido observador/observable, pero hay un par de cambios:

Un método de observador es un método de un Web Bean con un parámetro anotado @Observes.

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

El parámetro anotado es llamado el parámetro de evento. El tipo del parámetro de evento es el tipo de evento observado. Los métodos de observador pueden también especificar "selectores", los cuales son sólo instancias de tipos de enlaces de Web Beans. Cuando un tipo de enlace se utiliza como un selector de evento, es llamado un tipo de enlace de evento.

@BindingType

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

Especificamos los enlaces de evento del método de observador al anotar el parámetro de evento:

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

Un método de observador no necesita especificar ningún enlace de evento — en este caso está interesado en todos los eventos de un tipo determinado. Si no especifica enlaces de eventos, sólo está interesado en eventos que también tienen esos enlaces de eventos.

El método de observador puede tener parámetros adicionales, los cuales se inyectan de acuerdo con la semántica de inyección del parámetro usual de método de Web Beans:

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

El productor de evento puede obtener un objeto que notifica el eventopor inyección:

@Observable Event<Document

> documentEvent

La anotación @Observable define implícitamente un Web Bean con ámbito @Dependent y tipo de despliegue @Standard, con una implementación provista por el administrador de Web Bean.

Un productor crea eventos llamando al método fire() de la interfaz del Evento, pasando un objeto de evento:

documentEvent.fire(document);

Un objeto de evento puede ser una instancia de una clase de Java que no tiene variables de tecla o parámetros de comodines. El evento será entregado a cada método de observador que:

El administrador de Web Beans simplemente llama a todos los métodos de observador, pasando el objeto del evento como el valor de un parámetro de evento. Si cualquier método de observador produce una excepción, el administrador de Web Beans se detiene llamando a los métodos de observador y la excepción es reenviada por el método fire().

Para especificar un "selector", el productor del evento puede pasar una instancia del tipo de enlace del evento al método fire():

documentEvent.fire( document, new AnnotationLiteral<Updated

>(){} );

La Anotación Literal clase auxiliar hace posible crear una instancia de tipos de enlaces en línea, ya que de otra manera es difícil hacerlo en Java.

El evento será entregado a cada método de observador que:

De modo alternativo, se pueden especificar eventos de enlaces anotando el punto de inyección de notificador de eventos:

@Observable @Updated Event<Document

> documentUpdatedEvent

Luego cada evento disparado vía esta instancia de Evento tiene el enlace de evento anotado. El evento será enviado a cada método de observador que:

Suele ser útil registrar dinámicamente un observador de evento. La aplicación puede implementar la interfaz del Observador y registrar una instancia con un notificador de evento llamando el método de observe().

documentEvent.observe( new Observer<Document

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

Los tipos de enlace de evento pueden ser especificados por el punto de inyección del notificador del evento o pasando las instancias de tipo de enlace de evento al método observe():

documentEvent.observe( new Observer<Document

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

Un tipo de enlace de evento puede tener miembros de anotación:

@BindingType

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

El valor de miembro se utiliza para limitar los mensajes enviados al observador:

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

Los miembros de tipo de enlace de evento se pueden especificar estáticamente por el producto del evento, a través de anotaciones en el punto de inyección del notificador de evento.

@Observable @Role(ADMIN) Event<LoggedIn

> LoggedInEvent;}}

Alternativamente, el valor del miembro de tipo de enlace de evento puede ser determinado de modo dinámico por el productor de evento. Empezamos por escribir una subclase abstracta de AnnotationLiteral:

abstract class RoleBinding 

    extends AnnotationLiteral<Role
> 
    implements Role {}

El productor de evento pasa una instancia de esta clase a fire():

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

Los tipos de enlaces de evento pueden combinarse, por ejemplo:

@Observable @Blog Event<Document

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

Cuando un evento ocurre, todos los siguientes métodos de observador serán notificados:

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

Los observadores transaccionales reciben las notificaciones de sus eventos antes o después de la fase de finalización de la transacción en la se creó el evento. Por ejemplo, el siguiente método de observador necesita actualizar un conjunto de resultados de petición almacenado en caché en el contexto de aplicación, pero sólo cuando las transacciones que actualizan el árbol de Categoría tengan éxito:

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

Hay tres clases de observadores transaccionales:

Los observadores transaccionales son muy importantes en un modelo de objetos con estado como Web Beans, porque el estado suele ser mantenido para más de una transacción atómica.

Imagine que hemos almacenado en caché una serie de resultados de petición JPA en el ámbito de la aplicación:

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

De vez en cuando, se crea o borra un Producto. Cuando esto ocurre, necesitamos refrescar el catálogo del Producto. No obstante, deberíamos esperar hasta después de que la transacción finalice exitosamente antes de ¡actualizar!

El Web Bean que crea y borra Productos podría crear eventos, por ejemplo:

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

Ahora el Catálogo puede observar los eventos después de finalizar la transacción exitosamente:

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