4

I have to convert a large Spring boot application into a flexible CLI tool, where the requests sent by the Spring boot application (among other things) are determined by user input at the command line. I decided to use picocli to implement the command line functionality, however I can't figure out how to even do something as simple as print some text to stdout if the user passes a given option flag, Spring boot just runs as it normally does. How am I supposed to write this so picocli can function alongside Spring boot (and eventually control all the Spring boot stuff)

Guilty
  • 464
  • 4
  • 13
  • https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-command-line-runner – khmarbaise Apr 23 '21 at 12:06
  • Could I have the main @SpringBootApplication implement `CommandLineRunner` ? and then all the `commandline` arguments are handled by ` void run`, or do I define a separate component as a commandLineRunner, and spring automatically figures out that run() should be used if there are any commandline arguments given? – Guilty Apr 23 '21 at 12:14
  • Have you taken a look into the documentation? – khmarbaise Apr 23 '21 at 12:19
  • 1
    Yeah was looking at it there, should probably look closer before asking stupid questions. Sorry, being a simple C programmer up until now I'm sorta at my wit's end. – Guilty Apr 23 '21 at 12:21
  • The [picocli-spring-boot-starter](https://github.com/remkop/picocli/tree/master/picocli-spring-boot-starter) may also be useful. Maybe even necessary. – Remko Popma May 02 '21 at 12:38
  • @RemkoPopma , the picocli springboot starter is included in the base picocli dependency already (as of recent versions). So I wouldnt recommend specifying the separate one, as I had done this and turns out it is out of date, which caused issued. – Guilty May 04 '21 at 07:11

2 Answers2

8

As a follow-up to this I eventually got the code working by refactoring out the "controller methods" into 3 as follows:

|
|_ MainApp.java
|_ CmdRunner.java
|_ TheCommand.java

Where MainApp is the @SpringBootApplication which basically just does:

System.exit(SpringApplication.exit(new SpringApplication(MainApp.class).run(args)));

Kicking everything off.

CmdRunner is an @Component & simple implementation of the CommandLineRunner Interface provided by SpringBoot, the most important bit is below:

    @Autowired
    private TheCommand theCommand;

    @Override
    public void run(String... args) {
       new CommandLine(theCommand).execute(args);
    }

It executes the passed cli arguments (which were passed to it from MainApp.java) on a new picocli CommandLine object. Which brings us to the final class, TheCommand.java which is simultaneously a picocli @Command & Springboot @Controller implementing the Runnable interface. And essentially just contains all the logic and (ever-growing)functionality I needed to deliver.

The only downside of this implementation is that when a user runs it with the --help flag, the app still runs the spring boot stuff making it a little unresponsive in that particular scenario.

Guilty
  • 464
  • 4
  • 13
  • this is great, is there a way we can implement it without actually running the spring boot application in case when we have to run the cli application (for eg: when we have command line arguments) and still have all the spring boot DI? – Pavan Kumar May 05 '22 at 02:45
  • @guilty - were u figure this stuff out ? im running into the same issue with picocli and springboot. wondering how u fixed it – Sandeep Dec 14 '22 at 14:44
  • as un-intuitive as it sounds, the marked answer is the correct solution, and solved all of my issues. – Guilty Dec 14 '22 at 15:23
5

Spring Boot is supported by PicoCLI and they even have an example to follow: https://picocli.info/#_spring_boot_example

Galen Howlett
  • 530
  • 4
  • 12