Checked or unchecked?

Exception handling model in Java requires that all checked Exceptions (deriving from java.lang.Exception) are either caught or declared in the throws clause of the method where it is thrown. Compiler checks that this rule is followed and generates a compile-time error when it is not. Exceptions deriving from java.lang.RuntimeException are called unchecked since they do not have to be declared and compiler does not check for them.

The checked/unchecked dichotomy has been around since the invention of Java. Then, late in 1990-s Microsoft decided it liked Java, but wanted to own language. So they created C#, which was very much like Java, but with some improvements. Not surprisingly and in a typical Microsoft way, they did away with checked exceptions altogether. In C#, all exceptions are unchecked. Method signature says nothing about exceptions that can be thrown by the method. There is no throws clause, so there is no way to describe which exceptions a method may throw. At the first glance, it makes things easier: developers do not need to write code to deal with exceptions. This is also the problem: developers have no way of knowing which exceptions may be thrown by a method. Even if they have access to the source code of the method, it is still not enough: one needs access to the source code for all nested calls.

Since the release of C#, a vocal minority in the Java world has been advocating for adopting the same approach: doing away with checked Exceptions. While this position is rightly considered extreme, the choice between using checked and unchecked exceptions needs to be made every single time you create your own Exception class or write code throwing an Exception.

The debate about the proper place to draw the line between checked and unchecked has been ongoing in the Java community. I can not presume to cover the entire Java universe, but I’d offer my perspective as a Web and enterprise middleware architect and developer. While wholesale abolition of checked Exceptions is out of question, the boundary between the 2 classes and their appropriate usage is decided for each project. Let’s consider some approaches to drawing this boundary.

In a typical Web application, every Web requests requires access to some kind of backend (database, mainframe, etc). External resource access is by its nature “exposed to elements” over which our application has no control (a network outage, data center flooding, partner application down, database server overheating, etc). That’s why Java methods that implement external access always throw a checked exception, such as IOException or SQLException. In a large and stable application, exception processing is generalized in an exception handling framework, which may be responsible for the following:

  1. insulate end user from technical details of unexpected errors that are of no benefit to him, and facilitate presentment of a clean notification of an internal problem
  2. enable quick detection and remediation of infrastructure failures (out of disk space, lost database connection)
  3. collect and preserve exception information to aid troubleshooting and debugging
  4. record failed transaction outcome for traceability/auditing

With this in mind, I will explore 2 very different approaches to deciding on checked vs unchecked exceptions. First, let’s turn our attention to this Elliote Rusty Harold’s article. He suggests that we should start calling checked exceptions “external exceptions” and runtime exceptions “internal exceptions”.

If the exception is caused by problems internal to the program, it’s a runtime exception. If it’s caused by problems external to the program, it’s a checked exception.
Short, simple, to the point. Are there any exceptions to this rule? Are there any cases where an internal program bug legitimately should be signaled by a checked exception or a problem in the external environment should be signaled by a runtime exception? I can’t think of any.

I think this is a wrong way to draw the line, at least in a typical Web or middleware application.

It is true that in most situations where an external system access occurs, a checked Exception is usually thrown – most frequently an IOException or a SQLException. Let’s explore a bit more. What would cause a SQLException? Usually a program bug (unsatisfied constraint, failed to check for maximum field length, wrong table name) or an infrastructure problem (connection lost, out of disk space). Theoretically you could also use SQLException to signal a business condition (record with this ID already exists), but this is widely recognized anti-pattern and I hope you are not following it. So we are left with situations that do not allow successful completion of processing (program bug or infrastructure failure).

What do you do in response to a checked Exception in this situation? In my experience, individualized exception handling logic specific to a method is highly unusual. You can not fix a program bug programmatically and you normally can not work around an infrastructure failure or misconfiguration. To wit: if a checking account balance request returns a RemoteException, there is nothing you can do in your Web application to recover from this situation and still show account balance to the end user. You can only say “Sorry” in a nice way as prescribed by your application coding standards. Same if you try to retrieve order shipment details and SQLException comes back. And so on.

As a sidebar, once we’ve established that exception processing is generalized and boilerplate, we could call it a cross-cutting concern, and engage in some Aspect-Oriented Programming.

A typical generic exception handling code implementation that I’ve seen wraps original Exception in MyCompanyException and propagates the latter all the way down the call stack without examination or processing in intermediate layers. This pattern is widely accepted and generally makes sense, but it is not perfect. One thing that immediately jumps out at you is that almost every method in your application now throws MyCompanyException. This is so because Web and middleware applications exist to process data stored in different kinds of backend systems and methods for accessing those systems invariably have a chance to fail, so they throw a checked Exception (e.g. SQLException for databases). The typical approach described above propagates these Exceptions back in the form of checked MyCompanyException. This presents a glaring problem: signal overload. When everyone is a suspect, you do not really have one. If every method throws the same checked Exception, the “throws” clause adds no value, only overhead. You might as well close the book on checked Exceptions and go home – make MyCompanyException unchecked.

What can a developer do in this situation? Why would I want every programmer to worry about infrastructure problems, even at a trivial level (inserting standard calls for exception processing)? Most programmers should focus on implementing business logic. Java community built numerous frameworks that externalize “technical” concerns, remove the need for repetitive code and free the programmers to focus on the business problem at hand. This is one of the reasons Java development is productive enough to be a market success.

I should mention that Elliotte Harold, the author of the blog post I’ve discussed above, has become kind of an “unchecked exceptions skeptic”. He recently published an article on IBM developerWorks devoted specifically to avoiding throwing a RuntimeException. He goes to a great length and writes quite a bit of code to achieve that. He even implements his own bubble sort! Here’s what I don’t like about that. First, the more code one writes, the higher a chance of a bug. Second, when someone reimplements system functionality, there is an added risk of creating an inferior solution. This adds up to a steep price, so the author must believe unchecked Exceptions to be a very dangerous thing to justify this high price. I am among of those who disagree and find unchecked Exceptions a useful tool. In fact, I advocate a wider use of unchecked Exceptions in the domain of Java I’m familiar with.

For comparison, let’s look at what Tata’s ShriKant Vashishtha advocated a few years back in this artcile:

Exceptions for which the end user cannot take any useful action should be made unchecked. For example, exceptions that are fatal and unrecoverable should be made unchecked. There is no point in making XMLParseException (thrown while parsing an XML file) checked, as the only action to be taken may be to fix the root cause based on the exception trace.

This is the crux of his article. He devotes the rest of it to elaborating how this pattern would work with Struts.
I find this approach more reasonable. The difference between checked and unchecked is that developer is forces to “do something” about checked Exceptions. If a developer can not do something meaningful, this “doing something” becomes nothing but waste.

Combining ShriKant’s idea with an observation that most external access Exceptions switch the code execution over to the “sorry” path, I suggest an expanded usage of unchecked exceptions. Unless there are individualized exception processing / recovery possibilities, take checked Exception thrown by the code accessing an external system and quickly turn it into a RuntimeException, right at the point where original Exception is realized. This would eliminate useless and distracting “throws MyCompanyException” clauses all over your code. You will then need to remember to catch RuntimeException in the outer layer of your code (in Struts, a Front Controller) and decide what to do with it. In an extreme case, you can even ignore it and let container handle it. Web Containers catch all Runtime Exceptions and give developers some limited configuration options for dealing with those (error page). Of course, true business exceptions (such as failed input validation) would be exempted from this policy and would remain checked Exceptions, forcing developers to handle them explicitly.

You may ask: what about monitoring/infrastructure related goals of Exception handling framework (“quick detection and remediation of infrastructure failures” I mentioned above)? Since this is a cross-cutting concern that applies universally to all applications running in a particular server environment, it would be better to handle error detection and notification by server infrastructure independently from application code. This would free programmers from having to worry about it and increase reliability of the code (detection/notification happens automatically and does not depend on programmer remembering to insert right code). It is certainly possible. For example, when using IBM WebSphere, one would leverage FFDC for error detection and build a solution on top of that.

UPDATE 11/8 in response to Elliotte’s comment below.
Key point that Elliotte makes is that local error handler is preferable to a global error handler because it has context: information about specifics of the situation that resulted in an error. He also addresses the point that global error handler would often find itself in “nothing I can do here” position when trying to handle an error. But I was trying a completely different point: that local error handler is often times in no position to take a meaningful action, despite having the local context. Thus responsibility for handling the error may be safely transferred to a global handler. Even if you know precisely what you’re trying to do, most errors will break the “happy path” irreparably. In most cases, after an error, the difference may be in the tone of “Sorry” you say, but you will never be able to answer the original question (client request). Situations where a retry is reasonable excluded, obviously.

This point makes sense if you assume a separation of responsibilities that makes application code focus on the task at hand and leave infrastructure concerns to “others”. Please stay with me for an explanation.

It doesn’t know … whether the sys admin needs to be woken up at 3:00 AM in the morning or not

No, no. I don’t want local error handler to figure whether the error is so truly fatal to the functioning of the whole application that it requires an immediate attention. And don’t get me wrong – I don’t want global error handler to be responsible for that, either. I alluded to my preferred answer to this problem in the last paragraph of the original post – externalize this logic. To ensure reliable run of your application, you should monitor it. Monitoring would include intercepting and collecting exception information. Your monitoring software will then be configured to react appropriately to situations you are interested in. You achieve a separation of concerns: Java code in your application is concerned with solving the business problem at hand, while monitoring configuration defines reactions to system errors (including sending pages to sys admin staff).

Advertisements

2 Responses to Checked or unchecked?

  1. Interesting. You still haven’t convinced me, and I think you misread the developerWorks article, which was covering a very specific corner case, not all or even 90% of instances of checked and runtime exceptions.

    However reading this article, I do see something important that hadn’t occurred to me before. What you and others in similar situations advocate is a general top-level exception handler, and you don’t like being bothered by all the throws clauses in between. I understand that. The problem is this isn’t how Java is designed. There is no concept of a top-level handler in the language. That’s something bolted on in various frameworks after the fact.

    The interfaces in Java are along method boundaries. Each method knows and depends on no more than the preconditions it asserts and the signatures of the methods it invokes. There is nothing it can rely on beyond that. It’s part of the general movement toward avoiding global state that software development has followed for the last 50 years or so. (Functional languages take this even further than OO languages do.) Trusting a single global error handler is as suspect as trusting a single instance of any other global state, and probably especially so in multi-threaded applications.

    In fact, as I think about it now, trusting the global error handler is worse than global variables. Since it bypasses the usual chain of method calls and returns, this isn’t just a global variable. It’s friggin goto! Now in languages without a solid exception mechanism such as C, error handling is one of the few accepted uses of goto. Nonetheless it’s still ugly, and it’s still something to be avoided when possible.

    What’s especially pernicious about a global error handler is that it doesn’t have a lot of context. It doesn’t know as much about the error as the local context does. It doesn’t know how serious it is, whether the operation can be tried, whether the sys admin needs to be woken up at 3:00 AM in the morning or not, and many other things. If your only error handler is a global error handler, it’s no wonder you think the exception can’t be handled. At the global level, it probably can’t be. At the local level where there’s a lot more context, you can usually take more effective action.

    By all means do include a global error handler to cover cases you didn’t think of. However consider this a last resort, not a first resort. The global error handler is your safety, not your linebackers. If an exception makes it all the way up to the global error handler, then the developers missed something; and it’s time to dig back down into the code and figure out what, and how to stop it from happening again. The goal should be that the last resort catch block catches nothing at all. Treat every exception logged by the last resort error handler as a bug that needs to be squashed. If you mix up that information with expected transient conditions like occasional database timeouts or malformed client requests, then you’re going to miss real bugs in your code.

    • ivansmirnov says:

      Elliotte, I agree that your developerWorks article covered a corner case, but I was still surprised by the length you go to in order to avoid a RuntimeException. It may be easy for a guru like you to build an alternative solution you describe. But for a typical developer it would be asking too much.

      Also, I updated the article to respond to your larger point.

%d bloggers like this: