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 RuntimeException
s,
RemoteException
s, 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
.[41]
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 RemoteException
s. 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:
CreateException
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.
DuplicateKeyException
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.
FinderException
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.
ObjectNotFoundException
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.
RemoveException
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.
Table 8-3. Exception Summary
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 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 |
Receives the 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 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 |
Receives the 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 |
Receives the The client’s transaction is not affected. | ||||||
Bean Managed Transaction. The stateful or stateless session bean uses the
|
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 The client’s transaction is not affected. |
[41] Although the
instance is always discarded with a
RuntimeException
, the impact on the remote
reference may vary depending on the vendor.