Long time member of the Java community. Author of JHTML and the SmugMug Java API, worked with the DZone crew for a while and Product Services Manager at Genuitec. Also the creator of The "Break it Down" Blog and comedy site Up My Own Ass. Riyad has posted 6 posts at DZone. View Full User Profile

What's the Best Way to Handle Exceptions?

10.03.2008
| 12264 views |
  • submit to reddit

It seems, given my limited experience, that handling exceptions depends entirely on the context in which you are developing. As a fan of "rules" that can be applied to different scenarios I wanted to hit you guys up and see what the rules-of-thumb were with regards to exception handling.

Some approaches to exception handling sort of speak for them self, for example, swallowing exceptions in an API is always wrong, you aren't giving the caller/implementor an opportunity to handle the exception and possibly implement some intelligent work around:

try {
// open a network connection
// download a file
} catch(Exception e) {
// catch everything and just eat it
}

but I could think of some scenarios (especially with regards to user-interfaces) where swallowing an exception and doing nothing with it is a perfectly valid case.

Then there is the whole Checked vs Unchecked exception issue.

As an API author the idea of Checked exceptions appeals to my technical side; if I'm writing code that could do something bad, I should force the implementor to respond to that by making them try-catch my code... I'll leave it up to them to decide if they do something smart with it or not.

Unfortunately, the reality is that working with an API that uses nothing but checked exceptions is really frustrating (e.g. early versions of Hibernate) and cause your code to start looking overly complex. You might make some short-cuts by just marking your own methods as throwing those exceptions or throwing massive blocks of code into try-catch blocks just not to be bothered by it. The frustration can even be compounded when the code you are using almost always runs without a problem... you begin to doubt the need for good try-catch block semantics.

To work around this, when developing APIs, I've started re-throwing exceptions as Unchecked RuntimeExceptions, which my technical side hates, but my "I'm a real person and have things to get done" side likes, it lets the dev respond to potential exceptions (albeit a bit more generic) if they want to, otherwise they can ignore it:

try {
// do something that can throw an exception
} catch(Exception e) {
// catch all exceptions
throw new RuntimeException("<some detailed description of what barfed>", e);
}

The idea being that by-way of the human-readable message and the wrapped exception, the caller and developer, should have enough useful information to figure out what went south.

Now with regards to application development, I find myself simply logging and eating exceptions all over the place, something along the lines of:

try {
// do something error-ey
} catch(Exception e) {
logger.log("<some message that will help me later>", e);
// do nothing else
}

In some cases when I want my app to die due to an exception (or at least throw it back up a level) I'll re-throw the exception, but that seems to be few and far between for the most part.

It's these erratic treatments of exceptions that pushed me to finally write this all down and ask the community what they do as real developers, not as text-book writers where best-practices are always employed no matter what.

Specifically, the following scenarios:

  • As an application author, how do you catch/handle/log exceptions that occur that are non-critical? (obviously critical exceptions have to be handled, but what about the rest?)
  • As an API author, do you use Checked or Unchecked exceptions?
  • As an API author, do you rethrow exceptions or swallow them in hopes of making the API easier for the implementor?
Looking forward to feedback, suggestions, rants or just cat pictures with funny captions.
Published at DZone with permission of its author, Riyad Kalla.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jesper Nordenberg replied on Mon, 2008/10/06 - 2:47am

I think checked exceptions are a great feature in the Java language. Java is not expressive enough to handle maybe, option and either types as efficiently as many functional languages, so checked exceptions are the best tool you have. Think of a call to a method that declares a checked exception as a call that might return a value or might do some action. Clearly you must handle the cases where the call is not executed normally.

There are some simple rules to follow regarding exceptions:

  • If you can't handle the exception and still do some alternative thing that fulfills the method contract, you must pass it on to the caller (either rethrow or just let it pass through).
  • If you design a library, do not ever use implementation specific exceptions in your API. If you design an API where something can go wrong, add an API specific exception type or use a generic one from the JDK. Think about your library API as an interface that is not bound to an implementation. Clearly it would be incorrect to declare that a API method throws an implementation specific exception. An example would be if you design a persistence API and one implementation is backed by an SQL DB, you don't want to expose SQLException in your API.
  • You can re-throw a runtime exception as a checked exception, but never the other way around. There are places in the JDK that uses runtime exceptions for things that should clearly use a checked exception (Integer.parseInt comes to mind).
Sure it's more work handling exceptions in your code than just ignoring them, but checked exceptions are just as important as the return type in the method signature.

Ashraf Fouad replied on Mon, 2008/10/06 - 4:38am

IMHO, I think the best I have read regarding exception handling is in this link:

Exception Handling: Common Problems and Best Practice with Java 1.4

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.11.4355

Download the PDF in the upper right corner.

Thomas Mueller replied on Mon, 2008/10/06 - 4:52am

InterruptedException: ignore. Exceptions on closing something: try to log them, then ignore.

As a API and library author, I now use unchecked exceptions: in the library, catch checked exceptions and re-throw them as unchecked.

Jesper Nordenberg replied on Mon, 2008/10/06 - 5:40am in response to: Thomas Mueller

[quote=thomasmueller]

As a API and library author, I now use unchecked exceptions: in the library, catch checked exceptions and re-throw them as unchecked.

[/quote]

Why?

Riyad Kalla replied on Mon, 2008/10/06 - 6:53am in response to: Jesper Nordenberg

Jesper,

This was interesting to me:

You can re-throw a runtime exception as a checked exception, but never the other way around. There are places in the JDK that uses runtime exceptions for things that should clearly use a checked exception (Integer.parseInt comes to mind).

Could you clarify using my examples above? I am currently catching checked exceptions that require try-catch blocks and rethrowing as Runtime so the implementors *don't* have to wrap every single line of my API with try-catch... is this the "never do this" scenario you were referring to?

Riyad Kalla replied on Mon, 2008/10/06 - 6:56am in response to: Jesper Nordenberg

I've done what Thomas has done, and it was all in the name of making the API nicer for implementors (not so much cruft code), which I think is important to spur adoption of your work.

If Integer.parseInt has required a try-catch, I think that would have been one of the first things that someone would have written a 3rd party library for, for requested an "enhancement" for in the JDK.

Thomas Mueller replied on Mon, 2008/10/06 - 7:10am in response to: Jesper Nordenberg

> Why re-throw checked exception as unchecked?

Unchecked exceptions are easier to use. You don't have to catch or declare the exception. Examples: A build tool (http://code.google.com/p/pjmake) - when an exception occurs the best solution is to stop the build. An O/R mappign tool (http://www.h2database.com/html/jaqu.html) - the user can't do much if there is a low level database access problem. Recoverable problems (for example lost connection) should be solved by the database or library.

Sometimes you have to convert checked exceptions to unchecked ones. For example when writing a Comparator to use in Arrays.sort(list, comparator).

Jesper Nordenberg replied on Mon, 2008/10/06 - 7:47am in response to: Riyad Kalla

[quote=rkalla]

Jesper,

This was interesting to me:

You can re-throw a runtime exception as a checked exception, but never the other way around. There are places in the JDK that uses runtime exceptions for things that should clearly use a checked exception (Integer.parseInt comes to mind).

Could you clarify using my examples above? I am currently catching checked exceptions that require try-catch blocks and rethrowing as Runtime so the implementors *don't* have to wrap every single line of my API with try-catch... is this the "never do this" scenario you were referring to?[/quote]

Pretty much. Basically you're giving up static type checking and are relying on programmer knowledge instead. It's the old dynamic/static type checking debate, which is a matter of personal preference (I guess there are some scientific reports about this, but I don't know any references). I like the compiler to check as much of my code as possible at compile time, much in the style of functional programming languages.

 

Jesper Nordenberg replied on Mon, 2008/10/06 - 7:54am in response to: Thomas Mueller

[quote=thomasmueller]

> Why re-throw checked exception as unchecked?

Unchecked exceptions are easier to use. You don't have to catch or declare the exception. Examples: A build tool (http://code.google.com/p/pjmake) - when an exception occurs the best solution is to stop the build. An O/R mappign tool (http://www.h2database.com/html/jaqu.html) - the user can't do much if there is a low level database access problem. Recoverable problems (for example lost connection) should be solved by the database or library.

[/quote]

Sure, if you can recover from the exception there is no need to throw a checked exception. But what if you don't have recover functionality in your persistence library? Do you feel it's fair to not indicate to the library user that you're using out-of-process components and that things can break? It's not the library authors job to determine if an error is recoverable or not, it's a decision that should be made by the library user.

Riyad Kalla replied on Mon, 2008/10/06 - 8:10am in response to: Jesper Nordenberg

Jesper,

Excellent points and I think this is why I was hung up on the issue for so long, so is a very logical/systematic argument for using checked exceptions, but when I'm actually in my IDE working, it drives me nuts working with APIs that require it... I tend to bury myself in the minutia of how *exactly* I should be scoping and handling my try-catch blocks, trying not to cross over multiple boundries of exceptions (e.g. an IOException and then a ParseException) that aren't logically coupled.

Is this not your experience as well with checked exceptions?

Jesper Nordenberg replied on Mon, 2008/10/06 - 9:27am in response to: Riyad Kalla

[quote=rkalla]

Jesper,

Excellent points and I think this is why I was hung up on the issue for so long, so is a very logical/systematic argument for using checked exceptions, but when I'm actually in my IDE working, it drives me nuts working with APIs that require it... I tend to bury myself in the minutia of how *exactly* I should be scoping and handling my try-catch blocks, trying not to cross over multiple boundries of exceptions (e.g. an IOException and then a ParseException) that aren't logically coupled.

Is this not your experience as well with checked exceptions?

[/quote]

Java is not the most expressive language around, so you have to accept some awkwardness in your error handling code. In languages like C# and Scala (which unfortunately doesnt have checked exceptions) it quite simple to simplify the error handling by passing a function object to a common error handler function that handles the exceptions. I use this pattern a lot for GUI programming for example. If closures are added this would be a useful pattern in Java as well. Closures would also make it possible to, in some cases, replace checked exceptions by constructs like option and either which allows a higher form of composition (see the Scala libraries for some examples).

Other than that, just make sure you handle an exception as early as possible, so you don't propagate it unnecessarily to higher abstractions.

Martin Smith replied on Mon, 2008/10/06 - 4:47pm

I'm not sure why people think checked exceptions actually solve anything. In most real world situations I've seen, they only force the caller to wrap it in an unchecked exception or eat it silently. I think the bottom line here is that other programmers either will catch them or they won't -- so let them freaking do it already.

Checked exceptions  are like TPS reports. The people who want to do them already will, and the people that don't... well, you didn't force them, despite whatever rules you added :P

Riyad Kalla replied on Mon, 2008/10/06 - 5:45pm in response to: Martin Smith

Martin,

Great analogy and I think you have a pretty good point there. Devs that don't want to deal with exceptions (checked or otherwise) will do things to avoid them regardless of how the API is designed.

Mike P(Okidoky) replied on Tue, 2008/10/07 - 1:40pm

Applications that handle exception very thoughtfully, tend to be the stable applications.

Every application (source code) I've seen that sucks handled exceptions in sloppy, arbitrary ways.

Unless you figure out a very clean and very thoroughly thought out policy on exceptions in your architecture, and mandate that all developers on the team must follow that, count on your application to always show signs of weakness. That means zero wishy washy make-it-go-away attempts - anywhere!

Peter Huber replied on Wed, 2008/10/08 - 11:07am

Just some simple thoughts

> 1. As an API author, do you use Checked or Unchecked exceptions?

Unchecked exceptions for programming errors, i.e. NullpointerException for parameters that "MUST NOT be null" (term in quotation marks is what I append to @param javadoc of my methods) but are passed in as null. I do a explicit check for the parameter and then throw a new NullpointerException("<paramName>");

Checked Exceptions  for "business" exceptional conditions (whatever that means;-)

> 2. As an API author, do you rethrow exceptions or swallow them in hopes of making the

>API easier for the implementor?

I do a lot of Wrapping: But in case I wrap the Exceptions I try to add some value to it. At least I'd add information about the current context (parameters of the current method for instance, classification of exception: technical/business, an error-code, an issuer)

Regarding exceptions with error codes: Why error codes? It's simply to avoid exception bloating. What do I mean with exeption bloating? Take a look at the Exception hierarchy of NamingException. This is a good example of "too much". It contains a class for each and every single situation. Compare it to the JDBC-API! To find a compromise I like the idea of having just a few different exception classes and to make them specific with a specific error-code for each situation. Another advantage of error-codes is that they are a lot easier to evaluate by software (-> logging)

And I like the idea of making exceptions string representations "wiki"-capable: You might use a companies private wiki, if you use it for development also, you might introduce some wiki pages that cover "exception cause", "symptoms" and "remedy". It is so easy if you just go to your log, copy and paste the exception string representation and look up the information in your wiki

 Example: You read in your Log-File

com.a.b.ExampleException#CODE1686?param1=this&param2=that

Go to your wiki and add that string to your wiki-base-url...http://intranet/wiki/base/com.a.b.ExampleException#CODE1686

And voila, your right there...

Trust me, this is great help for the guys that have to operate your software...

 

>3. As an application author, how do you catch/handle/log exceptions

> that occur that are non-critical? (obviously critical exceptions have to be handled, but what about the rest?)

Depends. I think it's worth not only to think about classes and APIs but about systems, subsystems and system boundaries. My rule of thumb as long as you're in your "own" subsystem, you can pretty much do what you like. But if you cross system boundaries then you should log each an every exception and then rethrow it (maybe wrapped). Give you an example what I mean with the term crossing boundary. If you implement a web service then the caller is on the outside. The caller calls your service (enters your system, crossing the boundary), the service performs it's work and then returns (i.e. crossing the boundary again for the reply). If there's any system failure in your code and the clients are very loosely coupled then you never want to be dependend on the clients to get the exception stack trace in case you justforward exceptions

Another idea about handling I like very much is the notion of "ExceptionHandlers". Think of

 

try {

..

} catch (final Exception ex) {

myHandler.handle("UseCase", "MethodName", "Situation", ex, varArgs...)

}

Let the handler be dependency injecteded and you may choose the right behavoiur by doing some configuration magic. Is this a place for talking about AOP?

 

 

Riyad Kalla replied on Wed, 2008/10/08 - 11:38am in response to: Peter Huber

Peter,

I found your exception-handler suggestion in #3 very interesting. I've written log handles and things like that in the past (take a message, log it to console and DB and file or something like that) but never considered doing an exception handler before... it makes a lot of sense to compartmentalize that behavior somewhere.

Also the idea of injecting the correct handler makes the approach even slicker... now I have a new toy-idea to go implement, thanks!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.