///Understanding JCA Transactions

Understanding JCA Transactions

Learn How the Various Levels of Transaction Support Provided by Different EISs and Resource Adapters Can Affect Application Design.

Transaction processing is a vital part of most real-world J2EE application development. In this article, IBM Solution Architect Mikhail Genkin explains how different enterprise information systems (EIS) can participate in transactions via the J2EE Connector Architecture. Using an example e-commerce application, Mikhail demonstrates the various levels of transaction support provided by different EISs and resource adapters and shows how these factors can affect application design. The article concludes with Mikhail’s tips for choosing the right transaction demarcation strategy and EJB deployment descriptor settings for your enterprise development scenario.

The world of e-business is a rapidly changing environment. Businesses need to integrate business logic and data that are available on existing enterprise information systems (EIS) — such as Customer Information Control System (CICS), Information Management System (IMS), or SAP — into new applications. Frequently, vital business transactions are written in procedural languages such as Cobol or C. The J2EE™ platform includes a specification that provides developers with a standard interface for accessing EIS transactions and data: the J2EE Connector Architecture (JCA) specification.

In this article, I’ll explain how the JCA transaction contract helps implement transactional behavior in e-business applications. Specifically, you’ll learn about the JCA’s two transaction demarcation techniques, distributed transaction demarcation and programmatic transaction demarcation. I’ll explain the pros and cons of each and offer tips for deciding which one is best suited to your application development scenario. Then I’ll walk you through an enterprise programming example implementing programmatic transaction demarcation. The article will conclude with tips for choosing and applying the correct EJB deployment descriptor settings for your transaction demarcation solution.

Techniques described in this article can be implemented with any application server that is compliant with J2EE 1.3 or higher, as well as both JCA 1.0- and JCA 1.5-compliant resource adapters.

Overview of JCA Transactions

A working example will help explore some common JCA transaction issues. In this example, the task is to build an e-commerce application for a company whose primary business is selling manufactured goods to consumers. The company has decided to expand by establishing a Web presence and retailing its goods to a wider audience. The Web site will let any customer with a Web browser access the company’s home page; browse the catalog of available products; view detailed price information, availability, and a description of available items; add items to a shopping cart; and, finally, make a purchase. To illustrate the required transactional behavior, I’ll focus on the use case where the customer decides to make a purchase. Figure 1 shows the application design.

Figure 1. Accessing EIS in your e-commerce application

The company’s existing IT infrastructure is built around two enterprise information systems. EIS1 is a mainframe running Cobol transactions under CICS. Transactions running on this system implement business logic and access data necessary for order entry and customer relationship management (CRM). EIS2 is an IMS system containing the product description catalog, pricing information, and inventory control. To support the required functions, your J2EE application must be able to access and seamlessly integrate data from both systems. In order to make a purchase, you need to perform the following actions as a unit of work (for a single transaction):

1. Get current product price (EIS2)

2. Enter the customer’s order into the order system (EIS1)

3. Bill the customer (EIS1)

4. Update product availability information (EIS2)

A failure during steps 2, 3, or 4 will result in the undoing of all previous steps.

In this example Web application, the client (a JSP page in the Web tier) holds a reference to, and calls methods on, an instance of the CustomerSession stateful session EJB component. You use CustomerSession to store the shopping cart contents, product information from the catalog, and customer information. During the course of interaction with the end user, as shown in Figure 1, this session bean accumulates and stores product selections and customer-specific information necessary to make a purchase. Methods defined in CustomerSession make calls to methods on stateless session EJB components (with the bean serving as a facade) such as OrderService, to invoke transactions on an EIS.

In this design, the OrderService stateless session bean acts as a facade for EIS1. It defines the following methods:

1. OrderInfo addOrder(String customerId, ItemInfo itemInfo) uses the JCA common client interface (CCI) to invoke the SHIPTO transaction on EIS1. This transaction looks up the ship-to address for the customer and prepares information that the EIS then communicates to the shipping department. The return structure OrderInfo contains the order number (which is used for tracking), the shipping costs, and shipping address information.

2. BillingInfo billCustomer(String customerId, OrderInfo orderInfo) also uses the JCA CCI to invoke the BILLTO transaction on EIS1. This transaction looks up the customer’s credit card number and debits the customer’s account for the order amount. The return structure BillingInfo contains the total cost to the customer, including shipping fees and structures, the ID number for the order (used for tracking and cancellations), and customer information.

3. void cancelOrder(OrderInfo orderInfo) uses the JCA CCI to invoke the RMVORD transaction running on EIS1 to cancel the order.

The CatalogService session bean (see Figure 1) acts as a facade to EIS2. It defines the following methods:

1. double getItemPrice(String itemId) uses the JCA CCI to invoke the ITMPRICE transaction on EIS2. This transaction returns the latest pricing information for the item.

2. void updateStockInfo(String itemId, int numItems) uses the JCA CCI to invoke the UPDSTOCK transaction on EIS2. This transaction updates the current in-stock information based on item ID. The input parameter numItems can be positive or negative.

All five of these methods and the underlying EIS transactions running on two different systems have to execute as part of a single business transaction. In the next section, you’ll see how this is accomplished.

Transaction Support Levels

Different EISs have different transactional behavior. Some EISs do not support transactions at all. Some support transactions but do not support the two-phase commit (2PC) protocol. These are said to support local transactions. Some EISs support local transactions and 2PC. These are said to support distributed, or global transactions. Global transactions are also often called XA transactions because they involve the XAResource interface.

Listing 1 shows a snippet from ra.xml for the CICSECI resource adapter that you’ll use to access EIS1. The LocalTransaction value for the <transaction-support> element indicates that this resource adapter supports local transactions but cannot participate in global transactions. The OrderService session bean uses this resource adapter to access CRM transactions that contain EIS1.

Listing 1. Snippet of the ra.xml descriptor for CICSECI resource adapter

<!DOCTYPE connector PUBLIC "-//Sun Microsystems, Inc.//DTD Connector 1.0//EN"

"http://java.sun.com/dtd/connector_1_0.dtd">

<connector>
<display-name>ECIResourceAdapter</display-name>
<description>CICS J2EE ECI Resource Adapter</description>
<vendor-name>IBM</vendor-name>
<spec-version>1.0</spec-version>
<eis-type>CICS</eis-type>
<version>5.0.0 </version>
<license>
<description> </description>
<license-required>true</license-required>
</license>
<resourceadapter>
<managedconnectionfactory-class>
com.ibm.connector2.cics.ECIManagedConnectionFactory</managedconnectionfactory-class>
<connectionfactory-interface>
javax.resource.cci.ConnectionFactory</connectionfactory-interface>
<connectionfactory-impl-class>
com.ibm.connector2.cics.ECIConnectionFactory</connectionfactory-impl-class>
<connection-interface>javax.resource.cci.Connection</connection-interface>
<connection-impl-class>com.ibm.connector2.cics.ECIConnection</connection-impl-class>
<transaction-support>LocalTransaction</transaction-support>

Listing 2 shows a snippet from ra.xml for the IMS resource adapter that I use to access EIS 2. In this case the XATransaction value for the <transaction-support> element indicates that the resource adapter supports global, or distributed transactions.

Listing 2. Snippet of the ra.xml descriptor for IMS resource adapter

<!DOCTYPE connector PUBLIC "-//Sun Microsystems, Inc.//DTD Connector 1.0//EN"

"http://java.sun.com/dtd/connector_1_0.dtd">

<connector>
<display-name>IMS Connector for Java</display-name>
<description>J2EE Connector Architecture resource adapter for IMS
accessing IMS transactions using IMS Connect </description>
<vendor-name>IBM Corporation</vendor-name>
<spec-version>1.0</spec-version>
<eis-type>IMS</eis-type>
<version>1.2.6</version>
<license>
<description>IMS Connector for Java is a component of the IMS Connect product and as such
is not separately licensed. It requires an IMS Connect license.</description>
<license-required>true</license-required>
</license>
<resourceadapter>
<managedconnectionfactory-class>
com.ibm.connector2.ims.ico.IMSManagedConnectionFactory</managedconnectionfactory-class>
<connectionfactory-interface>
javax.resource.cci.ConnectionFactory</connectionfactory-interface>
<connectionfactory-impl-class>
com.ibm.connector2.ims.ico.IMSConnectionFactory</connectionfactory-impl-class>
<connection-interface>
javax.resource.cci.Connection</connection-interface>
<connection-impl-class>
com.ibm.connector2.ims.ico.IMSConnection</connection-impl-class>
<transaction-support>XATransaction</transaction-support>
<config-property>

JCA Transaction Support

The two resource adapters shown in Listings 1 and 2 differ in their levels of transactional support. In my e-commerce application, however, I need to coordinate a transaction across these two different systems. To help manage this, the JCA transaction contract defines a set of architected interfaces through which the application server and the EIS coordinate the transaction. The EJB container talks to the EIS via the resource adapter’s implementation of the ManagedConnection interface, which represents the physical connection to that EIS. The ManagedConnection implementation typically uses a proprietary library to talk to the EIS using a protocol it understands.

If the resource adapter supports local transactions, it also implements the javax.resource.spi.LocalTransaction interface. When the EJB container needs to initiate the transaction it will call the getLocalConnection() method on the ManagedConnection implementation instance. Subsequently it will call the LocalTransactions begin(), commit(), and rollback() methods to control the transaction.

If the resource adapter supports XA or global transactions it also implements the javax.transaction.xa.XAResource interface. Every J2EE-compliant application server has an internal component called the Transaction Manager. The Transaction Manager is actually an implementation of the javax.transaction.TransactionManager interface. This component helps the application server manage transaction boundaries. The Transaction Manager uses the getXAResource() method defined by the ManagedConnection interface to retrieve an instance of XAResource. The Transaction Manager uses methods defined on the XAResource interface to coordinate the 2PC protocol across multiple resource managers.

Transaction Demarcation Strategies

The JCA gives you two options for handling transactions: programmatic transaction demarcation or declarative transaction demarcation. The first option requires that you use the Java Transaction API (JTA) to write explicit code for each transaction’s begin, commit, and rollback operations. In this case, your transaction demarcation code is intermixed with code that implements the business logic.

The second approach, declarative transaction demarcation, does not involve any extra coding. If I choose this approach, the EJB deployer will configure the transactional behavior for me by modifying deployment descriptor settings for the beans. The EJB container will then use these deployment descriptor settings to automatically begin, commit, or rollback transactions at specified points. In this case the business logic implemented in the EJB component remains portable and the transactional behavior can be adjusted without re-writing the bean implementation.

For most cases declarative transaction demarcation is the preferred option. Programmatic demarcation is typically used only in cases where declarative transaction demarcation isn’t flexible enough. In the case of my example, the two EISs and their corresponding resource adapters have different transaction support levels. Were these the only factors under consideration, a distributed transaction using the 2PC protocol would be the best approach to guarantee data consistency and integrity.

Making things more interesting, however, is the fact that in my example only EIS2 supports distributed transactions. In order to use 2PC, all systems involved in the transaction have to support it. Therefore, I cannot use a distributed transaction, and will have to rely on local transactions in order to guarantee consistent updates across the two systems. I will need to group access to each EIS, and manually undo changes to one EIS if the other one fails. Transactions that undo previously committed changes are frequently called compensating transactions. I’ll use programmatic transaction demarcation to provide for greater flexibility for manual updates.

Programmatic Transaction Demarcation

Listing 3 shows the implementation of the application’s placeOrder method defined by the CustomerSession session bean. The method implementation uses the JTA API to start local transactions that control access to EIS1 and EIS2. Within the placeOrder method you first access EIS2 to get the most up-to-date price information. This access, though read-only in nature, should happen within the scope of a transaction. This is due to the fact that updates to EIS2 pricing information could be in progress via other applications, and you need to make sure that you are seeing consistent, committed pricing data. Note that exception handling and business logic have been simplified for the sake of brevity.

Listing 3. The placeOrder() method of the CustomerSession EJB (withsimplified error handling for brevity)

public OrderInfo placeOrder(ItemInfo itemInfo, CustomerInfo custInfo) 

throws OrderException {

// Get a reference to the UserTransaction.
// Initialize variables.
BillingInfo billingInfo = null;
OrderInfo orderInfo = null;
double itemPrice = 0.0;

UserTransaction ut = null;

try
{
InitialContext ic = new InitialContext();
ut = (UserTransaction)ic.lookup("jta/UserTransaction");
}
catch (Exception e)
{
throw new OrderException(e.getMessage());
}

// Look up latest pricing information in EIS2 including customer discount.
try
{
ut.begin();
itemPrice = catalogService.getItemPrice(itemInfo.getItemId(),
custInfo.getCustomerId());
ut.commit();
}
catch ( Exception e)
{
try
{
ut.rollback();
}
catch (Exception ex)
{
// Rollback failed.
// Log the error here, nothing to recover.
}

// Throw exception back to the UI tier, nothing to compensate yet.
throw new OrderException(e.getMessage());
}

itemInfo.setItemPrice(itemPrice);

// Update EIS1 - local transaction
try
{
ut.begin();
billingInfo = orderService.billCustomer( custInfo.getId(), itemInfo );
orderInfo = orderService.addOrder( custInfo.getId(), itemInfo );
ut.commit();
}
catch ( Exception e)
{
// Nothing to compensate in EIS2 yet.

try
{
ut.rollback();
}
catch (Exception ex)
{
// Rollback failed -- log the error.
// Additional checks and error handling to ensure consistency in EIS1.
}

throw new OrderException(e.getMessage());

}

// Update EIS2.
try
{
ut.begin();
catalogService.updateStockInfo(orderInfo.getItemId(), orderInfo.getItemNumber());
ut.commit();
}
catch( Exception e )
{

// Roll back the original transaction to EIS2.
try
{
ut.rollback();
}
catch (Exception ex)
{
// Rollback failed - log the error.
// Additional checks and error handling.
// Do not exit the method yet.
}

// Compensate changes to EIS1 as a single one-phase transaction.
try
{
ut.begin();
orderService.cancelOrder( orderInfo );
orderService.cancelCharge( billingInfo );
ut.commit();
}
catch ( Exception ex)
{
// Compensation failed, log error
try
{
ut.rollback();
}
catch (Exception exx)
{
// Rollback failed
// Log error
}

throw new OrderException(ex.getMessage());

}

// Throw exception back to the UI tier
throw new OrderException(e.getMessage());

}

return orderInfo;

}

The next step is to perform two updates to EIS1: one to the order system and one to the billing system. You can perform these updates within the scope of a single local transaction. If one of the updates fails, you can re-throw the exception to the UI tier and exit the method without attempting to update EIS2. The UI tier will need to notify the user of transaction failure, and solicit input on how to handle the situation. The user may choose to re-try or exit the application. Because both of these operations are running in the same transaction and the previous access to EIS2 is read-only no compensating transactions are required at this stage.

The last thing you’ll do is update stock availability on EIS2. You perform this again within the scope of a local transaction; however, this is a different transaction from the already-completed EIS1 transaction. Because you’re using programmatic transaction demarcation a failure to update EIS2 will result in the need to undo committed changes to EIS1. This is why the catch block contains calls to EIS1 transactions that cancel updates to the order and billing systems.

It is important to note that the programmatic transaction demarcation approach is not a perfect substitute for distributed transactions with 2PC. If the compensating transactions themselves fail, the systems will be left in an inconsistent state. In a real-world application, additional exception handling would be needed to handle this sort of situation. For example, in the case of compensating transaction failure you could send notification to the system administrator, or use messaging technology to retry these transactions at a later time.

EJB Deployment Descriptor Settings

In an EJB-based solution, the EJB deployer must tell the application server how transaction demarcation will be handled by configuring EJB deployment descriptor settings. The first step in choosing the correct deployment descriptor setting for your application is to asses its requirements. Here’s what you know about the example JCA implementation:

1. The deployment descriptor for the CustomerSession EJB component has to indicate that you’re using programmatic (that is, bean-managed) transaction demarcation (TX_BEAN_MANAGED).

2. Deployment descriptors for OrderService and CatalogService session beans should use declarative (that is, container-managed demarcation) and methods on these beans should always be executed in the context of a transaction.

3. The OrderService and CatalogService session beans provide access to underlying EIS transactions. Methods on these beans are called by the CustomerSession session bean, which implements the process logic behind your application, as well as by other process-oriented components.

4. The CustomerSession bean should start a transaction prior to calling methods on OrderService because the transaction will encompass multiple operations on EIS1. Because each of the two EIS2 operations can execute as independent transactions (and neither can be part of a global transaction encompassing all four operations), either programmatic or declarative demarcation could be used for the calls to CatalogService methods. (Note that I’ve already opted to let the CustomerSession bean control the transaction boundaries for all the operations.)

Given these requirements, you could use the TX_REQUIRED deployment descriptor setting for CatalogService and OrderService beans. This setting would ensure that bean methods could join a transaction already in progress or start a new transaction if need be. Note that with TX_REQUIRED, if the calling code does not start the transaction, then each call, including those to EIS1, will take place as separate transactions — which is not what you want.

A alternative approach would be to force any component (for example, the CustomerSession bean) that implements process logic to perform transaction demarcation. This approach, which could be accomplished using TX_MANDATORY deployment descriptor setting for the OrderService and the CatalogService session beans, would ensure the desired transactional behavior for the application. If you chose this approach, the calling component would need to start a transaction prior to calling methods on the EIS facade, or an exception would be raised. One disadvantage of this approach is that it requires components that need only single-method transactions to be responsible for controlling transaction boundaries.

Specifying the transaction isolation level

The J2EE specification defines a deployment descriptor attribute that specifies the transaction isolation level for a given EJB method. For example, the EJB deployer could configure the transaction isolation level for the getItemPrice() method of the CatalogService session bean to execute with the TRANSACTION_READ_COMMITTED isolation level to ensure that only committed pricing data are read in during the transaction. By changing the isolation level, the J2EE developer or deployer can balance data integrity against performance constraints to performance tune the application.

However, different EISs differ in their transactional capabilities and will react to transaction isolation settings in different ways. In this case, changing transaction isolation settings for EIS facades will make little difference, as the ECI and XCF communication protocols will not communicate this information to the back ends. COBOL transactions running on these systems will address transactional isolation aspects using programmatic techniques specific to those platforms.

Choosing a solution

In general, to determine how a given target EIS will respond to transaction isolation settings you will need to consult documentation for that EIS, and for the resource adapter that is used to provide connectivity. Although resource adapters used in the example do not respond to changes in J2EE transactional isolation level settings, some EISs built around relational database systems may be affected. It is important to understand the exact behavior that will result due to these changes in a particular EIS, as this can have significant impact on performance characteristics of the EIS and your application. In a worst-case scenario, the integrity of the underlying data could be compromised. Developers working on J2EE projects should consult with EIS administrators to ensure that correct policies for transactional isolation are being followed.

In Conclusion

The Java Connector Architecture specification provides J2EE developers with a convenient solution for building transactional J2EE applications that involve legacy systems. JCA allows you to integrate existing EIS while maintaining correct transactional semantics required for e-business.

The key problem you will typically need to address has to do with the fact that different resource adapters provide different levels of transaction support. In many cases it may not be possible to rely on the underlying transaction manager to coordinate a distributed transaction among affected systems. In some cases, although possible, distributed transactions may not be desirable due to the performance overhead associated with the two phase commit protocol.

In these cases you may need to rely on compensating transaction logic and programmatic transaction demarcation. This approach also has its drawbacks. Compensating transactions may themselves fail, leaving the system in an inconsistent state.

Another pitfall to watch for has to do with the fact that the JCA specification does not describe how the resource adapter should handle EJB transaction isolation levels. Some resource adapters simply ignore these settings due to the fact that the target EIS-specific communication protocol does not propagate this information to the back end, or due to the fact that the target EIS transaction model does not provide any equivalent notion. In other cases, changes to J2EE transaction isolation levels may dramatically impact EIS performance. To determine the exact behavior you should carefully study resource adapter documentation, and contact the manufacturer if this aspect is not described in sufficient detail.

Resources

• Willy Farrell’s ” Introduction to the J2EE Connector Architecture” tutorial covers the basics of working with JCA.

• ” Developing applications with JCA-based tools” ( developerWorks , January 2002) introduces the practical aspects of the J2EE Connector Architecture by using JCA-based tools from IBM to build, test, deploy, and run a J2EE EJB application. (Excerpted from J2EE Connector Architecture and Enterprise Application Integration by Rahul Sharma, Beth Stearns, and Tony Ng; Addison-Wesley, 2001.)

• ” Build JCA-compliant resource adapters with WebSphere Studio Application Developer” ( developerWorks , August 2003) shows you how to write your own JCA-compliant resource adapter.

• ” Getting old folks and whippersnappers together” ( developerWorks , June 2002) is a guide to integrating legacy CICS transactions using IBM’s JCA-based tools.

• The J2EE specification is the definitive reference for the EJB programming model.

• ” Getting started with EJB technology” ( developerWorks , April 2003) is a comprehensive tutorial introducing the basics of EJB programming and the J2EE environment.

• See the JCA home page to learn more about the JCA specification.

• You’ll find articles about every aspect of Java programming in the developerWorks Java technology zone.

• Visit the Developer Bookstore for a comprehensive Listing of technical books, including hundreds of Java-related titles.

• Also see the Java technology zone tutorials page for a complete listing of free Java-focused tutorials from developerWorks.

2010-05-26T11:29:56+00:00 April 22nd, 2005|Java|0 Comments

About the Author:

Mikhail Genkin is a solution architect working with IBM Integrated Services and Support for WebSphere. He helps IBM customers build business integration solutions. He has contributed to several releases of Visual Age for Java, Enterprise Edition, WebSphere Application Server, Enterprise Edition, and WebSphere Application Developer, Integration Edition.

Leave A Comment