10.2 Programmatic Transaction Demarcation

The preferred way to demarcate transactions in EJB applications is to use transaction attributes. By using transaction attributes, the bean developer does not have to manage transaction boundaries programmatically in the enterprise bean's code. However, although declarative transaction demarcation via transaction attributes works in most cases, the declarative demarcation does not provide the required functionality in some situations or is awkward to use, such as when it forces the application developer to partition the application unnaturally into multiple enterprise beans to achieve the required transaction demarcation.

This section discusses when and how the application developer should use programmatic transaction demarcation to control transaction boundaries programmatically. The application developer may control the transaction boundaries programmatically either in the client code for the enterprise bean or directly in the enterprise bean business methods.

10.2.1 Transaction Demarcation by a Client

Typically, an enterprise bean client, such as a Web application or a stand-alone Java client application, does not manage transaction boundaries. Instead, the client invokes methods on an enterprise bean, and the EJB container in which the target enterprise bean is deployed automatically manages transactions based on the values of the transaction attributes for the invoked method. This container-provided transaction demarcation is transparent to the client. The client either sees a successful completion of the invoked method or, if an error occurs, receives the java.rmi.RemoteException exception from the invoked method.

Certain situations require the client to demarcate transactions programmatically. Typically, in these situations, the client needs to combine the invocation of multiple methods into a single transaction. The methods can be on the same enterprise bean or on multiple beans. For this to work, the client needs to demarcate transactions programmatically, which the client accomplishes by using the javax.transaction.UserTransaction interface. (The javax.transaction.UserTransaction interface is part of the Java™ Transaction API [JTA]. More information about JTA is available at http://java.sun.com/j2ee/docs.html.) The client obtains the javax.transaction.UserTransaction interface from its environment, using the JNDI name java:comp/UserTransaction.

The MyServlet servlet, which transfers funds from one bank account to another, illustrates this. Figure 10.1 shows the OID for the interactions that occur during MyServlet's operation.

Figure 10.1. MyServlet OID

graphics/10fig01.gif

MyServlet executes the funds transfer in a single transaction by using the TransferFunds JavaBean component. (Note that TransferFunds is not an enterprise bean. However, as far as the discussion of transactions is concerned, the TransferFunds bean is considered part of the servlet.)

Code Example 10.1 shows the MyServlet code:

Code Example 10.1 The MyServlet Class
 public class MyServlet extends HttpServlet {    public void service(ServletRequest req, ServletResponse resp) {       ...       TransferFunds transferFunds = new TransferFunds();       transferFunds.setAccountFrom(...);       transferFunds.setAccountTo(...);       transferFunds.setAmount(...);       try {          transferFunds.execute();       } catch (TransferException ex) {          ...       }       ...    } } 

Code Example 10.2 shows the code for the TransferFunds JavaBean:

Code Example 10.2 The TransferFunds JavaBean Class
 import javax.transaction.*; ... public class TransferFunds {    String accountNumberFrom;    String accountNumberTo;    double amount;    public void setAccountFrom(String accountNumber) {       accountNumberFrom = accountNumber;    }    public void setAccountTo(String accountNumber) {       accountNumberTo = accountNumber;    }    public void setAmount(double amt) {       amount = amt;    }    public void execute() throws TransferException    {       UserTransaction ut = null;       try {          ...          AccountHome h1 = ...;          AccountHome h2 = ...;          Account acctFrom = h1.findByPrimaryKey(                                  accountNumberFrom);          Account acctTo = h2.findByPrimaryKey(                                  accountNumberTo);          // Obtain the UserTransaction interface.          Context initCtx = new InitialContext();          ut = (UserTransaction)initCtx.lookup(                "java:comp/UserTransaction");          // Perform the transfer.          ut.begin();          acctFrom.debit(amount);          acctTo.credit(amount);          ut.commit();          // Transfer was completed.       } catch (Exception ex) {          try {             if (ut != null)                ut.rollback();          } catch (Exception ex) {          }          // Transfer was not completed.          throw new TransferException(ex);       }    } } 

Let's take a closer look at the implementation of the execute method of the TransferFunds JavaBean. The servlet client uses the execute method to accomplish a number of tasks. Assuming that it does not encounter any failures, the client code works as described in the next paragraph.

First, the client obtains the remote interfaces for the two accounts acctFrom and acctTo involved in the transaction. The client then uses the JNDI API to obtain a reference to the UserTransaction interface from the servlet's environment. Once it obtains the reference, the client starts a transaction, using the begin method of the UserTransaction interface. The client then debits the acctFrom account and credits the acctTo account. Finally, the client commits the transaction, using the commit method of the UserTransaction interface.

However, what is even more interesting is how the execute method deals with failures. Note that the execute method wraps all its statements in a try block. If the execution of the statements in the try block raises an exception, this executes the block of code in the catch clause. The block of code in the catch clause attempts to roll back the in-progress transaction started by the execute method and throws TransferException to the caller.

If the servlet container crashes before the transaction is committed, the transaction manager will automatically roll back all updates performed by the execute method. For example, if the execute method debited acctFrom before the servlet container crashed, the transaction manager instructs the database that stores acctFrom to roll back the changes caused by the debit operation.

10.2.2 Transaction Demarcation by a Session Bean

A session bean or a message-driven bean can use the UserTransaction interface to demarcate transactions programmatically. However, an entity bean cannot use the UserTransaction interface. In this section, we describe a typical scenario in which the session bean developer uses the UserTransaction interface to demarcate a transaction rather than relying on the declarative transaction demarcation via transaction attributes.

The J2EE platform does not allow a stand-alone Java application to use the UserTransaction interface. How can a stand-alone Java application perform multiple invocations to an enterprise bean within a single transaction if it cannot use the UserTransaction interface? The application can use the bean-managed transaction demarcation feature of the EJB specification to combine multiple client-invoked methods into a single transaction. It would be impossible for a stand-alone Java application to achieve this combining multiple client-invoked methods into a single transaction with declarative transaction demarcation.

We illustrate this by using the session bean example from Chapter 4. Let's assume that a stand-alone Java client application uses the EnrollmentEJB session bean in that example. Let's further assume, for the sake of this illustration, that the logic of the Benefits Enrollment application requires that all data access performed by the multiple steps of the entire enrollment business process be part of a single transaction. (This is not a very realistic example!)

In such a scenario, the application developer would design the EnrollmentEJB session bean as a bean with bean-managed transaction demarcation. The developer would modify the EnrollmentBean class described in Section 4.4.2, EnrollmentBean Session Bean Class Details, on page 89 to obtain and use the UserTransaction interface in the ejbCreate and commitSelections methods, as illustrated in Code Example 10.3:

Code Example 10.3 EnrollmentBean Class with Bean-Managed Transaction Demarcation
 public class EnrollmentBean implements SessionBean {    UserTransaction ut = null;    ...    public void ejbCreate(int emplNum) throws EnrollmentException    {       // Obtain the UserTransaction interface from the       // session bean's environment.       Context initCtx = new InitialContext();       ut = (UserTransaction)initCtx.lookup(                "java:comp/UserTransaction");       // Start a transaction.       ut.begin();       // The rest of the ejbCreate method       employeeNumber = emplNum;          ...    }    ...    public void commitSelections() {       // Insert new or update existing benefits selection record.       if (recordDoesNotExist) {          benefitsDAO.insertSelection(selection);          recordDoesNotExist = false;       } else {          benefitsDAO.updateSelection(selection);       }       // Update information in the payroll system.       try {          payroll.setBenefitsDeduction(employeeNumber,                                       payrollDeduction);       } catch (Exception ex) {          throw new EJBException(ex);       }       // Commit the transaction started in ejbCreate.       try {          ut.commit();       } catch (Exception ex) {          // Handle exception from commit.          ...       }    }    ... } 

The ejbCreate method starts a transaction that then spans all the methods invoked by the client application. The commitSelections method commits the transaction after the client application has completed its work.

Figures 10.2 and 10.3 show the OIDs for these transaction operations. The OID diagrams illustrate the interactions between the Enrollment session object and the transaction manager that take place via the UserTransaction interface. They also illustrate the interactions between the transaction manager and the corporate databases.

Figure 10.2. Transaction OID, Part 1

graphics/10fig02.gif

Figure 10.3. Transaction OID, Part 2

graphics/10fig03.gif

The Enrollment session object starts the transaction by invoking the begin method on the UserTransaction interface, which causes the container to include the access to the corporate databases performed by the Enrollment object as part of the transaction. As the OID diagram illustrates, the container enlists the corporate databases with the transaction.

In addition, when the Enrollment object invokes the Payroll object, the container propagates the transaction context to the Payroll object to include the payroll-deduction update as part of the transaction. Finally, the Enrollment object commits the transaction by invoking the commit method on the UserTransaction interface. The transaction manager instructs the corporate databases to commit the changes made by the transaction. If the corporate databases are located on multiple servers, the transaction manager performs the two-phase commit protocol.

All the accesses to the corporate databases between the UserTransaction.begin method invoked in the ejbCreate method and the UserTransaction.commit method invoked at the end of the commitSelections method are part of a single transaction.

What are the pitfalls of using bean-managed transaction demarcation? As we stated earlier, bean-managed transaction demarcation is typically used to combine multiple client-invoked methods into a single transaction. This means that a transaction is "in progress" across a client's multiple interactions with the application. The transaction may block other transactions because a transaction causes the resource managers to hold locks on the data accessed by the transaction. If a user works slowly or leaves the application in the middle of the in-progress transaction, the transaction may block all other users' transactions that need access to the data now locked by the slow user's transaction.

Therefore, transactions that span multiple user interactions with an application should be used only in environments with a small population of well-behaved users. And just as important, such transactions should always be used with a great deal of care. For example, it would be very unusual if a Web-based application with its multitude of unregulated users used transactions that span user interactions. For this reason, the benefits applications described in Chapters 4 and 8 do not use transactions that span interactions with the user.

10.2.3 Pitfalls of Using Programmatic Transaction Demarcation

The developer using programmatic transaction demarcation needs to be very careful in the placement of the begin, commit, and rollback calls in the application code.

  • The programmer must ensure that begin is not called when the application is already associated with a transaction. J2EE does not support nested transactions.

  • The programmer must ensure that the application will eventually commit or roll back the transaction. This may be nontrivial if the application code has many execution paths and Java exceptions are thrown. If the application does not commit or roll back a transaction, the transaction manager will eventually timeout the transaction and roll it back. Before the timeout expires, the locks held by the transaction may block other transactions from making progress.

Therefore, an application developer should use declarative transaction demarcation whenever possible and should apply programmatic transaction demarcation only for those cases for which declarative transaction demarcation does not work. The container implements declarative transaction demarcation and properly handles all the application execution paths.



Applying Enterprise Javabeans
Applying Enterprise JavaBeans(TM): Component-Based Development for the J2EE(TM) Platform
ISBN: 0201702673
EAN: 2147483647
Year: 2003
Pages: 110

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net