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: ,

Arbitrary sort of selection options

_Problem_

Very often, options that are displayed in a selection control (drop-down list or simple list) need to be sorted in a logical order that is relevant to the business domain rather than in alpha-numerical order.

Let us take the lookup values for the status of a sale as an example. Usually, they have to be sorted in the order in which the status changes, as shown below:

# Preparation
# Marketing
# Negotiation
# Closed

A _simplistic_ database table that is used to store these lookup values typically consists of only two fields: ID and Description. Suppose such a table, *StatusLookup*, has the following structure:

The *ID* field is of type _CHAR_ and is a foreign key (FK) in referencing records. The choice of datatype and size for this field is driven by the desire to have meaningful values in it. So, _PREP_ stands for _Preparation_ status, _MKTG_ for _Marketing_, NEGO for _Negotiation_ and _CLOS_ for _Closed_.

The *Description* field holds the caption for each option that will be displayed in the selection control.

As can be seen, sorting by either the id or the description does not yield the result we expect, that is, a logical order. One solution would be to replace the meaningful ids with values that would produce the order we wish to achieve when sorted by the *ID* field. Doing so, however, negates our effort to have values that can be easily identified.

_Solution_

The better solution is to add a new field to the table; it contains values that are arbiitrarily defined to produce the sort order we desire. So, our new table structure becomes:

We fill in the *Sequence* field with integer values; when sorted by this field, the logical order should be the one we want to achieve. A typical table will be as follows:

|*ID*|*Description*|*Sequence*|
|CLOS|Closed|4|
|MKTG|Marketing|2|
|NEGO|Negotiation|3|
|PREP|Preparation|1|

The SQL statement or stored-procedure that we use to get the lookup values is as follows:

The recordset that contains the results returned from this query can be iterated through sequentially; the fields of each row are used to generate each option in the selection control. Thus, our goal to logically sort the lookup values is achieved.