public class CLI { @Parameter(names = { "-h", "--help" }, help = true) private boolean help; public static void main(final String[] args) { final Builder builder = JCommander.newBuilder().addObject(new CLI()); final Optional application = StreamSupport.stream(ServiceLoader.load(Application.class).spliterator(), false).findFirst(); application.map(Application::programName).ifPresent(builder::programName); application.map(Application::environmentVariable).ifPresent(varName -> builder.defaultProvider(new EnvironmentVariableDefaultProvider(varName, "-"))); final JCommander jCommander = builder.build(); ServiceLoader.load(Command.class).forEach(command -> CLI.registerCommand(jCommander, command)); JCommander leafCommander = jCommander; try { jCommander.parse(args); final String rootVerb = jCommander.getParsedCommand(); final JCommander rootCommander = jCommander.getCommands().get(rootVerb); if (rootCommander == null) { jCommander.usage(); System.exit(1); } leafCommander = rootCommander; do { final String subVerb = leafCommander.getParsedCommand(); final JCommander subCommander = leafCommander.getCommands().get(subVerb); if (subCommander != null) leafCommander = subCommander; else break; } while (true); final Command command = (Command) leafCommander.getObjects().get(0); command.execute(); } catch (final CommandException e) { System.err.printf("%1$s: %2$s. See '%1$s --help'.%n", leafCommander.getProgramName(), e.getMessage()); System.exit(e.getStatus()); } catch (final Exception e) { System.err.printf("%1$s: %2$s. See '%1$s --help'.%n", leafCommander.getProgramName(), e.getMessage()); System.exit(1); } } private static final void registerCommand(final JCommander jCommander, final Command command) { jCommander.addCommand(command); final Parameters commandParameters = command.getClass().getAnnotation(Parameters.class); if (commandParameters == null || commandParameters.commandNames().length == 0) return; final JCommander subCommander = jCommander.getCommands().get(commandParameters.commandNames()[0]); final Collection subCommands = command.commands(); if (subCommands != null) subCommands.forEach(subCommand -> CLI.registerCommand(subCommander, subCommand)); } public interface Command { default Collection commands() { return null; } void execute() throws CommandException; } }