Picocli framework with JBang CLI

5 min read
Table of contents
Picocli framework with JBang CLI
- Picocli is a one-file framework for creating Java command line applications.
- This blog will just demonstrate the usage of Picocli framework with Jbang, for more info refer the picocli documentation.
- In this blog demonstrated how we can use picocli framework with JBang that takes command line arguments and performs some logic.
- This blog doesn't explains the details of the code, just demonstrate how we can use the Picocli framework and JBang like script for different usage.
- Basic understanding of JBang will help to execute the code
Pre-requisites
- JBang CLI installed
Summary
- In code example we use JBang to load the Picocli dependency and use that framework to build the simple java application that takes command line arguments.
- When executing the java application a parameter needs to be passed which is considered as username it can be any random string.
- The application also takes an optional argument
-r
or--random-message
, when this argument is passed the code will fetch some random message from internet. - The application also takes an optional password argument
-p
or--password
which is set to be interactive. There is no need to provide the password value along with the argument, upon execution password prompt will appear for the user input. - The
-h
or--help
argument will display the usage of the java application. - Jayway json dependency is used to extract the information from the API.
Code
- The
demoappcli.java
is the entry point for the Jbang, for the-r
option to connect to the API in the internet the code is placed undersupport/
folder, which is referred in Jbang class with//SOURCES support/*
and import the packageimport support.apihelper
.
- To execute the application we can use the Jbang cli by passing the java class name and command line arguments like below
$ jbang demoappcli.java testUser -r
- Output looks like below
{ "username": "testUser", "password-provided": "false", "message": "What do you call a computer mouse that swears a lot?" }
- When the required parameter is missing the output would be like below
$ jbang demoappcli.java -r
Missing required parameter: '<user-name>'
Usage: demoappcli [-hr] [-p] <user-name>
demo app cli example with picocli
<user-name> Input an user name .
-h, --help display a help message
-p, --password Passphrase
-r, --random-message flag will print a random message from internet
- The main java class
///usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 23
/*
This is the jbang example with the use helper source class in another folder
and demonstrate the using helper class as package in the class
*/
//SOURCES support/*
//DEPS info.picocli:picocli:4.7.7
//DEPS com.jayway.jsonpath:json-path:2.9.0
//DEPS org.slf4j:slf4j-api:2.0.17
//DEPS org.slf4j:slf4j-simple:2.0.17
import java.util.concurrent.Callable;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import com.jayway.jsonpath.JsonPath;
import support.apihelper;
@Command(name = "demoappcli", mixinStandardHelpOptions = true, version = "demo-app-cli v1.0.0",
description = "demo app cli example with picocli")
public class demoappcli implements Callable<Integer>{
@Option(names = {"-r", "--random-message"}, description = "flag will print a random message from internet")
private boolean randomMsg = false;
@Parameters(paramLabel = "<user-name>", description = "Input an user name .")
private String username = "User Name";
@Option(names = {"-p", "--password"}, description = "Passphrase", interactive = true)
char[] password;
@Option(names = { "-h", "--help" }, usageHelp = true, description = "display a help message")
private boolean helpOption = false;
private final static String RESPONSE_FORMAT_UNAME_PSWD_RANDMSG = """
{ "username": "%s", "password-provided": "%s", "message": "%s" }
""";
private final static String RESPONSE_FORMAT_UNAME="""
{ "username": "%s","password-provided": "%s" }
""";
private final String JSON_PATH_RULE="$.message";
private JsonPath jsonPath = JsonPath.compile(JSON_PATH_RULE);
@Override
public Integer call() throws Exception {
String result = "";
boolean passwordInput = false;
if (password != null ){
byte[] bytes = new byte[password.length];
for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) password[i]; }
if (bytes.length > 0){
passwordInput = true;
}
}
if (randomMsg){
try{
apihelper apiAccess = new apihelper();
result = apiAccess.accessAPIForMessage();
// parse json string for the path
String apiResponseValue = jsonPath.read(result);
result = String.format(RESPONSE_FORMAT_UNAME_PSWD_RANDMSG, username, passwordInput, apiResponseValue);
}catch (Exception e){
result = String.format(RESPONSE_FORMAT_UNAME_PSWD_RANDMSG, "Err","Err","Exception occurred accessing API endpoint");
e.printStackTrace();
}
System.out.printf("%s",result);
} else if( !randomMsg && username != null ) {
result = String.format(RESPONSE_FORMAT_UNAME, username, passwordInput);
System.out.printf("%s",result);
}else{
System.out.println("usage:");
System.out.println("jbang demoappcli -r username");
System.out.println("jbang demoappcli -h ");
}
return 0;
}
public static void main(String... args) {
int exitCode = new CommandLine(new demoappcli()).execute(args);
System.exit(exitCode);
}
}
- The support
apihelper.java
class
package support;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import com.jayway.jsonpath.JsonPath;
public class apihelper {
private final static String OUTPUT_FORMAT = """
{ "statusCode": "%s",
"message": "%s"
}
""";
private final String API_JSON_RULE="$.setup";
private final static String API_URL="https://official-joke-api.appspot.com/random_joke";
//"https://api.chucknorris.io/jokes/random"; //$.value
public String accessAPIForMessage(){
String output = "";
// Create an HttpClient instance
HttpClient client = HttpClient.newHttpClient();
// Create a HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_URL))
.GET()
.build();
try {
// Send the request and get the response
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
//System.out.println(response.body());
JsonPath jsonPath = JsonPath.compile(API_JSON_RULE);
// parse json string for the path
String apiResponseValue = jsonPath.read(response.body());
output = String.format(OUTPUT_FORMAT, response.statusCode(),apiResponseValue);
} catch (IOException | InterruptedException e) {
output = String.format(OUTPUT_FORMAT, "Err","Exception occurred accessing API endpoint");
e.printStackTrace();
throw new RuntimeException(e);
}
return output;
}
}
Additionally we can create an alias command and pass just the arguments
# assume the the main class is placed in /c/demo/ we use that in the alias command
alias demoappcli='jbang /c/demo/demoappcli.java'
- output with the alias command
$ demoappcli user-01 -p
Enter value for --password (Passphrase):
{ "username": "user-01","password-provided": "true" }
0
Subscribe to my newsletter
Read articles from Thirumurthi S directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
