Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do Distributed Transactions XA in Spring and GlassFish 5?

I am trying to create a transaction comprising two REST web services, whose data source point to the same data base. The first service, named 1, invokes another web service named 2 using Spring RestTemplate.

To implement the transaction I am using a JNDI connection pool, the MySql JDBC driver (version 5.1.35), JTA, XA, Spring and the GlassFish 5 AppServer.

Now, I have downloaded the maven dependencies in the Spring project, defined a configuration class using JtaTransactionManager, and configured the datasource and JTA properties in an application.yml file, like in the following code:

Configuration class:

@Configuration
@EnableTransactionManagement
public class Transacciones {

    @Bean
     public PlatformTransactionManager platformTransactionManager(){ 
        return new JtaTransactionManager();
    }

}

application.yml file

spring:
  datasource:
    jndi-name: jdbc/Prueba  
    driver-class-name: com.mysql.jdbc.Driver

  jta:
    enabled: true                               

I configured the JNDI datasource in GlassFish 5 defining a "JDBC Resource" named jdbc/Prueba in the "Connections pools" page using a javax.sql.XADataSourcedatasource named pruebaXA:

GlassFish, Connections pools

In the control layer of the web service 1, the method calls the service 2 using the RestTemplate class of Spring Framework:

Service 1 code:

@RestController
@RequestMapping("/servicio")
@EnableTransactionManagement
public class a {

    @Autowired
    private JdbcTemplate objJdbcTemplate;


    @Transactional(rollbackFor = RuntimeException.class)
    @GetMapping("/1")
    public Integer getValor(){
        try{
            int numero;
            int n=50;
            RestTemplate restTemplate = new RestTemplate();

            Integer intRes1;
            Integer intRes2;
            numero = (int) (Math.random() * n) + 1;

            intRes2 = restTemplate.postForObject("http://localhost:8080/servicio2-1.0-SNAPSHOT/servicio/2",numero,Integer.class);

            intRes1=objJdbcTemplate.update("INSERT INTO A VALUES(" +numero + ")");

            return numero;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }
}

Service 2 code:

@RestController
@RequestMapping("/servicio")
public class a {

    @Autowired
    private JdbcTemplate objJdbcTemplate;

    @Transactional(rollbackFor = RuntimeException.class)
    @PostMapping("/2")
    public Integer getValor(@RequestBody Integer intNum){
        try{
            Integer intRes;

            intRes=objJdbcTemplate.update("INSERT INTO B VALUES(" + intNum + ")");
            return intRes;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }
}

If the two services work without errors, there is not problem. However, when the service 1 falls, the service 2 does not know about the error and does not do the rollback.

I do not know if I need to configure another feature/option in the GlassFish 5, or in the Spring program.

I have read that in Spring only needs a JtaTransactionManager bean and the framework performs all the work related to configure and use JTA-transactions.Spring and JTA

JTA

Now, if you still need to use JTA, at least the choice is yours to make. There are two common scenarios: using JTA in a heavyweight application server (which has all the nasty disadvantages of being tied to a JavaEE server), or using a standalone JTA implementation. Spring provides support for JTA-based global transaction implementations through a PlatformTransactionManager implementation called JtaTransactionManager. If you use it on a JavaEE application server, it will automatically find the correct javax.transaction.UserTransaction reference from JNDI. Additionally, it will attempt to find the container-specific javax.transaction.TransactionManager reference in 9 different application servers for more advanced use cases like transaction suspension. Behind the scenes, Spring loads different JtaTransactionManager subclasses to take advantage of specific, extra features in different servers when available, for example: WebLogicJtaTransactionManager, WebSphereUowTransactionManager and OC4JJtaTransactionManager.

So, if you’re inside a Java EE application server, and can’t escape, but want to use Spring’s JTA support, then chances are good that you can use the following namespace configuration support to factory a JtaTransactionManager correctly (and automatically):

Alternatively, you can register a JtaTransactionManager bean instance as appropriate, with no constructor arguments, like this:

@Bean public PlatformTransactionManager platformTransactionManager(){

return new JtaTransactionManager(); } Either way, the end result in a JavaEE application server is that you can now use JTA to manage

your transactions in a unified manner thanks to Spring.

Thanks for your help and time.

like image 906
J. Abel Avatar asked Sep 01 '17 21:09

J. Abel


People also ask

What is distributed transaction in spring?

Spring Boot supports distributed JTA transactions across multiple XA resources by using either an Atomikos or Bitronix embedded transaction manager. JTA transactions are also supported when deploying to a suitable Java EE Application Server.

How does @transactional works in Spring?

The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions. The default rollback behavior in the declarative approach will rollback on runtime exceptions.

What are XA transactions?

XA transactions are designed to allow distributed transactions, where a transaction manager (the application) controls a transaction which involves multiple resources. Such resources are usually DBMSs, but could be resources of any type. The whole set of required transactional operations is called a global transaction.


2 Answers

How to do Distributed Transactions XA ?

If you invoke first a REST or web service and then another one, both operations will not be part of a transaction. To form a transaction, these operations must "start" a transaction or be "joined" to an existing one. To execute that transaction, your program must interact with a transaction monitor (TM) such as the proposed by AT&T/Oracle Tuxedo (released in the 80s), the X/Open XA standard (released in the 90s) and the JTA-based systems.

Note how the TM-based transaction works:

  • A transaction using XA datasources is basically a program that invokes database operations on two different databases. The same program (e.g. invoking methods in one or more bean) starts the transaction, performs some operations on a database and performs other operations on another database. If one of the operation fails, the other operations are not performed or are rollbacked.

  • A transaction using XA datasources and JTA-enabled components is basically a program that combines operations on one or more databases with other transaction-enabled operations. For instance, you can combine operations on a database with operations on a content repository or a network-based file system. You can define transactions that, when a database operation fails, does not perform or rollbacks operations on the file system. Non-transactional applications such as COBOL-based programs can be integrated in a transaction by defining operations and compensations in the transaction monitor.

  • A transaction integrating web-services requires an special handling. For instance, there is a proposal for webservice transactions such as the WS-Transaction and WS-Coordination specification. There is a coordinator that works like the transaction monitor. The software must invoke the coordinator to start the transaction. Each participant in the transaction reports if each operation is successful or failed. The coordinator invokes the other operations or invokes the compensations according to the results.

Nowadays, there are some proposals for software architecture that do not rely on TM-based transactions. Designs based on CQRS and Event Sourcing implement transactions using the Sagas design pattern. If you are interested on defining a transaction-like operation that invokes two REST services, you may consider to avoid the XA/JTA and program a Sagas.

How to do Distributed Transactions XA in Spring and GlassFish 5?

You may check many tutorials in the Internet. For instance,

  • a tutorial that shows you three use cases: one updating two databases, one combining database operations and outgoing JMS-messages, and another one combining incoming JMS messages and database operations.
  • a video describing how to manage distributed transactions in Spring with JTA
  • and the documentation from the Spring Framework.

If the two services work without errors, there is not problem. However, when the service 1 falls, the service 2 does not know about the error and does not do the rollback.

That is the correct behavior. When you invoke a REST/webservice, the operations performed by that REST/webservice do not join to the transaction. If the invoking service fails, the invoked service will not notice it and will not rollback their operations in the database.

I do not know if I need to configure another feature/option in GlassFish 5, or in the Spring program.

No. You only have to configure the XA-datasources. Spring will use your configuration class and annotations to join automatically into a transaction the operations performed by your application on these datasources. For instance, if you have a bean that invokes multiple methods that performs operations on one or more XA-datasources, these operations will join into a transaction.

However, when you invoke a method in REST/webservice in another application, the database operations performed by that REST/webservice will not join to the transaction.

like image 196
Jaime Avatar answered Oct 02 '22 12:10

Jaime


rest web services (http based) are non-transactional by their nature (they are http based). you have made each method/operation transactional, but they do not share any state between the resources (rest operations). generally - you can have XA transactions over database or mesaging, not over http calls.

intRes2 = restTemplate.postForObject("http://localhost:8080/servicio2-1.0-
SNAPSHOT/servicio/2",numero,Integer.class);

Calling a remote web service is without any transaction context. If you need to maintains transaction between services, call the secord service as EJB (or as an injected managed bean)

Basically: using http-based rest services - forget any transactions between them. The protocol (HTTP) is not built for that.

The only thing I've seen transactional is SOAP with WS-RM extension (SOAP with reliable messaging)., however it is not very easy to setup (read: it can be nightmare to make it work when you don't know what are you doing) and not all WS frameworks support it.

When you really need reliable delivery between web services, there's a way. what is comonly used to achieve assured delivery is messaging with idempotent services (https://en.m.wikipedia.org/wiki/Idempotence) with store-and-forward pattern. In simple terms - service 1 stores a JMS message to a queue and there's a processor (MDB) which calls service 2. (yes, calling a remote web service it may happen that the service 2 will receive a message multiple times. Indempotency is a way how to deal with it.)

like image 27
gusto2 Avatar answered Oct 02 '22 11:10

gusto2