Gestione delle Transazioni con JMS

Introduzione

In questo articolo viene mostrato il modello di gestione delle transazioni messo in atto durante l’utilizzo delle code JMS. Per un dettaglio sula specifica JMS si rimanda agli articoli:

Local Transaction

Per transazione si intende una unità atomica di lavoro in cui sono raggruppate una serie di operazioni. Se una qualsiasi di tale operazioni fallisce, l’intera transazione è annullata (rollback) e con essa tutte le operazioni eseguite, che possono quindi essere attuate di nuovo. Diversamente se tutte le operazioni vanno a buon fine, la transazione è dichiarata conclusa (commit).

Un client JMS può utilizzare una local transaction per raggruppare in modo atomico tutte le operazioni di invio e ricezione di messaggi. L’interfaccia Session delle API JMS forniscono allo scopo i metodi di commit() e rollback(), che possono essere invocati dal client. Il comportamento del provider JMS nei due casi è il seguente:

  • in caso di commit della transazione tutti i messaggi prodotti vengono presi in carico dal provider ed inviati, mentre tutti i messaggi consumati ricevono l’acknowledge.
  • in caso di rollback della transazione tutti i messaggi prodotti vengono scartati, mentre tutti quelli consumati sono recuperati e inviati di nuovo (a meno che non siano spirati).

tale meccanismo consente quindi di combinare diverse operazioni di invio e ricezione in una stessa transazione. Nel farlo però è necessario porre attenzione all’utilizzo del meccanismo di request/reply perché potrebbe provocare un blocco permanente del client. Il codice seguente, ad esempio, mostra un client che rimane bloccato sul metodo receive() in attesa di una risposta al messaggio inviato. Il provider, infatti, prende in carico il messaggio solamente a seguito del commit eseguito alla riga successiva.

In modo analogo un messaggio non può essere inviato e ricevuto nella stessa transazione.

Quando un client invoca il metodo createSession() per generare una sessione da un connessione con il provider, deve indicare se desidera una sessione tansazionale, valorizzando il parametro transacted a TURE o FALSE. Il secondo parametro di tale metodo specifica acknowledge mode ed è completamente ignorato nel caso  transacted sia TRUE (si veda Il Modello di Programmazione delle API JMS). E’ buona regola in tali casi invocare il metodo indicando 0 come acknowledge mode.

JTA Transaction

Nel caso di componenti EJB non è possibile utilizzare i metodi Session.commit e Session.rollback come visto nel paragrafo precedente. Inoltre in contesti applicativi complessi oltre all’invio e la ricezione di messaggi da e verso una coda JMS, possono essere eseguite operazioni verso altri sistemi, ad esempio una base dati, il tutto in modo transazionale. In tali casi una sessione con local transaction non è sufficiente perché gestisce esclusivamente le transazioni JMS.

In questi casi l’applicazione deve utilizzare una singola transazione JTA che includere tutte le operazione JMS e JDBC. Per fare in modo che la sessione JMS interagisca con la transazione JTA tale sessione deve essere non transazionale, inoltre l’acknowledge è ignorato.

In alternativa è possibile utilizzare la versione del metodo che non accetta parametri.

Il client può quindi utilizzare la sessione così generata demarcando la transazione utilizzando l’interfaccia javax.transaction.UserTransaction e realizzando così un modello transazionale denominato bean-managed-transaction. La soluzione migliore è però quella di demandare il compito al container, condizione di default, realizzando il modello denominato container-managed-transaction.