Welcome to the Airline Users Guide, this guide is intended to show you how to use every aspect of Airline.
Many of the examples contained in this user guide may be found in compilable form in our Git repository at GitHub.
If you have the code checked out and compiled locally you can use the provided runExamples
script to launch an example
e.g.
airline-examples> ./runExample GettingStarted
Throughout this website where you see a box like the following:
This is a warning that describes a potential pit-fall that users might encounter. These are used to call out places where it is possible for users to introduce dangerous behaviours, or where they are strict constraints they must respect.
Also where the library has evolved over time you may see the following:
This is used to denote features that are only available in newer versions of the library or where behaviours have changed across versions.
Where a specific optional module is required then you may see the following:
Before you get started reading this guide it is useful to introduce the terminology that Airline uses to make sure you are clear what we are referring to.
git
is a popular example of a CLIgit remote
is an example of a group within a CLI--name
followed by zero or more values that are used to
populate a field of a command, for example --name Example
Example
When talking about CLI parsing libraries people typically talk about three phases.
Firstly there is the Definition Phase in which you define your CLI i.e. command groups, commands, options, arguments, restrictions etc. In Airline the definition phase is done primarily through the use of declarative annotations, you can also do parts of the definition phase using imperative code but it is usually much easier to just rely on declarative annotations.
Secondly there is the Parsing Phase in which you create and run a parser. Since Airline relies on a declarative annotation based approach to the Definition Phase the parsing phase is very simple since you can have Airline generate a parser from your class and then run that parser in as little as two lines of code.
Finally there is the Interrogation Phase in which you look at the results of the parser and run your command accordingly. With Airline the result of the parser is an instance of a Java class/interface with appropriately populated fields and so you can simply access these fields as desired.
In order to use Airline in your applications you will need to add a dependency on the airline
library to your project
at a minimum. Assuming a Maven project you can do this like so:
<dependency>
<groupId>com.github.rvesse</groupId>
<artifactId>airline</artifactId>
<version>X.Y.Z</version>
</dependency>
Where X.Y.Z
is your desired version, the current stable release is 3.0.0
If you want to use some of the other features Airline provides then you may also need to add additional dependencies to grab additional features.
At its most basic defining a command in Airline means adding some annotation to a class.
Let’s take a look at GettingStarted.java
:
package com.github.rvesse.airline.examples.userguide;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.github.rvesse.airline.SingleCommand;
import com.github.rvesse.airline.annotations.Arguments;
import com.github.rvesse.airline.annotations.Command;
import com.github.rvesse.airline.annotations.Option;
@Command(name = "getting-started", description = "We're just getting started")
public class GettingStarted {
@Option(name = { "-f", "--flag" }, description = "An option that requires no values")
private boolean flag = false;
@Arguments(description = "Additional arguments")
private List<String> args;
public static void main(String[] args) {
SingleCommand<GettingStarted> parser = SingleCommand.singleCommand(GettingStarted.class);
GettingStarted cmd = parser.parse(args);
cmd.run();
}
private void run() {
System.out.println("Flag was " + (this.flag ? "set" : "not set"));
if (args != null)
System.out.println("Arguments were " + StringUtils.join(args, ","));
}
}
We’ll talk about each of the things introduced in the subsequent sections and provide links to more in-depth pages where you can explore each introduced concept in detail.
The definition phase in Airline is driven primarily through the use of annotations on classes and their fields. 99% of the time you can define your desired CLI entirely through annotations, on the rare occasion where this is not the case you can define some portions directly in Java code.
At a minimum we need to annotate our class with the @Command
annotation. This annotation
tells Airline about the command, you must at a minimum specify the name
field giving it the name of the command.
Names should ideally be short and memorable and they must not contain whitespace.
Here we’ve also defined the description
field which gives a short description of the command.
In our example our class is annotated with @Command
like so:
@Command(name = "getting-started", description = "We're just getting started")
public class GettingStarted {
There are lots of other things that we can define on our command if we want, please see the @Command
Annotation documentation to learn more.
In order to accept options we need to add a field to our class and annotate it with the
@Option
annotation. In our example the flag
field is annotated with @Option
like so:
@Option(name = { "-f", "--flag" }, description = "An option that requires no values")
private boolean flag = false;
Here we define a boolean
option which the user may invoke by passing -f
or --flag
at the command line.
There are lots of additional things we can define for our option if we want, please see the @Option
Annotation documentation to learn more.
You can have as many @Option
definitions as you need provided that none of the definitions have overlapping name
values. Each @Option
definition MUST be associated with a specific field.
We can accept additional arbitrary inputs by annotating a field with the @Arguments
annotation e.g.
@Arguments(description = "Additional arguments")
private List<String> args;
This will be populated with a list of arguments received at the command line which were not otherwise interpreted by Airline i.e. anything that was not an option or a command/group name.
There are several additional things we can define for our arguments if we want, please see the @Arguments
Annotation documentation to learn more.
For a single command like this we can create a parser in a single line like so:
SingleCommand<GettingStarted> parser = SingleCommand.singleCommand(GettingStarted.class);
This tells Airline to extract the annotation meta-data from the given class - GettingStarted
in our example - and to
use that to prepare the meta-data necessary to parse user input. We can then create an instance of our class with the
fields appropriately populated based on our option and argument definitions by parsing the command line arguments like
so:
GettingStarted cmd = parser.parse(args);
Here we simply pass the received command line arguments to our previously created parser and Airline creates and populates an instance of our class appropriately.
Finally we can now interrogate the options received and act accordingly, in this example this phase has been placed into
a separate run()
method for convenience e.g.
private void run() {
System.out.println("Flag was " + (this.flag ? "set" : "not set"));
if (args != null)
System.out.println("Arguments were " + StringUtils.join(args, ","));
}
Since we have an instance of the class we can access the fields as we would do normally since Airline has populated them with the values passed in at the command line.
Now you’ve seen how to create a simple single command program we can move on to creating a more complex Git style CLI
where multiple commands are provided. Let’s take a look at BasicCli.java
:
@Cli(name = "basic",
description = "Provides a basic example CLI",
defaultCommand = GettingStarted.class,
commands = { GettingStarted.class, Tool.class })
public class BasicCli {
public static void main(String[] args) {
com.github.rvesse.airline.Cli<Runnable> cli = new com.github.rvesse.airline.Cli<>(BasicCli.class);
Runnable cmd = cli.parse(args);
cmd.run();
}
}
For a more complex CLI we need to use the @Cli
annotation to specify our CLI. In our example
our @Cli
annotation looks like the following:
@Cli(name = "basic",
description = "Provides a basic example CLI",
defaultCommand = GettingStarted.class,
commands = { GettingStarted.class, Tool.class })
This states that our CLI has a name
of basic
and that it consists of two commands - GettingStarted.class
and
Tool.class
. Each command itself needs to be appropriately defined with at minimum a @Command
annotation as shown
in the earlier GettingStarted.java
example.
We also specify that GettingStarted.class
will serve as our defaultCommand
, this specifies what the behaviour of our
CLI is if a user does not explicitly provide the name of the command to be run. The commands
field is used to provide
an array of all the commands that make up the CLI.
You can see the @Cli
documentation for many more advanced options that the annotation supports.
For a CLI we can create a parser in a single line like so:
Cli<Runnable> cli = new Cli<Runnable>(BasicCli.class);
This instructs Airline to extract the annotation meta-data from the given class - BasicCli
in our example - and use it
to prepare the necessary meta-data to parse user input. Since this is a CLI this will also extract all the meta-data
for all the commands
that you specified as part of your CLI annotation.
The type parameter given (Runnable
in this example) specifies a common type for all the commands in the CLI and will
be the resulting type from parsing. Object
can always be used but often it may be useful to have all your commands
implement a common interface as in this example so that they have a standard method that your code can then invoke on
the parsed command to have the actual command logic run.
Once we have the Cli
object we can parse a command like so:
Runnable cmd = cli.parse(args);
Finally you can now run the received command and interrogate the received options and act accordingly. Since we defined
our CLI to have all our commands implement Runnable
then we can simply call the run()
method:
cmd.run();
As Airline has populated the fields of the parsed command appropriately it will have all the necessary information it needs to run its command logic.
You will probably want to read more about the various annotations that Airline provides in order to learn how to annotate your commands and CLI with more advanced features.
It is also worth taking a look at the Airline in Practise pages which detail various practicalities of using Airline in the real world.
We’d also recommend learning about the Help system which you can use to produce help for your commands and CLIs in a variety of common formats.
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.