Monday, September 29, 2014

Overview of Exceptions In Java and Some SonarQube Recommendations

...So I had a chat with SonarQube earlier today. She was a little unhappy with the way things were in a project I was working on, and since I can't push this conversation away forever, I decided to bite the bullet and just hear her out. I figure that, apart from hearing what she has to say and making her happy, I could learn one or two things from her recommendations...

And sure I did. Especially when it came to Exception Handling. Got reminded about stuff I already know. Also picked up some one or two good things to always keep in mind when thinking/working with Exceptions in Java.

Since I have been able to get her Rules Compliance to 99.3%, I guess I could penned down some of the wisdom I gleaned from her: some new, some already known.


On Java's Exception Class Hierarchy...



Throwable is at the top of the hierarchy. Error and Exception extends Throwable, while RuntimeException extends Exception. All other exceptions, custom and ones that comes with the Java standard library extends any of these classes.

On Checked and Unchecked Exception...

As you can see from the hierarchy, all exceptions in Java are checked exception  with the *exception of RuntimeException and Error.

*pun not intended.

So what are checked and unchecked exceptions? Let us look at it this way...

Exceptions are basically things taking an “unexpected” turn in the course of an application run time. 

Things go wrong all the time. That is life. We can't help it. Sometimes though, when these unexpected event occurs, your application should be able to face it, deal with it and continue running...You know, sort of when life hands you a lemon make a lemonade out of it... 

While some other time, these unexpected turn of events could just be beyond what your application can handle or should be handling. And in such a scenario It is perfectly fine for your application to throw in the towel and just quit.    

Use checked exceptions for those exceptions that your application should be able to anticipate and recover from.

Use unchecked exception for those exception that your application should not try recovering from. 

For example if you write a web crawler that saves copies of crawled pages to the file system, a user of your application would most likely need to supply your application the address of the website to crawl. In this case, your application expects a well formed URL. But it is possible that instead of having a well formed URL, a user types in gibberish...an unexpected turn of events, but one in which it makes sense for your application to anticipate and deal with: no need to quite, just catch the exception and inform who ever the twerp is that in other to use a “web” crawler, you need a "valid" web url...This is a scenario where checked exception is appropriate.

But in a scenario where the machine running your web crawler runs out of disk space...an unexpected turn of events..then it becomes impossible for your application to save copies of pages crawled to the file system. Now in such a situation it Is perfectly okay for your application to just quit. it really does not make sense to anticipate not having enough disk space to write to (what would you rather do? email the crawled pages to the user?) ...This is a scenario where unchecked exception is appropriate.

On Java Compiler, Checked and Unchecked Exception...

Do the Java compiler treat these two exceptions differently? Yes it sure does.

Two rules apply to Checked exception, which does not hold true for Unchecked Exceptions. Namely:

  1. A piece of code calling a method that throws a checked exception (ie IOException) must surround the call within a try/catch block.
  2. A method that throws a checked exception must announce to the world that it does in the method signature.
The difference is in the must. You may or may not apply this rules for unchecked exceptions, but they must be applied to checked exceptions.

On Catching Exceptions...

The process of anticipating these unexpected turn of events, in the life time of an application and reacting to them is by the try/catch block mentioned above. 

Ideally you should not be catching Unchecked exceptions. (Exception, RuntimeException or any of its sub classes). Whatever leads to these classes of exception occurring should be fixed, instead of recovered from: when they occur your application should blow up. 

RuntimeException, most often than not, points to bugs in your program. A flaw in the logic... 

For example if you have an array of 7 items and you are trying to access an element at the seventh index, then you would get an ArrayIndexOutOfBoundsException...rightly so. In such a case the code causing this kind of exception should be fixed instead of anticipating such situation and recovering from it.

So also, Error is an indication that something went wrong in areas where your application don't have control over. In in such a scenario, the exception thrown should not be catched. 

For example, the JVM could encounter unexpected turn of events. If this happens, your application has no business anticipating such an unexpected turn of events. It should just quit. 

An example of this, could be when the JVM runs out memory and throws an OutOfMemoryError. Catching such an exception is pointless, or how absurd can it be trying to catch an OutOfMemoryError?

As you can see ArrayOutOfBoundsException extends RuntimeException while OutOfMemoryError extends Error: the two unchecked exceptions.

Some Specific SonarQube Recommendations:

And here are some specific sonarqube recommendations encountered. I guess this portion of this post would be updated accordingly...

1. Generic exceptions Error, RuntimeException, Throwable and Exception should never be thrown



Why does this make sense? Well an unexpected turn of event is a specific thing. You should be throwing an exception that reflects this not its super class.

2. Exception handlers should provide some context and preserve the original exception



Why this make sense? Well exception embodies something going wrong, their context should be preserved for debugging, right?

3. Exception As Flow Control




4. Dodgy - Exception is caught when Exception is not thrown

This complaint was raised over a portion of code that looked something like this:

try {
// This could lead to a run time exception
someMethodThatCanThrowARunTimeException()
someOtherMethodThatCanThrowRuntTimeException()
...
yetAnotherRunTimeExceptionThrowingMethod();

} catch (Exception e) { // Dodgy exception catcher
 
}

This method uses a try-catch block that catches Exception objects, but Exception is not thrown within the try block, and RuntimeException is not explicitly caught. It is a common bug pattern to say try { ... } catch (Exception e) { something } as a shorthand for catching a number of types of exception each of whose catch blocks is identical, but this construct also accidentally catches RuntimeException as well, masking potential bugs. --> Source

No comments: