Exceptions and Error Handling

As with most Java libraries Airline uses Java exceptions as a way to signal problems. Airline exceptions are unchecked exceptions (which means calling code is not required to handle them) however in order to provide a good user experience you will usually want to handle these. The root type for Airline exception is simply ParseException but there is a whole hierarchy of more specific exceptions below that:

IO Exceptions

Some aspects of Airline that deal with IO, most notably the Help subsystem will use standard Java IOException and these are checked exceptions which you should handle appropriately.

Error Control Flow

As with most things in Airline you have some ability to customise the error control flow. While some things are considered as fatal errors, e.g. missing/invalid metadata annotations, and always throw errors in the actual parsing phase you can control how errors are handled. Error handling is done by the ParserErrorHandler interface, the desired handler can be defined in your parser configuration via the @Parser annotations errorHandler field e.g.

@Parser(errorHandler = CollectAll.class)

The default error handler is FailFast, this error handler will simply throw the first parsing error encountered i.e. parsing halts immediately on encountering an error.

Sometimes you may wish to collect up all the errors before failing in which case the FailAll handler will collect and throw either a single exception if only 1 error was encountered or an aggregated exception containing all the errors as suppressed exceptions.

Alternatively you may wish to collect up all the errors and then make an intelligent decision on how to proceed. For example if the user has specified your help option then you probably want to show them your help message regardless of the errors. In this case the CollectAll handler will allow you to do this.

In order to act intelligently both the Cli and the SingleCommand classes provide a parseWithResult(String... args) method that returns a ParseResult<T> instance. This can be used to inspect the results of parsing and act appropriately. For example if we wanted to show the error messages and the help output we might do the following:

public static void main(String[] args) {
        com.github.rvesse.airline.Cli<ExampleRunnable> cli = new com.github.rvesse.airline.Cli<ExampleRunnable>(ShipItCli.class);
        try {
            // Parse with a result to allow us to inspect the results of parsing
            ParseResult<ExampleRunnable> result = cli.parseWithResult(args);
            if (result.wasSuccessful()) {
                // Parsed successfully, so just run the command and exit
                System.exit(result.getCommand().run());
            } else {
                // Parsing failed
                // Display errors and then the help information
                System.err.println(String.format("%d errors encountered:", result.getErrors().size()));
                int i = 1;
                for (ParseException e : result.getErrors()) {
                    System.err.println(String.format("Error %d: %s", i, e.getMessage()));
                    i++;
                }
                
                System.err.println();
                
                com.github.rvesse.airline.help.Help.<ExampleRunnable>help(cli.getMetadata(), Arrays.asList(args), System.err);
            }
        } catch (Exception e) {
            // Errors should be being collected so if anything is thrown it is unexpected
            System.err.println(String.format("Unexpected error: %s", e.getMessage()));
            e.printStackTrace(System.err);
        }
        
        // If we got here we are exiting abnormally
        System.exit(1);
    }

Improving this Documentation

This documentation is itself open source and lives in GitHub under the docs/ directory.

I am not a professional technical writer and as the developer of this software I can often make assumptions of knowledge that you as a user reading this may not have. Improvements to the documentation are always welcome, if you have suggestions for the documentation please submit pull requests to the main branch.