Types of exceptions. In EJB 3.1 there are two types of exceptions:
- Application Exceptions: any checked exception and any exception with @ApplicationException annotation
- System Exceptions: all other exceptions, i.e. any unchecked exception (runtime exception) without @ApplicationException annotation
Notice that a checked exception is an application exception, regardless it have @ApplicationException annotation. Example of an application exception:
@ApplicationException public class MyAppException extends RuntimeException { }
The @ApplicationException annotation is class-level annotation, and it can only be applied to an exception class. In particular, a given exception is either application or system exception, it is a global setting.
Throwing an application exception in a business methods. EJBean business method can throw an application exceptions. If the exception is a runtime exception, then the exception need not to be declared in the business method signature. A client receives the exception as it is, in case of local client pass-by-reference paradigm is used, in case of remote client pass-by-value paradigm is used.
Transactions and application exceptions. @ApplicationException annotation has an attribute rollback, default value is false. When it is set to false and an exception is thrown, then a container does not rollback transaction. When it is set to true and an exception is thrown, then if EJBean is CMT bean and a business method executes in an active transaction context, in particular the transaction is not marked for rollback already, then a container marks the transaction for rollback. Marking a transaction for rollback by a container has the same consequences as marking for rollback programmatically.
Assume that a client receives an application exception. If transaction has been propagated and the exception has the attribute rollback set to true, then the client knows that the transaction is marked for rollback. If the exception has the attribute rollback set to false, then the client should test the transaction status, because the transaction could be marked for rollback programmatically.
There is no way to test the value of an attribute rollback of an application exception in runtime other than using the reflection.
Throwing a system exception. Any method (business, callback, interceptor, timeout and listener method) can throw a system exception and it is always treated the same way:
- Container logs the exception
- Container marks transaction for rollback if EJBean is CMT bean and rollbacks transaction if EJBean is BMT bean
- Container discards the instance that threw the exception, see the following section
- Container wraps the exception with EJBException, client receives EJBException
Typical situations when a system exception is trown:
- EJBean code has programmatical error and throws runtime exception like NullPointerException
- EJBean method encounters an infrastructural failure, for example receives EJBException while invoking other EJBean or when javax.persistence.PersistenceException is thrown
- EJBean method throws a system exception intentionally
- it is legal to throw EJBException in EJBean method code, then it is also considered as a system exception
Handling a system exception. EJBException is a runtime excetion and client should be ready to receive such an exception. When client receives EJBException, it does not imply that EJBean threw a system exception. EJBException indicates that a method call could not be completed due to an infrastructural failure or an application error. EJBException can be thrown by:
- Business Interface instance while initializing the method invocation
- ORBs or other RMI infrastructure, this includes network failure — EJBean instance is not discarded and client transaction is not marked for rollback
- Container upon receiving the service call, before dispatching it to the instance of EJBean, for example then the service was already undeployed — EJBean instance is not discarded and transaction is not marked for rollback
- Container due to a system exception thrown by the instance of EJBean, this is a typical situation– EJBean instance is discarded and transaction is marked for rollback
- Container while finishing the method invocation, for example when container failed to commit a transaction or release resources or BMT bean did not end a transaction — EJBean instance is discarded and transaction is marked for rollback
- Business Interface instance while finishing the method invocation, for example when a response could not be deserialized — EJBean instance is discarded and clients transaction is marked for rollback
You must remember that when EJBean throws a system exception then client receives EJBException, but when client receives EJBException then it is not always true that EJBean instance threw a system exception and is discarded.
If a client receives EJBException then it can get cause of the exception to try to determine what went wrong.
Discarding instances. When a business method of Stateless Session Bean, Stateful Session Bean or Message Driven Bean throws a system exception then the instance, that threw the exception, is discarded by a container, i.e. it is send to Garbage Collector and PreDestroy callback method is not invoked! The situation when an instance is discarded and PreDestroy is not invoked is called “missed pre destroy calls”. Such mechanism is provided for a following reason. An instance that threw a system exception is considered as unstable whereas did not or was not able to programmatically clean its state, such instance is not able to handle following client invocations in a proper manner. PreDestroy callback method is not invoked because it assumes that the instance is stable.
The above does not apply to Singletons. Container never destroys initialized Singleton instance until the Singleton is undeployed.
Throwing an exception in non business method. Callback, timeout and listener methods cannot throw application exceptions. If such method declares an application exception it is considered as an error, container should not deploy an application that contains such EJBeans. If such method throw a runtime application exception, then container treats it as a system exception with all consequences.
If PostConstruct method in Singleton throws a system exception then Singleton instance is discarded and service is marked as unavailable i.e. client receives NoSuchEJBException anytime it invokes Singleton business method,container does not make attempt to create second instance of Singleton.
Interceptor method can throw any application exception that is admitted by an intercepted method. Any other exception is treated as system exception. I.e. interceptor method can throw an application exception that is a runtime application exception or is declared by an intercepted method.
When we think of a listener method, we think of onMessage method in Message Driven Bean declared by javax.jms.MessageListener interface. But it may happen that Message Driven Bean is a client of non JMS, asynchronous service. It may happen also that a listener interface of that asynchronous service admits some checked exceptions. Then you listener method can throw this application exception.
Transactions and system exceptions. Assume that a client invokes EJBean, its transaction is propagated and EJBean throws a system exception. Then a container wraps the exception with EJBTransactionRollbackedException instead of EJBException and returns it to the client. If transaction is not propagated then the exception is wrapped with EJBException and it is returned to the client. EJBTransactionRollbackedException is subclass of EJBException. When the client receives EJBTransactionRollbackedException then the client knows that its transaction is marked for rollback. When the client receives EJBException then client’s transaction status is not determined, the client does not even determine if the invocation was delivered to EJBean.
Exceptions and interceptors. If some operation performed by EJBean method throws a system exception, but the method catches it before the method completes, then it is not considered as EJBean error and the instance is not discarded. The same applies to interceptors. If a business method throws a system exception, then @AroundInvoke method of an interceptor can catch it and the instance is not discarded. @AroundInvoke method is treated as an extension of a business method. From container point of view, if the last @AroundInvoke method (of first, depends how we look at it) throws a system exception then the business method throws the system exception.
List of EJBException subclasses. When client receives EJBException, it means that invocation of EJBean failed. It can mean that the invocation could not be delivered to EJBean or EJBean throw a system exception. The client’s transaction can be marked for rollback or not, it depends on a source of the exception. To determine it, the client should use getRollbackOnly method or test the transaction status explicitly. Eventually, the client can check the type of the exception. For example, if it is a EJBTransactionRollbackedException then the client is sure that the transaction is marked for rollback.
The following exceptions are subclasses of EJBException and can be received by a client when invoking EJBean:
- EJBAccessException — client have not permission to invoke EJBean
- NoSuchEJBException — EJBBean was undeployed, invoked Stateful Session Bean instance has already been removed, creation of Singleton instance has failed and service is indicated as unavailable
- TransactionRequiredException — client operates without active transaction context and invokes EJBean with MANDATORY transaction attribute
- EJBTransactionRollbackedException — client’s transaction is propagated and EJBean throws a system exception, transaction is rollbacked and original exception is wrapped with EJBTransactionRollbackedException
- ConcurrentAccessException — Stateful Session Bean or Singleton is busy and @AccessTimeout(0) configuration is set
- ConcurrentAccessException — Stateful Session Bean or Singleton is busy and positive timeout configured by @AccessTimeout setting has elapsed
- IllegalLoopbackException — reentrence in Singleton has occurred, invoker is a READ method and invokee is a WRITE method
The following exceptions are non EJBException and can be received by a client when invoking EJBean:
- java.concurrent.ExecutionException — when asynchronous business method throws exception and client calls get method on Future<E> object, ExecutionException wraps the exception that client would received in synchronous invocation, in particular ExecutionException can wrap EJBException
The following exceptions are subclasses of EJBException and can be received by EJBean method:
- NoMoreTimeoutsException — thrown in Stateless Session Bean’s, Singleton’s and Message Driven Bean’s timeout callback methods when executing Timer does not have more executions and the method getNextTimeout or getTimeRemaining is invoked
- NoSuchObjectLocalException — thrown in Stateless Session Bean, Singleton and Message Driven Bean when operates on canceled or inactive (dead) Timer
The following exceptions are non EJBException and can be received by EJBean method:
- java.lang.IllegalStateException — thrown by a container when EJBean method performs operation not allowed in current context or operation should not be performed in current context, for example: invoking setRollbackOnly and getRollbackOnly methods in BMT beans or without active transaction context, getInvokedBusinessInterface in non business methods, wasCancelCalled in non asynchronous business methods etc.
Application exceptions inheritance. If Exception1 has the annotation @ApplicationException then it is an application exception. If Exception2 extends Exception1 then it inherits also the annotation @ApplicationException and it is also an application exception. Unless, the annotation @ApplicationException has the attribute inherited set to false. Default value of the attribute inherited is true.