EJB Exception Handling

Introduction

It is amazing that there are only so few resources [on the Internet] that show how to handle exceptions in EJB. A search on Google returns a large number of results, but almost all of these point to the same article on IBM DeveloperWorks. This shows how scarce information about this subject are. The alternative, of course, is to refer to the J2EE specifications, but the its sheer volume and jargon can be intimidating for someone wanting to learn the basics.

After foraging the Internet and reading various books, I managed to compile my own documentation that I present in this article.

Definitions

First, let us start with some basic definitions that will make it easier for the reader to understand the article.

A checked exception is one that is derived from java.lang.Exception, but is not a subclass of java.lang.RuntimeException.

An unchecked exception is one that is derived from java.lang.RuntimeException and is thrown by the Java Virtual Machine (JVM).

A system exception is one that is thrown by the system and is not recoverable. For example, an EJB container losing its connection to a database server causes a system exception. Similarly, a failed remote object method call causes a system exception.

An application exception is one that is specific to the application and is thrown due to a violation of the application business rules. therefore, an application exception must be determining for the client to know how to handle it. Such an exception is raised, for example, when the balance for a savings account becomes negative.

System Exception

System exceptions are unchecked and, by extension, are derived from java.lang.RuntimeException. Because it is nearly impossible for developers to predict their occurrences, the EJB container is the only one that is responsible for trapping system exceptions. The container automatically swallows (or wraps) an EJBException in a RemoteException that is subsequently thrown to the client. The wrapping process discards some information about the error, but the RemoteException remains sufficient since the client does not need to know about the specific erros that occurred (because they are system errors) and are unable to fix them anyway.

In addition to intercepting system exceptions, the container, depending on the implementation, may log errors.

From the above, it is now clear that if developers want system exceptions to be managed properly by the container and propagated to the client, they need to wrap them in EJBException that can be intercepted. The following code extract illustrates this point.

Note that since an EJBException is a system (and unchecked) exception, there is no need to declare it in the throws clause.

create(...) is a delegate method of the Home object created by the container. Also, the container will throw a RemoteException if the method fails due to a system error. Therefore, in the Home interface of our remote object, we declare that the create(...) method throws RemoteException, like this:

The client code will then look like this:

In this example, the container notifies the client about a system exception by means of the RemoteException.

Application Exception

Application exceptions are checked and, therefore, need to be declared in a throws clause. They are thrown explicitly by code to indicate that a breach of the business rules has occurred. For example, trying to debit a sum of money that will make a savings account balance negative triggers a SavingsAccountDebitException. Because of their purpose, application exceptions are relied upon by the client to determine the cause of the error and send the proper notification to the end-user. Because the EJB container does not know how to process an application-specific exception, it does not intercept any. Also, application exceptions are not logged by the container.

Developers need not wrap such an exception in an EJBException. In fact, to prevent the container from hi-jacking the exception, developers SHOULD NOT write code to swallow the exception.

Below is a code extract that shows a typical application exception handling.

Again, notice that even if an EJBException is thrown in the code, it needs not be declared in the throws clause. Also, this code does not demonstrate how transactions are rolled back, a topic which is covered further down in this article.

In the remote interface for the bean containing the above code, SavingsAccountDebitException must be declared, as in the following:

The client code using the remote interface may then look like this:

As can be seen, the application exception has been successfully propagated to the client without being intercepted by the container. The client uses the information from the exception to notify the end-user; in this example, it simply displays the error message.

Exceptions and Transactions

The way that exceptions are handled affects the way transactions are managed

When a transaction is managed by the container, it is automatically rolled back by the latter when a system exception occurs. This is possible because the container can intercept this type of exceptions. However, when an application exception occurs, the container does not intercept it and, therefore, leaves it to the code to roll back the transaction.

The following code extract illustrates this point.

Conclusion

I should point that the article mostly applies to remote EJB. When local EJB are used, RemoteException is never thrown.

As demonstrated, handling exceptions in EJB can be very different from what is done in standalone Java applications. In fact, the guidelines for dealing with each situation can be contradictory in some cases. For a comparison, please refer to my previous article, Exception Handling Best Practice.

To conclude, here are the four basic rules that, I believe, suffice to properly handle exceptions in EJB.

  • If you cannot recover from a system exception, let the container handle it.
  • If a business rule is violated, throw an application exception.
  • Catch exceptions in the order in which you can handle them. Alternatively, catch them in descending order of likelihood of them occurring.
  • Always catch a java.lang.Exception last.

Technorati Tags: ,

23 Replies to “EJB Exception Handling”

  1. Pingback: Infloop
  2. I am glad you found it useful. As I wrote, all the documentation that I found before I wrote this article was very confusing. So, I had to sit down with pen and paper to simplify all what I read.

  3. Concerning the practice of having EJBs throw application exceptions to indicate business rule violations, other best practice references indicate that return error codes should be used for this rather than exceptions? Is this wrong?

  4. Consider result code as low-tech, and exception as high tech :-)

    Personally, I think that exceptions are more powerful since it does not interfere with the processing. Instead, it is up to the JVM to identify errors and notify the calling party. If you have to return result code, what happens if you want to return a result even if an error occurred and yet you need a result code?

  5. I prefer to to let ejb-methods return a Result object that contains error-codes.Because then it is possible to return multiple (validation)errors. If the Result object contains errors the transaction is marked as rollback only. All Exceptions are caught in the ejb-method and thrown as EJBExceptions. I dont think that client code is interested in whether an ejb-method failed because of a IOException / EJBCreateException or whatever exception, it failed and there is not much more he can do than popping up a message “service temporily offline please notify your system administrator”. Exceptions are reserved for unexpected events.

  6. … and this is why I prefer to use exceptions. I don’t want to have to consider every single possibile outcome of a function call and proceed according to the result contained in the Result object.

    With exceptions, I can just say, “OK, I can handle this, this and that exception, but I will just pop up an error message box for or log any other error.”

    At the end of the day, it depends on the preferred way of the developer and whether he/she wishes to ride the wave the way most developers are doing [to ease code maintenance and readability]. I’m sure there are a lot of applications that are based on either method and work fine nonetheless.

  7. This is a nice article. Do you recommend passing back all application exceptions to the client? Currently we have a few methods like your ejbRemove() example where we catch a number of application exceptions. Currently we ‘convert’ all of them to EJBExcption, so that the transaction gets rolled back. For the one that we do pass back intact, we make sure that the transaction is rolled back, but for some reason it is view as an ‘ugly hack’. Thanks again for the article

  8. Given that the manual transaction rollback is described in the specifications, I do not think it is an _ugly hack_. However, I think that swallowing the application exceptions in EJBExceptions is a hack. You should always notify your client of the exact cause of an error if it knows how to handle it (display error message or retry). Either with an exception, or a result code. Masquerading an error by swallowing the exception is analogous to, I quote Bob Lee, _losing a wheel and keeping on driving_.

  9. I am basically doing what your article espouses – I put workflow/business logic in stateless session EJBs with container-managed transactions. The methods then throw application exceptions for business rule violations. The issue that I am having is that those applications get swallowed up by the container and the container instead throws rollback exceptions, thereby losing any information that I am passing back to my client as part of my application exception.

    I am using Oracle 9iAS and Oracle claims that this is the behavior specified by the J2EE specs and followed by other leading J2EE container providers. I have read the specs and it seems to support my position.

  10. Indeed, the container should not rollback transactions when application exceptions are thrown. Are you explicitly swallowing them in system exceptions? If yes, then this may be the cause.

    If not, maybe you should try another application server. I use Orion Server and am quite satisfied with it.

  11. Funny. I was just noticing how all the Google hits were pointing to the same IBM Dev Works article.

    Nice job. Thanks for taking the time.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: