Over a million developers have joined DZone.

Improving Exception Handling: One Subtle, One Radical Approach

· Java Zone

Microservices! They are everywhere, or at least, the term is. When should you use a microservice architecture? What factors should be considered when making that decision? Do the benefits outweigh the costs? Why is everyone so excited about them, anyway?  Brought to you in partnership with IBM.

Exception handling via try...catch...finally has some pointless headaches that really don't have any good reasons for their existence.

The Problem

Consider a classic database/session hunk of code:

try {
DBConnection connection = Driver.createCollection("url");
connection.execupdate("UPDATE something");
finally {

Oops, can't do that, I have to do this:

DBConnection connection = null;
try {
connection = Driver.createCollection("url");
connection.execupdate("UPDATE something");
finally {

Note the extra line of code, in this simple example. You all have probably had much more complicated versions with five or ten local var declarations at the top


The Subtle Proposal

So, my subtle proposal: make the finally and catch blocks child scopes to the main try block. That way, all the variables declared in the try section would be accessible/visible from the catch clauses for reporting and logging and error message composition, and for cleanup in the finally clause.

You may not buy this from the simple example above. One line of code, what's the big deal? Consider a complicated set of multiple calls, which I'll build upon for the second proposal:

try {
int status = checkconnection();
String configURL = getConnectionUrlFromConfig();
Connection connection = getDBConnection(configURL);
User user = (User)connection.getObject("a username");
} catch (NullPointerException npe) {
Logger.log("what was null? configUrl = "+configURL+" status = "+status+" user = "+user);

It's pretty common in complicated programs for there to be code that has lots of dependencies. This connection gets this setting based on this config value which invokes this service url. Things can go wrong at each level, with common NPEs. Since we don't have convenient syntax for per-statement exception checking (preview of my other major complaint), we usually end up with large try..catch blocks in a sort of crude batch processing of errors. However, since the catch block can't see multitudes of local vars declared in the try, we either need to add four variable declarations above the try statement, or just log an generic "something went wrong in the previous five lines" message - which sucks.

My suggestion for that doesn't even change syntax. At all. It does change the lexical scoping rules around trys and catches and finallys, which has a slim chance of breaking existing code. Maybe. It should be 99% backwards compatible.

The Radical Proposal

I just hinted at this. Because try { } catch (Exception e) {} is a statement involving two blocks and two keywords, it seems pretty long and heavyweight. Error handling is an important part of mature code, and it should be possible to place it into code without disrupting the readability of the core functional statements. What happens with existing try..catch is alluded to above, people end up batching ten or twenty or thirty or whatever lines of code in try...catches and then handling the errors that arise. In reality in the previous four-line example, we would want to check the error at EACH and EVERY. Right now that would look like, at best:

  int status = -1;
try {status = checkconnection();} catch (ConnectionDownException cde) {Log("conn down",cde); throw new AppException(cde);}
String configURL = null;
try {configURL = getConnectionUrlFromConfig();} catch (NullPointerException npe) {Log("Config not setup right",npe); throw new AppException(npe);}
Connection connection = null;
try {connection = getDBConnection(configURL);} catch (ServerDownException sde) {Log("server down",cde); throw new AppException(sde);}
User user = null;
try{user = (User)connection.getObject("a username"); catch (NotFoundException nfe) {Log("user not found in db",nfe); throw new AppException(nfe);}

Ugh is that annoying. Basically caused by the imposition of a scope block by the try, plus that try { keyword in the begining of each line ruins the readability of what's happening.

What If?

int status = checkconnection() 
$(ConnectionDownException cde) {Log("conn down",cde); throw new AppException(cde);};
String configURL = getConnectionUrlFromConfig()
$(NullPointerException cde) {Log("Config not setup right",npe); throw new AppException(npe);}
$(FileNotFoundException fnfe) {Log("Config file not created",fnfe); throw new AppException(fnfe);};
Connection connection = getDBConnection(configURL);
$(ServerDownException sde) {Log("server down",cde); throw new AppException(sde);};
User user = (User)connection.getObject("a username");
$(NotFoundException nfe) {Log("user not found in db",nfe); throw new AppException(nfe);}
So we have a means for inline handling of exceptions. I can still read what's going on pretty well and filter out the $exception handling. Or we could user @ or # or !! or ^.

Combine the above with the lexical changes I want for catch/finally (I'll try @ instead of $ to see if it's an improvement). Let's assume I architected a ProcessException service that will handling logging and (potentially) rethrowing/repackaging:
try {   
int status = checkconnection()
@(ConnectionDownException cde) {ProcessException.process("conn down",cde);};
String configURL = getConnectionUrlFromConfig()
@(NullPointerException cde) {ProcessException.process"Config not setup right",npe);}
@(FileNotFoundException fnfe) {ProcessException.process("Config file not created",fnfe);};
Connection connection = getDBConnection(configURL);
@(ServerDownException sde) {ProcessException.process("server down",cde);};
User user = (User)connection.getObject("a username");
@(NotFoundException nfe) {ProcessException.process("user not found in db",nfe);}
} finally {
if (connection != null) releaseConnection(connection);
That encapsulates a logically related set of statements into error handling/reporting and cleanup, with a minimized number of lines and pretty good readability IMHO. Exceptions are dealt with/detected immediately, and recovery can be done on the spot. Syntax can be highlighted. Handling can be nested.

Even if it isn't done in the Java main chunk, why not Groovy or Scala?

Discover how the Watson team is further developing SDKs in Java, Node.js, Python, iOS, and Android to access these services and make programming easy. Brought to you in partnership with IBM.


Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}