Bean-managed transactions (EJB 3)

The greatest strength of CMT is also its greatest weakness. Using CMT, you are limited to having the transaction boundaries set at the beginning and end of business methods and relying on the container to determine when a transaction starts, commits, or rolls back. BMT, on the other hand, allows you to specify exactly these details programmatically, using semantics similar to the JDBC transaction model with which you might already be familiar. However, even in this case, the container helps you by actually creating the physical transaction as well as taking care of a few low-level details. With BMT, you must be much more aware of the underlying JTA transaction API, primarily the javax.transaction.User-Transaction interface, which we’ll introduce shortly. But first, we’ll redevelop the Snag-It ordering code in BMT so that we can use it in the next few sections. You’ll learn more about the javax.transaction.UserTransaction interface and how to use it. We’ll also discuss the pros and cons of using BMT over CMT.

Snag-It ordering using BMT

Listing 6.3 reimplements the code in listing 6.1 using BMT. It checks if there are any bids on the item ordered, validates the user’s credit card, charges the customer,and removes the item from bidding. Note that the import statements are omitted and error handling trivialized to keep the code sample short.

Listing 6.3 Implementing Snag-It using BMT


Listing 6.3 Implementing Snag-It using BMT

Briefly scanning the code, you’ll note that the @TransactionManagement annotation specifies the value TransactionManagementType.BEAN as opposed to Transac-tionManagementType.CONTAINER, indicating that we are using BMT this time Q. The TransactionAttribute annotation is missing altogether since it is applicable only for CMT. A UserTransaction, the JTA representation of a BMT, is injected C and used explicitly to begin Q, commit Q, or roll back Q a transaction. The transaction boundary is much smaller than the entire method and includes only calls that really need to be atomic. The sections that follow discuss the code in greater detail, starting with getting a reference to the javax.transaction. UserTransaction.

Getting a UserTransaction

The UserTransaction interface encapsulates the basic functionality provided by a Java EE transaction manager. JTA has a few other interfaces used under different circumstances. We won’t cover them, as most of the time you’ll be dealing with UserTransaction. (For full coverage of JTA, check out http://java.sun.com/products/jta/.) As you might expect, the UserTransaction interface is too intricate under the hood to be instantiated directly and must be obtained from the container. In listing 6.3, we used the simplest way of getting a UserTransaction: injecting it through the @Resource annotation. There are a couple of other ways to do this: using JNDI lookup or through the EJBContext.

JNDI lookup

The application server binds the UserTransaction to the JNDI name java:comp/ UserTransaction. You can look it up directly using JNDI with this code:

tmp2F434_thumb

This method is typically used outside of EJBs—for example, if you need to use a transaction in a helper or a non managed class in the EJB or web tier where dependency injection is not supported. If you find yourself in this situation, you might want to think long and hard about moving the transactional code to an EJB where you have access to greater abstractions.

Using UserTransaction

You’ve already seen the UserTransaction interface’s most frequently used methods: begin, commit, and rollback. The UserTransaction interface has a few other useful methods we should take a look at as well. The definition of the entire interface looks like this:

tmp2F436_thumb

The begin method creates a new low-level transaction behind the scenes and associates it with the current thread. You might be wondering what would happen if you called the begin method twice before calling rollback or commit. You might think this is possible if you want to create a nested transaction, a paradigm supported by some transactional systems. In reality, the second invocation of begin would throw a NotSupportedException since Java EE doesn’t support nested transactions. The commit and rollback methods, on the other hand, remove the transaction attached to the current thread by using the begin method. While commit sends a "success" signal to the underlying transaction manager, rollback abandons the current transaction. The setRollbackOnly method on this interface might be slightly counterintuitive as well. After all, why bother marking a transaction as rolled back when you can roll it back yourself?

To understand why, consider the fact that we could call a cmi method from our BMT bean that contains a lengthy calculation and checks the transactional flag before proceeding (like our Power Seller credit validation example in section 6.2.4). Since our BMT transaction would be propagated to the CMT method, it might be programmatically simpler, especially in a long method, to mark the transaction rolled back using the setRollbackOnly method instead of writing an involved if-else block avoiding such conditions. The getStatus method is a more robust version of getRollbackOnly in the CMT world. Instead of returning a boolean, this method returns an integer-based status of the current transactions, indicating a more fine-tuned set of states a transaction could possibly be in. The javax.transaction.Status interface defines exactly what these states are, and we list them in table 6.3.

The setTransactionTimeout method specifies the time, in seconds, in which a transaction must finish. The default transaction timeout value is set to different values for different application servers. For example, JBoss has a default transaction timeout value of 300 seconds whereas Oracle Application Server lOg has a default transaction timeout value of 30 seconds. You might want to use this method if you’re using a very long-running transaction. Typically, it is better to simply set the application server-wide defaults using vendor-specific interfaces, however. At this point, you are probably wondering how to set a transaction timeout when using CMT instead. This is only supported by containers using either an attribute in the vendor-specific deployment descriptor or vendor-specific annotations.

Table 6.3 The possible values of the javax.transaction.Status interface. These are the status values returned by the UserTransaction.getStatus method.

Status

Description

tmp2F4-37

The associated transaction is in an active state.

tmp2F4-38

The associated transaction is marked for rollback, possibly due to invocation of the setRollbackOnly method.

tmp2F4-39

The associated transaction is in the prepared state because all resources have agreed to commit (refer to the two-phase commit discussion in section 6.1.4).

tmp2F4-40

The associated transaction has been committed.

tmp2F4-41

The associated transaction has been rolled back.

tmp2F4-42

The status for associated transaction is not known (very clever, don’t you agree?).

tmp2F4-43

There is no associated transaction in the current thread.

tmp2F4-44

The associated transaction is preparing to be committed and awaiting response from subordinate resources (refer to the two-phase commit discussion in section 6.1.4).

tmp2F4-45

The transaction is in the process of committing.

tmp2F4-46

The transaction is in the process of rolling back.

Comparing listings 6.1 and 6.3, you might ask if the additional complexity and verbosity associated with BMT is really worth it. Let’s explore this issue in detail next.

The pros and cons of BMT

CMT is the default transaction type for EJB transactions. In general, BMT should be used sparingly because it is verbose, complex, and difficult to maintain. There are some concrete reasons to use BMT, however. BMT transactions need not begin and end in the confines of a single method call. If you are using a stateful session bean and need to maintain a transaction across method calls, BMT is your only option. Be warned, however, that this technique is complicated and error prone and you might be better off rewriting your application rather than attempting this. Can you spot a bug in listing 6.3? The last catch block did not roll back the transaction as all the other catch blocks did. But even that is not enough; what if the code throws an error (rather than an exception)? Whichever way you do it, it is error prone and we recommend using CMT instead.

Another argument for BMT is that you can fine-tune your transaction boundaries so that the data held by your code is isolated for the shortest time possible. Our opinion is that this idea indulges in premature optimization, and again, you are probably better off refactoring your methods to be smaller and more specific anyway. Another drawback for BMT is the fact that it can never join an existing transaction. Existing transactions are always suspended when calling a BMT method, significantly limiting flexible component reuse.

This wraps up our discussion of EJB transaction management. It is now time to turn our attention to another critical aspect of enterprise Java development: security.

Next post:

Previous post: