🏠Spring BootCustomizing Exit Codes of a Spring Boot Application

Customizing Exit Codes of a Spring Boot Application

Let’s take a look at exit codes in Spring boot and the various ways we can customize them.

What is an Exit Code?

In the UNIX shell, An exit code or sometimes known as a return code is a number from 0 to 255 by a process or an executable. These codes let the caller know whether the process succeeded or failed by simply looking at the exit code. As per the convention, 0 means a successful process completion. And any other return code means it is a failure.

As anything other than 0 is a failure, the application developers can provide unique non-zero error codes depending on the cause of the failure. For example, 23 for the unsupported parameters, 90 for database errors, etc. ShapeShed has a nice article on UNIX exit codes for you to read.

Spring Boot and exit codes

When you run a spring boot application, you may have seen that the “Process finished with exit code 1”. This means that the application couldn’t start properly or exit due to a failure.

Intellij showing Process finished with exit code 1 for a spring boot application

But wouldn’t it make sense to provide a unique error code depending on the cause of the failure? This is where the Exit code generators and the exception exit code mappers come into the picture.

Implementing Exit codes

To begin with, Exit codes only make sense in command-line applications that work on some tasks and eventually exit. There are a couple of ways you can alter the exit codes of a spring boot application. You could provide an ExitCodeGenerator bean, or use an exception mapper. Let’s go through each one of them in detail.

ExitCodeGenerator

The ExitCodeGenerator beans allow the SpringApplication.exit() to find an appropriate error code. So let’s implement and see this interface in action.

@Component
public class ApplicationExitCodeGenerator implements ExitCodeGenerator {
    @Override
    public int getExitCode() {
        return 22;
    }
}
Code language: PHP (php)

Once you have defined this component(bean), you can use the SpringApplication.exit() method to test the results.

@SpringBootApplication
public class SpringBootExitCodeApplication{


    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringBootExitCodeApplication.class, args);
        int exitCode = SpringApplication.exit(run);
        System.exit(exitCode);
    }
}Code language: JavaScript (javascript)

As you see here, we are creating a SpringApplication and then immediately exiting the context. This would pick up the exit code as 22 from ApplicationExitCodeGenerator. You can then use this exit code to call System.exit() which is the java way to return error codes.

In this case, the application would start and immediately exit with code 22.

Demonstrating custom error code in spring boot

But this implementation is the dumb way to implement error codes as the exit code never changes. Ideally, you should be able to provide different exit codes based on the issues.

ExitCodeExceptionMapper to map exception in to Exit code

This approach lets you map an exception to an error code with few lines of code. Let’s implement the ApplicationExitCodeExceptionMapper bean and map some exit codes for a set of exceptions.

@Component
public class ApplicationExitCodeExceptionMapper implements ExitCodeExceptionMapper {
    @Override
    public int getExitCode(Throwable exception) {
        if (exception.getCause() instanceof InvalidArgumentException) {
            return 44;
        }
        if (exception.getCause() instanceof RuntimeException) {
            return 12;
        }
        return 22;
    }
}
Code language: PHP (php)

Now, You need to simulate an exception that would cause the application to exit. To demonstrate this, we are using the spring boot command-line runner. Command-line runners are always prone to runtime exceptions and if not handled, they will cause the application to exit. When such an exit occurs, Spring Boot can intercept the exception and map it to an exit code.

@SpringBootApplication
public class SpringBootExitCodeApplication implements CommandLineRunner {

    public static void main(String[] args) {
         SpringApplication.run(SpringBootExitCodeApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        throw new RuntimeException();
    }
}Code language: JavaScript (javascript)

When you run this application, the run method will eventually throw a RuntimeException thus causing the application to stop. But we defined an ExitCodeExceptionMapper bean, so the exit code will be 12.

exitcodeexceptionmapper demo spring boot

As you can see here, the spring boot application failed with an error code 12 because of a RuntimeException.

Even though this approach seems elegant, we are not done yet. When the number of exceptions grows, the mapping grows as well. Which will eventually lead to code smell. But we can avoid them as well.

Exceptions implementing Spring Boot ExitCodeGenerator

As you saw earlier, the mapper implementation works great. But the mapper class can grow big too quickly. This is why you can have custom exception classes that implement the ExitCodeGenerator interface.

public class ApplicationException extends RuntimeException implements ExitCodeGenerator {
    
    @Override
    public int getExitCode() {
        return 14;
    }
}Code language: PHP (php)

When spring boot sees this exception in the command-line runner, it will automatically return 14 as exit code.

custom exception with exit code.

As you see, you can create as many types of exceptions as you want and your business logic can throw them anywhere in your command line application. Based on each exception, an appropriate exit code will be returned by spring boot.

Things to consider

  1. If you define more than one ExitCodeGenerator bean, then the one that provide higher return code will be used.
  2. An ExitCodeExceptionMapper will take precedence over an exception implementing ExitCodeGenerator, even for their sub-types.
  3. The Web Applications and MQ listening applications handle exception within their WEB and listener contexts. Thus they never endup in an application exit. This is why the examples here are only for command-line runner.(where exit codes actually makes sense)

Related

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *