Transaction (Enterprise JavaBeans 3.1)

ACID Transactions

Unfortunately, good business-object design is not enough to make EJBs useful in an industrial-strength application. The problem is not with the definition of the EJBs or the taskflow; the problem is that a good design does not, in and of itself, guarantee that a business method represents a good transaction. To understand why, we will take a closer look at what a transaction is and what criteria a transaction must meet to be considered reliable.

In business, a transaction usually involves an exchange between two parties. When you purchase an ice cream cone, you exchange money for food; when you work for a company, you exchange skill and time for money (which you use to buy more ice cream). When you are involved in these exchanges, you monitor the outcome to ensure that you aren’t “ripped off.” If you give the ice cream vendor a $20 , you don’t want him to drive off without giving you your change; likewise, you want to make sure that your paycheck reflects all the hours that you worked. By monitoring these commercial exchanges, you are attempting to ensure the reliability of the transactions; you are making sure that each transaction meets everyone’s expectations.

In business software, a transaction embodies the concept of a commercial exchange. A business system transaction (transaction for short) is the execution of a unit-of-work that accesses one or more shared resources, usually databases. A unit-of-work is a set of activities that relate to each other and must be completed together. The reservation process is a unit-of-work made up of several activities: recording a reservation, debiting a credit card, and generating a ticket.

The object of a transaction is to execute a unit-of-work that results in a reliable exchange. Here are some types of business systems that employ transactions:

ATM

The ATM (automatic teller machine) you use to deposit, withdraw, and transfer funds executes these units-of-work as transactions. In an ATM withdrawal, for example, the ATM checks to make sure you don’t overdraw, and then it debits your account and spits out some money.

Medical system

In a medical system, important data—some of it critical—is recorded about patients every day, including information about clinical visits, medical procedures, prescriptions, and drug allergies. The doctor prescribes the drug, and then the system checks for allergies, contraindications, and appropriate dosages. If all tests pass, the drug can be administered. These tasks make up a unit-of-work. A unit-of-work in a medical system may not be financial, but it’s just as important. Failure to identify a drug allergy in a patient could be fatal.

As you can see, transactions are often complex and usually involve the manipulation of a lot of data. Mistakes in data can cost money, or even lives. Transactions must therefore preserve data integrity, which means that the transaction must work perfectly every time or not be executed at all. This is a pretty tall order. As difficult as this requirement is, however, when it comes to commerce, there is no room for error. Units-of-work involving money or anything of value always require the utmost reliability because errors affect the revenues and the well-being of the parties involved.

To give you an idea of the accuracy required by transactions, think about what would happen if a transactional system suffered from even infrequent errors. ATMs provide customers with convenient access to their bank accounts and represent a significant percentage of the total transactions in personal banking. The transactions handled by ATMs are simple but numerous, providing us with a great example of why transactions must be error-proof. Let’s say that a bank has 100 ATMs in a metropolitan area, and each ATM processes 300 transactions (deposits, withdrawals, and transfers) a day, for a total of 30,000 transactions per day. If each transaction involves the deposit, withdrawal, or transfer of an average of $100, then about $3 million will move through the ATM system per day. In the course of a year, that’s a little more than $1 billion:

365 days x 100 ATMs x 300 transactions x $100 = $1,095,000,000

How well do the ATMs have to perform to be considered reliable? For the sake of argument, let’s say that ATMs execute transactions correctly 99.99% of the time. This seems to be more than adequate; after all, only one out of every 10,000 transactions executes incorrectly. But if you do the math, that could result in more than $100,000 in errors over the course of a year!

$1,095,000,000 x .01% = $109,500

Obviously, this example is an oversimplification of the problem, but it illustrates that even a small percentage of errors is unacceptable in high-volume or mission-critical systems. For this reason, experts have identified four characteristics of a transaction that must be met for a system to be considered safe. Transactions must be atomic, consistent, isolated, and durable (ACID)—the four horsemen of transaction services:

Atomic

An atomic transaction must execute completely or not at all. This means that every task within a unit-of-work must execute without error. If any of the tasks fail, the entire unit-of-work or transaction is aborted, meaning that any changes to the data are undone. If all the tasks execute successfully, the transaction is committed, which means that the changes to the data are made permanent or durable. Consistent

Consistency refers to the integrity of the underlying data store. It must be enforced by both the transactional system and the application developer. The transactional system fulfills this obligation by ensuring that a transaction is atomic, isolated, and durable. The application developer must ensure that the database has appropriate constraints (primary keys, referential integrity, and so forth) and that the unit-of-work—the business logic—doesn’t result in inconsistent data (i.e., data that is not in harmony with the real world it represents). In an account transfer, for example, the debit to one account must equal the credit to another account.

Isolated

Isolation means that a transaction must be allowed to execute without interference from other processes or transactions. In other words, the data that a transaction accesses cannot be affected by any other part of the system until the transaction or unit-of-work is completed. Durable

Durability means that all the data changes made during the course of a transaction must be written to some type of physical storage before the transaction is successfully completed. This ensures that the changes are not lost if the system crashes.

To get a better idea of what these principles mean, we will introduce a BlackjackEJB to model an online blackjack game in terms of the four ACID properties.

Example: The BlackjackEJB

In this example, we’re not so much interested with handling the business rules of a real game of blackjack. However, the outputs of gameplay (whether money exchanges parties as expected) are centrally important to the function of the service. As such, we’ll expose a single business method that allows users to play single trials, which compose a single unit of work.

tmp97236_thumb1_thumb1

Depending upon the result of the bet, the designated amount should either be added to the user’s account or transferred into the game service’s account.

To facilitate the transfers of money, we’ll also expose a simple bank:

tmp97237_thumb1_thumb1tmp97238_thumb2_thumb1

Through the BankEJB, we’ll expect to safely exchange funds.

Helper EJBs for Testing Transactions

During testing and development, it’s often helpful to get a hook into the underlying system to manually specify the boundaries that mark the beginning and end of a unit of work. For this we’ll provide another simple view:

tmp97239_thumb_thumbtmp97240_thumb1_thumb1

Although we would not ever let a real client arbitrarily submit java.util.concurrent.Callable types for execution, this is a nice way to allow tests to run business logic inside the scope of a transaction. This has the added benefit of providing access to managed JPA entities before they’re detached from the EntityManager at transaction completion. We can also force rollbacks via this mechanism to test that our application’s state is left consistent in the case of an exceptional condition.

Is the BlackjackEJB Atomic?

Our first measure of the BlackjackEJB’s reliability is its atomicity: does it ensure that the transaction executes completely or not at all? What we are really concerned with are the critical tasks that change or create information. When placing a bet, the game-play must succeed without error, and funds must leave the user’s account and be added to the winner’s. All of these tasks must be successful for the entire transaction to be successful.

To understand the importance of the atomic characteristic, imagine what would happen if even one of the subtasks failed to execute. For example, if the withdrawal from the loser’s account succeeded but the money failed to be placed in the winner’s account, the money would be lost from the system and no longer accounted for (loss of consistency).

So the only way bet() can be completed is if all the critical tasks execute successfully. If something goes wrong, the entire process must be aborted. Aborting a transaction requires more than simply not finishing the tasks; in addition, all the tasks that did execute within the transaction must be undone. In our example failure case, this means that the money must be restored to the loser’s account, and an exception must be thrown to signal that the game didn’t complete successfully.

Is the BlackjackEJB Consistent?

In order for a transaction to be consistent, the business system must make sense after the transaction has completed. In other words, the state of the business system must be consistent with the reality of the business. This requires the transaction to enforce the atomic, isolated, and durable characteristics, and it also requires diligent enforcement of integrity constraints by the application developer. In our failure example, failing to deposit funds after withdrawing from another as part of a transfer constitutes a loss of consistency; the money has evaporated from the system.

In addition, the database must be set up to enforce integrity constraints. For example, it should not be possible for a bank account to be associated with a null user or have a foreign key reference to a user ID that does not exist.

Is the BlackjackEJB Isolated?

If you are familiar with the concept of thread synchronization in Java or row-locking schemes in relational databases, isolation will be a familiar concept. To be isolated, a transaction must protect the data it is accessing from other transactions. This is necessary to prevent other transactions from interacting with data that is in transition. During a transfer between accounts in our blackjack game’s bank, the transaction is isolated to prevent other transactions from modifying the entities and tables that are being updated. Imagine the problems that would arise if separate transactions were allowed to change any entity bean at any time—transactions would walk all over one another. Several account transfers could take place at the same time and withdraw from the same account, possibly overdrawing it.

The isolation of data accessed by EJBs does not mean that the entire application shuts down during a transaction. Only those entity beans and data directly affected by the transaction are isolated. That is to say, during an account transfer, only the removal and destination accounts need isolation.

Is the BlackjackEJB Durable?

To be durable, a business method must write all changes and new data to a permanent data store before it can be considered successful. This may seem like a no-brainer, but often it does not happen in real life. In the name of efficiency, changes are often maintained in memory for long periods of time before being saved on a disk drive. The idea is to reduce disk accesses—which slow systems down—and only periodically write the cumulative effect of data changes. Although this approach is great for performance, it is also dangerous because data can be lost when the system goes down and memory is wiped out. Durability requires the system to save all updates made within a transaction as the transaction successfully completes, thus protecting the integrity of the data.

Only when data is made durable are those specific records accessible through their respective entities from other transactions. Hence, durability also plays a role in isolation. A transaction is not finished until the data is successfully recorded.

Ensuring that transactions adhere to the ACID principles requires careful design. The system has to monitor the progress of a transaction to ensure that it does all of its work, that the data is changed correctly, that transactions do not interfere with each other, and that the changes can survive a system crash. Engineering all of this functionality into a system is a lot of work, and not something you would want to reinvent for every business system on which you work. Fortunately, EJB is designed to support transactions automatically, making the development of transactional systems easier. The rest of this topic examines how EJB supports transactions implicitly (through declarative transaction attributes) and explicitly (through the Java Transaction API, or JTA).


Declarative Transaction Management

One of the primary advantages of Enterprise JavaBeans is that it allows for declarative transaction management. Without this feature, transactions must be controlled using explicit transaction demarcation, which involves the use of explicit APIs such as the Java Transaction Service (JTS). At best, explicit demarcation is difficult if you use the aforementioned APIs, particularly if you are new to transactional systems. In addition, it requires that the transactional code be written within the business logic, which reduces the clarity of the code. We talk more about explicit transaction management and EJB later in this topic.

With declarative transaction management, the transactional behavior of EJBs can be controlled using the @javax.ejb.TransactionAttribute annotation or the EJB deployment descriptor, both of which can set transaction attributes for individual enterprise bean methods. This means that the transactional behavior of an EJB can be changed without changing the EJB’s business logic by simply annotating the method in a different way or modifying XML. Declarative transaction management reduces the complexity of transactions for EJB developers and application developers and makes it easier to create robust transactional applications. Where no explicit declarative transaction properties have been defined, EJB will provide a default (which we’ll soon see).

Transaction Scope

Transaction scope is a crucial concept for understanding transactions. In this context, transaction scope refers to managed resources (such as EJBs and entities) that are participating in a particular transaction. In the wrapInTx() method of the test-only TxWrappingEJB, all of the business logic designated in the supplied Callable is executed within a single transaction. When the method invocation completes, the transaction is over and its scope is no longer in context.

As you know, a transaction is a unit-of-work made up of one or more tasks. In a transaction, all the tasks that make up the unit-of-work must succeed for the entire transaction to succeed; in other words, the transaction must be atomic. If any task fails, the updates made by all the other tasks in the transaction will be rolled back or undone. In EJB, tasks are generally expressed as enterprise bean methods, and a unit-of-work consists of every enterprise bean method invoked in a transaction. The scope of a transaction includes every EJB that participates in the unit-of-work.

It is easy to trace the scope of a transaction by following the thread of execution. If the invocation of the wrapInTx() method begins a transaction, then logically, the transaction ends when the method completes. The scope of the wrapInTx() transaction would include any transaction-aware service it contains—every EJB, entity, or other managed resources. A transaction is propagated to an EJB when that EJB’s method is invoked and included in the scope of that transaction. The transaction is also propagated to the persistence context of an EntityManager. The persistence context keeps track of changes made to persistent managed objects and commits them if the transaction succeeds.

A transaction can end if an exception is thrown while the wrapInTx() method is executing. The exception can be thrown from any referenced code or the wrapInTx() method itself. An exception may or may not cause a rollback, depending on its type. We discuss exceptions and transactions in more detail later.

The thread of execution is not the only factor that determines whether an EJB is included in the scope of a transaction; the EJB’s transaction attributes also play a role. Determining whether an EJB participates in the transaction scope of any unit-of-work is accomplished implicitly, using the EJB’s transaction attributes, or explicitly, using the JTA.

Transaction Attributes

As an application developer, you normally don’t need to control transactions explicitly when using an EJB server. EJB servers can manage transactions implicitly, based on the transaction attributes established at deployment time. When an EJB is deployed, you can set its runtime transaction attribute in the @javax.ejb.TransactionAttribute annotation or deployment descriptor to one of several values:

NotSupported

Supports

Required

RequiresNew

Mandatory

Never

You may set a transaction attribute for the entire EJB (in which case it applies to all methods) or at different transaction attributes for individual methods. The former method is much simpler and less error-prone, but setting attributes at the method level offers more flexibility. The code in the following sections shows how to set the default transaction attribute of an EJB via annotations.

Using the @TransactionAttribute annotation

The @javax.ejb.TransactionAttribute annotation can be used to apply transaction attributes to your EJB’s bean class or business methods. The attribute is defined using the javax.ejb.TransactionAttributeType Java enum:

tmp97241_thumb1_thumb

The @TransactionAttribute can be applied per method, or you can use it on the bean class to define the default transaction attribute for the entire bean class. In the case of our test TxWrappingEJB, the bean implementation class achieves the proper transaction isolation by always starting a new transaction and by running the business method inside of its context. This is implemented by using TransactionAttribute Type.REQUIRES_NEW:

tmp97242_thumb_thumb

In this example, the default transaction attribute will be REQUIRES_NEW for every method of the class because we have applied the @TransactionAttribute annotation to the bean class. This default can be overridden by applying @TransactionAttribute individually to the business methods.

If you do not specify any @TransactionAttribute and there is no XML deployment descriptor, the default transaction attribute will be REQUIRED. One of the ideas behind EJB 3.0 is to provide common defaults so that you do not have to be explicit about transaction demarcation. In the majority of cases, EJB methods will be transactional, especially if they are interacting with an entity manager.

Transaction attributes defined

Here are the definitions of the transaction attributes listed earlier. In a few of the definitions, the client transaction is described as suspended. This means the transaction is not propagated to the enterprise bean method being invoked; propagation of the transaction is temporarily halted until the enterprise bean method returns. To make things easier, we will talk about attribute types as if they were bean types. For example, we’ll say “a Required EJB” as shorthand for “an enterprise bean with the Required transaction attribute.” The attributes are:

NotSupported

Invoking a method on an EJB with this transaction attribute suspends the transaction until the method is completed. This means that the transaction scope is not propagated to the NotSupported EJB or to any of the EJBs it calls. Once the method on the NotSupported EJB is done, the original transaction resumes its execution.

Figure 17-1 shows that a NotSupported EJB does not propagate the client transaction when one of its methods is invoked.

The NotSupported attribute Supports

Figure 17-1. The NotSupported attribute Supports

This attribute means that the enterprise bean method will be included in the transaction scope if it is invoked within a transaction. In other words, if the EJB or client that invokes the Supports EJB is part of a transaction scope, the Supports EJB and all EJBs accessed by it become part of the original transaction. However, the Supports EJB doesn’t have to be part of a transaction and can interact with clients and other EJBs that are not included in a transaction scope.

Figure 17-2a shows the Supports EJB being invoked by a transactional client and propagating the transaction. Figure 17-2b shows the Supports EJB being invoked by a nontransactional client.

The Supports attribute Required

Figure 17-2. The Supports attribute Required

This attribute means that the enterprise bean method must be invoked within the scope of a transaction. If the calling client or EJB is part of a transaction, the Required EJB is automatically included in its transaction scope. If, however, the calling client or EJB is not involved in a transaction, the Required EJB starts its own new transaction. The new transaction’s scope covers only the Required EJB and all other EJBs accessed by it. Once the method invoked on the Required EJB is done, the new transaction’s scope ends.

Figure 17-3a shows the Required EJB being invoked by a transactional client and propagating the transaction. Figure 17-3b shows the Required EJB being invoked by a nontransactional client, which causes it to start its own transaction.

The Required attribute

Figure 17-3. The Required attribute

RequiresNew

This attribute means that a new transaction is always started. Regardless of whether the calling client or EJB is part of a transaction, a method with the RequiresNew attribute begins a new transaction when invoked. If the calling client is already involved in a transaction, that transaction is suspended until the RequiresNew EJB’s method call returns. The new transaction’s scope covers only the RequiresNew EJB and all the EJBs accessed by it. Once the method invoked on the RequiresNew EJB is done, the new transaction’s scope ends and the original transaction resumes. Figure 17-4a shows the RequiresNew EJB being invoked by a transactional client. The client’s transaction is suspended while the EJB executes under its own transaction. Figure 17-4b shows the RequiresNew EJB being invoked by a nontransac-tional client; the RequiresNew EJB executes under its own transaction.

The RequiresNew attribute

Figure 17-4. The RequiresNew attribute

Mandatory

This attribute means that the enterprise bean method must always be made part of the transaction scope of the calling client. The EJB may not start its own transaction; the transaction must be propagated from the client. If the calling client is not part of a transaction, the invocation will fail, throwing a javax.ejb.EJBTransactionRequiredException.

Figure 17-5a shows the Mandatory EJB invoked by a transactional client and propagating the transaction. Figure 17-5b shows the Mandatory EJB invoked by a non-transactional client; the method throws an EJBTransactionRequiredException because there is no transaction scope.

Never

This attribute means that the enterprise bean method must not be invoked within the scope of a transaction. If the calling client or EJB is part of a transaction, the Never EJB will throw an EJBException. However, if the calling client or EJB is not involved in a transaction, the Never EJB will execute normally without a transaction.

Figure 17-6a shows the Never EJB being invoked by a nontransactional client. Figure 17-6b shows the Never EJB being invoked by a transactional client; the method throws an EJBException to EJB clients because a client or EJB that is included in a transaction can never invoke the method.

The Mandatory attribute

Figure 17-5. The Mandatory attribute

The Never attribute

Figure 17-6. The Never attribute

EJB 3.0 persistence and transaction attributes

The EJB specification strongly advises that EntityManagers be accessed within the scope of a JTA transaction. So, if you are wrapping access to your persistent entities with EJBs, use only the Required, RequiresNew, and Mandatory transaction attributes. This restriction ensures that all database access occurs in the context of a transaction, which is important when the container is automatically managing persistence. There are valid exceptions to this rule when using extended persistence contexts with stateful session beans, but we’ll talk about these exceptions later in the topic.

Message-driven beans and transaction attributes

Message-driven beans may declare only the NotSupported or Required transaction attribute. The other transaction attributes don’t make sense in message-driven beans, because they apply to client-initiated transactions. The Supports, RequiresNew, Mandatory, and Never attributes are all relative to the transaction context of the client. For example, the Mandatory attribute requires the client to have a transaction in progress before calling the enterprise bean. This is meaningless for a message-driven bean, which is decoupled from the client.

The NotSupported transaction attribute indicates that the message will be processed without a transaction. The Required transaction attribute indicates that the message will be processed with a container-initiated transaction.

EJB endpoints and transaction attributes

The Mandatory transaction attribute cannot be used with EJB endpoints, because an EJB endpoint does not propagate a client transaction. This may change when web service transactions become standardized, but for now, using Mandatory with an EJB endpoint method is prohibited.

Transaction Propagation

To illustrate the impact of transaction attributes, we’ll test a bank account transfer as we saw earlier. Using the test TxWrappingEJB will ensure all operations must be initiated from the same client transaction. If any operation fails, the entire transaction fails. We’ll take the following steps in the context of one transaction:

1. Transfer $100 from Account A to Account B.

2. Check that Account A has $100 less than before, and Account B has $100 more.

3. Force a rollback.

Because we’ll encounter a mock error to make a rollback, checking the account balance of Account A after the transaction completes should yield the same balance as before we started, as if the transfer was never made.

First, let’s see some of the transaction attributes for the BankEJB:

tmp97249_thumb2_thumbtmp97250_thumb_thumb

The getBalance() method does not require a currently running transaction from the caller. Because this is a read-only operation, it does not honor atomicity, consistency, or durability. However, because of the “I” in ACID, for transaction isolation, when we have a currently running transaction, it needs to have the correct visibility to see any potential changes made within that transaction. Marking this method as SUPPORTS means we don’t impose any transactional overhead for obtaining a current balance, but we’ll respect the current balance inside of a running transaction.

The transfer() method, however, does involve write operations, and as such requires a transaction to be in play. This may be supplied as either a currently running transaction from the caller or a new one started by the EJB container if a transactional context is not present. This will guarantee that the transfer operation is treated as a single unit of work. Remember that REQUIRED does not need to be defined explicitly; this is the default value for EJBs if left out.

Now we can write our test:

tmp97251_thumb_thumb

Here we make a transfer. Because we have no currently running transaction and the BankEJB’s transfer() method has TransactionAttributeType.REQUIRED, a new transaction is created for us to handle the exchange:

tmp97252_thumb_thumb

As a precondition check, we ensure that the baltmp97253_thumb_thumbances of the two accounts in question are as expected. The executeInTx() method uses our TxWrappingEJB as we’ve described:

 

tmp97254_thumb_thumb

After checking the balances of the accounts to ensure the money was moved as expected, we throw a wrench into the system. In the next step, we’ll force a transaction rollback by making another transfer but raising an exception before the transaction finishes:

tmp97255_thumb_thumb

tmp97256_thumb_thumb

Finally, we check that we’ve gotten the expected exception, and that the account transfer did not take place outside the context of the transaction. In other words, the state kept by the transaction before the error was encountered is ditched, and the system is left in consistent state.

As a transaction monitor, an EJB server watches each method call in the transaction. If any of the updates fail, all the updates to all the EJBs and entities will be reversed, or rolled back. A rollback is like an undo command. If you have worked with relational databases, the concept of a rollback should be familiar to you. Once an update is executed, you can either commit the update or roll it back. A commit makes the changes requested by the update permanent; a rollback aborts the update and leaves the database in its original state. Making EJBs transactional provides the same kind of rollback/ commit control.

In cases in which the container implicitly manages the transaction, the commit and rollback decisions are handled automatically. When transactions are managed explicitly within an enterprise bean or by the client, the responsibility falls on the enterprise bean or application developer to commit or roll back a transaction. Programmatic demarcation of transactions is covered in detail later in this topic.

The transaction manager coordinates transactions, propagating the transaction scope from one EJB to the next to ensure that all EJBs touched by a transaction are included in the transaction’s unit-of-work. That way, the transaction manager can monitor the updates made by each enterprise bean and decide, based on the success of those updates, whether to commit all changes made by all enterprise beans to the database or roll them all back. If a system exception or a rollback application exception is thrown by a business method, the transaction is automatically rolled back. We talk more about exceptions later in this topic.

When all the EJBs and persistence contexts are registered and their updates are made, the transaction manager checks to ensure that their updates will work. If all the updates will work, the transaction manager allows the changes to become permanent. If one of the EJBs or entity managers reports an error or fails, any changes made are rolled back by the transaction manager.

In addition to managing transactions in its own environment, an EJB server can coordinate with other transactional systems. If, for example, an EJB actually came from a different application server than the BankEJB, the two application servers would cooperate to manage the transaction as one unit-of-work. This is called a distributed transaction.” A distributed transaction requires what is called a two-phase commit (2-PC or TPC). A 2-PC allows transactions to be managed across different servers and resources (e.g., databases and JMS providers). The details of a 2-PC are beyond the scope of this topic, but a system that supports it will not require any extra operations by an EJB or application developer. If distributed transactions are supported, the protocol for propagating transactions, as discussed earlier, will be supported. In other words, as an application or EJB developer, you should not notice a difference between local and distributed transactions.

A number of topics on transaction processing and 2-PC are available. Perhaps the best topics on the subject are Principles of Transaction Processing (Morgan Kaufmann) and Transaction Processing: Concepts and Techniques (Morgan Kaufmann). A much lighter resource is the series of “XA Exposed” articles (I, II, and III) by Mike Spille, which you can find at http://jroller.com/page/pyrasun/?anchor=xa_exposed.

Transactions and persistence context propagation

There are some transaction propagation rules to consider when invoking on multiple different EJBs within the same transaction that use entity managers. Here is a detailed list of persistence context-propagation rules:

• When a transaction-scoped entity manager is invoked outside the scope of a transaction, it creates a persistence context for the duration of that method call. After the method call completes, any managed objects produced by the call are immediately detached.

• If a transaction-scoped entity manager is invoked from within a transaction, a new persistence context is created (if there isn’t one already) and associated with that transaction.

• If an entity manager is invoked upon and a persistence context is already associated with the transaction, use that persistence context. The persistence context is propagated between EJB invocations in the same transaction. This means that if an EJB interacts with an injected entity manager within a transaction and then invokes on another EJB within that same transaction, that EJB call will use the same enlisted persistence context.

• If an EJB with a transaction-scoped persistence context invokes on a stateful session bean that uses an extended persistence context, an error is thrown.

• If a stateful session bean with an extended persistence context calls another EJB that has injected a transaction-scoped persistence context, the extended persistence context is propagated.

• If an EJB calls another EJB with a different transaction scope, the persistence context, regardless of whether it is extended, is not propagated.

• If a stateful session bean with an extended persistence context calls another non-injected stateful session bean with an extended persistence context, an error is thrown. If you inject a stateful session bean into another stateful session bean, those beans share the same extended persistence context. However, if you manually create a stateful session, there is no sharing of persistence contexts.

Isolation and Database Locking

Transaction isolation (the “I” in ACID) is a critical part of any transactional system. This section explains isolation conditions, database locking, and transaction isolation levels. These concepts are important when deploying any transactional system.

Dirty, Repeatable, and Phantom Reads

Transaction isolation is defined in terms of isolation conditions called dirty reads, repeatable reads, and phantom reads. These conditions describe what can happen when two or more transactions operate on the same data.f When two users execute data access methods concurrently, different problems can surface or be avoided entirely, depending on the isolation level used by the database. Transaction isolation seeks to avoid these situations.

Dirty reads

A dirty read occurs when a transaction reads uncommitted changes made by a previous transaction. If the first transaction is rolled back, the data read by the second transaction becomes invalid because the rollback undoes the changes. The second transaction will not be aware that the data it has read has become invalid.

Repeatable reads

A repeatable read occurs when the data read is guaranteed to look the same if read again during the same transaction. Repeatable reads are guaranteed in one of two ways: either the data read is locked against changes, or it is a snapshot that doesn’t reflect changes. If the data is locked, it cannot be changed by any other transaction until the current transaction ends. If the data is a snapshot, other transactions can change the data, but these changes will not be seen by this transaction if the read is repeated.

A nonrepeatable read occurs when the data retrieved in a subsequent read within the same transaction can return different results. In other words, the subsequent read can see the changes made by other transactions.

Phantom reads

A phantom read occurs when new records added to the database are detectable by transactions that started prior to the insert. Queries will include records added by other transactions after their transaction has started.

Database Locks

Databases, especially relational databases, normally use several different locking techniques. The most common are read locks, write locks, and exclusive write locks. (We’ve taken the liberty of adding snapshots to this list of techniques, although this isn’t a formal term.) These locking mechanisms control how transactions access data concurrently. Locking mechanisms impact the read conditions described in the previous section. These types of locks are simple concepts that are addressed to a degree by the Java Persistence specification, but we’ll discuss this later. Database vendors implement these locks differently, so you should understand how your database addresses these locking mechanisms to best predict how the isolation levels described in this section will work.

The four types of locks are: Read locks

Read locks prevent other transactions from changing data read during a transaction until the transaction ends, thus preventing nonrepeatable reads. Other transactions can read the data but not write to it. The current transaction is also prohibited from making changes. Whether a read lock locks only the records read, a block of records, or a whole table depends on the database being used.

Write locks

Write locks are used for updates. A write lock prevents other transactions from changing the data until the current transaction is complete but allows dirty reads by other transactions and by the current transaction itself. In other words, the transaction can read its own uncommitted changes. Exclusive write locks

Exclusive write locks are used for updates. An exclusive write lock prevents other transactions from reading or changing the data until the current transaction is complete. It also prevents dirty reads by other transactions. Some databases do not allow transactions to read their own data while it is exclusively locked.

Snapshots

A snapshot is a frozen view of the data that is taken when a transaction begins. Some databases get around locking by providing every transaction with its own snapshot. Snapshots can prevent dirty reads, nonrepeatable reads, and phantom reads. They can be problematic because the data is not real-time data; it is old the instant the snapshot is taken.

Transaction Isolation Levels

Transaction isolation is defined in terms of the isolation conditions (dirty reads, re-peatable reads, and phantom reads). Isolation levels are commonly used in database systems to describe how locking is applied to data within a transaction. The following terms are used to discuss isolation levels:

Read Uncommitted

The transaction can read uncommitted data (i.e., data changed by a different transaction that is still in progress). Dirty reads, nonrepeatable reads, and phantom reads can occur. Bean methods with this isolation level can read uncommitted changes. Read Committed

The transaction cannot read uncommitted data; data that is being changed by a different transaction cannot be read. Dirty reads are prevented; nonrepeatable reads and phantom reads can occur. Bean methods with this isolation level cannot read uncommitted data.

Repeatable Read

The transaction cannot change data that is being read by a different transaction. Dirty reads and nonrepeatable reads are prevented; phantom reads can occur. Bean methods with this isolation level have the same restrictions as those in the Read Committed level and can execute only repeatable reads. Serializable

The transaction has exclusive read and update privileges; different transactions can neither read nor write to the same data. Dirty reads, nonrepeatable reads, and phantom reads are prevented. This isolation level is the most restrictive.

These isolation levels are the same as those defined for JDBC. Specifically, they map to the static final variables in the java.sql.Connection class. The behavior modeled by the isolation levels in the connection class is the same as the behavior described here.

The exact behavior of these isolation levels depends largely on the locking mechanism used by the underlying database or resource. How the isolation levels work depends in large part on how your database supports them.

In EJB, the deployer sets transaction isolation levels in a vendor-specific way if the container manages the transaction. The EJB developer sets the transaction isolation level if the enterprise bean manages its own transactions. Up to this point, we have discussed only container-managed transactions; we discuss bean-managed transactions later in this topic.

Balancing Performance Against Consistency

Generally speaking, as the isolation levels become more restrictive, the performance of the system decreases because transactions are prevented from accessing the same data. If isolation levels are very restrictive—in other words, if they are at the Serializable level—then all transactions, even simple reads, must wait in line to execute. This can result in a system that is very slow. EJB systems that process a large number of concurrent transactions and need to be very fast will therefore avoid the Serializable isolation level where it is not necessary.

Isolation levels, however, also enforce consistency of data. More restrictive isolation levels help to ensure that invalid data is not used for performing updates. The old adage, “garbage in, garbage out,” applies. The Serializable isolation level ensures that data is never accessed concurrently by transactions, thus ensuring that the data is always consistent.

Choosing the correct isolation level requires some research about the database you are using and how it handles locking. You must also carefully analyze how each piece of data in your application is being used. For instance, most systems have some entities which will rarely, if ever, change. Even if these types of data do change, they would rarely affect the integrity of the system. Therefore, a low isolation level can be specified when a piece of business logic is viewing only this type of data. Other data, such as the current balance in an Account, on the other hand, can greatly affect the integrity of our BankEJB; this may require isolation to be more restrictive.

Controlling isolation levels

Different EJB servers allow different levels of granularity for isolation levels, and some servers defer this responsibility to the database. Most EJB servers and EntityManager implementations control the isolation level through the resource access API (e.g., JDBC and JMS) and may allow different resources to have different isolation levels. However, they will generally require a consistent isolation level for access to the same resource within a single transaction. Consult your vendor’s documentation to find out the level of control your server offers.

Bean-managed transactions in session beans and message-driven beans, however, allow you to specify the transaction isolation level using the database’s API. The JDBC API, for instance, provides a mechanism for specifying the isolation level of the database connection. For example:

tmp97257_thumb_thumb

You can have different isolation levels for different resources within the same transaction, but all enterprise beans using the same resource in a transaction should use the same isolation level.

Optimistic Locking

Using an isolation level that’s too restrictive results in the locking of resources. When many clients contend for the same resource, they must block until it’s their turn. This leads to a natural bottleneck and scalability problem that results in both high latency and reduced overall throughput.

So, how can we solve this concurrency problem? One solution is to use the optimistic locking design pattern. Optimistic locking isn’t locking in the traditional sense. Instead, we assume that no other client is trying to access the same data at the same time. Then, at transaction commit time, we let the database resolve whether the data has been altered by another transaction. If it has, we throw an exception and roll back our transaction. In other words, we are being optimistic that the data hasn’t been touched until we need to commit. How does this work? How does this avoid table-level locks? Well, to use optimistic locking, we have to use a special feature of Java Persistence.

The first thing we need to do is model our Account for use in the BankEJB:

tmp97258_thumb_thumb

The new and interesting property is the version property, which is annotated with @javax.persistence.Version. An @Version property is a column in the Account table that will hold a version ID of a particular row. Whenever the Account entity class is updated, the version column is incremented automatically by JPA. When a transaction beginning the commit process and business logic has updated the Account, the entity manager first checks to see whether the version property of the in-memory Account instance matches the version column currently stored in the database. If the versions match, then the version property is incremented. If they don’t match, then the entity manager throws an exception and the whole transaction rolls back. When the entity manager detects that the Account has been updated by another transaction and a concurrency error has occurred, it throws the javax.persistence.OptimisticLock-Exception and rolls back the transaction. Otherwise, the transaction completes successfully, and the queried Account is updated as requested and its version property is incremented. This optimistic locking solution creates a quick write-lock on one row in our database instead of the vastly unscalable table lock in the Serializable solution that was presented earlier in this topic.

It should be noted that the optimistic locking design pattern does not work all the time. If you have a row in your database that has a high concurrent write contention, then it is probably less efficient to use the optimistic locking pattern because it will create a lot of rollbacks, which create a lot of overhead in your system. In that scenario, the Serializable solution is possibly more scalable. A redesign of your data model is probably more appropriate in this situation, however. If you have high concurrent access to one particular row in your database, then your system probably won’t scale much anyway.

Programmatic Locking

The EntityManager interface has a specific lock() method for performing entity locks. To use it, you pass in the entity object you want to lock and indicate whether you want a read or write lock:

tmp97259_thumb_thumb

LockModeType.READ ensures that no dirty and nonrepeatable reads can occur on the locked entity. LockModeType.WRITE has the same semantics as READ, but it also forces an increment of the entity’s @Version property. To implement these semantics, a database row lock is usually performed (i.e., SELECT … FOR UPDATE).

Vendor implementations are not required to support locking on entities that do not have an @Version property.

Programmatic locking becomes important when you want to ensure nonrepeatable reads on entity beans that may be read within the transaction but not updated.

Nontransactional EJBs

Beans outside of a transaction’s scope normally provide some kind of stateless service that does not manipulate data in a data store. While these types of enterprise beans may be necessary as utilities during a transaction, they do not need to meet the ACID requirements. Consider a nontransactional stateless session bean, the QuoteEJB, which provides live stock quotes. This EJB may respond to a request from an EJB involved in a stock purchase transaction. The success or failure of the stock purchase as a transaction will not impact the state or operations of the QuoteEJB, so it does not need to be part of the transaction. Beans that are involved in transactions are subjected to the isolated ACID property, which means that their services cannot be shared during the life of the transaction. Making an enterprise bean transactional can be expensive at runtime. Declaring an EJB to be nontransactional (i.e., NotSupported) leaves it out of the transaction scope, which may improve the performance and availability of that service.

Explicit Transaction Management

Although this section covers JTA, it is strongly recommended that you not attempt to manage transactions explicitly. Through transaction attributes, Enterprise JavaBeans provides a comprehensive and simple mechanism for delimiting transactions at the method level and propagating transactions automatically. Only developers with a thorough understanding of transactional systems should attempt to use JTA with EJB.

EJB provides implicit transaction management on the method level: we can define transactions that are delimited by the scope of the method being executed. This is one of the primary advantages of EJB over cruder distributed object implementations because it reduces complexity, and therefore, programmer error. In addition, declarative transaction demarcation, as used in EJB, separates the transactional behavior from the business logic, meaning that a change to transactional behavior does not require changes to the business logic. In rare situations, however, it may be necessary to take control of transactions explicitly.

Explicit management of transactions is normally accomplished using the Object Management Group’s (OMG) Object Transaction Service (OTS) or the Java implementation of OTS, the Java Transaction Service (JTS). OTS and JTS provide APIs that allow developers to work with transaction managers and resources (e.g., databases and JMS providers) directly. While the JTS implementation of OTS is robust and complete, it is not the easiest API to work with; it requires clean and intentional control over the bounds of enrollment in transactions.

Enterprise JavaBeans supports a much simpler API, the Java Transaction API (JTA), for working with transactions. This API is implemented by the javax.transaction package. JTA actually consists of two components: a high-level transactional client interface and a low-level X/Open XA interface. We are concerned with the high-level client interface, since it is accessible to enterprise beans and is recommended for client applications. The low-level XA interface is used by the EJB server and container to coordinate transactions with resources such as databases.

Your use of explicit transaction management will probably focus on one simple interface: javax.transaction.UserTransaction. UserTransaction allows you to manage the scope of a transaction explicitly. Here’s how explicit demarcation might be used in an EJB:

tmp97260_thumb2_thumb2tmp97261_thumb_thumb

Here’s an interesting case. In the previous example, initialize() is both a business method and a lifecycle callback through @PostConstruct. Because @PostConstruct methods are executed by the container inside of an unspecified transactional context, if we need to interact with managed resources such as an EntityManager, we must initiate our own transaction. For this we use the injected TransactionManager APIs to demarcate the beginning and end of the transaction. Note that it’s very important to attempt the commit() in a finally block, such that any exceptional conditions are accounted for. We catch any exceptions and mark the transaction to roll back later if encountered.

Obviously, this example is contrived, but the point it makes is clear. Transactions can be controlled directly instead of depending on method scope to delimit them. The advantage of using explicit transaction demarcation is that it gives the client control over the bounds of a transaction. The client, in this example, could be a client application or another enterprise bean.i In either case, the same javax.transaction.User Transaction is used, but it is obtained from different sources, depending on whether it is needed on the client or in an enterprise bean.

Java Enterprise Edition (Java EE) specifies how a client application can obtain a User Transaction object using JNDI. Here’s how a client obtains a UserTransaction object if the EJB container is part of a Java EE system: the @javax.ejb.TransactionManager annotation can manage their own transactions. Enterprise beans that manage their own transactions are frequently referred to as bean-managed transaction (BMT) beans. Entity beans can never be BMT beans.

tmp97262_thumb_thumb

Enterprise beans can also manage transactions explicitly. Only session beans and message-driven beans that define a javax.ejb.TransactionManagementType of Bean using

Transaction Propagation in Bean-Managed Transactions

With stateless session beans, transactions that are managed using UserTransaction must be started and completed within the same method. In other words, UserTransaction transactions cannot be started in one method and ended in another. This makes sense because stateless session bean instances are shared across many clients; while one stateless instance may service a client’s first request, a completely different instance may service a subsequent request by the same client. With stateful session beans, however, a transaction can begin in one method and be committed in another because a stateful session bean is used by only one client. Therefore, a stateful session bean can associate itself with a transaction across several different client-invoked methods.

When a client that is already involved in a transaction invokes a bean-managed transaction method, the client’s transaction is suspended until the method returns. This suspension occurs regardless of whether the BMT bean explicitly started its own transaction within the method or the transaction was started in a previous method invocation. The client transaction is always suspended until the BMT method returns.

Transaction control across methods is strongly discouraged because it can result in improperly managed transactions and long-lived transactions that lock up or even leak resources. It’s best practice to let the method defining the transaction start also dictate where the transaction ends.

Message-driven beans and bean-managed transactions

Message-driven beans also have the option of managing their own transactions. In the case of MDBs, the scope of the transaction must begin and end within the onMessage() method. It is not possible for a bean-managed transaction to span onMessage() calls.

It is important to understand that in a BMT, the message consumed by the MDB is not part of the transaction. When an MDB uses container-managed transactions, the message it handles is a part of the transaction, so if the transaction is rolled back, the consumption of the message is also rolled back, forcing the JMS provider to redeliver the message. But with bean-managed transactions, the message is not part of the transaction, so if the BMT is rolled back, the JMS provider will not be aware of the transaction’s failure. However, all is not lost, because the JMS provider can still rely on message acknowledgment to determine whether the message was delivered successfully.

The EJB container will acknowledge the message if the onMessage() method returns successfully. If, however, a RuntimeException is thrown by the onMessage() method, the container will not acknowledge the message, and the JMS provider will suspect a problem and probably attempt to redeliver the message. If redelivery of a message is important when a transaction fails, your best course of action is to ensure that the onMessage() method throws an EJBException so that the container will not acknowledge the message received from the JMS provider.

Vendors use proprietary (declarative) mechanisms to specify the number of attempts to redeliver messages to BMT/NotSupported MDBs that “fail” to acknowledge receipt. The JMS-MDB provider may provide a “dead message” area into which such messages will be placed if they cannot be processed successfully according to the retry count. Administrators can monitor the dead message area so that delivered messages can be detected and handled manually.

Other than the message, everything between the UserTransaction.begin() and User Transaction.commit() methods is part of the same transaction. If a transaction failure occurs, these operations will be rolled back and the message will not be sent.

Heuristic Decisions

Transactions are normally controlled by a transaction manager (often the EJB server), which manages the ACID characteristics across several enterprise beans, databases, and servers. The transaction manager uses a two-phase commit (2-PC) to manage transactions. 2-PC is a protocol for managing transactions that commits updates in two stages. 2-PC is complex, but basically it requires that servers and databases cooperate through an intermediary—the transaction manager—in order to ensure that all of the data is made durable together. Some EJB servers support 2-PC and others do not, and the value of this transaction mechanism is a source of some debate. The important point to remember is that a transaction manager controls the transaction; based on the results of a poll against the resources (databases, JMS providers, and other resources), it decides whether all the updates should be committed or rolled back. A heuristic decision takes place when one of the resources makes a unilateral decision to commit or roll back without permission from the transaction manager. When a heuristic decision has been made, the atomicity of the transaction is lost and data-integrity errors can occur.

UserTransaction throws a few different exceptions related to heuristic decisions, discussed in the following section.

UserTransaction

EJB servers are required to support UserTransaction but are not required to support the rest of JTA, nor are they required to use JTS for their transaction service. UserTransaction is defined as follows:

tmp97263_thumb1_thumb1

Here’s what the methods defined in this interface do: begin()

Invoking the begin() method creates a new transaction. The thread that executes the begin() method is immediately associated with the new transaction, which is then propagated to any EJB that supports existing transactions. The begin() method can throw one of two checked exceptions. An IllegalStateException is thrown when begin() is called by a thread that is already associated with a transaction. You must complete any transactions associated with that thread before beginning a new transaction. A SystemException is thrown if the transaction manager (i.e., the EJB server) encounters an unexpected error condition. commit()

The commit() method completes the transaction that is associated with the current thread. When commit() is executed, the current thread is no longer associated with a transaction. This method can throw several checked exceptions. An IllegalStateException is thrown if the current thread is not associated with a transaction. A SystemException is thrown if the transaction manager (the EJB server) encounters an unexpected error condition. A TransactionRolled-backEx ception is thrown when the entire transaction is rolled back rather than committed; this can happen if one of the resources was unable to perform an update or if the UserTransaction.rollBackOnly() method was called. A HeuristicRollback Exception indicates that one or more resources made a heuristic decision to roll back the transaction. A HeuristicMixedException indicates resources made heuristic decisions to both roll back and commit the transaction; that is, some resources decided to roll back and others decided to commit. rollback()

The rollback() method is invoked to roll back the transaction and undo updates. The rollback() method can throw one of three different checked exceptions. A SecurityException is thrown if the thread using the UserTransaction object is not allowed to roll back the transaction. An IllegalStateException is thrown if the current thread is not associated with a transaction. A SystemException is thrown if the transaction manager (the EJB server) encounters an unexpected error condition.

setRollbackOnly()

The setRollbackOnly() method is invoked to mark the transaction for rollback. This means that, regardless of whether the updates executed within the transaction succeed, the transaction must be rolled back when completed. This method can be invoked by any BMT EJB participating in the transaction or by the client application. The setRollBackOnly() method can throw one of two checked exceptions: an IllegalStateException is thrown if the current thread is not associated with a transaction, and a SystemException is thrown if the transaction manager (the EJB server) encounters an unexpected error condition. setTransactionTimeout(int seconds)

The setTransactionTimeout(int seconds) method sets the lifespan of a transaction (i.e., how long it will live before timing out). The transaction must complete before the transaction timeout is reached. If this method is not called, the transaction manager (EJB server) automatically sets the timeout. If this method is invoked with a value of 0 seconds, the default timeout of the transaction manager will be used. This method must be invoked after the begin() method. A SystemException is thrown if the transaction manager (EJB server) encounters an unexpected error condition.

getStatus()

The getStatus() method returns an integer that can be compared to constants defined in the javax.transaction.Status interface. A sophisticated programmer can use this method to determine the status of a transaction associated with a UserTransaction object. A SystemException is thrown if the transaction manager (EJB server) encounters an unexpected error condition.

Status

Status is a simple interface that contains constants but no methods. Its sole purpose is to provide a set of constants that describe the status of a transactional object—in this case, UserTransaction:

tmp97264_thumb_thumbtmp97265_thumb_thumb

The value returned by getStatus() tells the client using the UserTransaction the status of a transaction. Here’s what the constants mean:

STATUS_ACTIVE

An active transaction is associated with the UserTransaction object. This status is returned after a transaction has been started and prior to a transaction manager beginning a two-phase commit. (Transactions that have been suspended are still considered active.)

STATUS_COMMITTED

A transaction is associated with the UserTransaction object, and the transaction has been committed. It is likely that heuristic decisions have been made; otherwise, the transaction would have been destroyed and the STATUS_NO_TRANSACTION constant would have been returned instead. STATUS_COMMITTING

A transaction is associated with the UserTransaction object, and the transaction is in the process of committing. The UserTransaction object returns this status if the transaction manager has decided to commit but has not yet completed the process.

STATUS_MARKED_ROLLBACK

A transaction is associated with the UserTransaction object, and the transaction has been marked for rollback, perhaps as a result of a UserTransaction.setRoll backOnly() operation invoked somewhere else in the application. STATUS_NO_TRANSACTION

No transaction is currently associated with the UserTransaction object. This occurs after a transaction has completed or if no transaction has been created. This value is returned instead of throwing an IllegalStateException.

STATUS_PREPARED

A transaction is associated with the UserTransaction object. The transaction has been prepared, which means that the first phase of the two-phase commit process has completed. STATUS_PREPARING

A transaction is associated with the UserTransaction object, and the transaction is in the process of preparing, which means that the transaction manager is in the middle of executing the first phase of the two-phase commit.

STATUS_ROLLEDBACK

A transaction is associated with the UserTransaction object, and the outcome of the transaction has been identified as a rollback. It is likely that heuristic decisions have been made; otherwise, the transaction would have been destroyed and the STATUS_NO_TRANSACTION constant would have been returned. STATUS_ROLLING_BACK

A transaction is associated with the UserTransaction object, and the transaction is in the process of rolling back.

STATUS_UNKNOWN

A transaction is associated with the UserTransaction object, and its current status cannot be determined. This is a transient condition and subsequent invocations will ultimately return a different status.

EJBContext Rollback Methods

Only BMT beans can access UserTransaction from EJBContext and the JNDI ENC. Container-managed transaction (CMT) beans cannot use UserTransaction. Instead, CMT beans use the setRollbackOnly() and getRollbackOnly() methods of EJBContext to interact with the current transaction. Later in this topic, we’ll see that exceptions can be used to roll back the transaction.

The setRollbackOnly() method gives an enterprise bean the power to veto a transaction, which can be used if the enterprise bean detects a condition that would cause inconsistent data to be committed when the transaction completes. Once an enterprise bean invokes the setRollbackOnly() method, the current transaction is marked for rollback and cannot be committed by any other participant in the transaction, including the container.

The getRollbackOnly() method returns true if the current transaction has been marked for rollback. This information can be used to avoid executing work that would not be committed anyway. For example, if an exception is thrown and captured within an enterprise bean method, getRollbackOnly() can be used to determine whether the exception caused the current transaction to be rolled back. If it did, there is no sense in continuing the processing. If it did not, the EJB has an opportunity to correct the problem and retry the task that failed. Only expert EJB developers should attempt to retry tasks within a transaction. Alternatively, if the exception did not cause a rollback (i.e., getRollbackOnly() returns false), a rollback can be forced using the setRollback Only() method.

BMT beans must not use the setRollbackOnly() and getRollbackOnly() methods of the EJBContext. BMT beans should use the getStatus() and rollback() methods on the UserTransaction object to check for rollback and force a rollback, respectively.

Exceptions and Transactions

Exceptions have a large impact on the outcome of transactions.

Application Exceptions Versus System Exceptions

System exceptions represent unknown internal errors. The EJB container throws system exceptions when it encounters an internal application server failure. Business logic can throw system exceptions when it wants to abort the business process. Application exceptions are exceptions that are part of your business logic. They denote a strongly typed definition of a specific business problem or failure but do not necessarily abort or roll back the business process.

System exceptions

System exceptions include java.lang.RuntimeException and its subclasses. EJBException is a subclass of RuntimeException, so it is considered a system exception. System exceptions also include java.rmi.RemoteException and its subclasses. The Run timeException and RemoteException subclasses differ in that they can be turned into application exceptions using the @javax.ejb.ApplicationException annotation. This annotation is discussed later in this topic.

System exceptions always cause a transaction to roll back when they are thrown from an enterprise bean method. Any RuntimeException not annotated with @Applica tionException that is thrown within a business method (for instance, EJBException, NullPointerException, IndexOutOfBoundsException, and so on) is handled by the container automatically and results in a transaction rollback. In Java, RuntimeException types do not need to be declared in the throws clause of the method signature or handled using try/catch blocks; they are automatically thrown from the method.

The container handles system exceptions automatically and it will always do the following:

1. Roll back the transaction.

2. Log the exception to alert the system administrator.

3. Discard the EJB instance.

When a system exception is thrown from any callback method (@PostConstruct, @Post Activate, and so on), it is treated the same way as exceptions thrown from any business method.

Although EJB requires system exceptions to be logged, it does not specify how they should be logged or the format of the logfile. The exact mechanism for recording exceptions and reporting them to the system administrator is left to the vendor.

When a system exception occurs, the EJB instance is discarded, which means that it is dereferenced and garbage-collected. The container assumes that the EJB instance may have corrupt variables or otherwise be unstable and is therefore unsafe to use.

The impact of discarding an EJB instance depends on the enterprise bean’s type. In the case of stateless session beans, the client does not notice that the instance has been discarded. These instance types are not dedicated to a particular client; they are swapped in and out of an instance pool, and so any instance can service a new request. With stateful session beans, however, the impact on the client is severe. Stateful session beans are dedicated to a single client and maintain conversational state. Discarding a stateful bean instance destroys the instance’s conversational state and invalidates the client’s reference to the EJB. When stateful session instances are discarded, subsequent invocations of the EJB’s methods by the client result in a NoSuchEJBException, which is a subclass of RuntimeException.§

With message-driven beans, a system exception thrown by the onMessage() method or one of the callback methods (@PostConstruct or @PreDestroy) will cause the bean instance to be discarded. If the MDB was a BMT bean, the message it was handling may or may not be redelivered, depending on when the EJB container acknowledges delivery. In the case of container-managed transactions, the container will roll back the transaction, so the message will not be acknowledged and may be redelivered.

In session beans, when a system exception occurs and the instance is discarded, a RuntimeException is always thrown, regardless of whether the client is a remote or a local invocation. If the client started the transaction, which was then propagated to the EJB, a system exception (thrown by the enterprise bean method) will be caught by the container and rethrown as a javax.ejb.EJBTransactionRolledbackException. EJBTransactionRolledbackException is a subtype of RuntimeException and gives a more explicit indication to the client that a rollback occurred. If the client did not propagate a transaction to the EJB, the system exception will be caught and rethrown as an EJBException.

An EJBException generally should be thrown when a nonbusiness subsystem throws an exception, such as JDBC throwing an SQLException or JMS throwing a JMSException. In some cases, however, the bean developer may attempt to handle the exception and retry an operation instead of throwing an EJBException. This should be done only when the exceptions thrown by the subsystem and their repercussions on the transaction are well understood. As a rule of thumb, rethrow nonbusiness subsystem exceptions as EJBExceptions (or @ApplicationExceptions that cause a rollback) and allow the EJB container to roll back the transaction and discard the bean instance automatically.

Application exceptions

An application exception is normally thrown in response to a business-logic error, as opposed to a system error. Application exceptions are always delivered directly to the client without being repackaged as an EJBException type. By default, they do not cause a transaction to roll back. In this case, the client has an opportunity to recover after an application exception is thrown. Application errors are frequently used to report validation errors in this manner. In this case, the exception is thrown before tasks are started and is clearly not the result of a subsystem failure (e.g., JDBC, JMS, Java RMI, and JNDI).

The @javax.ejb.ApplicationException annotation may be used to force an application exception to roll back the transaction automatically:

tmp97266_thumb_thumb

For instance, our BankEJB makes use of an application exception to denote that a withdrawal or transfer operation cannot complete due to an insufficient balance:

tmp97267_thumb1_thumb

We want the transaction to be rolled back automatically, but business logic may be able to catch InsufficientBalanceException and retry the transaction automatically (by depositing more funds first, for example).

The @ApplicationException annotation can also be used on subclasses of java.lang. RuntimeException and java.rmi.RemoteException. This is useful because you may not want a thrown RuntimeException to be wrapped in an EJBException, or you may not want a particular subclass of RemoteException to roll back the exception.

Table 17-1 summarizes the interactions among different types of exceptions and transactions in session and entity beans.

Table 17-1. Exception summary for session and entity beans

Transaction type

Exception

 

 

Transaction scope

attributes

thrown

Container’s action

Client’s view

Client-initiated transaction. The transaction is started by the client (application or EJB) and propagated to the enterprise bean method.

transaction-type = Container

transaction-attribute = Required | Mandatory |Supports

Application exception

If the EJB invoked setRoll backOnly() or the application exception is annotated with @ApplicationException (rollback=true), mark the client’s transaction for rollback. Rethrow the application exception.

Receives the application exception. The client’s transaction may or may not have been marked for rollback.

 

 

System exception

Mark the client’s transaction for rollback. Log the error. Discard the instance. Rethrow the javax.ejb.EJBTransac tionRolledback Exception.

Clients receive the JTA javax.ejb.EJB TransactionRol ledbackExcep tion. The client’s transaction has been rolled back.

Container-managed transaction. The transaction started when the EJB’s method was invoked and will end when the method completes.

transaction-type = Container

transaction-attribute = Required | RequiresNew

Application exception

If the EJB invoked setRoll backOnly() or the application exception is annotated with @ApplicationException (rollback=true), roll back the transaction and rethrow the application exception. If the EJB did not explicitly roll back the transaction, attempt to commit the transaction and rethrow the application exception.

Receives the application exception. The EJB’s transaction may or may not have been rolled back. The client’s transaction is not affected.

 

 

System exception

Roll back the transaction. Log the error. Discard the instance. Rethrow the RemoteExcep tion or EJBException.

Remote clients receive theRemoteExcep tionorEJBExcep tion. The EJB’s transaction was rolled back. The client’s transaction may be marked for rollback, depending on the vendor.

The bean is not part of a transaction. The EJB was invoked but doesn’t propagate the client’s transaction and doesn’t start its own transaction.

transaction-type = Container

transaction-attribute = Never | NotSupported | Supports |

Application exception

Rethrow the application exception.

Receives the application exception. The client’s transaction is not affected.

Transaction scope

Transaction type attributes

Exception thrown

Container’s action

Client’s view

 

 

System exception

Log the error. Discard the instance. Rethrow the RemoteEx ception or EJBException.

Remote clients receive theRemoteExcep tionorEJBExcep tion. The client’s transaction may or may not be marked for rollback, depending on the vendor.

Bean-managed transaction. The statefulor stateless session EJB uses the EJBContext to explicitly manage its own transaction.

transaction-type = Bean

transaction-attribute = Bean-managed transaction

EJBs do not use

transaction

attributes.

Application exception

Rethrow the application exception.

Receives the application exception. The client’s transaction is not affected.

 

 

System exception

Roll back the transaction. Log the error. Discard the instance. Rethrow the RemoteExcep tion or EJBException.

Remote clients receive theRemoteExcep tionorEJBExcep tion. The client’s transaction is not affected.

Table 17-2 summarizes the interactions among different types of exceptions and transactions in message-driven beans.

Table 17-2. Exception summary for message-driven beans

Transaction type

Exception

Transaction scope

attributes

thrown

Container’s action

Container-initiated transaction. The transaction started before the onMessage() method was invoked and will end when the method completes.

transaction-type =Container

transaction-attribute = Required

System exception

Roll back the transaction. Log the error. Discard the instance.

 

 

Application exception

If the instance called setRollback Only() or the exception is annotated with @ApplicationException (rollback=true), roll back the transaction and rethrow to the resource adapter.

Transaction scope

Transaction type attributes

Exception thrown

Container’s action

Container-initiated transaction. No transaction was started.

transaction-type =Container

transaction-attribute = Not-Supported

System exception

Application exception

Log the error. Discard the instance.

Rethrow the exception to the resource adapter.

Bean-managed transaction. The message-driven bean uses EJBContext to manage its own transaction explicitly.

transaction-type = Bean

transaction-attribute =

Bean-managed transaction attributes.

System exception

Roll back the transaction. Log the error. Discard the instance.

All Tx Scopes

 

Application exception

Rethrow the exception to the resource adapter.

Transactional Stateful Session Beans

Session beans can interact directly with the database as easily as they can manage the taskflow of other enterprise beans. Stateless session beans have no conversational state, so each method invocation must make changes to the database immediately. With stateful session beans, however, we may not want to make changes to the database until the transaction is complete. Remember, a stateful session bean can be one of many participants in a transaction, so it might be advisable to postpone database updates until the entire transaction is committed or to avoid updates if it is rolled back.

There are several different scenarios in which a stateful session bean might cache changes before applying them to the database. For example, think of a shopping cart implemented by a stateful session bean that accumulates several items for purchase. If the stateful bean implements SessionSynchronization, it can cache the items and write them to the database only when the transaction is complete.

The javax.ejb.SessionSynchronization interface allows a session bean to receive additional notification of the session’s involvement in transactions. The addition of these transaction callback methods by the SessionSynchronization interface expands the EJB’s awareness of its lifecycle to include a new state, the Transactional Method-Ready state. This is always a part of the lifecycle of a transactional stateful session bean. Implementing the SessionSynchronization interface simply makes it visible to the application provider. Figure 17-7 shows the stateful session bean with the additional state.

Lifecycle of a stateful session bean

Figure 17-7. Lifecycle of a stateful session bean

The SessionSynchronization interface is defined as follows:

tmp97269_thumb_thumb

When a method of the SessionSynchronization bean is invoked outside of a transaction scope, the method executes in the Method-Ready state. However, when a method is invoked within a transaction scope (or creates a new transaction), the EJB moves into the Transactional Method-Ready state.

The Transactional Method-Ready State

The SessionSynchronization methods are called in the Transactional Method-Ready state.

Transitioning into the Transactional Method-Ready state

When a transactional method is invoked on a SessionSynchronization bean, the stateful bean becomes part of the transaction, causing the afterBegin() callback method defined in the SessionSynchronization interface to be invoked. This method should take care of reading any data from the database and storing the data in the bean’s instance fields. The afterBegin() method is called before the EJB object delegates the business-method invocation to the EJB instance.

Life in the Transactional Method-Ready state

When the afterBegin() callback method completes, the business method originally invoked by the client is executed on the EJB instance. Any subsequent business methods invoked within the same transaction will be delegated directly to the EJB instance.

Once a stateful session bean is a part of a transaction—whether it implements Session Synchronization or not—it cannot be accessed by any other transactional context. This is true regardless of whether the client tries to access the EJB with a different context or the EJB’s own method creates a new context. If, for example, a method with a transaction attribute of RequiresNew is invoked, the new transactional context causes an error to be thrown. Since the NotSupported and Never attributes specify a different transactional context (no context), invoking a method with these attributes also causes an error. A stateful session bean cannot be removed while it is involved in a transaction. This means that invoking an @Remove annotated method while the SessionSynchronization bean is in the middle of a transaction will cause an error to be thrown.

At some point, the transaction in which the SessionSynchronization bean has been enrolled will come to an end. If the transaction is committed, the SessionSynchronization bean will be notified through its beforeCompletion() method. At this time, the EJB should write its cached data to the database. If the transaction is rolled back, the beforeCompletion() method will not be invoked, avoiding the pointless effort of writing changes that won’t be committed to the database.

The afterCompletion() method is always invoked, whether the transaction ended successfully with a commit or unsuccessfully with a rollback. If the transaction was a success—which means that beforeCompletion() was invoked—the committed parameter of the afterCompletion() method will be true. If the transaction was unsuccessful, then the committed parameter will be false.

It may be desirable to reset the stateful session bean’s instance variables to some initial state if the afterCompletion() method indicates that the transaction was rolled back.

Conversational Persistence Contexts

Entity managers participate in transactions just like any other resource. We’ve seen examples of this throughout this topic. Extended persistence contexts have some interesting transactional behavior that you can exploit. You are allowed to invoke EntityManager operations such as persist(), merge(), and remove() outside of a transaction when you interact with an extended persistence context. These inserts, updates, and deletes are queued until the extended persistence context is enlisted in an active transaction and is committed. In other words, the database is not touched until the persistence context becomes involved with a transaction. Also, any executed queries do not hold their database connection after they complete. Let’s look at an example of this:

tmp97270_thumb_thumb

Line 1 creates an extended persistence context. Lines 2-4 create, update, and delete some entity beans. These actions are queued until the persistence context becomes enlisted in a transaction in Line 5. The act of calling an EntityManager method enlists the persistence context in the transaction. The batched actions are then committed in Line 7.

You can really exploit this behavior by using it with stateful session beans.

The combination of stateful session beans, extended persistence contexts, and specific transaction demarcation gives you a lot of power to optimize and control your conversational state. Without these integrated features, you would have the painstaking task of managing all of these state changes within the stateful session bean itself. Now you can let the entity manager do most of the work for you.

Next post:

Previous post: