EJB Exception Handling

It is striking that most search results for the keywords “EJB” and “exceptions” point to the same few articles at IBM DeveloperWorks. Unfortunately, they are too advanced for beginners. This post is a simple guide to handling exceptions in EJB.

Definitions

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

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

A system exception 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 specific to the application and is thrown according to business rules. For example, an application exception is raised when the balance for a savings account becomes negative.

System Exception

A system exception is unchecked and is derived from java.lang.RuntimeException. Because of its unpredictability, it must be handled by the EJB container.

A container wraps an EJBException in a RemoteException and throws it to the client. Although some information about the error is lost, the RemoteException is sufficient because the client cannot recover from a system error.

Therefore, for an exception to be caught by the container and sent to the client, a developer must wrap it in an EJBException. The following code extract demonstrates this.

...
public String ejbCreate(String id, String username, String password) 
    throws CreateException {

    try {
        this.id = id;
        this.username = username;
        this.password = password;

        insertRecord();
    } catch (SQLException e) {
        throw new EJBException(e); // swallow the SQLException
    } catch (Exception e) {
        throw new EJBException(e);
    }
    return id;
}
...

Since an EJBException is a system (and unchecked) exception, it does not  have to be declared in the throws clause.

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

...
public UserAccount create(String id, String username, String password) 
    throws RemoteException;
...

The client code then looks like this:

...
try {
    UserAccount userAccount = 
        userAccountHome.create("1001", "user", "password");
} catch (CreateException e) {
    System.out.println("Object could not be created");
} catch (RemoteException e) {
    System.out.println("EJB Error: " + e.getMessage() );
} catch (Exception e) {
    e.printStackTrace();
}
...

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

Application Exception

An application exception is checked and, therefore, must be declared in a throws clause. It is thrown explicitly by code to indicate an error linked to the business rules. For example, debiting a sum of money that makes a savings account balance go negative triggers a SavingsAccountDebitException. An application exception is needed for a client to be alerted of an error, to determine what caused it, and to notify users. An EJB container does not intercept an application exception because it does not know how to handle it. Consequently, the container does not log an application exception be default.

A developer does not need to wrap such an application exception in an EJBException. In fact, to prevent the container from hi-jacking the exception, a developer must not do this.

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

...
public void debit(double amount) 
    throws SavingsAccountDebitException {

    if (this.balance == 0) {
        throw new SavingsAccountDebitException(
            "Cannot make a debit");
    }

    try {
        balance -= amount;
        updateRecord();
    } catch (SQLException e) {
        throw new EJBException(e);
    } catch (Exception e) {
        throw new EJBException(e);
    }
}
...

Notice that when an EJBException is thrown, it can be left out in the throws clause. Also, this code does not demonstrate how transactions are rolled back, a topic which is covered later in this post.

In the remote interface for the bean containing the above code, a throws clause declaring SavingsAccountDebitException is needed, as in the following.

...
public void debit(double amount) 
    throws SavingsAccountDebitException;
...

The client code using the remote interface could look like this:

...
try {
    savingsAccount.debit(1000.00);
} catch (SavingsAccountDebitException e) {
    System.out.println(e.getMessage() );
} catch (Exception e) {
    e.printStackTrace();
}
...

Observe that the application exception is successfully propagated to the client and is ignored by the container. The client uses the information from the exception to notify users; in this example, it displays an error message.

Exceptions and Transactions

How transactions are managed is affected by how exceptions are handled.

A transaction managed by the container is automatically rolled back when a system exception occurs. This happens because the container intercepts system exceptions. However, when an application exception occurs, the container does not intercept it and, therefore, does not roll back a transaction.

The following code extract illustrates this point.

...
EntityContext ctx;
...
public void ejbRemove() 
    throws RemoteException {
    try {
        deleteRecord();
    } catch (SavingsAccountDeleteException e) {
        ctx.setRollbackOnly()
        throw new RemoveException(e.getMessage() );
    } catch (SQLException e) {
        throw new EJBException(e);
    } catch (Exception e) {
        throw new EJBException(e);
    }
}
...

Conclusion

This post applies to remote EJB; when local EJB is  used, RemoteException is never thrown.

As shown, handling exceptions in EJB is different to how it 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 the post Exception Handling Best Practice.

These are the four rules to follow for proper handling of 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.

Join the Conversation

22 Comments

  1. Pingback: Infloop
  2. really it is fantastic.
    it completly clarified my doubts on EJB Exception handling

  3. 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.

  4. 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?

  5. 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?

  6. 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.

  7. … 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.

  8. 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

  9. 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_.

  10. 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.

  11. 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.

  12. The article is very usefull.
    Please queue up all the EJBException in catching order

  13. 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 comment

Leave a Reply

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