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. This involves the use of fairly complex APIs like the OMG's OTS (Object Transaction Service) or its Java implementation, JTS ( Java Transaction Service). Explicit demarcation is difficult for developers to use at best, particularly if you are new to transactional systems. In addition, explicit transaction demarcation requires that the transactional code be written within the business logic, which reduces the clarity of the code and more importantly creates inflexible distributed objects. Once transaction demarcation is "hardcoded" into the business object, changes in transaction behavior require changes to the business logic itself. We talk more about explicit transaction management and EJB later in this chapter.
With EJB's declarative transaction management, the transactional behavior of beans can be controlled using the deployment descriptor, which sets transaction attributes for individual bean methods. This means that the transactional behavior of a bean within an application can be changed easily without changing the bean's business logic. In addition, a bean deployed in one application can be defined with very different transactional behavior than the same bean deployed in a different application. Declarative transaction management reduces the complexity of transactions for bean developers and application developers and makes it easier for you to create robust transactional applications.
Transaction scopeis a crucial concept for understanding transactions. In this context, transaction scope means those beans--both session and entity--that are participating in a particular transaction.
In the bookPassage() method of the TravelAgent bean, all the beans involved are a part of the same transaction scope. The scope of the transaction starts when a client invokes the TravelAgent bean's bookPassage() method. Once the transaction scope has started, it is propagated to both the newly created Reservation bean and the ProcessPayment bean:
public Ticket bookPassage(CreditCard card, double price) throws IncompleteConversationalState{// EJB 1.0: also throws RemoteException if (customer == null || cruise == null || cabin == null) { throw new IncompleteConversationalState(); } try { ReservationHome resHome = (ReservationHome) getHome("ReservationHome",ReservationHome.class); Reservation reservation = resHome.create(customer, cruise, cabin, price); ProcessPaymentHome ppHome = (ProcessPaymentHome) getHome("ProcessPaymentHome",ProcessPaymentHome.class); ProcessPayment process = ppHome.create(); process.byCredit(customer, card, price); Ticket ticket = new Ticket(customer,cruise,cabin,price); return ticket; } catch(Exception e) { // EJB 1.0: throw new RemoteException("",e); throw new EJBException(e); } }
As you know, a transaction is a unit-of-work that is 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; 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 expressed as bean methods, and a unit-of-work consists of every bean method invoked in a transaction. The scope of a transaction includes every bean 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 bookPassage() method begins a transaction, then logically, the transaction ends when the method completes. The scope of the bookPassage() transaction would include the TravelAgent, Reservation, and ProcessPayment beans--every bean touched by the bookPassage() method. A transaction is propagated to a bean when that bean's method is invoked and included in the scope of a transaction.
A transaction can end if an exception is thrown while the bookPassage() method is executing. The exception could be thrown from one of the other beans or from the bookPassage() method itself. An exception may or may not cause a rollback, depending on its type. More about exceptions and transactions later.
The thread of execution isn't the only factor that determines whether a bean is included in the scope of a transaction; the bean's transaction attributes also play a role. Determining whether a bean participates in the transaction scope of any unit-of-work is accomplished either implicitly using EJB's transaction attributes or explicitly using the Java Transaction API ( JTA).
As an application developer, you do not normally need to control transactions explicitly when using an EJB server. EJB servers can manage transactions implicitly, based on the transaction attributes established for beans at deployment time. The ability to specify how business objects participate in transactions through attribute-based programming is a common characteristic of CTMs, and one of the most important features of the EJB component model.
When an enterprise bean is deployed, you can set its runtime transaction attribute in the deployment descriptor to one of several values. Table 8-1 shows the transaction attributes, the XML attribute values used to specify these transaction attributes in an EJB 1.1 deployment descriptor, and the constants that represent these attributes in an EJB 1.0 deployment descriptor. We'll discuss the meaning of these attributes later in the chapter.
Transaction Attribute |
EJB 1.1 Text Value |
EJB 1.0 Constant |
---|---|---|
Not Supported |
NotSupported |
TX_NOT_SUPPORTED |
Supports |
Supports |
TX_SUPPORTS |
Required |
Required |
TX_REQUIRED |
Requires New |
RequiresNew |
TX_REQUIRES_NEW |
Mandatory |
Mandatory |
TX_MANDATORY |
Never (1.1) |
Never |
|
Bean Managed (1.0) |
|
TX_BEAN_MANAGED |
NOTE
The transaction attributes are declared differently in EJB 1.1 and EJB 1.0. In EJB 1.1, they are text values declared without spaces between words as shown in the "EJB 1.1 Text Value" column. In the EJB 1.0 deployment descriptors, transaction attributes are uppercase constants as shown in the "EJB 1.0 Constant" column. In this book we use the natural language format shown in the first column of Table 8-1 (words are separated by spaces), which maps to either the EJB 1.1 or EJB 1.0 declarations.1
Using transaction attributes simplifies building transactional applications by reducing the risks associated with improper use of transactional protocols like JTA (discussed later in this chapter). It's more efficient and easier to use transaction attributes than to control transactions explicitly.
It is possible to set a transaction attribute for the entire bean (in which case, it applies to all methods) or to set different transaction attributes for individual methods. The former is much simpler and less error prone, but setting attributes at the method level offers more flexibility. The code fragments in the following sections show how the default transaction attribute of a bean can be set in the bean's deployment descriptor.
In EJB 1.1, a <container-transaction> element specifies the transaction attributes for the beans described in the deployment descriptor:
<ejb-jar> ... <assembly-descriptor> ... <container-transaction> <method> <ejb-name>TravelAgentBean</ejb-name> <method-name> * </method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> <container-transaction> <method> <ejb-name>TravelAgentBean</ejb-name> <method-name>listAvailableCabins</method-name> </method> <trans-attribute>Supports</trans-attribute> </container-transaction> ... </assembly-descriptor> ... </ejb-jar>
This deployment descriptor specifies the transaction attributes for the TravelAgent bean. The <container-transaction> element specifies a method and the transaction attribute that should be applied to that method. The first <container-transaction> element specifies that all methods by default have a transaction attribute of Required; the * is a wildcard that indicates all of the methods of the TravelAgent bean. The second <container-transaction> element overrides the default setting to specify that the listAvailableCabins() method will have a Supports transaction attribute. Note that we have to specify which bean we're referring to with the <ejb-name> element; an XML deployment descriptor can cover many beans.
EJB 1.0 uses a control descriptor object within the deployment descriptor to set transaction attributes:
ControlDescriptor cd = new ControlDescriptor(); cd.setMethod(null); cd.setTransactionAttribute (ControlDescriptor.TX_NOT_SUPPORTED); ControlDescriptor [] cdArray = {cd}; sd.setControlDescriptors(cdArray);
The null argument to setMethod() means that the ControlDescriptor applies to the entire bean. To set the transaction attributes for a specific method, change the argument passed into setMethod() from null to a java.lang.reflect.Method object representing a business method in the bean class. Here is an example of how this might work:
ControlDescriptor cd = new ControlDescriptor(); Class [] parameters = new Class[0]; Method method = ShipBean.class.getDeclaredMethod("getName",parameters); cd.setMethod(method); cd.setTransactionAttribute(ControlDescriptor.TX_NOT_SUPPORTED); ControlDescriptor [] cdArray = {cd}; sd.setControlDescriptors(cdArray);
Here are the definitions of the transaction attributes identified in the table of transaction attributes (Table 8-1). In a few of the definitions, we say that the client transaction is suspended. This means that the transaction is not propagated to the bean method being invoked; propagation of the transaction is temporarily halted until the bean method returns.
Invoking a method on a bean with this transaction attribute suspends the transaction until the method is completed. This means that the transaction scope is not propagated to the Not Supportedbean or any of the beans it calls. Once the method on the Not Supported bean is done, the original transaction resumes its execution.
Figure 8-1 shows that a Not Supported bean does not propagate the client transaction when one of its methods is invoked.
This attribute means that the bean method will be included in the transaction scope if it is invoked within a transaction. In other words, if the bean or client that invokes the Supportsbean is part of a transaction scope, the Supports bean and all beans accessed by that bean become part of the original transaction. However, the Supports bean doesn't have to be part of a transaction and can interact with clients and beans that are not included in a transaction scope.
Figure 8-2(a) shows the Supports bean being invoked by a transactional client and propagating the transaction. Chapter 8, "Transactions"(b) shows the Supports bean being invoked from a non-transactional client.
This attribute means that the bean method must be invoked within the scope of a transaction. If the calling client or bean is part of a transaction, the Requiredbean is automatically included in its transaction scope. If, however, the calling client or bean is not involved in a transaction, the Required bean starts its own new transaction. The new transaction's scope covers only the Required bean and all beans accessed by that bean. Once the method invoked on the Required bean is done, the new transaction's scope ends.
Figure 8-3(a) shows the Required bean being invoked by a transactional client and propagating the transaction. Chapter 8, "Transactions"(b) shows the Required bean being invoked from a non-transactional client, which causes the Required bean to start its own transaction.
This attribute means that a new transaction is always started. Regardless of whether the calling client or bean is part of a transaction, a method with the Requires Newattribute begins a new transaction when invoked. If the calling client is already involved in a transaction, that transaction is suspended until the Requires New bean's method call returns. The new transaction's scope only covers the Requires New bean and all the beans accessed by that bean. Once the method invoked on the Requires New bean is done, the new transaction's scope ends and the original transaction resumes.
Figure 8-4(a) shows the Requires New bean being invoked by a transactional client. The client's transaction is suspended while the bean executes under its own transaction. Figure 8-4(b) shows the Requires New bean being invoked from a non-transactional client; the Requires New executes under its own transaction.
This attribute means that the bean method must always be made part of the transaction scope of the calling client. If the calling client or bean is not part of a transaction, the invocation will fail, throwing a javax.transaction.TransactionRequiredException.
Figure 8-5(a) shows the Mandatory bean being invoked by a transactional client and propagating the transaction. Chapter 8, "Transactions"(b) shows the Mandatory bean being invoked from a non-transactional client; the method throws the TransactionRequiredException because there is no transaction scope.
This attribute means that the bean method must never be invoked within the scope of a transaction. If the calling client or bean is part of a transaction, the Never bean will throw a RemoteException. If, however, the calling client or bean is not involved in a transaction, the Never bean will execute normally without a transaction.
Figure 8-6(a) shows the Never bean being invoked by a non-transactional client. Chapter 8, "Transactions"(b) shows the Never bean being invoked by transactional client; the method throws the RemoteException because the method can never be invoked by a client or bean that is included in a transaction.
This attribute means that the bean or method doesn't have its transactional context implicitly managed by the EJB server. Instead, the developer can use the Java Transaction API ( JTA) to explicitly manage transactions. The use of JTA and explicit transaction management are described later in this chapter.
The use of Bean Managedimposes the unusual restriction that transaction attributes of methods cannot be mixed. If one of a bean's methods is Bean Managed, then all methods of that bean must be Bean Managed. This is not the case with the other transaction attributes, which can be mixed within the same bean: different methods of the same bean may have different attributes. Transactions created within a Bean Managed bean can be propagated normally to other beans that support existing transactions (Supports, Required, and Mandatory).
How are bean-managed transactions supported in EJB 1.1? We'll discuss this in more detail later in the chapter. For the time being, it's enough to say that only session beans are allowed to manage transactions explicitly; entity beans cannot. The deployment descriptor of a session bean can have a transaction-type element that specifies whether the bean manages its own transactions.
Figure 8-7(a) shows the Bean Managed bean being invoked by a transactional client. The client's transaction is suspended while the bean executes under its own transaction. Figure 8-7(b) shows the Bean Managed bean being invoked from a non-transactional client; the Bean Managed bean executes under its own transaction.
To illustrate the impact of transaction attributes on bean methods, we'll look once again at the bookPassage() method of the TravelAgent bean created in Chapter 7, "Session Beans":
public Ticket bookPassage(CreditCard card, double price) throws IncompleteConversationalState{// EJB 1.0: also throws RemoteException if (customer == null || cruise == null || cabin == null) { throw new IncompleteConversationalState(); } try { ReservationHome resHome = (ReservationHome) getHome("ReservationHome",ReservationHome.class); Reservation reservation = resHome.create(customer, cruise, cabin, price); ProcessPaymentHome ppHome = (ProcessPaymentHome) getHome("ProcessPaymentHome",ProcessPaymentHome.class); ProcessPayment process = ppHome.create(); process.byCredit(customer, card, price); Ticket ticket = new Ticket(customer,cruise,cabin,price); return ticket; } catch(Exception e) { // EJB 1.0: throw new RemoteException("",e); throw new EJBException(e); } }
In order for bookPassage() to execute as a successful transaction, both the creation of the Reservation bean and the charge to the customer must be successful. This means that both operations must be included in the same transaction. If either operation fails, the entire transaction fails. In these beans, we could have specified the Required transaction attribute as the default. This transaction attribute enforces our desired policy that all beans must execute within a transaction and thus ensures data consistency.
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 beans will be reversed or rolled back. A rollback is like an undo command. If you have worked with relational databases, then the concept of a rollback should be familiar. 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 beans transactional provides the same kind of rollback/commit control. For example, if the Reservation bean cannot be created, the charge made by the ProcessPayment bean is rolled back. Transactions make updates an all-or-nothing proposition. This ensures that the unit-of-work, like the bookPassage() method, executes as intended, and it prevents inconsistent data from being written to databases.
In cases where the container implicitly manages the transaction, the commit and rollback decisions are handled automatically. When transactions are managed explicitly within a bean or by the client, the responsibility falls on the bean or application developer to commit or roll back a transaction. Explicit demarcation of transactions is covered in detail later in this chapter.
Let's assume that the TravelAgent bean is created and used on a client as follows:
TravelAgent agent = agentHome.create(customer); agent.setCabinID(cabin_id); agent.setCruiseID(cruise_id): try { agent.bookPassage(card,price); } catch(Exception e) { System.out.println("Transaction failed!"); }
Furthermore, let's assume that the bookPassage() method has been given the transaction attribute Requires New. In this case, the client that invokes the bookPassage() method is not itself part of a transaction. When bookPassage() is invoked on the TravelAgent bean, a new transaction is created, as required by the Requires New attribute. This means that the TravelAgent bean registers itself with the EJB server's transaction manager, which will manage the transaction automatically. The transaction manager coordinates transactions, propagating the transaction scope from one bean to the next to ensure that all beans 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 bean and decide, based on the success of those updates, whether to commit all changes made by all beans to the database or roll them all back. If a system exception is thrown by the bookPassage() method, the transaction is automatically rolled back. We will talk more about exceptions later in this chapter.
NOTE
In EJB 1.0, where the transaction scope begins and ends with the bookPassage() method, an application exception thrown by bookPassage() also causes a transaction rollback.
When the byCredit() method is invoked within the bookPassage() method, the ProcessPayment bean registers with the manager under the transactional context that was created for the TravelAgent bean; the transactional context is propagated to the ProcessPayment bean. When the new Reservation bean is created, it is also registered with the manager under the same transaction. When all the beans are registered and their updates made, the transaction manager checks to ensure that their updates will work. If all the updates will work, then the manager allows the changes to become permanent. If one of the beans reports an error or fails, any changes made by either the ProcessPayment or Reservation bean are rolled back by the manager. Figure 8-8 illustrates the propagation and management of the TravelAgent bean's transactional context.
In addition to managing transactions in its own environment, an EJB server can coordinate with other transactional systems. If, for example, the ProcessPayment bean actually came from a different EJB server than the TravelAgent bean, the two EJB servers would cooperate to manage the transaction as one unit-of-work. This is called a distributed transaction.[1]
[1] Not all EJB servers support distributed transactions.
A distributed transaction is a great deal more complicated, requiring what is called a two-phase commit (2-PC or TPC). 2-PC is a mechanism that allows transactions to be managed across different servers and databases. The details of a 2-PC are beyond the scope of this book, but a system that supports it will not require any extra operations by a bean 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 bean developer, you should not notice a difference between local and distributed transactions.
Copyright © 2001 O'Reilly & Associates. All rights reserved.
This HTML Help has been published using the chm2web software. |