Exceptions have a large impact on the outcome of transactions.
System exceptions
are java.lang.RuntimeException
and its subtypes,
including EJBException
. An application
exception
is any exception that does
not extend
java.lang.RuntimeException
or
java.rmi.RemoteException
.
An application exception must never extend 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
illustrates how to use application exceptions:
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); } }
System
exceptions include 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
, and so on) 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.
The container handles system exceptions automatically; it will always:
Roll back the transaction.
Log the exception to alert the system administrator.
Discard the EJB instance.
When a system exception is thrown from the callback methods
(ejbLoad( )
, ejbActivate( )
,
and so on) are treated the same as exceptions thrown from business
methods.
While EJB requires system exceptions must 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
.[51]
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 a 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 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
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 failure (e.g., JDBC, JMS, Java RMI,
JNDI).
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
CreateException
is thrown by the create( )
method in the remote interface. The container can throw
this exception 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
DuplicateKeyException
is a subtype of
CreateException
; it is thrown by the
create( )
method in the remote interface. The
container can throw this exception 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 EJB
provider or container typically does not roll the transaction back
before throwing this exception.
FinderException
FinderException
is thrown by the find methods in
the home interface. The container can throw this exception 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 EJB provider or container typically does not roll the
transaction back before throwing this exception.
ObjectNotFoundException
ObjectNotFoundException
is thrown from a
single-entity find method to indicate 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
shouldn’t 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 EJB provider or container
typically does not roll the transaction back before throwing this
exception.
RemoveException
The RemoveException
is thrown from the
remove( )
methods in the remote and home
interfaces. The container can throw this exception 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 16-1 summarizes the interactions between different types of exceptions and transactions in session and entity beans.
Table 16-1. Exception summary for session and entity beans
Transaction scope |
Transaction type attributes |
Exception thrown |
Container’s action |
Client’s view |
---|---|---|---|---|
Client-initiated transaction. The transaction is started by the client (application or EJB) and propagated to the enterprise bean method. |
|
Application exception |
If the EJB 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 JTA |
Remote clients receive the JTA
The client’s transaction has been rolled back. | ||
Container-mangaged transaction. The transaction started when the EJB’s method was invoked and will end when the method completes. |
|
Application exception |
If the EJB invoked If the EJB did not explicitly roll back the transaction, attempt to commit the transaction and rethrow the application exception. |
Receives the application exception. The EJB’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 the |
Remote clients receive the The EJB’s transaction was rolled back. The client’s transaction may marked for rollback, depending on the vendor. | ||
The bean is not part of a transaction. The EJB was invoked but doesn’t propagate the client’s transaction and doesn’t start its own transaction. |
|
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 the |
Remote clients receive the The client’s transaction may or may not be marked for rollback, depending on the vendor. | ||
Bean-managed transaction. The stateful or stateless session EJB uses the
|
|
Application exception |
Rethrow the application exception. |
Receives the application exception. The client’s transaction is not affected. |
System exception |
Roll back the transaction. Log the error. Discard the instance. Rethrow the |
Remote clients receive the The client’s transaction is not affected. |
Table 16-2 summarizes the interactions between different types of exceptions and transactions in message-driven beans.
Table 16-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 |
|
System exception |
Roll back the transaction. Log the error. Discard the instance. |
Container-initiated transaction. No transaction was started. |
|
System exception |
Log the error. Discard the instance. |
Bean-managed transaction. The message-driven bean uses the |
|
System exception |
Roll back the transaction. Log the error. |
[51] Although the
instance is always discarded with a
RuntimeException
, the impact on the remote
reference may vary depending on the vendor.