SeamFramework.orgCommunity Documentation
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:
non solo i produttori di eventi sono disaccoppiati dagli osservatori; gli osservatori sono completamente disaccoppiati dai produttori,
gli osservatori possono specificare una combinazione di "selettori" per restringere il set di notifiche di eventi da ricevere, e
gli osservatori possono essere notificati immediatamente, o possono specificare che la consegna degli eventi venga ritardata fino alla fine della transazione conrrente
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) { ... }
Il produttore dell'evento può ottenere tramite iniezione un oggetto notificatore d'evento:
@Observable Event<Document
> documentEvent
L'annotazione @Observable
definisce implicitamente un Web Bean con scope @Dependent
e tipo di deploy @Standard
, con un'implementazione fornita dal manager Web Bean.
Un produttore solleva eventi chiamando il metodo fire()
dell'intefaccia Event
, passando un oggetto evento:
documentEvent.fire(document);
Un oggetto evento può essere un'istanza di una classe Java che non ha variabili tipo o parametri tipo wildcard. L'evento verrà consegnato ad ogni metodo osservatore che:
ha un parametro evento a cui l'oggetto evento è assegnabile, e
non specifica binding d'evento.
Il manager Web Bean chiama semplicemente tutti i metodi osservatori, passando l'oggento evento come valore del parametro evento. Se il metodo osservatore lancia un'eccezione, il manager Web Bean smette di chiamare i metodi osservatori, e l'eccezione viene rilanciata dal metodo fire()
.
Per specificare un "selettore" il produttore d'evento può passare un'istanza del tipo di binding d'evento al metodo fire()
:
documentEvent.fire( document, new AnnotationLiteral<Updated
>(){} );
La classe helper AnnotationLiteral
rende possibile istanziare inline i tipi di binding, dato che questo risulta difficile da fare in Java.
L'evento verrà consegnato ad ogni metodo osservatore che:
ha un parametro evento a cui l'oggetto evento è assegnabile, e
non specifica alcun event binding tranne per gli event binding passati a fire()
.
In alternativa gli event binding possono essere specificati annotando il punto di iniezione del notificato d'evento:
@Observable @Updated Event<Document
> documentUpdatedEvent
Quindi ciascun evento sollevato tramite quest'istanza di Event
ha annotato l'event binding. L'evento verrà consegnato ad ogni metodo osservatore che:
ha un parametro evento a cui l'oggetto evento è assegnabile, e
non specifica alcun event binding tranne per gli event binding passati a fire()
o per gli event binding annotati del punto di iniezione del notificatore eventi.
E' spesso utile registrare un osservatore d'evento in modo dinamico. L'applicazione può implementare l'interfaccia Observer
e registrare un'istanza con un notificatore d'evento chiamando il metodo observe()
.
documentEvent.observe( new Observer<Document
>() { public void notify(Document doc) { ... } } );
I tipi di event binding possono essere specificati dal punto di iniezione del notificatore d'eventi o passando istance del tipo di event binding al metodo observe()
:
documentEvent.observe( new Observer<Document
>() { public void notify(Document doc) { ... } },
new AnnotationLiteral<Updated
>(){} );
Un tipo di event binding può avere membri annotati:
@BindingType
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface Role {
RoleType value();
}
Il valore del membro è usato per restringere i messaggi consegnati all'osservatore:
public void adminLoggedIn(@Observes @Role(ADMIN) LoggedIn event) { ... }
I membri del tipo di eventbinding possono essere specificati staticamente dal produttore di eventi tramite annotazioni nel punto di iniezione del notificatore d'evento:
@Observable @Role(ADMIN) Event<LoggedIn
> LoggedInEvent;}}
Alternativamente il valore del membro del tipo di event binding può esserre dinamicamente determinato dal produttore di eventi. Iniziamo scrivendo una sottoclasse astratta di AnnotationLiteral
:
abstract class RoleBinding
extends AnnotationLiteral<Role
>
implements Role {}
Il produttore di eventi passa un'istanza di questa classe a fire()
:
documentEvent.fire( document, new RoleBinding() { public void value() { return user.getRole(); } } );
I tipi di event binding possono essere combinati, per esempio:
@Observable @Blog Event<Document
> blogEvent;
...
if (document.isBlog()) blogEvent.fire(document, new AnnotationLiteral<Updated
>(){});
Quando si genera un evento, tutti i seguenti metodi osservatori verranno notificati:
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) { ... }}}
Gli osservatori transazionali ricevono notifiche d'evento prima o dopo la fase di completamento della transazione, nella quale l'evento viene sollevato. Per esempio, il seguente metodo osservatore ha bisogno di aggiornare il set di risultati della query memorizzato nel contesto dell'applicazione, ma solo quando hanno successo le transazioni che aggiornano l'albero Category
.
public void refreshCategoryTree(@AfterTransactionSuccess @Observes CategoryUpdateEvent event) { ... }
Ci sono tre tipi di osservatori transazionali:
gli osservatori @AfterTransactionSuccess
vengono chiamati dopo la fase di completamento della transazione, ma solo se questa si completa con successo
gli osservatori @AfterTransactionFailure
vengono chiamati dopo la fase di completamento della transazione, ma solo se questa fallisce e quindi non completa con successo
gli osservatori @AfterTransactionCompletion
vengono chiamati dopo la fase di completamento della transazione
gli osservatori @BeforeTransactionCompletion
vengono chiamati prima della fase di completamento della transazione
Gli osservatori transazionali sono molto importanti in un modello ad oggetto stateful come Web Beans, poiché lo stato è spesso mantenuto per un tempo più lungo di una singola transazione atomica.
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!
Il Web Bean che crea o cancella Product
può sollevare eventi, per esempio:
@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 ora Catalog
può osservare gli eventi dopo il completamento (con successo) della transazione:
@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);
}
}