Java

How many exceptions?

We all know what exceptions are. We have checked and unchecked exceptions in java. Checked exceptions are for recovering. Like reading a file from disk.

public class ReadFile {

    private AtomicInteger retryCount = new AtomicInteger(0);
    private static final int maxRetries = 3;
    private static Logger log = getLogger(ReadFile.class.getName());

    private String readFromFile(String fileLocation) {
        StringBuilder resultStringBuilder = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new FileReader(fileLocation))) {
            String line;
            while ((line = br.readLine()) != null) {
                resultStringBuilder.append(line).append("\n");
            }
        } catch (IOException e) {
            //retry again or look in a secondary place
            if (retryCount.incrementAndGet() <= maxRetries) {
                try {
                    TimeUnit.SECONDS.sleep(5);
                    readFromFile(fileLocation);
                } catch (InterruptedException ex) {
                    log.error("Thread interrupted", ex);
                }
            } else {
                throw new RuntimeException("File not found");
            }
        }
        return resultStringBuilder.toString();
    }
}

These are verbose because the situations demands it. Too many of these will introduce the dreaded spaghetti code.

Unchecked exceptions are errors, they appear at runtime. They do not appear in the method’s signature and do not need to be caught(they actually do but at a higher level). These make our code less verbose.

In my experience I’ve encountered the overuse of checked exception. They described a unrecoverable situation and were being chained to a master exception if you want. This leads me to the next question. Should there we only one exception per application? An ApplicationException? An chain all the others into this? It depends. Again if you’re designing a library it makes sense, but if you design a rest api probably it does not make sense.

Coming back to unchecked exceptions. These make our code more readable. But how many of these should be? Should there be a UserNotFoundException, AccountNotFoundException, UnauthorizedUserException, InsufficientFundsException? You get where I’m going to. Should the exception be descriptive in it’s name or it should contains a descriptive message? What would a message will be for UserNotFoundException? “admin does not exists” for example?

Let’s come be and see how we can caught these, because they need to be caught or else the user will see NullPointerException, IllegalArgumentException directly into the app. This is not professional to say the least. Obviously we cannot do try catch on the highest level, which is the rest-services in the case of a web app. Image a @Controller looking something like.

@RestController
public class MyController {

    @PostMapping(value = "/post")
    public ResponseEntity doPost() {
        HttpStatus result;
        try {
            //do something
            result = HttpStatus.OK;
        } catch (Exception e) {
            //log exception
            result = HttpStatus.BAD_REQUEST;
        }
        return new ResponseEntity(result);
    }

    @PutMapping(value = "/put")
    public ResponseEntity doPut() {
        HttpStatus result;
        try {
            //do something
            result = HttpStatus.OK;
        } catch (Exception e) {
            //log exception
            result = HttpStatus.BAD_REQUEST;
        }
        return new ResponseEntity(result);
    }
}

These must be decoupled from the code, and how we decouple things in spring? With aspects of course. We use the @RestControllerAdvice annotation in conjunction with @ExceptionHandler.

@RestControllerAdvice
public class ExceptionController {

    @ExceptionHandler(value = UsernameNotFoundException.class)
    public ResponseEntity handle(UsernameNotFoundException e) {
        //log it
        return new ResponseEntity(buildEntity(e.getMessage()), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity handle(ResourceNotFoundException e) {
        //log it
        return new ResponseEntity(buildEntity(e.getMessage()), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(value = BadRequestException.class)
    public ResponseEntity handle(BadRequestException e) {
        //log it
        return new ResponseEntity(buildEntity(e.getMessage()), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(value = PasswordExpiredException.class)
    public ResponseEntity handle(PasswordExpiredException e) {
        //log it
        return new ResponseEntity(buildEntity(e.getMessage()), HttpStatus.BAD_REQUEST);
    }

    private HttpEntity buildEntity(String message) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return new HttpEntity<>(message, headers);
    }
}

Still there will be a lot of methods for each type of exception. Let’s think what do we do when we catch an exception? Well we log it and put out a message. And we return the appropriate status code.(eg 404). Now if we group all the 404s how many exceptions do we get?  What about other response codes?

Well then I’m asking why do we need all those exceptions mentioned above. Wouldn’t be enough an exception that says NotFoundException with an appropriate message. Like

throw new NotFoundException("Could the find user admin");
throw new NotFoundException("Could the find account X");

and then rewrite the @RestControllerAdvice:

@RestControllerAdvice
public class ExceptionController {

    @ExceptionHandler(value = NotFoundException.class)
    public ResponseEntity handle(NotFoundException e) {
        //log it
        return new ResponseEntity(buildEntity(e.getMessage()), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(value = BadRequestException.class)
    public ResponseEntity handle(BadRequestException e) {
        //log it
        return new ResponseEntity(buildEntity(e.getMessage()), HttpStatus.BAD_REQUEST);
    }

    private HttpEntity buildEntity(String message) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return new HttpEntity<>(message, headers);
    }
}

How many exceptions you have in your codebase?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.