Ce contenu n'est pas disponible dans la langue sélectionnée.

Chapter 3. Interfaces for configuring and referencing transaction managers


JavaEE and Spring Boot each provide a transaction client interface for configuring the transaction manager in Fuse and for using the transaction manager in deployed applications. There is a clear distinction between configuration, which is an administrative task, and referencing, which is a development task. The application developer is responsible for pointing the application to a previously configured transaction manager.

3.1. What transaction managers do

A transaction manager is the part of an application that is responsible for coordinating transactions across one or more resources. The responsibilities of the transaction manager are as follows:

  • Demarcation – starting and ending transactions by using begin, commit, and rollback methods.
  • Managing the transaction context – a transaction context contains the information that a transaction manager needs to keep track of a transaction. The transaction manager is responsible for creating transaction contexts and attaching them to the current thread.
  • Coordinating the transaction across multiple resources – enterprise-level transaction managers typically have the capability to coordinate a transaction across multiple resources. This feature requires the 2-phase commit protocol and resources must be registered and managed using the XA protocol. See Section 1.7.1.2, “Support for the XA standard”.

    This is an advanced feature that is not supported by all transaction managers.

  • Recovery from failure – transaction managers are responsible for ensuring that resources are not left in an inconsistent state if there is a system failure and the application fails. In some cases, manual intervention might be required to restore the system to a consistent state.

3.2. About local, global, and distributed transaction managers

A transaction manager can be local, global, or distributed.

3.2.1. About local transaction managers

A local transaction manager is a transaction manager that can coordinate transactions for only a single resource. The implementation of a local transaction manager is typically embedded in the resource itself and the transaction manager used by application is a thin wrapper around this built-in transaction manager.

For example, the Oracle database has a built-in transaction manager that supports demarcation operations (by using SQL BEGIN, COMMIT, or ROLLBACK statements or by using a native Oracle API) and various levels of transaction isolation. Control over the Oracle transaction manager can be exported through JDBC, and this JDBC API is used by applications to demarcate transactions.

It is important to understand what constitutes a resource, in this context. For example, if you are using a JMS product, the JMS resource is the single running instance of the JMS product, not the individual queues and topics. Moreover, sometimes, what appears to be multiple resources might actually be a single resource, if the same underlying resource is accessed in different ways. For example, your application might access a relational database both directly (through JDBC) and indirectly (through an object-relational mapping tool like Hibernate). In this case, the same underlying transaction manager is involved, so it should be possible to enroll both of these code fragments in the same transaction.

Note

It cannot be guaranteed that this will work in every case. Although it is possible in principle, some detail in the design of the Spring Framework or other wrapper layers might prevent it from working in practice.

It is possible for an application to have many different local transaction managers working independently of each other. For example, you could have one Camel route that manipulates JMS queues and topics, where the JMS endpoints reference a JMS transaction manager. Another route could access a relational database through JDBC. But you could not combine JDBC and JMS access in the same route and have them both participate in the same transaction.

3.2.2. About global transaction managers

A global transaction manager is a transaction manager that can coordinate transactions over multiple resources. This is required when you cannot rely on the transaction manager built into the resource itself. An external system, sometimes called a transaction processing monitor (TP monitor), is capable of coordinating transactions across different resources.

The following are the prerequisites for transactions that operate on multiple resources:

  • Global transaction manager or TP monitor – an external transaction system that implements the 2-phase commit protocol for coordinating multiple XA resources.
  • Resources that support the XA standard – to participate in a 2-phase commit, resources must support the XA standard. See Section 1.7.1.2, “Support for the XA standard”. In practice, this means that the resource is capable of exporting an XA switch object, which gives complete control of transactions to the external TP monitor.
Tip

The Spring Framework does not by itself provide a TP monitor to manage global transactions. It does, however, provide support for integrating with an OSGi-provided TP monitor or with a JavaEE-provided TP monitor (where the integration is implemented by the JtaTransactionManager class). Hence, if you deploy your application into an OSGi container with full transaction support, you can use multiple transactional resources in Spring.

3.2.3. About distributed transaction managers

Usually, a server connects directly to the resources involved in a transaction. In a distributed system, however, it is occasionally necessary to connect to resources that are exposed only indirectly, through a Web service. In this case, you require a TP monitor that is capable of supporting distributed transactions. Several standards are available that describe how to support transactions for various distributed protocols, for example, the WS-AtomicTransactions specification for Web services.

3.3. Using a JavaEE transaction client

When using JavaEE, the most fundamantal and standard method to interact with a transaction manager is the Java Transaction API (JTA) interface, javax.transaction.UserTransaction. The canonical usage is:

InitialContext context = new InitialContext();
UserTransaction ut = (UserTransaction) context.lookup("java:comp/UserTransaction");
ut.begin();

// Access transactional, JTA-aware resources such as database and/or message broker

ut.commit(); // or ut.rollback()

Obtaining a UserTransaction instance from JNDI (Java Naming and Directory Interface) is one way of getting a transaction client. In a JavaEE environment, you can access a transaction client, for example, with CDI (context and dependency injection).

The following figure shows a typica JavaEE Camel application.

javaee transaction api

The figure shows that both Camel code and application code may access:

  • A javax.transaction.UserTransaction instance to demarcate transactions either directly from an application or internally through transaction-aware Camel components by using the Spring TransactionTemplate class.
  • Databases through JDBC APIs either directly or, for example, by using Spring’s JdbcTemplate, or by using the camel-jdbc component.
  • Message brokers through a JMS API either directly, by using Spring’s JmsTemplate class or by using the camel-jms component.

When using a javax.transaction.UserTransaction object, you do not need to be aware of the actual transaction manager that is being used because you are working directly with only the transaction client. (See Section 1.3, “About transaction clients”.) A different approach is taken by Spring and Camel, as it uses Spring’s transaction facilities internally.

JavaEE Application

In typical JavaEE scenario, the application is deployed to a JavaEE application server, usually as a WAR or EAR archive. By means of JNDI or CDI, the application may access an instance of the javax.transaction.UserTransaction service. The aplication then uses this transaction client instance to demarcate transactions. Within a transaction, the application performs JDBC and/or JMS access.

Camel component and application code

These represent the code that performs JMS/JDBC operations. Camel has its own advanced methods to access JMS/JDBC resources. The application code may use a given API directly.

JMS Connection Factory

This is the javax.jms.ConnectionFactory interface that is used to obtain instances of javax.jms.Connection and then javax.jms.Session (or javax.jms.JmsContext in JMS 2.0). This may be used directly by the application or indirectly in Camel components, which may use org.springframework.jms.core.JmsTemplate internally. Neither application code nor a Camel component require the details of this connection factory. The connection factory is configured at the application server. You can see this configuration in a JavaEE server. An OSGi server such as Fuse is similar. A system administrator configures the connection factory independently of the application. Typically, the connection factory implements pooling capabilities.

JDBC Data Source

This is the javax.sql.DataSource interface that is used to obtain instances of java.sql.Connection. As with JMS, this data source may be used directly or indirectly. For example, the camel-sql component uses the org.springframework.jdbc.core.JdbcTemplate class internally. As with JMS, neither application code nor Camel require the details of this data source. The configuration is done inside the application server or inside the OSGi server by using methods that are described in Chapter 4, Configuring the Narayana transaction manager.

3.4. Using a Spring Boot transaction client

One of the main goals of the Spring Framework (and Spring Boot) is to make JavaEE APIs easier to use. All major JavaEE vanilla APIs have their part in the Spring Framework (Spring Boot). These are not alternatives or replacements of given APIs, but rather wrappers that add more configuration options or more consistent usage, for example, with respect to exception handling.

The following table matches a given JavaEE API with its Spring-related interface:

JavaEE APISpring UtilityConfigured With

JDBC

org.springframework.jdbc.core.JdbcTemplate

javax.sql.DataSource

JMS

org.springframework.jms.core.JmsTemplate

javax.jms.ConnectionFactory

JTA

org.springframework.transaction.support.TransactionTemplate

org.springframework.transaction.PlatformTransactionManager

JdbcTemplate and JmsTemplate directly use javax.sql.DataSource and javax.jms.ConnectionFactory respectively. But TransactionTemplate uses the Spring interface of PlatformTransactionManager. This is where Spring does not simply improve JavaEE, but replaces the JavaEE client API with its own.

Spring treats javax.transaction.UserTransaction as an interface that is too simple for real-world scenarios. Also, because javax.transaction.UserTransaction does not distinguish between local, single resource transactions and global, multi-resource transactions, implementations of org.springframework.transaction.PlatformTransactionManager give developers more freedom.

Following is the canonical API usage of Spring Boot:

// Create or get from ApplicationContext or injected with @Inject/@Autowired.
JmsTemplate jms = new JmsTemplate(...);
JdbcTemplate jdbc = new JdbcTemplate(...);
TransactionTemplate tx = new TransactionTemplate(...);

tx.execute((status) -> {
    // Perform JMS operations within transaction.
    jms.execute((SessionCallback<Object>)(session) -> {
        // Perform operations on JMS session
        return ...;
    });
    // Perform JDBC operations within transaction.
    jdbc.execute((ConnectionCallback<Object>)(connection) -> {
        // Perform operations on JDBC connection.
        return ...;
    });
    return ...;
});

In the above example, all three kinds of templates are simply instantiated, but they may also be obtained from Spring’s ApplicationContext, or injected by using @Autowired annotations.

3.4.1. Using the Spring PlatformTransactionManager interface

As mentioned earlier, javax.transaction.UserTransaction is usually obtained from JNDI in a JavaEE application. But Spring provides explicit implementations of this interface for many scenarios. You do not always need full JTA scenarios and sometimes an application requires access to just a single resource, for example, JDBC.

Usually, org.springframework.transaction.PlatformTransactionManager is the Spring transaction client API that provides the classic transaction client operations: begin, commit and rollback. In other words, this interface provides the essential methods for controlling transactions at runtime.

Note

The other key aspect of any transaction system is the API for implementing transactional resources. But transactional resources are usually implemented by the underlying database, so this aspect of transactional programming is rarely a concern for the application programmer.

3.4.1.1. Definition of the PlatformTransactionManager interface

public interface PlatformTransactionManager {

    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

3.4.1.2. About the TransactionDefinition interface

You use the TransactionDefinition interface to specify the characteristics of a newly created transaction. You can specify the isolation level and the propagation policy of the new transaction. For details, see Section 9.4, “Transaction propagation policies”.

3.4.1.3. Definition of the TransactionStatus interface

You can use the TransactionStatus interface to check the status of the current transaction, that is, the transaction that is associated with the current thread, and to mark the current transaction for rollback. This is the interface definition:

public interface TransactionStatus extends SavepointManager, Flushable {

    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();
}

3.4.1.4. Methods defined by the PlatformTransactionManager interface

The PlatformTransactionManager interface defines the following methods:

getTransaction()
Creates a new transaction and associates it with the current thread by passing in a TransactionDefinition object that defines the characteristics of the new transaction. This is analogous to the begin() method of many other transaction client APIs.
commit()
Commits the current transaction, which makes all of the pending changes to the registered resources permanent.
rollback()
Rolls back the current transaction, which undoes all pending changes to the registered resources.

3.4.2. Steps for using the transaction manager

Usually, you do not use the PlatformTransactionManager interface directly. In Apache Camel, you typically use a transaction manager as follows:

  1. Create an instance of a transaction manager. There are several different implementations available in Spring, see Section 3.4, “Using a Spring Boot transaction client”).
  2. Pass the transaction manager instance to either an Apache Camel component or to the transacted() DSL command in a route. The transactional component or the transacted() command is then responsible for demarcating transactions. For details, see Chapter 9, Writing a Camel application that uses transactions).

3.4.3. About Spring PlatformTransactionManager implementations

This section provides a brief overview of the transaction manager implementations that are provided by the Spring Framework. The implementations fall into two categories: local transaction managers and global transaction managers.

Starting from Camel:

  • The org.apache.camel.component.jms.JmsConfiguration object that is used by the camel-jms component requires an instance of the org.springframework.transaction.PlatformTransactionManager interface.
  • The org.apache.camel.component.sql.SqlComponent uses the org.springframework.jdbc.core.JdbcTemplate class internally and this JDBC template also integrates with org.springframework.transaction.PlatformTransactionManager.

As you can see, you must have some implementation of this interface. Depending on the scenario, you can configure the required platform transaction manager.

3.4.3.1. Local PlatformTransactionManager implementations

The list below summarizes the local transaction manager implementations that are provided by the Spring Framework. These transaction managers support only a single resource.

org.springframework.jms.connection.JmsTransactionManager
This transaction manager implementation is capable of managing a single JMS resource. You can connect to any number of queues or topics, but only if they belong to the same underlying JMS messaging product instance. Moreover, you cannot enlist any other type of resource in a transaction.
org.springframework.jdbc.datasource.DataSourceTransactionManager
This transaction manager implementation is capable of managing a single JDBC database resource. You can update any number of different database tables, but only if they belong to the same underlying database instance.
org.springframework.orm.jpa.JpaTransactionManager
This transaction manager implementation is capable of managing a Java Persistence API (JPA) resource. It is not possible, however, to simultaneously enlist any other kind of resource in a transaction.
org.springframework.orm.hibernate5.HibernateTransactionManager
This transaction manager implementation is capable of managing a Hibernate resource. It is not possible, however, to simultaneously enlist any other kind of resource in a transaction. Moreover, the JPA API is preferred over the native Hibernate API.

There are also other, less frequently used, implementations of PlatformTransactionManager.

3.4.3.2. Global PlatformTransactionManager implementation

The Spring Framework provides one global transaction manager implementation for use in the OSGi runtime. The org.springframework.transaction.jta.JtaTransactionManager supports operations on multiple resources in a transaction. This transaction manager supports the XA transaction API and can enlist more than one resource in a transaction. To use this transaction manager, you must deploy your application inside either an OSGi container or a JavaEE server.

While single-resource implementations of PlatformTransactionManager are actual implementations, JtaTransactionManager is more of a wrapper for an actual implementation of the standard javax.transaction.TransactionManager.

This is why it is better to use the JtaTransactionManager implementation of PlatformTransactionManager in an environment where you can access (by means of JNDI or CDI) an already configured instance of javax.transaction.TransactionManager and usually also javax.transaction.UserTransaction. Usually, both these JTA interfaces are implemented by a single object/service.

Here is an example of configuring/using JtaTransactionManager:

InitialContext context = new InitialContext();
UserTransaction ut = (UserTransaction) context.lookup("java:comp/UserTransaction");
TransactionManager tm = (TransactionManager) context.lookup("java:/TransactionManager");

JtaTransactionManager jta = new JtaTransactionManager();
jta.setUserTransaction(ut);
jta.setTransactionManager(tm);

TransactionTemplate jtaTx = new TransactionTemplate(jta);

jtaTx.execute((status) -> {
    // Perform resource access in the context of global transaction.
    return ...;
});

In the above example, the actual instances of JTA objects (UserTransaction and TransactionManager) are taken from JNDI. In OSGi, they may as well be obtained from the OSGi service registry.

3.5. OSGi interfaces between transaction clients and the transaction manager

After a description of the JavaEE transaction client API and the Spring Boot transaction client API, it is helpful to see the relationships within an OSGi server, such as Fuse. One of the features of OSGi is the global service registry, which may be used to:

  • Look up services by filter or interface(s).
  • Register services with given interface(s) and properties.

In the same way that applications that are deployed in a JavaEE application server obtain references to javax.transaction.UserTransaction by using JNDI (service locator method) or get them injected by CDI (dependency injection method), in OSGi you can obtain the same references (directly or indirectly) in any of the following ways:

  • Invoking the org.osgi.framework.BundleContext.getServiceReference() method (service locator).
  • Get them injected in a Blueprint container.
  • Use Service Component Runtime (SCR) annotations (dependency injection).

The following figure shows a Fuse application that is deployed in the OSGi runtime. Application code and/or Camel components use their APIs to obtain references to the transaction manager, data sources, and connection factories.

osgi transaction architecture

Applications (bundles) interact with services that are registered in the OSGi registry. The access is performed through interfaces and this is all that should be relevant to applications.

In Fuse, the fundamental object that implements (directly or through a tiny wrapper) transactional client interfaces is org.jboss.narayana.osgi.jta.internal.OsgiTransactionManager. You can use the following interfaces to access the transaction manager:

  • javax.transaction.TransactionManager
  • javax.transaction.UserTransaction
  • org.springframework.transaction.PlatformTransactionManager
  • org.ops4j.pax.transx.tm.TransactionManager

You can use any of these interfaces directly or you can use them implicitly by choosing a framework or library, such as Camel.

For information about the ways to configure org.jboss.narayana.osgi.jta.internal.OsgiTransactionManager in Fuse, see Chapter 4, Configuring the Narayana transaction manager. Later chapters in this guide build on the information in that chapter and describe how to configure and use other services, such as JDBC data sources and JMS connection factories.

Red Hat logoGithubRedditYoutubeTwitter

Apprendre

Essayez, achetez et vendez

Communautés

À propos de la documentation Red Hat

Nous aidons les utilisateurs de Red Hat à innover et à atteindre leurs objectifs grâce à nos produits et services avec un contenu auquel ils peuvent faire confiance.

Rendre l’open source plus inclusif

Red Hat s'engage à remplacer le langage problématique dans notre code, notre documentation et nos propriétés Web. Pour plus de détails, consultez leBlog Red Hat.

À propos de Red Hat

Nous proposons des solutions renforcées qui facilitent le travail des entreprises sur plusieurs plates-formes et environnements, du centre de données central à la périphérie du réseau.

© 2024 Red Hat, Inc.