XA Transaction in Spring

Introduzione

In questo articolo vediamo come poter eseguire transazioni distribuite (XA Transaction) in applicazioni stand alone, quindi al di fuori di un container J2EE, utilizzando il popolare framework Spring. Queste tipologie di transazioni sono molto comuni in ambienti enterprise, dove sistemi diversi devono essere sincronizzati per raggiungere uno stato consistente. Per ottenere questo obbiettivo abbiamo bisogno di utilizzare un gestore delle transazioni conforme alle specifiche JTA (Java Transaction API) e che quindi supportino il two fase commit, come ad esempio JBossTS, Atomikos and Bitronix.

Two Phase Commit (2PC)

Two Phase Commit ProtocolAccenniamo brevemente al funzionamento di tale protocollo. Una transazione distribuita, anche detta XA (eXtended Architecture) Transaction, è caratterizzata da un id globale e tanti id locali (xid) per ogni risorsa XA. La prima fase del protocollo si realizza invocando il metodo prepare(xid) su tutte le risorse distribuite, le quali possono rispondere con OK o ABORT. Dopo aver ricevuto la risposta da tutte le risorse, l’XA manager decide se inviare il comando commit(xid) o abort(xid): Il commit sarà inviato nel caso in cui tutte le risorse abbiano risposto con OK. L’abort nel caso in cui anche una sola risorsa abbia inviato l’ABORT. Infine il metodo end(xid) è invocato su tutte le risorse per indicare che la transazione è terminata. Il protocollo è in grado di ritornare in situazioni consistenti a seguito di diverse situazioni di errore (failure) che possono verificarsi durante le fasi descritte. La spiegazione di come ciò accada è al di fuori degli scopi di questo articolo. Per maggiori dettagli si rimanda quindi alla specifica X/Open Distributed Transaction Processing definita dall’Open Group.

Le API JTA, definite da Sun Microsystem, sono API di alto livello che specificano le interfacce tra il Transaction Manager e tutte le altre componenti coinvolte in una transazione distribuita: risorse XA e applicazioni. JTA si compone principalmente di tre parti:

  • Una interfaccia di alto livello utilizzata dalle applicazione per la demarcazione delle transazioni (UserTransaction).
  • Una implementazione dello standard X/Open XA protocol, inclusa nel package javax.transaction.xa, che consiste nelle interfacce XAResource, Xid e XAException.
  • Una interfaccia di alto livello per la specifica di un transaction manager che integrato in un application server consente di gestire le transazioni utente.

Spring e Bitronix

Spring fornisce supporto alle transazioni JTA attraverso il componente JtaTransactionManager. Se l’applicazione è eseguita in un container J2EE, tale componente è in grado di recuperare la corretta transazione utente (javax.transaction.UserTransaction) dalla directory JNDI. Il componente è poi specializzato in altri 9 sottoclassi specifiche per differenti application server, e che sono automaticamente referenziati da Spring, senza alcun intervento utente, quali, ad esempio WebLogicJtaTransactionManager e WebSphereUowTransactionManager.

Per configurare l’utilizzo di JTA in Spring è sufficiente inserire nell’xml di contesto il seguente namespace:

Diversamente in applicazioni non ospitate in application server J2EE, è necessario utilizzare nel progetto uno specifico JTA transaction manager, come ad esempio Bitronix che utilizzeremo nel nostro esempio. Bitronix Transaction Manager (BTM) è una implementazione semplice ma completa delle specifiche JTA (1.1) che supporta diverse tipologie di risorse XA: database, JMS e JCA.

Configurazione del Progetto

Generiamo una applicazione standalone utilizzando Maven e modifichiamo il pom.xml inserendo le necessarie dipendenze.

In particolare inseriamo le dipendenze di Spring:

E le dipendenze di Bitronix:

Per il nostro progetto abbiamo bisogno anche dei driver per Oracle 12. Per includerli come dependency è innanzitutto necessario installarli nel repository locale. Scaricate quindi il jar ojdbc7.jar ed eseguite la seguente istruzione maven (eventualmente aggiornando il numero di versione):

Quindi è possibile inserire la dipendenza nel pom.xml:

Configurazione della Connessione

Per configurare il JTA Transaction Manager ed i datasource verso i due database di test creiamo il file connection.xml nella cartella resources del progetto maven. In tale file introduciamo innanzitutto i due bean di configurazione di Bitronix:

Quindi configuriamo il JTA transaction manager di Spring:
Infine configuriamo, nello stesso file, i due datasource (per semplicità ne riportiamo uno solo):

Configurazione dei Bean

Si è stabilito di mantenere separato l’xml in cui è definita la connessione da quello in cui sono dichiarati e bean dell’applicazione. Creiamo quindi un file context.xml e configuriamo il bean MyFacade per l’accesso ai dati. Per utilizzare la transazione definita abbiamo due possibilità. Utilizzare direttamente il bean JtaStransactionManager nel codice oppure, più elegantemente, utilizzare Spring AOP e wrappare il bean in un TransactionProxyfactoryBean. Questo proxy intercetta le invocazioni ai metodi del bean e gestisce le transazioni in base a come viene configurato. Per i nostri scopi la configurazione nel context.xml del bean sarà:

Il codice seguente mostra la classe MyFacade che utilizza i due datasource per eseguire due insert. Per eseguire dei test si consiglia di modificare le insert in modo da generare eccezioni, ad esempio utilizzando nella seconda insert una tabella inesistente.

Ovviamente affinché il TransactionProxyfactoryBean faccia il suo lavoro non deve assolutamente essere silenziata l’eccezione all’interno del codice del metodo update().

Riportiamo infine la classe Main che inizializza il contesto di Spring ed esegue l’update sul facade.

Codice Sorgente

Il codice sorgente può essere scaricato qui spring-jta

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

As you found this post useful...

Follow us on social media!