I Message-Driven Bean sono gli unici bean di EJB3 con funzionamento asincrono, attivato dalla ricezione tramite JMS (Java Message Service) di un particolare messaggio su un argomento o coda alla quale si siano iscritti.
Comunicazione asicrona e messaging
La maggior parte delle comunicazioni fra i componenti di un'architettura software è generalmente sincrona: questo significa che mittente e destinatario della comunicazione devono essere presenti contemporaneamente affinchè la comunicazione abbia successo ed inoltre il destinatario deve attendere il completamento della trasmissione da parte del mittente prima di poter processare una eventuale richiesta.
Questo è anche cio che ad esempio accade nel caso di RMI (Remote Method Invocation).
In alcune circostanze sorge tuttavia la necessità di mettere in piedi una forma di comunicazione asincrona fra i vari componenti del sistema; è ad esempio ciò che accade quando si vuole lanciare un'elaborazione batch che dura diverse ore senza voler attendere il completamento della stessa prima di svolgere ulteriori operazioni.
In Java EE questo concetto prende il nome di Messaging e i software che consentono di realizzare una comunicazione asincrona fra i vari componenti prendono il nome di Message-Oriented Middleware (MOM).
In particolare quando un messaggio viene inviato, il MOM memorizza il messaggio nel posto (destination) indicato dal mittente (producer) e, in un momento successivo, un componente (consumer) interessato al messaggio può recuperare lo stesso per accederne al contenuto.
Modelli di messagging
Un modello di messaging definisce il modo in cui un determinato numero di producer e consumer sono coinvolti nella comunicazione asincrona.
Esistono fondamentalmente due modelli di messaging: point-to-point e publish-subscribe.
point-to-point
Il modello point-to-point prevedere la presenza di un singolo messaggio che viaggia da un singolo provider a un singolo consumer: le destinazioni del messaggio in un modello point-to-point prendono il nome di queues (code).
Se esistono più potenziali consumer per il messaggio esistente soltanto uno fra questi verrà scelto per la consegna del messaggio.
publish-subscribe
Il modello publish-subscribe invece prevede la presenza di un singolo producer che crea il messaggio e più consumer (in questo caso detti subscriber) connessi alla destinazione (in questo caso detta topic) nello stesso istante che possono riceverlo.
Quest'ultimo modello di messaging si adatta molto bene nel caso in cui si vuole effettuare il broadcasting di informazioni.
Java Messaging Service (JMS)
Java Messaging Service fornisce un accesso standard e uniforme ai Message Oriented Middleware.
In JMS esistono alcuni oggetti di fondamentale importanza:
ConnectionFactory
: è la factory per la creazione di oggettiConnection
Connection
: rappresenta una connessione attiva con il MOM. Tali connessioni sono thread-safe e progettate per essere condivisibili dal momento che l'apertura di una nuova connessione costituisce un'operazione particolarmente onerosa.Session
: attraverso una sessione è possibile inviare messaggi verso una particolare destinazione (le Session vengono create a partire da una connection).Destination
: rappresenta la destinazione di un messaggio.
Interfaccia Message
Un messaggio in JMS è rappresentato dall'interfaccia Message
, questa fornisce un meccanismo di incapsulamento dei dati in tre parti: l'header, le proprietà ed il corpo del messaggio.
L'header costituisce l'intestazione del messaggio ed è formato da coppie nome-valore che sono comuni a tutti i messaggi.
Le proprietà sono costituisce da coppie nome-valore ma sono create esplicitamente dall'applicazione.
Il body contiene il messaggio vero e proprio e può essere di diversi tipi: ObjectMessage
, BytesMessage
, MapMessage
, StreamMessage
o TextMessage
.
Message-driven bean (MDB)
I message-driven bean (MDB) sono component EJB progettati per svolgere il ruolo di consumer all'interno di una comunicazione asincrona.
I vantaggi legati all'utilizzo dei MDB sono diversi: in particolare i MDB sono organizzati in un pool così che un messaggio entrante possa essere servito dalla prima istanza di MDB disponibile nel pool, inoltre l'utilizzo dei MDB ci consente di evitare di occuparci degli aspetti legati alla creazione di connessioni, apertura e chiusura di sessioni, lookup di destinazioni etc...
Come gli altri Enterprise Java Bean, anche i MDB sono dei POJO (Plain Old Java Object) che seguono alcune semplici regole:
- implementare l'interfaccia
MessageListener
- essere classi concrete publiche
- essere POJO
- avere un costruttore senza argomenti
Ciclo di vita di un MDB
Il ciclo di vita di un MDB è piuttosto semplice e può essere riassunto nei seguenti punti:
- Il container EJB crea le istanze degli Enterprise Java Bean, iniettandone le risorse e piazzandole nel pool
- Quando arriva un messaggio, una delle istanze di MDB presenti nel pool viene prelevata e viene invocato il metodo
onMessage
dell'istanza - Quando il metodo
onMessage
si conclude la risorsa MDB viene riposta nel pool per processare un eventuale nuovo messaggio.
Ancora una volta è possible gestire le fasi pre-distruzione e post-costruzione attraverso i metodi di callback postConstruct
e preDestroy
.
La struttura di un MDB può essere in definitiva riassunta nel seguente modo:
@MessageDriven(
name="MDBProcessor",
activationConfig = {
@ActivationConfigProperty(
propertyName="destinationType",
propertyValue="javax.jms.Queue"
),
@ActivationConfigProperty(
propertyName="destinationName",
propertyValue="jms/MiaCoda"
)
}
)
public class MioMDB implements MessageListener {
@PostConstruct
public void inizializza() {
// ...
}
@PreDestroy
public void distruggi() {
// ...
}
public void onMessage(Message message) {
// ...
}
}
Come vediamo dal codice l'annotazione @MessageDriven
identifica il POJO come Message-driven Bean e specifica la configurazione dello stesso attraverso le @ActivationConfigProperty
.
La prima delle due property indica che la destinazione è una queue (coda) per cui il MDB lavorerà all'interno di un modello di messaging point-to-point.
La seconda property indica il nome della coda dalla quale prelevare i messaggi (nel nostro caso "jms/MiaCoda").
Il MDB implementa l'interfaccia MessageListener
e in particolare definisce il metodo onMessage
che elabora i messaggi entranti.
I metodi inizializza()
e distruggi()
costituiscono le chiamate di callback e verranno invocati dopo la costruzione e prima della distruzione dell'EJB.