SeamFramework.orgCommunity Documentation

Capítulo 9. Eventos

9.1. Observadores de eventos
9.2. Produtores de Eventos
9.3. Resgistrando observadores (observers) dinamicamente
9.4. Bindings de eventos com os membros
9.5. Múltiplos evento bindings
9.6. Observadores transacionais

The Web Beans event notification facility allows Web Beans to interact in a totally decoupled manner. Event producers raise events that are then delivered to event observers by the Web Bean manager. This basic schema might sound like the familiar observer/observable pattern, but there are a couple of twists:

Um método observador (observer method) é um método de um Web Bean com um parâmetro anotado@Observes.

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

The annotated parameter is called the event parameter. The type of the event parameter is the observed event type. Observer methods may also specify "selectors", which are just instances of Web Beans binding types. When a binding type is used as an event selector, it is called an event binding type.

@BindingType

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

Especificamos os bindings de eventos do observador anotando o parâmetro do evento:

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

Um método observador não necessita especificar qualquer bindings de evento — neste caso, ele está interessado em todos os eventos de um determinado tipo. Se ele especificar o bindings de evento, ele estará apenas interessado em eventos que também têm esses bindings de evento.

O método observador pode ter parâmetros adicionais que são injetados de acordo com a semântica usual de injeção de parâmetros em métodos de Web Beans

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

O evento produtor pode obter um objeto notificador de evento (event notifier) por injeção:

@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.

Um produtor lança eventos chamando o método fire() da interface Event, passando um objeto event object:

documentEvent.fire(document);

Um objeto de evento pode ser uma instância de qualquer classe Java que não tem qualquer tipo de variáveis ou parâmetros tipo curinga. O evento será entregue a cada método observador que:

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.

Para especificar um "seletor", o produtor do evento pode passar uma instância do tipo de binding do evento para o método fire():

documentEvent.fire( document, new AnnotationLiteral<Updated

>(){} );

O classe auxiliar AnnotationLiteral permite instanciar tipo de binding inline, uma vez que, esta é de outra maneira, difícil de fazer em Java.

O evento será entregue a todo método observador (observer method) que:

Alternativamente, bindings de eventos podem ser especificados anotando o ponto de injeção do notificador do evento:

@Observable @Updated Event<Document

> documentUpdatedEvent

Em seguida, todos os eventos disparados por essa instância de Event tem o binding de evento anotada. O evento será entregue a cada método observador que:

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

Tipo de binding de eventos podem ser especificados pelo notificador do evento no ponto de injeção ou passando instâncias do tipo de binding de evento para o método observador observe() method:

documentEvent.observe( new Observer<Document

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

Um tipo de binding de evento pode ter anotações membro:

@BindingType

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

O valor do membro é utilizado para reduzir as mensagens entregues ao observador:

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

Membros de tipo de binding de evento podem ser especificados estaticamente pelo produtor do evento, através de anotações no ponto de notificação do evento:

@Observable @Role(ADMIN) Event<LoggedIn

> LoggedInEvent;}}

Alternativamente, o valor do membro do tipo de binding de evento pode ser determinado dinamicamente pelo produtor do evento. Vamos começar escrevendo uma sub classe abstrata de AnnotationLiteral:

abstract class RoleBinding 

    extends AnnotationLiteral<Role
> 
    implements Role {}

O produtor do evento (event producer) passa uma instância dessa classe para fire():

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

Tipos de event binding podem ser combinados, por exemplo:

@Observable @Blog Event<Document

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

Quando esse evento ocorre, todos os métodos observadores que seguem esse evento serão 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) { ... }}}

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

Existem três tipos de observadores transacionais:

Observadores transacionais são muito importantes para um modelo de objetos stateful como o Web Beans, porque o estado é muitas vezes mantido por mais de uma única transação atômica.

Imagine que fizemos cache do conjunto de resultados da consulta JPA no escopo de aplicação (application scope):

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

From time to time, a Product is created or deleted. When this occurs, we need to refresh the Product catalog. But we should wait until after the transaction completes successfully before performing this refresh!

O Web Bean que cria e remove Products pode lançar eventos, por exemplo:

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

E agora Catalog pode observar os eventos após o término da transação com sucesso:

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