8.6. EJB 1.1: Exceptions and Transactions8.6.1. Application Exceptions Versus System ExceptionsIn EJB 1.1, an application exception is any exception that does not extend java.lang.RuntimeException or the java.rmi.RemoteException. System exceptions are java.lang.RuntimeException and java.rmi.RemoteException types and subtypes, including EJBException. Transactions are automatically rolled back if a system exception is thrown from a bean method. Transactions are not automatically rolled back if an application exception is thrown. If you remember these two rules, you will be well prepared to deal with exceptions and transactions in EJB 1.1. The bookPassage() method provides a good illustration of an application exception and how it's used. The following code shows the bookPassage() method with the relevant exception handling in bold: public Ticket bookPassage(CreditCard card, double price) throws IncompleteConversationalState { 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 EJBException(e); } } 8.6.1.1. System exceptionsSystem exceptions are RuntimeExceptions, RemoteExceptions, and their subtypes. The EJBException is a subclass of the RuntimeException, so it's considered a system exception. System exceptions always cause a transaction to roll back when thrown from a bean method. Any RuntimeException (NullPointerException, IndexOutOfBoundsException, etc.) thrown within the bookPassage() method is handled by the container automatically, and also 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. System exceptions thrown from within beans always cause the current transaction to roll back. If the method in which the exception occurs started the transaction, the transaction is rolled back. If the transaction started from a client that invoked the method, the client's transaction is marked for rollback and cannot be committed. System exceptions are handled automatically by the container, which will always:
Exceptions thrown from the callback methods (ejbLoad(), ejbActivate(), etc.) are treated the same as exceptions thrown from business methods. While EJB 1.1 requires that system exceptions be logged, it does not specify how exceptions should be logged or the format of the log file. The exact mechanism for recording the exception and reporting it to the system administrator is left to the vendor. When a system exception occurs, the bean instance is discarded, which means that it's dereferenced and garbage collected. The container assumes that the bean instance may have corrupt variables or otherwise be unstable, and is therefore unsafe to use. The impact of discarding a bean instance depends on the bean's type. In the case of stateless session beans and entity beans, the client does not notice that the instance was discarded. These types of beans are not dedicated to a particular client; they are swapped in and out of an instance pool, 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 conversation state and invalidates the client's reference to the bean. When stateful session instances are discarded, subsequent invocations of the bean's methods by the client result in a NoSuchObjectException, a subclass of the RemoteException.[6]
When a system exception occurs and the instance is discarded, a RemoteException is always thrown to the client. If the client started the transaction, which was then propagated to the bean, a system exception (thrown by the bean method) will be caught by the container and rethrown as a javax.transaction.TransactionRolledbackException. The TransactionRolledbackException is a subtype of the RemoteException; it's a more explicit indication to the client that a rollback occurred. In all other cases, whether the bean is container-managed or bean-managed, a system exception thrown from within the bean method will be caught by the container and rethrown as a RemoteException. A system exception always results in a rollback of the transaction. 8.6.1.2. Application exceptionsAn application exception is normally thrown in response to a business logic error, as opposed to a system error. They are always delivered directly to the client, without being repackaged as RemoteExceptions. They do not typically cause transactions to roll back; the client usually has an opportunity to recover after an application exception is thrown. The bookPassage() method throws an application exception called IncompleteConversationalState; this is an application exception because it does not extend RuntimeException or RemoteException. The IncompleteConversationalState exception is thrown if one of the arguments passed into the bookPassage() method is null. (Application errors are frequently used to report validation errors like this.) In this case, the exception is thrown before tasks are started, and is clearly not the result of a subsystem (JDBC, Java RMI, JNDI, etc.) failure. Because it is an application exception, throwing the IncompleteConversationalState exception does not result in a transaction rollback. The exception is thrown before any work is done, avoiding unnecessary processing by the bookPassage() method and providing the client (the bean or application that invoked the bookPassage() method) with an opportunity to recover and possibly retry the method call with valid arguments. The ProcessPayment bean also throws an application exception, PaymentException, to indicate that a validation error has occurred. In the bookPassage() method, we have always allowed this exception to be captured by the try/catch block and rethrown as a EJBException, which would result in a transaction rollback. An alternative would be to rearrange the sequence of events a little and allow the bookPassage() method to throw the PaymentException. This approach would allow more concise reporting of the business error to the client and, if organized correctly, would avoid a transaction rollback. Upon catching the PaymentException, the client could attempt to recover by retrying the bookPassage() method with the valid payment arguments. The following code shows a revised bookPassage() method that illustrates this strategy. Notice that the payment is processed before the reservation and that more explicit exception handling allows the PaymentException (thrown by the byCredit() method) to escape the try/catch block, so it can be thrown by the bookPassage() method. public Ticket bookPassage(CreditCard card, double price) throws IncompleteConversationalState, PaymentException { if (customer == null || cruise == null || cabin == null){ throw new IncompleteConversationalState(); } try { ProcessPaymentHome ppHome = (ProcessPaymentHome) getHome("ProcessPaymentHome",ProcessPaymentHome.class); ProcessPayment process = ppHome.create(); process.byCredit(customer, card, price); ReservationHome resHome = (ReservationHome) getHome("ReservationHome", ReservationHome.class); Reservation reservation = resHome.create(customer, cruise, cabin, price); Ticket ticket = new Ticket(customer,cruise,cabin,price); return ticket; } catch(RemoteException re) { throw new EJBException(re); } catch(NamingException ne) { throw new EJBException(ne); } catch(CreateException ce) { throw new EJBException(ce); } catch(FinderException fe) { throw new EJBException(fe); } } Business methods defined in the remote interface can throw any kind of application exception. These application exceptions must be declared in the method signatures of the remote interface and in the corresponding method in the bean class. The EJB create, find, and remove methods can also throw several exceptions defined in the javax.ejb package: CreateException, DuplicateKeyException, FinderException, ObjectNotFoundException, and RemoveException. These exceptions are also considered application exceptions: they are delivered to the client as is, without being repackaged as RemoteExceptions. Furthermore, these exceptions don't necessarily cause a transaction to roll back, giving the client the opportunity to retry the operation. These exceptions may be thrown by the beans themselves; in the case of container managed persistence (CMP), the container can also throw any of these exceptions while handling the bean's create, find, or remove methods (ejbCreate(), ejbFind...(), and ejbRemove()). The container might, for example, throw a CreateException if the container encounters a bad argument while attempting to insert a record for a container-managed bean. A bean developer can always choose to throw a standard application exception from the appropriate method regardless of how persistence is managed. Here is a detailed explanation of the five standard application exceptions and the situations in which they are thrown:
Table 8-3 summarizes the interactions between different types of exceptions and transactions.
Table 8-3. Exception Summary
Copyright © 2001 O'Reilly & Associates. All rights reserved. | |||||||||||||||||||||||||||||||||||||||||||||
|