I’ve always struggled with exceptions, and I think many people do. If you read a computer science textbook and do some toy projects you feel like you understand them, and then you find yourself working with a larger project and there’s some super dodgy stuff that goes on. I’m gonna do this with Java assumed as the language for the examples but the basic concepts should work for any language which has exceptions – at least the ones I know of.
Disclaimer: I’m just some guy. Be skeptical – try the stuff, see if it works, compare with what other people say. This is very much my personal way of doing things.
Don’t swallow exceptions
You see this a lot in projects where people don’t really know what to do with an exception they’re getting back from e.g. a database call. Here’s how it looks:
try
{
callDatabase();
}
catch (DatabaseException e)
{
// do nothing here
}
This is super bad bad bad and you should never do it. Spoiler alert. Everyone does it at some point and then feels guilty for the rest of their natural lives – and probably after they’ve died in a coding accident and returned as a zombie process.
The basic principle of exceptions and errors in general is that if something goes wrong you holler. So catching an exception at the point of calling the method and doing nothing with it basically pretends to your user that nothing went wrong. If that method is saving things to the database and you’ve swallowed the exception when something went wrong, you’re in poo.
OK so what should we do instead? how much 1% ivermectin for dogs
So you’re calling some method, some dependency outside of your control and the stupid thing is throwing exceptions every now and then like “Wawawa, I can’t connnect to the database” or “Wawawa, the Http endpoint says request invalid”. You can wrap the exception in a try catch, but you’re not gonna swallow it so what do you do instead?
First off, you probably log it. Getting an exception usually means that something unexpected happened. So you want to log it. Now what?
What I like to do is convert the dependency into something specific to my program and rethrow it. Then when I catch it further up the call stack I can do something logical with it like display an error message to the user or send an Http error code back to the caller.
I like to put my dependencies into a service implementation and wall them off from the rest of my code with an interface, for example a data storage dependency interface might look like this:
public interface DataStoreService
{
MyDataClass readFromDataStore(String dataId);
void saveToDataStore(MyDataClass data);
}
Then what you do is you define a RuntimeException
specific to the DataStoreService
interface, which is only thrown from the DataStoreService
. It’s important for it to be a class derived from RuntimeException
because Java has two types of exceptions, and exceptions derived from Exception
are checked exceptions and they’re a pain in the butt to work with ‘cos you have to declare them in the method signature and they break encapsulation and they’re generally frowned upon nowadays, although I’m sure if you google “java checked exception” you’ll find a vigorous stackoverflow debate.
public class DataStoreServiceException extends RuntimeException
{
public DataStoreServiceException(String message)
{
super(message);
}
public DataStoreServiceException(String message, Exception exception)
{
super(message, exception);
}
}
Now for the methods in your interface you can do this:
public class DataStoreServiceImpl extends DataStoreService
{
public MyDataClass readFromDataStore(String dataId)
{
try
{
var data = readStuffFromDB();
// other stuff
// let's say you want to validate and throw an error if the data read fails validation
if (!passesValidation(data))
throw new DataStoreServiceException("Data not valid"); // throw one of our fancy new exceptions
return data;
}
catch (DataStoreServiceException e) // because we're throwing our own exceptions in the method we don't wanna let them be caught by the catch-all handler.
{
throw e;
}
catch (Exception e) // this will catch **all** exceptions, you could be more specific and only catch e.g. IOException
{
e.printStackTrace(); // or some equivalent logging
// by rethrowing it but wrapping the original exception you don't lose any info in case you want to do more diagnosis in the exception handler
throw new DataStoreServiceException("Unknown exception in DataStoreService", e);
}
}
public void saveToDataStore(MyDataClass data)
{
// same sort of thing. Wrap the whole thing in try...catch
}
}
This way all the exceptions that come out of the DataStoreService
are DataStoreServiceException
s, so you know exactly where they originated in your program. You can even be more specific if you want and define exceptions derived from DataStoreServiceException
like this:
public class DataNotFoundException extends DataStoreServiceException
{
// etc
}
The form of the exception handler
OK so now each of the services in the program (a service is a class which does something and is available throughout your program) has its own specific exception defined, and each of its methods is trapping all other exceptions and converting them into the appropriate service exception, how do we use this? Well, remember that what we’re looking for is that when something goes wrong in the guts of our program we want to do something to notify whoever’s using it that something happened. If the program is a desktop program this means displaying an error message, if it’s an Http web server it means sending an appropriate error code.
Let’s look at an Http controller, the desktop program will be similar except that things will be initiated e.g. by a mouseclick rather than an HttpRequest. So the controller receives a request and what’s gonna happen is, it’s gonna call a bunch of the program’s services to help it handle the request and return an appropriate response. If we have a controller that fetches data, transforms it and logs the transformed data, the method might look something like this – please don’t nail me for not getting the code quite right, it’s just sample code, what’s important are the ideas.
// this is a Spring Boot style controller method
ResponseEntity<TransformedData> handleRequest(String dataId)
{
var data = dataService.readFromDataStore(dataId);
var transformedData = transformService.transform(data);
loggingService.logRequest(request);
return new ResponseEntity<>(transformedData, HttpStatus.OK);
}
This is the flow assuming that nothing goes wrong. Now we know that each of our services throws its own specific exception, so we can write the error handling like this:
ResponseEntity<TransformedData> handleRequest(String dataId)
{
try
{
var data = dataService.readFromDataStore(dataId);
var transformedData = transformService.transform(data);
loggingService.logRequest(request);
return new ResponseEntity<>(transformedData, HttpStatus.OK);
}
catch (DataServiceException e)
{
if (e instanceof DataNotFoundException)
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
log.debug("Data service exception {}, e.getMessage());
return new ResponseEntity<>("Data error", HttpStatus.INTERNAL_SERVER_ERROR);
}
catch (TransformServiceException e)
{
log.debug("Transform service exception {}, e.getMessage());
return new ResponseEntity<>("Data transform error", HttpStatus.INTERNAL_SERVER_ERROR);
}
catch (LoggingServiceException e)
{
log.debug("Logging service exception {}, e.getMessage());
return new ResponseEntity<>("Logging error", HttpStatus.INTERNAL_SERVER_ERROR);
}
catch (Exception e)
{
e.printStackTrace();
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
If we wanted to get even more fancy we could look into aspect oriented programming and define an exception handler for all of our controller methods, but that’s a bit hectic so we’ll leave that out for the time being.
Note what we’ve achieved now:
- the body of the controller method is still the same, just wrapped in a try catch. It’s written as if nothing ever goes wrong.
- we can catch exceptions from each service individually and choose an appropriate response code. In this case I made everything report an internal server error, except if the data was not found I return a 404.
- the last catch statement is a catch-all statement which allows us to process any unexpected exceptions that popped out.
- whoever hit the controller with an Http request will receive a coherent response, either the data they requested or a jolly nice error message.
TL; DR
We want to respond with appropriate error messages when things go wrong in our application.
We achieved this by the following:
- For each service in our application we defined a service interface to hide the implementation behind.
- We also defined an exception derived from RuntimeException for each service.
- If we want more granularity we can derive more exceptions from the ServiceException in each service.
- In the service implementation, if we encounter exceptions of any kind we convert them into the appropriate service exception and rethrow them. acheter securo
- In the controller / the click handler / the layer of the application that responds to the requester, we define an exception handler that handles each service exception and responds to the user with an appropriate error message.
And that’s it 🙂
PS the image I used as a header is from here: https://commons.wikimedia.org/wiki/File:Java_exception_classes.svg, shared under Creative Commons licence.