CLI with Picocli: Call main command before sub command get called - java

I switched from Apache Commons CLI to Picocli because of the sub command support (and annotation-based declaration).
Consider a command line tool like git, with sub commands like push. Git have a main switch --verbose or -v for enable verbose mode in all sub commands.
How can I implement a main switch that is executed before any sub commands?
This is my test
#CommandLine.Command(name = "push",
description = "Update remote refs along with associated objects")
class PushCommand implements Callable<Void> {
#Override
public Void call() throws Exception {
System.out.println("#PushCommand.call");
return null;
}
}
#CommandLine.Command(description = "Version control", subcommands = {PushCommand.class})
public class GitApp implements Callable<Void> {
#CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Display this help message.")
private boolean usageHelpRequested;
#CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.")
private boolean verboseMode;
public static void main(String[] args) {
GitApp app = new GitApp();
CommandLine.call(app, "--verbose", "push");
System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
}
#Override
public Void call() throws Exception {
System.out.println("#GitApp.call");
return null;
}
}
Output is
#PushCommand.call
#GitApp.main after. verbose: true
I would expect, that GitApp.call get called before the sub command get called. But only the sub command get called.

The CommandLine.call (and CommandLine.run) methods only invoke the last subcommand by design, so what you are seeing in the original post is the expected behaviour.
The call and run methods are actually a shortcut. The following two lines are equivalent:
CommandLine.run(callable, args); // internally uses RunLast, equivalent to:
new CommandLine(callable).parseWithHandler(new RunLast(), args);
Update: from picocli 4.0, the above methods are deprecated, and replaced with new CommandLine(myapp).execute(args). The "handler" is now called the "execution strategy" (example below).
There is also a RunAll handler that runs all commands that were matched. The following main method gives the desired behaviour:
public static void main(String[] args) {
args = new String[] { "--verbose", "push" };
GitApp app = new GitApp();
// before picocli 4.0:
new CommandLine(app).parseWithHandler(new RunAll(), args);
// from picocli 4.0:
//new CommandLine(app).setExecutionStrategy(new RunAll()).execute(args);
System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
}
Output:
#GitApp.call
#PushCommand.call
#GitApp.main after. verbose: true
You may also be interested in the #ParentCommand annotation. This tells picocli to inject an instance of the parent command into a subcommand. Your subcommand can then call methods on the parent command, for example to check whether verbose is true. For example:
Update: from picocli 4.0, use the setExecutionStrategy method to specify RunAll. The below example is updated to use the new picocli 4.0+ API.
import picocli.CommandLine;
import picocli.CommandLine.*;
#Command(name = "push",
description = "Update remote refs along with associated objects")
class PushCommand implements Runnable {
#ParentCommand // picocli injects the parent instance
private GitApp parentCommand;
public void run() {
System.out.printf("#PushCommand.call: parent.verbose=%s%n",
parentCommand.verboseMode); // use parent instance
}
}
#Command(description = "Version control",
mixinStandardHelpOptions = true, // auto-include --help and --version
subcommands = {PushCommand.class,
HelpCommand.class}) // built-in help subcommand
public class GitApp implements Runnable {
#Option(names = {"-v", "--verbose"},
description = "Verbose mode. Helpful for troubleshooting.")
boolean verboseMode;
public void run() {
System.out.println("#GitApp.call");
}
public static void main(String[] args) {
args = new String[] { "--verbose", "push" };
GitApp app = new GitApp();
int exitCode = new CommandLine(app)
.setExecutionStrategy(new RunAll())
.execute(args);
System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
System.exit(exitCode);
}
}
Other minor edits: made the annotations a bit more compact by importing the inner classes. You may also like the mixinStandardHelpOptions attribute and the built-in help subcommand that help reduce boilerplate code.

As Picocli supports inheritance with Options I've extracted the --help and --verbose Option into an abstract class BaseCommand and invoke super.call from the subcommands.
abstract class BaseCommand implements Callable<Void> {
#CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Display this help message.")
private boolean usageHelpRequested;
#CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.")
private boolean verboseMode;
#Override
public Void call() throws Exception {
if (verboseMode) {
setVerbose();
}
return null;
}
private void setVerbose() {
System.out.println("enter verbose mode");
}
}
#CommandLine.Command(name = "push",
description = "Update remote refs along with associated objects")
class PushCommand extends BaseCommand {
#Override
public Void call() throws Exception {
super.call();
System.out.println("Execute push command");
return null;
}
}
#CommandLine.Command(description = "Version control", subcommands = {PushCommand.class})
public class GitApp extends BaseCommand {
public static void main(String[] args) {
GitApp app = new GitApp();
CommandLine.call(app, "push", "--verbose");
}
#Override
public Void call() throws Exception {
super.call();
System.out.println("GitApp.call called");
return null;
}
}

Related

Spring batch return custom process exit code

I have one jar with several jobs, I want to execute only one job each time and retrieve a custom exit code.
For example, I have basic job (retrieveErrorsJob) configuration with one step that will read an input XML file and write the data in specific database table.
Application class
#SpringBootApplication
#EnableBatchProcessing
#Import(CoreCommonsAppComponent.class)
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
private ConfigurationConstants constants;
#Autowired
public Application(ConfigurationConstants constants) {
this.constants = constants;
}
#EventListener(ApplicationStartedEvent.class)
public void idApplication()
{
logger.info("================================================");
logger.info(constants.APPLICATION_NAME() + "-v." + constants.APPLICATION_VERSION() + " started on " + constants.REMOTE_HOST());
logger.info("------------------------------------------------");
}
public static void main(String... args) throws Exception{
ApplicationContext context = SpringApplication.run(Application.class, args);
logger.info("================================================");
SpringApplication.exit(context);
}
}
I can choose one job from command line:
java -jar my-jar.jar --spring.batch.job.names=retrieveErrorsJob --input.xml.file=myfile.xml
Spring Batch starts the correct job.
The problem is that I need the jar to return a custom process exit integer like ExitCode.FAILED == 4 etc. But I always have a ZERO (if ExitCode = SUCCESS or FAILED).
As per the docs, I need to implement ExitCodeMapper interface.
Code (not finished)
public class CustomExitCodeMapper implements ExitCodeMapper {
private static final int NORMAL_END_EXECUTION = 1;
private static final int NORMAL_END_WARNING = 2;
private static final int ABNORMAL_END_WARNING = 3;
private static final int ABNORMAL_END_ERROR = 4;
#Override
public int intValue(String exitCode) {
System.out.println("EXIT CODE = " + exitCode);
switch (exitCode)
{
case "FAILED":
return ABNORMAL_END_WARNING;
default:
return NORMAL_END_EXECUTION;
}
}
}
I can't find a way to use this custom implementation. I could set the custom implementation to CommandLineJobRunner but how to use this class?
Thanks to #Mahendra I've got an idea :)
I've created a JobCompletionNotificationListener class as #Mahendra suggested:
#Component
public class JobCompletionNotificationListener extends JobExecutionListenerSupport {
private static final Logger logger = LoggerFactory.getLogger(JobCompletionNotificationListener.class);
#Override
public void afterJob(JobExecution jobExecution) {
SingletonExitCode exitCode = SingletonExitCode.getInstance();
if(jobExecution.getStatus() == BatchStatus.COMPLETED)
{
logger.info("Exit with code " + ExitCode.NORMAL_END_OF_EXECUTION);
exitCode.setExitCode(ExitCode.NORMAL_END_OF_EXECUTION);
}
else {
logger.info("Exit with code " + ExitCode.ABNORMAL_END_OF_EXECUTION_WARNING);
exitCode.setExitCode(ExitCode.ABNORMAL_END_OF_EXECUTION_WARNING);
}
}
}
But I don't force the application to exit with System.exit() from this class. I've implemented a simple singleton like this:
public class SingletonExitCode {
public ExitCode exitCode = ExitCode.ABNORMAL_END_OF_EXECUTION_WARNING; // Default code 3
private static SingletonExitCode instance = new SingletonExitCode();
private SingletonExitCode() {}
public static SingletonExitCode getInstance() {
return instance;
}
public void setExitCode(ExitCode exitCode) {
this.exitCode = exitCode;
}
}
and I ask the ExitCode from my singleton after closing Spring context:
#SpringBootApplication
#EnableBatchProcessing
#Import(CoreCommonsAppComponent.class)
public class Application {
// a lot of nice things
public static void main(String... args) throws Exception{
ApplicationContext context = SpringApplication.run(Application.class, args);
logger.info("================================================");
SpringApplication.exit(context);
System.exit(SingletonExitCode.getInstance().exitCode.getCode());
}
}
I did this because if we exit directly from JobCompletionNotificationListener class we miss an important line in the logs:
Job: [FlowJob: [name=writeErrorFromFile]] completed with the following parameters: [{-input.xml.file=c:/temp/unit-test-error.xml, -spring.batch.job.names=writeErrorFromFile, run.id=15, input.xml.file=c:/temp/unit-test-error.xml}] and the following status: [FAILED]
And seems that Spring context is not properly closed.
Despite of exit-status of Sprint-Batch's Job (i.e. COMPLETED or FAILED), java process will be completed successfully (and you will get process exit-code as 0).
If you want a custom exit-code for java process so that you can use it any script or somewhere else, you can use JobExecutionListener.
You can check the job's exitStatus in afterJob() and accordingly exit the java process with your desired exit-code (i.e. 4 for FAILURE)
Example of JobExecutionListener
public class InterceptingExitStatus implements JobExecutionListener{
#Override
public void beforeJob(JobExecution jobExecution) {
}
#Override
public void afterJob(JobExecution jobExecution) {
ExitStatus exitStatus = jobExecution.getExitStatus() ;
if(exitStatus == ExitStatus.COMPLETED ){
System.exit(0);
}
if(exitStatus == ExitStatus.FAILED ){
System.exit(4);
}
}
}
and this is how you can configure job-listener in the xml file -
<job id="job">
....
....
<listeners>
<listener ref="interceptingExitStatus "/>
</listeners>
</job>
Spring Boot and Sring Batch already have an internal solution for this, all you need is an extra line of code:
System.exit(SpringApplication.exit(applicationContext));
Here is another example:
public class BatchApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(BatchApplication.class, args);
System.exit(SpringApplication.exit(applicationContext));
}
}
EDIT: If you would like to know how it works check this class: org.springframework.boot.autoconfigure.batch.JobExecutionExitCodeGenerator

How to dynamically handle commands in a chat program?

My question is more of a design issue than anything else. I'm currently developing a classic Server-Client chat program in Java. Everything is fine until I get to the commands. I thought it would be convenient for users to send commands that would then be treated by the server for changing their nickname for example. The thing is I want to make flexible code and above all, object-oriented code. To avoid endless if/else if statements to know what command was typed I believe it would be better to create a class for each command which inherit from a superclass Command. Then I could return the specific command through a getCommand() function overriden in all subclasses. But it does not solve my problem at all. The server still needs to test with instanceof what command has been returned. One way to do it dynamically would be to sort of auto downcasting it from the superclass Command and then call the appropriate function in the server class. For example:
public void processCommand(CommandNick c) {}
public void processCommand(CommandKick c) {}
But I haven't found any proper way of doing that and even if I did, I feel like there's still a design issue here. And I am convinced there is a nice and flexible way to do it but days weren't enough for me to figure it out. Any ideas? Thanks in advance! :)
I assume your server receives the message as an Object with a Sender and a String. Create your Command classes, and in the server init code, make a HashMap<String, AbstractCommand> with a String as key and your AbstractCommand class as value. Your commands should extend this class. Register all your commands, like so:
commandRegistry.put("help", new HelpCommandHandler());
I assume a command is a message with a ! before it. So when you receive a message, check if it is a command:
Message message = (Your Message)
String messageBody = message.getBody();
Sender messageSender = message.getSender();
if(messageBody.startsWith("!")) {
// Split the message after every space
String[] commandParts = messageBody.split(" ");
// The first element is the command base, like: !help
String baseCommand = commandParts[0];
// Remove the first character from the base, turns !help into help
baseCommand = baseCommand.substring(1, baseCommand.length());
// Creates a new array for the arguments. The length is smaller, because we won't copy the command base
String[] args = new String[commandParts.length - 1];
// Copy the elements of the commandParts array from index 1 into args from index 0
if(args.length > 0) {
System.arraycopy(commandParts, 1, args, 0, commandParts.length - 1);
}
// Your parse method
processCommand(sender, baseCommand, args);
}
public void processCommand(Sender sender, String base, String[] args) {
if(commandRegistry.containsKey(base)) {
commandRegistry.get(base).execute(sender, args);
} else {
// Handle unknown command
}
}
public abstract class AbstractCommand {
public abstract void execute(Sender sender, String[] args);
}
Sample implementation. I assume your server is a Singleton, and you can get on Object of it with Server.get() or any similar method.
public class HelpCommandHandler extends AbstractCommand { /* !help */
#Override
public void execute(Sender sender, String[] args) {
sender.sendMessage("You asked for help."); // Your code might not work like this.
}
}
public class ChangeNickCommandHandler extends AbstractCommand { /* !changenick newNick */
#Override
public void execute(Sender sender, String[] args) {
// I assume you have a List with connected players in your Server class
String username = sender.getUsername(); // Your code might not work like this
Server server = Server.get(); // Get Server instance
server.getUsers().get(username).setNickname(args[0]); // Argument 0. Check if it even exists.
}
}
// Server class. If it isn't singleton, you can make it one like this:
public class Server {
private static Server self;
public static Server init(/* Your args you'd use in a constructor */) { self = new Server(); return get(); }
public static Server get() { return self; }
private List<User> users = new List<User>();
private HashMap<String, AbstractCommand> commandRegitry = new HashMap<>();
// Make construcor private, use init() instead.
private Server() {
commandRegistry.put("help", new HelpCommandHandler());
commandRegistry.put("changenick", new ChangeNickCommandHandler());
}
// Getters
public List<User> getUsers() {
return users;
}
public HashMap<String, AbstractCommand> getRegistry() {
return commandRegistry;
}
}
This is a bit of pseudo code to illustrate that your controller doesn't need to know about the command processors (no need for instanceof).
abstract class CommandProcessor {
/* return boolean if this Command processed the request */
public static boolean processCommand(String command, User user, Properties chatProperties, Chat chat);
}
/* Handle anything */
public class CommandRemainder extends CommandProcessor {
#Override
public static boolean processCommand(String command, User user, Properties chatProperties, Chat chat) {
chat.appendText("[" + user.getName() + "] " + command);
return true;
}
}
/* Handle color changing */
public class CommandColorizer extends CommandProcessor {
protected static List<String> ALLOWED_COLORS = new ArrayList<>(Arrays.asList("red", "blue", "green"));
#Override
public static boolean processCommand(String command, User user, Properties chatProperties, Chat chat) {
if ("fg:".equals(command.trim().substring(0,3)) {
String color = command.trim().substring(3).trim();
if (ALLOWED_COLORS.contains(color)) {
chat.setForeground(color);
}
return true;
}
return false;
}
}
public class ChatController {
protected Chat chat = new Chat();
protected User user = getUser();
protected Properties chatProperties = getChatProperties();
protected List<CommandProcessor> commandProcessors = getCommandProcessors();
{
chat.addChatListener(new ChatListener(){
#Override
public void userChatted(String userChatString) {
for (CommandProcessor processor : commandProcessors) {
if (processor.processCommand(userChatString, user, chatProperties, chat)) {
break;
}
}
}
});
}
List<CommandProcessor> getCommandProcessors() {
List<CommandProcessor> commandProcessors = new ArrayList<>();
commandProcessors.add(new CommandColorizer());
commandProcessors.add(new CommandRemainder()); // needs to be last
return commandProcessors;
}
}

How to print help using jcommander?

How to print help using jcommander?
I couldn't find an API for this.
Find this small snippet to show the application help. Fore simplicity everthing was done in one class.
public class JCommanderExample {
#Parameter(names = "-debug", description = "Debug mode")
private boolean debug = false;
#Parameter(names = "--help", help = true)
private boolean help = false;
public static void main(String[] args) {
JCommanderExample jct = new JCommanderExample();
JCommander jCommander = new JCommander(jct, args);
jCommander.setProgramName("JCommanderExample");
if (jct.help) {
jCommander.usage();
return;
}
System.out.println("your logic goes here");
}
}
If you run the snippet with parameter --help the output will be
Usage: JCommanderExample [options]
Options:
--help
Default: false
-debug
Debug mode
Default: false
With the newer version of JCommander you need to create a instantiation of JCommander.
For example the main is:
public class Usage {
public static void main(String...argv) {
Args args = new Args();
JCommander jct = JCommander.newBuilder().addObject(args).build();
jct.parse(argv);
if (args.isHelp()) {
jct.usage();
}
}
}
With a Args Class like that (if you not define your parameter in the Main):
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
public class Args {
#Parameter(names = { "--help", "-h" }, help = true)
private boolean help = false;
public boolean isHelp() {
return help;
}
}

error: <identifier> expected

When I try to compile the Report.java I'm getting an error on line 6 saying: error: <identifier> expected aClient.setClientName("Michael"); with and arrow pointing to the first parenthese.
public class Client {
private String _clientName;
public String getClientName(){
return _clientName;
}
public void setClientName(String clientName){
_clientName = clientName;
}
}
public class Report {
Client aClient = new Client();
//ClientLawn aClientLawn = new ClientLawn();
aClient.setClientName("Michael");
//aClientLawn.setLawnWidth(10);
//aClientLawn.setLawnLength(10);
public void output(){
System.out.println(aClient.getClientName());
//System.out.println(aClientLawn.calcLawnSize());
}
}
I also want to make note that I am new to Java so please be gentle.
This line should be put into an initializer block:
{
aClient.setClientName("Michael");
}
So it it executed after creating the aClient.
The code here is run for every instance of the Report. Unfortunately you cannot set parameters to it. If you want to do so, put this block into the constructor:
public Report (String clientName) {
aClient.setClientName(clientName);
//aClientLawn.setLawnWidth(10);
//aClientLawn.setLawnLength(10);
}
Use instance initialization block.
public class Report {
Client aClient = new Client();
//ClientLawn aClientLawn = new ClientLawn();
{
aClient.setClientName("Michael");
//aClientLawn.setLawnWidth(10);
//aClientLawn.setLawnLength(10);
}
...
}
As everybody else pointed out, you cannot execute code outside of a method, so the following lines are illegal:
Client aClient = new Client();
aClient.setClientName("Michael");
They need to be wrapped within a method, such as the class' constructor:
public class Report {
public Report() {
Client aClient = new Client();
aClient.setClientName("Michael");
}
// ....
}
It looks like you want this code to be executable though, in which case you want to put all that in a main method such as:
public class Report {
public static void main(String... args) {
Client aClient = new Client();
aClient.setClientName("Michael");
System.out.println(aClient.getName());
}
}
You can then compile and execute the Report class.

Close another application from Java but not started by the present Java application

Basically if I start a Notepad by going to start->accessories->notepad then my Java program should close this.
Is it possible?
If not in Java, any other language?
If you know PID of a process use below code to kill a application from Java:
Runtime.getRuntime().exec("taskkill /F /PID <process_id>");
where, is your process id, /F is used to force kill. Note this works only if you're using windows.
From here you can expand this to work for getting process id dynamically and killing instead of hard-coding.
Using system property os.name you can get name of os and change command accordingly.
In addition to the post above, you can filter out the PID with this code(donĀ“t know if it can work more simple)
This code does list all Processes, and you can filter out some specific one, if you want
package runtime;
import java.util.Scanner;
public class Process_Check {
public static void main(String[] args) throws Exception {
Processes.ALL_PROCESSES.listProcesses();
}
public static enum Processes implements IProcessListingStrategy {
ALL_PROCESSES;
private IProcessListingStrategy processListing = selectProcessListingStrategy();
public void listProcesses() throws Exception {
processListing.listProcesses();
}
private IProcessListingStrategy selectProcessListingStrategy() {
//todo add support for mac ...
return isWindows() ? new WinProcessListingStrategy() : new LinuxProcessListingStrategy();
}
private static boolean isWindows() {
return System.getProperty("os.name").toLowerCase().indexOf("win") >= 0;
}
}
static interface IProcessListingStrategy {
void listProcesses() throws Exception;
}
static abstract class AbstractNativeProcessListingStrategy implements IProcessListingStrategy {
public void listProcesses() throws Exception {
Process process = makeProcessListingProcessBuilder().start();
Scanner scanner = new Scanner(process.getInputStream());
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
scanner.close();
process.waitFor();
}
protected abstract ProcessBuilder makeProcessListingProcessBuilder();
}
static class WinProcessListingStrategy extends AbstractNativeProcessListingStrategy {
#Override
protected ProcessBuilder makeProcessListingProcessBuilder() {
return new ProcessBuilder("cmd", "/c", "tasklist");
}
}
static class LinuxProcessListingStrategy extends AbstractNativeProcessListingStrategy {
#Override
protected ProcessBuilder makeProcessListingProcessBuilder() {
return new ProcessBuilder("ps", "-e");
}
}
}

Categories

Resources