In 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); } }
System 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:
Roll back the transaction
Log the exception to alert the system administrator
Discard the bean instance
Throw a RemoteException or one of its subtypes
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]
[6]Although the instance is always discarded with a RuntimeException, the impact on the remote reference may vary depending on the vendor.
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.
An 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:
The CreateException is thrown by the create() method in the remote interface. This exception can be thrown by the container if the container is managing persistence, or it can be thrown explicitly by the bean developer in the ejbCreate() or ejbPostCreate() methods. This exception indicates that an application error has occurred (invalid arguments, etc.) while the bean was being created. If the container throws this exception, it may or may not roll back the transaction. Explicit transaction methods must be used to determine the outcome. Bean developers should roll back the transaction before throwing this exception only if data integrity is a concern.
The DuplicateKeyException is an subtype of the CreateException; it is thrown by the create() method in the remote interface. This exception can be thrown by the container, if the container is managing persistence, or it can be thrown explicitly by the bean developer in the ejbCreate() method. This exception indicates that a bean with the same primary key already exists in the database. The transaction is typically not rolled back by the bean provider or container before throwing this exception.
The FinderException is thrown by the find methods in the home interface. This exception can be thrown by the container, if the container is managing persistence, or it can be thrown explicitly by the bean developer in the ejbFind...() methods. This exception indicates that an application error occurred (invalid arguments, etc.) while the container attempted to find the beans. Do not use this method to indicate that entities were not found. Multi-entity find methods return an empty collection if no entities were found; single-entity find methods throw an ObjectNotFoundException to indicate that no object was found. The transaction is typically not rolled back by the bean provider or container before throwing this exception.
The ObjectNotFoundException is thrown from a single-entity find method to indicate that the container couldn't find the requested entity. This exception can be thrown by the container if the container is managing persistence, or it can be thrown explicitly by the bean developer in the ejbFind...() methods. This exception should not be thrown to indicate a business logic error (invalid arguments, etc.). Use the FinderException to indicate business logic errors in single-entity find methods. The ObjectNotFoundException is only thrown by single-entity find methods to indicate that the entity requested was not found. Find methods that return multiple entities should return an empty collection if nothing is found. The transaction is typically not rolled back by the bean provider or container before throwing this exception.
The RemoveException is thrown from the remove() methods in the remote and home interfaces. This exception can be thrown by the container, if the container is managing persistence, or it can be thrown explicitly by the bean developer in the ejbRemove() method. This exception indicates that an application error has occurred while the bean was being removed. The transaction may or may not have been rolled back by the container before throwing this exception. Explicit transaction methods must be used to determine the outcome. Bean developers should roll back the transaction before throwing the exception only if data integrity is a concern.
Table 8-3 summarizes the interactions between different types of exceptions and transactions.
Transaction Scope |
Transaction Type Attributes |
Exception Thrown |
Container's Action |
Client's View |
---|---|---|---|---|
Client Initiated Transaction
Transaction is started by the client (application or bean) and is propagated to the bean method. |
transaction-type =
transaction-attribute =
|
Application Exception |
If the bean invoked setRollbackOnly(), then 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 TransactionRollbackException. |
Receives the TransactionRollbackException The client's transaction has been rolled back. |
Container Initiated Transaction
The transaction started when the bean's method was invoked and will end when method completes. |
transaction-type =
transaction-attribute =
|
Application Exception |
If the bean called setRollbackOnly(), then roll back the transaction and rethrow the Application Exception. If the bean didn't explicitly roll back the transaction, then attempt to commit the transaction and rethrow the Application Exception. |
Receives the Application Exception. The bean'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 RemoteException. |
Receives the RemoteException. The bean's transaction was rolled back. The client's transaction is not affected. |
Bean is not part of a transaction
The bean was invoked but does not propagate the client's transaction and does not start its own transaction. |
transaction-type =
transaction-attribute =
|
Application Exception |
Rethrow the Application Exception. |
Receives the Application Exception. The client's transaction is not affected. |
|
|
System Exception |
Log the error. Discard the instance. Rethrow RemoteException. |
Receives the RemoteException. The client's transaction is not affected. |
Bean Managed Transaction. The stateful or stateless session bean uses the EJBContext to explicitly manage its own transaction |
transaction-type =
transaction-attribute =
|
Application Exception |
Rethrow the Application Exception. |
Receives the Application Exception. The client's transaction is not affected. |
|
|
Roll back the transaction. Log the error. Discard the instance. |
Receives the RemoteException. The client's transaction is not affected. |
Copyright © 2001 O'Reilly & Associates. All rights reserved.
This HTML Help has been published using the chm2web software. |