SeamFramework.orgCommunity Documentation
A habilidade de notificação de eventos da Web Beans permite aos Web Beans interagirem de maneira totalmente desacoplada. Produtores de eventos disparam eventos que são entregues aos observadores de eventos pelo gerenciador do Web Bean. Esse esquema básico pode soar como o familiar padrão observador/observável padrão, mas há várias diferenças:
não só os produtores são desacoplados dos observadores; os observadores são totalmente desacoplados dos produtores,
os observadores podem especificar uma combinação de "seletores" para reduzir o conjunto de eventos que irão receber notificações, e
os observadores podem ser notificados imediatamente ou podem especificar que a notificação do evento deveria esperar até o término da transação corrente
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) { ... }
O parâmetro anotado é chamado parâmetro de evento. O tipo do parâmetro de evento é observado pelo event type. Os métodos observadores também podem especificar "seletores", que são apenas instâncias de tipo de bindings de Web Beans. Quando um tipo de binding é utilizado como um seletor de evento, é chamado de tipo de binding de evento.
@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 nenhum binding de evento nesse 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
A anotação @Observable
define, implicitamente, uma Web Bean com o escopo @Dependent
e tipo de implantação @Standard
, com uma implementação fornecida pelo gerenciador do Web Bean.
Um produtor lança eventos chamando o método fire()
da interface Event
e 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 nenhum tipo de variáveis ou parâmetros tipo curinga. O evento será entregue a cada método observador que:
tenha um parâmetro evento em que o evento objeto é atribuído, e
não especifique nenhum evento bindings.
O gerenciador do Web Bean simplesmente chama todos os métodos de observação, passando o objeto do evento como o valor do parâmetro do evento. Se algum método observador lança uma exceção, o gerenciador do Web Bean deixa de chamar os métodos do observador, e a exceção é relançada pelo método fire()
.
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, o que é difícil de se fazer em Java.
O evento será entregue a todo método observador (observer method) que:
tenha um parâmetro evento em que o evento objeto é atribuído, e
não especifique nenhum binding de evento exceto para o binding de evento passado para o fire()
.
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:
tenha um parâmetro evento em que o evento objeto é atribuído, e
não especifique nenhum binding de evento exceto para os bindings de evento passados para o fire()
ou os bindings de evento anotados do ponto de injeção do notificador de evento.
Frequentemente, é útil registrar um evento observador dinamicamente. A aplicação pode implementar a interface Observer
e registrar a instância com um evento notificador chamando o método observe()
.
documentEvent.observe( new Observer<Document
>() { public void notify(Document doc) { ... } } );
Tipos 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, por meio 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 subclasse 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 binding de evento 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) { ... }}}
Observadores transacionais recebem notificações de eventos durante, antes ou após a conclusão da transação em que o evento foi disparado. Por exemplo: o seguinte método observador necessita atualizar um conjunto de resultados de uma consulta que está armazenada no contexto da aplicação, mas apenas quando as transações que atualizam a àrvore de Category
forem concluídas com sucesso:
public void refreshCategoryTree(@AfterTransactionSuccess @Observes CategoryUpdateEvent event) { ... }
Existem três tipos de observadores transacionais:
@AfterTransactionSuccess
observadores são chamados durante a fase após a conclusão da transação, mas somente se a transação tiver sido concluída com sucesso
@AfterTransactionFailure
observadores são chamados durante a fase após a conclusão da transação, mas somente se a transação não tiver sido concluída com sucesso
@AfterTransactionCompletion
observadores são chamados durante a fase após a conclusão da transação
@BeforeTransactionCompletion
observadores são chamados durante a fase antes da conclusão da transação
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;
}
}
De tempos em tempos, um Product
é criado ou excluído. Quando isso ocorre, é preciso atualizar o catálogo de Product
. Mas devemos esperar até depois da transação ser concluída com sucesso antes de realizar essa atualização!
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 concluída 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);
}
}