Archive for January, 2011
Managing exceptions correctly in Java is not as simple as it may sound, there are many wide extended bad practices and programmers who simply don’t care. But having good exception handling is essential to diagnose problems in production and to basically make the maintenance of the application easier. What follows are, in my opinion, the 4 main considerations to effectively handle exceptions in Java.
1.- An exception should only be handled once.
Handling an exception more than once is one of the most common mistakes when dealing with exceptions. There are two main anti patterns:
NEVER shallow an exception. This is, without a question, the most dangerous error when dealing with exceptions. Swallowing an exception = Making your application lie to you. Once that you shallow an exception there is no coming back. Any error caused as consequence of this first exception is going to be extremely hard to diagnose.
Exception handled more than once
Havin an exception handled more than once basically means that in its way out of the stack trace, more than one method catches the same exception. The two main scenarios when a exception is handled more that once are:
Log and throw exception.
Logging and throwing an exception, from a naive perspective, may seem like a valid approach, but is an approach that generates noise and lacks elegance. It lacks elegance because the exception is thrown again after being logged, causing further catch blocks in the stack trace to also deal with it and consequently force them to shallow the exception or to log duplicated information. To prove it let´s imagine that a method logs and throws again one exception, the following would be the main options for further catch blocks in the stack trace.
1) Log exception. This will cause a duplicate stack trace being printed in the logs.
2) Ignore exception, the exception will get to the top of the stack trace, and the JVM will handle it. This will also cause a duplicate stack trace in the logs.
3) Shallow exception. This is a common case of exception swallowing. It usually happens when we know the internals of the method we are calling and we know that the exception is already logged. As already mentioned, exception swallowing is very dangerous.
Don’t wrap runtime exceptions without adding more information.
This is another common scenario of handling an exception more than once that causes a lot of noise. It usually happens when inside a method we have to handle a checked exception, so the programmer decides to catch all the exceptions and wrap them in runtime exceptions. i.e.
catch (Exception e) throw new RuntimeException (e)
The issue with the code above, is that it will also catch all RuntimeExceptions, and it will wrap them in a new RuntimeException, which is useless, because it doesn´t add more information. A better approach would be:
catch (RuntimeException re) throw re catch (Exception e) throw new RuntimeException (e)
2.- Create your own type and tree of exceptions.
Having your own type of exceptions, and your own hierarchy of exceptions, have a lot of advantages.
– Code becomes more readable.
– Logs become more readable.
– Helps to standardize the output of the exceptions.
– Makes it easy to tell what is the source of the exception.
3.- Who should catch the exception?
Place catch blocks carefully, if you are not sure if you want to handle the exception, don’t handle it. In general, only use catch when:
a.- You need to provide graceful error recovery. For instance: every entry point of your code that faces the customer should have a try catch strategy. Remember always to handle the exception as late as possible, otherwise you will end up swallowing the exception or handling it more than once.
b.- You are forced. If you receive a checked exception and you don’t want to handle it just now, feel free to wrap it up in a RuntimeException, but NEVER, shallow the exception.
c.- In batch like process that loop through a series of elements. inside the loop, ALWAYS, catch all exceptions.
d.- You want to add more information to the exception. For instance: Wrap an exception in a more specific, more related to your domain exception.
4.- Understand the purpose and nature of the exceptions.
The root cause for most of the anti patterns when handling with exceptions is the fact that the developers don’t fully understand the nature and purpose of the exceptions. An exception represents unexpected exceptional behavior, which we can try to recover from or handle gracefully. Catching exceptions is the mechanism the application gives us to step in before the JVM deals itself with the exception, is the last frontier. Having this is mind
– Be careful with exceptions when creating new exceptions. When dealing with exceptions, don’t make much assumptions, if you try to do something smart and you cause a further exception, you will loose the first exception.
– Don’t use exceptions to handle standard business cases. They are better off handled by your standard Java code.
Interacting with external resources from your application is a common practice in modern applications, especially for “enterprise like” applications.
An external resource is anything we need to interact with, which sits outside of the application runtime environment. An integration point is where our application handles the interaction with the external resource. The most common integration point types you are likely to deal with are: Web services, databases, files, ftp, external devices…
The problem with any integration point is that you don’t have any control over them, which means that they are not reliable; this requires the developer to have in mind additional considerations to successfully develop robust integration points.
What follows are, in my opinion, the 4 main considerations for software developers to have in mind when developing an integration point.
1.- Always assume that your integration point may fail.
Every single integration point may fail; keep always this mind when developing. This would be the only consideration I would highlight if I only could highlight one.
Is important to make it clear that this consideration doesn’t mean that you have to always handle exceptional errors on the integration points. For instance, let’s say you are logging to a file. A file is an integration point, so you know it may fail, in this scenario it could be fair to say: If there’s an exceptional error on logging, let the log process crash, so you don’t handle the exceptional behavior. But at least you are asking yourself the golden question “What if the integration point crashes? ”
The golden rule to deal with unexpected errors in your integration points is to flag and record them so they can be reproduced later.
2.- Cover your back.
One of the main problems when dealing with integration errors is that when they fail, they usually cause data corruption, or other major issues in your application. These issues can many times only be recovered or tracked by extracting information from your logs. That is the reason why you need good logs in production.
Logging requires additional thought in your integration points than other parts of your applications, just because they are more likely to fail. In general any integration point should be able to tell you in the logs 3 things:
• What is the message that initiated the interaction?
• Was there any unexpected error?
• The result of the interaction with the integration point.
Keep in mind that saying that the integration point should be able to log the three previous questions, doesn’t mean that it always have to log them, or that is the only required logging, that’s something that is only going to be clear once that the application is deployed and actually used. It means that you should be able to turn on or off this logging. There is very few worst things than having an integration point failing in production and not being able to turn on any logging to see what is going on.
3.- Isolate the interactions with the integration point.
As already specified, interacting with integration points is dangerous, so is good to strategize its interactions to make sure that their outcome don’t affect the normal behaviour of the application. Some considerations would be:
• Minimize the amount of interactions with integration points.
• Wrap the logic of your interactions in transactions.
• Allow for automatic retries
• Separate the logic that handles your integration point and your business logic.
4.- Do “what if..” integration testing before going to production.
Getting your integration points right is very difficult, in my opinion, this and synchronicity are the two most difficult things to get right in enterprise software development.
For this reason is important that you assume that if you don’t prove yourself wrong, your integration points are going to fail. Make sure that before you deploy into production you test as close as end to end as many as possible “what if…” scenarios as possible.