Quarkus - Command Mode with Picocli
Quarkus provides support for using Picocli. This guide contains examples of extension usage.
If you are not familiar with the Quarkus Command Mode, consider reading the Command Mode reference guide first. |
This will add the following to your pom.xml
:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-picocli</artifactId>
</dependency>
Simple command line application
Simple PicocliApplication with only one Command
can be created as follows:
package com.acme.picocli;
import picocli.CommandLine;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
@CommandLine.Command (1)
public class HelloCommand implements Runnable {
@CommandLine.Option(names = {"-n", "--name"}, description = "Who will we greet?", defaultValue = "World")
String name;
private final GreetingService greetingService;
public HelloCommand(GreetingService greetingService) { (2)
this.greetingService = greetingService;
}
@Override
public void run() {
greetingService.sayHello(name);
}
}
@Dependent
class GreetingService {
void sayHello(String name) {
System.out.println("Hello " + name + "!");
}
}
Beans with @CommandLine.Command should not use proxied scopes (e.g. do not use @ApplicationScope ) because Picocli will not be able set field values in such beans. This extension will register classes with @CommandLine.Command annotation using @Depended scope. If you need to use proxied scope, then annotate setter and not field, for example: |
Command line application with multiple Commands
When multiple classes have the picocli.CommandLine.Command
annotation, then one of them needs to be also annotated with io.quarkus.picocli.runtime.annotations.TopCommand
. This can be overwritten with the quarkus.picocli.top-command
property.
package com.acme.picocli;
import io.quarkus.picocli.runtime.annotations.TopCommand;
import picocli.CommandLine;
@TopCommand
@CommandLine.Command(mixinStandardHelpOptions = true, subcommands = {HelloCommand.class, GoodByeCommand.class})
}
@CommandLine.Command(name = "hello", description = "Greet World!")
class HelloCommand implements Runnable {
@Override
public void run() {
System.out.println("Hello World!");
}
}
@CommandLine.Command(name = "goodbye", description = "Say goodbye to World!")
class GoodByeCommand implements Runnable {
@Override
public void run() {
System.out.println("Goodbye World!");
}
}
You can customize CommandLine classes used by the picocli
extension by producing your own bean instance:
package com.acme.picocli;
import io.quarkus.picocli.runtime.PicocliCommandLineFactory;
import io.quarkus.picocli.runtime.annotations.TopCommand;
import picocli.CommandLine;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
@TopCommand
@CommandLine.Command
public class EntryCommand implements Runnable {
@CommandLine.Spec
CommandLine.Model.CommandSpec spec;
@Override
public void run() {
System.out.println("My name is: " + spec.name());
}
}
@ApplicationScoped
class CustomConfiguration {
@Produces
CommandLine customCommandLine(PicocliCommandLineFactory factory) { (1)
return factory.create().setCommandName("CustomizedName");
}
}
Different entry command for each profile
1 | You can return instance of java.lang.Class here. In such case CommandLine will try to instantiate this class using CommandLine.IFactory . |
Configure CDI Beans with parsed arguments
You can use Event<CommandLine.ParseResult>
or just CommandLine.ParseResult
to configure CDI beans based on arguments parsed by Picocli. This event will be generated in QuarkusApplication
class created by this extension. If you are providing your own @QuarkusMain
this event will not be raised. CommandLine.ParseResult
is created from default CommandLine
bean.
@CommandLine.Command
public class EntryCommand implements Runnable {
String connectionString;
@Inject
DataSource dataSource;
@Override
public void run() {
try (Connection c = dataSource.getConnection()) {
// Do something
} catch (SQLException throwables) {
// Handle error
}
}
}
@ApplicationScoped
class DatasourceConfiguration {
@Produces
@ApplicationScoped (1)
DataSource dataSource(CommandLine.ParseResult parseResult) {
PGSimpleDataSource ds = new PGSimpleDataSource();
ds.setURL(parseResult.matchedOption("c").getValue().toString());
return ds;
}
}
You can also provide your own application entry point annotated with QuarkusMain
(as described in ).
package com.acme.picocli;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;
import picocli.CommandLine;
import javax.inject.Inject;
@QuarkusMain
@CommandLine.Command(name = "demo", mixinStandardHelpOptions = true)
public class ExampleApp implements Runnable, QuarkusApplication {
@Inject
CommandLine.IFactory factory; (1)
@Override
public void run() {
// business logic
}
@Override
public int run(String... args) throws Exception {
return new CommandLine(this, factory).execute(args);
}
}
1 | Quarkus-compatible CommandLine.IFactory bean created by picocli extension. |
Native mode support
This extension uses the Quarkus standard build steps mechanism to support GraalVM Native images. In the exceptional case that incompatible changes in a future picocli release cause any issue, the following configuration can be used to fall back to the annotation processor from the picocli project as a temporary workaround:
For Gradle, you need to add the following in dependencies
section of the build.gradle
file:
annotationProcessor enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")