Exceptions have a large impact on the outcome of transactions and must be discussed in detail so that bean developers understand the relationship between them.
An application
exception is any exception that does not extend
java.lang.RuntimeException
or
java.rmi.RemoteException
. System exceptions are
java.lang.RuntimeException
and its subtypes,
including EJBException
.
An application exception must never extend either the
RuntimeException
, the
RemoteException
, or one of their subtypes.
Transactions are automatically rolled back if a system exception is thrown from an enterprise 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.
The bookPassage()
method is a good illustration of
an application exception and how it is used. The following sections
show the code for the bookPassage()
method.
public TicketDO bookPassage(CreditCardDO card, double price) throws IncompleteConversationalState { if (customer == null || cruise == null || cabin == null) { throw new IncompleteConversationalState(); } try { ReservationHomeLocal resHome = (ReservationHomeLocal) jndiContext.lookup("java:comp/env/ejb/ReservationHomeLocal"); ReservationLocal reservation = resHome.create(customer, cruise, cabin, price); Object ref = jndiContext.lookup("java:comp/env/ejb/ProcessPaymentHomeRemote"); ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote) PortableRemoteObject.narrow(ref, ProcessPaymentHomeRemote.class); ProcessPaymentRemote process = ppHome.create(); process.byCredit(customer, card, price); TicketDO ticket = new TicketDO(customer,cruise,cabin,price); return ticket; } catch(Exception e) { throw new EJBException(e); } }
public TicketDO bookPassage(CreditCardDO card, double price) throws IncompleteConversationalState { if (customer == null || cruise == null || cabin == null) { throw new IncompleteConversationalState(); } try { ReservationHomeRemote resHome = (ReservationHomeRemote) getHome("ReservationHomeRemote", ReservationHomeRemote.class); ReservationRemote reservation = resHome.create(customer, cruise, cabin, price); ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote) getHome("ProcessPaymentHomeRemote", ProcessPaymentHomeRemote.class); ProcessPaymentRemote process = ppHome.create(); process.byCredit(customer, card, price); TicketDO ticket = new TicketDO(customer,cruise,cabin,price); return ticket; } catch(Exception e) { throw new EJBException(e); } }
System
exceptions are the RuntimeException
and its
subclasses. The EJBException
is a subclass of the
RuntimeException
, so it is considered a system
exception.
System exceptions always cause a transaction to roll back when they
are thrown from an enterprise bean method. Any
RuntimeException
(EJBException
,
NullPointerException
,
IndexOutOfBoundsException
, etc.) thrown within the
bookPassage()
method is handled by the container
automatically and 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.
RuntimeException
types thrown from within
enterprise 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 EJB instance.
RuntimeExceptions
thrown from the callback methods
(ejbLoad()
, ejbActivate()
,
etc.) are treated the same as exceptions thrown from business
methods.
While EJB 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 exceptions and reporting them to the system administrator is left to the vendor.
When a system exception occurs, the EJB instance is discarded, which means that it is dereferenced and garbage collected. The container assumes that the EJB instance may have corrupt variables or otherwise be unstable and is therefore unsafe to use.
The impact of discarding an EJB instance depends on the enterprise
bean’s type. In the case of stateless session beans and entity
beans, the client does not notice that the instance was discarded.
These instance types 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 conversational
state and invalidates the client’s reference to the EJB. When
stateful session instances are discarded, subsequent invocations of
the EJB’s methods by the client result in a
NoSuchObjectException
, a subclass of the
RemoteException
.[59]
With message-driven beans, a system exception thrown by the
onMessage()
method or one of the callback methods
(ejbCreate()
or ejbRemove()
)
will cause the bean instance to be discarded. If the MDB was BMT
bean, the message it was handling may or may not be redelivered,
depending on when the EJB container acknowledges delivery. In the
case of container-managed transactions, the container will roll back
the transaction, so the message will not be acknowledged and may be
redelivered.
In session and entity beans, when a system exception occurs and the
instance is discarded, a RemoteException
is always
thrown to remote clients; that is, clients using the beans’
remote component interfaces. If the client started the transaction,
which was then propagated to the EJB, a system exception (thrown by
the enterprise bean method) will be caught by the container and
rethrown as a
javax.transaction.TransactionRolledbackException
.
The TransactionRolledbackException
is a subtype of
the RemoteException
; it is a more explicit
indication to the client that a rollback occurred.
In EJB 2.0 session and entity beans, when a system exception occurs
and the instance is discarded, an EJBException
is
always thrown to any local enterprise bean clients (i.e., clients
using the enterprise bean’s local component interfaces). If the
client started the transaction and it was then propagated to the EJB,
a system exception (thrown by the enterprise bean method) will be
caught by the container and rethrown as a
javax.ejb.TransactionRolledbackLocalException
. The
TransactionRolledbackLocalException
is a subtype
of the EJBException
; it is a more explicit
indication to the client that a rollback occurred. In all other
cases, whether the EJB is container-managed or bean-managed, a
RuntimeException
thrown from within the enterprise
bean method will be caught by the container and rethrown as an
EJBException
.
An EJBException
should generally be thrown when a
subsystem throws an exception, such as JDBC throwing a
SQLException
or JMS throwing a
JMSException
. In some cases, however, the bean
developer may attempt to handle the exception and retry an operation
rather then throw an EJBException
. This should be
done only when the exceptions thrown by the subsystem and their
repercussions on the transaction are well understood. As a rule of
thumb, rethrow subsystem exceptions as
EJBException
s and allow the EJB container to roll
back the transaction and discard the bean instance.
The callback methods defined in the
javax.ejb.EntityBean
and
javax.ejb.SessionBean
interfaces declare the
java.rmi.RemoteException
in their
throws
clauses. This is left over from EJB 1.0 and
has been deprecated since EJB 1.1. You should never throw
RemoteException
s from callback methods or any
other bean class methods.
An application exception is normally
thrown in response to a business-logic error, as opposed to a system
error. Application exceptions are always delivered directly to the
client, without being repackaged as
RemoteException
or EJBException
(EJB 2.0) types. They do not typically cause transactions to roll
back; the client usually has an opportunity to recover after an
application exception is thrown. For example, 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 (e.g., JDBC,
JMS, Java RMI, JNDI) failure.
Because it is an application exception, throwing an
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
enterprise bean or application that invoked the
bookPassage()
method) with an opportunity to
recover and possibly retry the method call with valid arguments.
Business methods defined in the remote and local interfaces can throw any kind of application exception. These application exceptions must be declared in the method signatures of the remote and local interfaces and in the corresponding methods in the enterprise EJB classes.
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 considered
application exceptions: they are delivered to the client as-is,
without being repackaged as RemoteException
s.
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 EJBs themselves;
in the case of container-managed persistence, the container can also
throw any of these exceptions while handling the EJB’s create,
find, or remove methods (ejbCreate()
,
ejbFind()
, and ejbRemove()
).
The container might, for example, throw a
CreateException
if it encounters a bad argument
while attempting to insert a record for a container-managed EJB. You
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 EJB developer in
the ejbCreate()
or
ejbPostCreate()
methods. It indicates that an
application error (invalid arguments, etc.) occurred while the EJB
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 a 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 EJB developer in
the ejbCreate()
method. It indicates that an EJB
with the same primary key already exists in the database. The
transaction is typically not rolled back by the EJB 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 EJB developer in the ejbFind()
methods. It indicates that an application error (invalid arguments,
etc.) occurred while the container was attempting to find the EJBs.
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 EJB
provider or container before throwing this exception.
ObjectNotFoundException
The ObjectNotFoundException
is thrown from a
single-entity find method to indicate that the container could not
find the requested entity. This exception can be thrown either by the
container (if the container is managing persistence) or explicitly by
the EJB developer in the ejbFind()
methods. It
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 thrown by single-entity
find methods only 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 EJB 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 EJB developer in the ejbRemove()
method. It
indicates that an application error has occurred while the EJB 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 14-1 summarizes the interactions between different types of exceptions and transactions in session and entity beans.
Table 14-1. Exception summary for session and entity beans
Table 14-2 summarizes the interactions between different types of exceptions and transactions in message-driven beans.
Table 14-2. Exception summary for message-driven beans
Transaction scope |
Transaction type attributes |
Exception thrown |
Container’s action |
---|---|---|---|
Container-initiated transaction. The transaction started before the |
transaction-type = Container transaction-attribute =
|
System exception |
Roll back the transaction. Log the error. Discard the instance. |
Container-initiated transaction. No transaction was started. |
transaction-type = Container transaction-attribute =
|
System exception |
Log the error. |
Bean-managed transaction. The message-driven bean uses the |
transaction-type = Bean transaction-attribute = Bean-managed transaction EJBs do not use transaction attributes. |
System exception |
[59] Although the
instance is always discarded with a
RuntimeException
, the impact on the remote
reference may vary depending on the vendor.