пример picocli, показывающий использование нескольких команд

У меня есть код, который очень хорошо работает с picocli:

 @Command(name = "parse", sortOptions = false, description = "parse input files and write to database")
class CommandLineArgumentParser {


@Option(names = { "-h", "--help" }, usageHelp = true, description = "display this message")
private boolean helpRequested = false;


@Option(names = { "-s", "--startDate"}, description = "First day at which to parse data",
        converter = GermanDateConverter.class, paramLabel = "dd.MM.yyyy")
public LocalDate start;

@Option(names = { "-e", "--endDate"}, description = "Last day (inclusive) at which to stop parsing",
        converter = GermanDateConverter.class, paramLabel = "dd.MM.yyyy")
public LocalDate end;


private static class GermanDateConverter implements ITypeConverter<LocalDate> {
    @Override
    public LocalDate convert(String value) throws Exception {
        LocalDate result = null;

            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
            result = LocalDate.parse(value, formatter);
            if (result.getYear() < 1900) {
                throw new IllegalArgumentException("year should be after 1900");
            }
        return result;
    }
}


@SpringBootApplication
public class Application implements CommandLineRunner {

public void run(String... args) throws Exception {
    CommandLineArgumentParser commandlineparser = new CommandLineArgumentParser();

    CommandLine commandLine = new CommandLine(commandlineparser);
    try {
        commandLine.parseArgs(args);

    } catch (MissingParameterException e) {
        System.out.println(e);
        System.out.println();
        CommandLine.usage(CommandLineArgumentParser.class, System.out);
        System.exit(1);
    } catch (ParameterException e) {
        System.out.println(e);
        System.out.println();
        CommandLine.usage(CommandLineArgumentParser.class, System.out);
        System.exit(1);
    }

    if (commandLine.isUsageHelpRequested()) {
        commandLine.usage(System.out);
        return;
    } else if (commandLine.isVersionHelpRequested()) {
        commandLine.printVersionHelp(System.out);
        return;
    }

    if (commandlineparser.start == null) {
        log.warn("no start date specified, using: 01.01.2005");
        commandlineparser.start = LocalDate.of(2005, 01, 01);
    }

    if (commandlineparser.end == null) {
        LocalDate timePoint = LocalDate.now();
        log.warn("no end date specified, using today: " + timePoint.toString());
        commandlineparser.end = timePoint;
    }
}

но я не нашел простого примера, который показывает использование нескольких команд, например этот:
https://github.com/remkop/picocli/blob/master/src/test/java/picocli/Demo.java

не компилируется:

int exitCode = new CommandLine(new Demo()).execute(args);

The method execute(CommandLine, List<Object>) in the type CommandLine is not applicable for the    arguments (String[])

Может ли кто-нибудь опубликовать пример того, как использовать несколько команд?


person James Baker    schedule 27.09.2019    source источник


Ответы (1)


Я подозреваю, что вы используете более старую версию библиотеки. Метод execute(String []) : int был введен в picocli 4.0.

Обновление и использование метода execute вместо метода parseArgs позволит вам удалить много шаблонного кода из приложение: обработка запросов на справку по использованию/версии и обработка неверных данных выполняется автоматически с помощью метода execute.

Ваши команды должны реализовывать Runnable или Callable, и именно здесь живет бизнес-логика каждой команды.

Измените свой пример и добавьте к нему подкоманду:

@Command(name = "parse", sortOptions = false,
        mixinStandardHelpOptions = true, version = “1.0”,
        description = "parse input files and write to database",
        subcommands = MySubcommand.class)
class CommandLineArgumentParser implements Callable<Integer> {

    @Option(names = { "-s", "--startDate"}, description = "First day at which to parse data",
            converter = GermanDateConverter.class, paramLabel = "dd.MM.yyyy")
    public LocalDate start;

    @Option(names = { "-e", "--endDate"}, description = "Last day (inclusive) at which to stop parsing",
            converter = GermanDateConverter.class, paramLabel = "dd.MM.yyyy")
    public LocalDate end;


    private static class GermanDateConverter implements ITypeConverter<LocalDate> {
        @Override
        public LocalDate convert(String value) throws Exception {
            LocalDate result = null;

                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
                result = LocalDate.parse(value, formatter);
                if (result.getYear() < 1900) {
                    throw new IllegalArgumentException("year should be after 1900");
                }
            return result;
        }
    }

    @Override
    public Integer call() {
        if (start == null) {
            log.warn("no start date specified, using: 01.01.2005");
            start = LocalDate.of(2005, 01, 01);
        }

        if (end == null) {
            LocalDate timePoint = LocalDate.now();
            log.warn("no end date specified, using today: " + timePoint.toString());
            end = timePoint;
        }

        // more business logic here ...

        // add finally return an exit code 
        int exitCode = ok ? 0 : 1;
        return exitCode;
    }
}

@Command(name = "foo")
class MySubcommand implements Callable<Integer> {
    @Override
    public Integer call() {
        System.out.println("hi");
        return 0;
    }
}


@SpringBootApplication
public class Application implements CommandLineRunner {

    public void run(String... args) throws Exception {
        int exitCode = new CommandLine(
                CommandLineArgumentParser.class,
                new picocli.spring.PicocliSpringFactory())
                .execute(args);
        System.exit(exitCode);
     }
}

Обратите внимание, что при использовании Spring в сочетании с подкомандами picocli вам необходимо вызвать конструктор CommandLine с параметром picocli.spring.PicocliSpringFactory.

Более полный пример Spring см. в picocli-spring-boot. -стартер README.

person Remko Popma    schedule 27.09.2019
comment
поэтому я могу запустить: java -jar target/jsontest2-0.0.1-SNAPSHOT.jar foo - печатает привет, как и ожидалось. но вместо этого при работе с синтаксическим анализом выводится: Непревзойденный аргумент в индексе 0: 'parse' - поскольку ни начало, ни конец не требуются, я ожидал увидеть строки: дата начала не указана, используя: 01.01.2005 и другой для конца .. .? - person James Baker; 05.10.2019
comment
В примере определены параметры --startDate и --endDate, а также подкоманда foo. Если вы передаете аргумент, который не соответствует ни одному из них (например, строка parse), то picocli считает этот ввод недопустимым и показывает сообщение об ошибке. Вы можете указать picocli игнорировать такие недопустимые аргументы с помощью метода CommandLine.setUnmatchedArgumentsAllowed. См. picocli.info/#_unmatched_input . Кроме того, вы можете зафиксировать эти аргументы, определив позиционные параметры в дополнение к параметрам. - person Remko Popma; 05.10.2019
comment
Но разве @Command(name = parse...) и @Command(name = foo) не определены одинаково, просто у этого разбора есть некоторые параметры, которые могут с ним работать? - person James Baker; 05.10.2019
comment
О, теперь я понимаю, что вы имеете в виду. Вы спрашиваете, почему имя команды верхнего уровня не нужно указывать в командной строке. Идея состоит в том, чтобы обернуть вызов java -jar myjar.jar в скрипт с именем parse или, альтернативно, использовать GraalVM для компиляции приложения CLI в собственный образ с именем parse. После этого пользователи смогут вызывать вашу программу с помощью parse foo. (И вы не хотите, чтобы они вводили parse parse foo.) - person Remko Popma; 06.10.2019
comment
точно, спасибо за это объяснение и за поддержку вашего собственного программного обеспечения здесь, в stackoverflow. - person James Baker; 06.10.2019
comment
Пожалуйста! Пожалуйста, пометьте проект на GitHub, если он вам нравится. :-) - person Remko Popma; 06.10.2019