In EJB 1.0, the impact of exceptions on transactions largely depends on who initiates the transaction. A transaction that is started automatically when a bean method is invoked is a container-initiated transaction. Specifying the TX_REQUIRES_NEW transaction attribute, for example, always results in a container-initiated transaction. A TX_REQUIRED method invoked by a non-transactional client also results in a container-initiated transaction. A transaction that is started explicitly using JTA (on the client or in a TX_BEAN_MANAGED bean) is not a container-initiated transaction.
With container-initiated transactions, any exception thrown during a transaction can cause the transaction to roll back. The impact of an exception thrown during a transaction depends on the type of exception (checked or unchecked) and the transaction attribute of the bean method throwing the exception. This section examines the different combinations of exceptions (checked or unchecked) and transaction attributes and their combined affect on transactional outcomes.
Any exception (application exception, unchecked exception, or RemoteException) not handled within the scope of the container-initiated transaction causes the container to roll back the entire transaction.
NOTE
An exception that is not handled within the scope of container-initiated transaction is an exception that is propagated, through the call stack, beyond the bean method that started the container-initiated transaction.
As an example, take another look at the bookPassage() method from the TravelAgent bean:
public Ticket bookPassage(CreditCard card, double price) throws IncompleteConversationalState, 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) { throw new RemoteException("",e); } }
The beans (Reservation and ProcessPayment) accessed by the bookPassage() method have a transaction attribute of TX_REQUIRED. This means that the transaction associated with the bookPassage() method will be propagated to the methods called on the ProcessPayment and Reservation beans. If bookPassage() has a transaction attribute of TX_REQUIRES_NEW, then we can assume that it will always be called in the scope of a container-initiated transaction; when bookPassage() is invoked, a new container-initiated transaction is created.
In container-initiated transactions, any application exception thrown within the scope of the container-initiated transaction does not cause a rollback. This provides the bean with an opportunity to recover and retry an operation. However, an application exception that is not handled within the scope of the container-initiated transaction does cause the transaction to be rolled back.
This can be demonstrated by redefining the bookPassage() method:
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(); try { process.byCredit(customer, card, price); } catch(PaymentException pe) { // Attempt to recover. } Ticket ticket = new Ticket(customer,cruise,cabin,price); return ticket; } catch(Exception e){ // EJB 1.0: throw new RemoteException("",e); throw new EJBException(e); } }
Here, the byCredit() method of the ProcessPayment bean has been wrapped in its own exception-handling logic; we can imagine some sophisticated code in the catch block that evaluates the problem and attempts to reinvoke the method. In this case, the PaymentException thrown by the byCredit() method does not cause the bookPassage() container-initiated transaction to be rolled back because the application exception is thrown from within the container-initiated scope. However, if an IncompleteConversationalState exception is thrown by the bookPassage() method itself, the transaction will be rolled back. Once an exception is thrown by the bookPassage() method, and therefore not handled within the scope of the container-initiated transaction, the transaction will be rolled back.
These rules for application exceptions also apply to RemoteException types. If a RemoteException is thrown by a bean or by some other resource (JNDI, for example), that exception does not automatically cause a rollback. Rollback only occurs if the remote exception is not handled within of the scope of the container-initiated transaction.
When a transaction is propagated from a client to a bean, the client defines the scope of the transaction. This means that application exceptions and RemoteExceptions thrown by the beans do not automatically cause the transaction to be rolled back. Again, consider the possibility that the byCredit() method throws an application exception. If the client initiated the transaction, it's the client's responsibility to determine whether the operation can be retried and to roll back if appropriate. Similarly, if the bookPassage() method had a transaction attribute of TX_REQUIRED or TX_SUPPORTS and was invoked from a client-initiated transaction (perhaps the client uses JTA), then any application exception or RemoteException thrown from within the bookPassage() method will not automatically cause a rollback. The client must handle the exception and determine for itself if a rollback is appropriate.
The effect of exceptions is different on bean-managed transactions (TX_BEAN_MANAGED). With TX_BEAN_MANAGED entity beans and stateless session beans, any exception thrown by the bean method causes the transaction to be rolled back by the container. Remember that with TX_BEAN_MANAGED, transactions must begin and be completed within the same method. If an exception unexpectedly ends the method, the container rolls back the transaction.
With TX_BEAN_MANAGED stateful session beans, only unchecked exceptions thrown by bean methods cause the container to roll back the transaction. Any other exception thrown by the bean method, whether it be an application exception or RemoteException, does not affect the transaction. This can cause major headaches if the exception is thrown before an intended commit was reached. For this reason, beans that manage their own transactions must be extremely careful about exception handling. The EJBContext rollback methods can be used in these situations.
Regardless of the method's transaction attribute (unless it's TX_NOT_SUPPORTED), an unchecked exception causes the transaction to be rolled back, regardless of whether the transaction is container-initiated, client-initiated, or bean-managed. Unchecked exceptions thrown by a bean in the scope of a transaction always cause a rollback. In addition, the container intercepts the unchecked exception and rethrows it to the bean's client as a javax.transaction.TransactionRolledbackException.[7]
[7] Bean methods with the TX_SUPPORTS attribute that are invoked by non-transactional clients are not included in this policy. They simply throw the unchecked exception as a RemoteException.
Copyright © 2001 O'Reilly & Associates. All rights reserved.
This HTML Help has been published using the chm2web software. |