Activate Maven Profile by Operating System
Russia has invaded Ukraine and already killed tens of thousands of civilians, with many more raped and tortured. Ukraine needs your help!
Who would have thought that you can develop a command line application with Spring? Since Spring Boot 3 has a support for building native executables, it has become not only possible but also a reasonable choice! Let's dive into how to do it step by step.
Create a new Spring Boot project
- use GraalVM distrubution of Java
- select "Picocli" in the dependency list
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootPicocliSampleApplication {
public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(SpringBootPicocliSampleApplication.class, args)));
}
}
import java.util.concurrent.Callable;
import picocli.CommandLine.Command;
import org.springframework.stereotype.Component;
@Component
@Command
public class MainCommand implements Callable<Integer> {
@Override
public Integer call() {
System.out.println("Hello from the main command");
return 0;
}
}
import picocli.CommandLine;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.stereotype.Component;
@Component
public class PicoCommandLineRunner implements CommandLineRunner, ExitCodeGenerator {
private final MainCommand mainCommand;
private final CommandLine.IFactory factory;
private int exitCode;
public PicoCommandLineRunner(MainCommand mainCommand, CommandLine.IFactory factory) {
this.mainCommand = mainCommand;
this.factory = factory;
}
@Override
public void run(String... args) {
exitCode = new CommandLine(mainCommand, factory).execute(args);
}
@Override
public int getExitCode() {
return exitCode;
}
}
Run with:
$ ./mvnw package
$ java -jar target/spring-boot-picocli-sample-0.0.1-SNAPSHOT.jar
Build to native:
...
Add options:
@Component
@Command
public class MainCommand implements Callable<Integer> {
@CommandLine.Option(names = "--name", required = true)
private String name;
@Override public Integer call() {
System.out.println("Hello %s from the main command".formatted(name));
return 0;
}
}
Add parameters:
@CommandLine.Parameters(index = "0", defaultValue = "0")
private int times;
@Option
and @Parameters
annotation can be placed either on field or on a setter method. in native image it must be on the field.
Add help command
@Command(subcommands = CommandLine.HelpCommand.class)
public class MainCommand implements Callable<Integer> {
// ...
}
or
@Command(mixinStandardHelpOptions = true) // adds help and version
get rid of the default logging
logging.level.root=error
create custom banner
and paste it into src/main/resources/banner.txt
banner colors
you can use ${AnsiColor.BLUE}
etc
remember to set to default at the end
${AnsiColor.DEFAULT}
.
remove the banner
spring.main.banner-mode=off
Add version command
normally version could be read from the manifest but it's not possible with current status of spring native
- Add property to proeprties in pom.xml
<app.version>${project.version}</app.version>
- Generate version.properties file during the build:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.1.0</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>write-project-properties</goal>
</goals>
<configuration>
<outputFile>${project.build.outputDirectory}/version.properties</outputFile>
</configuration>
</execution>
</executions>
</plugin>