12 January 2010

Avoid throwing RuntimeException from you Session beans

Working with session beans usually requires you to throw some type of exceptions to the calling layer (usually the presentation layer), So beware, don't try to throw RuntimeExceptions or RemoteException because the Container will encapsulate these exceptions in EJBException and then destroys the bean instance from the bean bool.

If you need to throws Runtime/Remote Exceptions, then add @ApplicationException(rollback=true) as a class level annotation to your Exception class, and this applicable only on your own custom exceptions.

In all cases (RuntimeException or Application Exceptions) the container will rollback the transactino.

From EJB in Action :
“In EJB , an application exception is an exception that the client is expected to handle. When thrown, such exceptions are passed directly to the method invoker. By default, all checked exceptions except for
java.rmi.RemoteException are assumed to be application exceptions.
On the other hand, all exceptions that inherit from either java.rmi. RemoteExceptions or java.lang.RuntimeException are assumed to be system exceptions (as you might already know, all exceptions that inherit
from java.lang.RuntimeException are unchecked). In EJB , it is not assumed that system exceptions are expected by the client. When encountered, such exceptions are not passed to the client as is but are wrapped in a javax.ejb.EJBException instead.”

Here's an example session bean that throws IllegalArguemntException ( a well-known runtime exception):

@Stateless
@Remote(AccountManagement.class)
@Local(AccountManagement.class)
public class AccountManagementBean extends DBBaseBean implements AccountManagement {

public void registerUser(User user) throws IllegalArgumentException {

if (user == null || StringUtil.isNullSpacesOrEmpty(user.getUsername()) || user.getPassword() == null)
throw new IllegalArgumentException("Uncomplete user registeration data");

em.persist(user);
logger.info("User created :" + user.getUsername());
}
@PostConstruct
public void init() {
System.out.println("INIT");
}
@PreDestroy
public void destroy() {
System.out.println("DESTROY");
}

}

And here's a test client :

public class Main {
public static void main(String[] args) throws Exception{
AccountManagement test = InitialContext.doLookup("SimpleWiki/AccountManagementBean/remote");

try {
test.registerUser(null);
}catch (Exception ex) {
if (ex.getCause() instanceof IllegalArgumentException) {
System.out.println("IllegalArgumentException : " + ex.getCause().getMessage());
}else {
ex.printStackTrace();
}
}
}
}

And here's the output of the EJB Container :

21:33:12,504 INFO [STDOUT] INIT
21:33:28,595 INFO [STDOUT] DESTROY

As you can see from the output of the session bean, When a RuntimeException thrown, the EJB Container destroys the bean instance from the bean bool !

No comments: