I have two options (-n and -t) under a command where if -n is used, then -t is required, but bothare not required. However, I keep getting an error about
I am trying to send the options as a parameter to another method (with business logic) as a parameter.
Valid Usage:
agent.bat install -n -t <blahblah>
agent.bat install -t <blahblah> -n
agent.bat install -t <blah blah>
agent.bat install -t <----This is on interactive so it would ask for a parameter later
Invalid Usage:
agent.bat install -n
agent.bat install -n -t
Current output with valid usage:
agent.bat install -t
Missing required parameter: '<arg0>'
Usage: agent install [-hV] <arg0>
Setup or update the agent service program by install token.
<arg0>
public class Agent implements Callable<Integer> {
static class InstallArgs {
#Option(names = {"-t", "--token"},
order = 0,
arity = "0..1",
interactive = true,
description = "The agent install token.",
required = true) String installToken ;
#Option(names = {"-n", "--noninteractive"},
order = 1,
description = "Sets installation to non-interactive",
required = false) boolean nonInteractive ;
public String toString() {
return String.format("%s,%s", installToken, nonInteractive);
}
}
private static String[] programArgs;
#ArgGroup(exclusive = false, multiplicity = "1")
#CommandLine.Command(name = AgentCommand.INSTALL_COMMAND, mixinStandardHelpOptions = true,
description = "Setup or update the agent service program by install token.")
void install(InstallArgs installArgs) {
String[] installArgsValues = installArgs.toString().split(",");
String installToken = installArgsValues[0];
boolean nonInteractive = Boolean.parseBoolean(installArgsValues[1]);
IcbProgram.initProgramMode(ProgramMode.INSTALL);
MainService mainService = MainService.createInstallInstance(configFile, agentUserFile, backupAgentUserFile, installToken, nonInteractive);
}
public static void main(String... args) {
if (ArgumentValidator.validateArgument(args)) {
programArgs = args;
int exitCode = new CommandLine(new Agent()).execute(args);
System.exit(exitCode);
} else
//Exit with usage error
System.exit(ExitCode.USAGE);
}
}
Can you try using arity=1 for installToken?
static class InstallArgs {
#Option(names = {"-t", "--token"},
order = 0,
arity = "1",
interactive = true,
description = "The agent install token.",
required = true) String installToken ;
Related
When I pass arguments to my program to be parsed by the CommandLineParser, it doesn't take my arguments from passed to the main method.
The code:
public class Main {
private static final Option ARG_HELP = new Option("h", "help", false, "HELP - Prints command line arguments and their uses");
private static final Option ARG_SERVER = new Option("s", "server", true, "SERVER_URL - Full HTTP(s) url of the server");
private static final Option ARG_USER = new Option("u", "user", true, "USERNAME - Username used for authentication");
private static final Option ARG_PASSWORD = new Option("p", "password", true, "PASSWORD - Password used for authentication");
private static final Option ARG_TARGET = new Option("t", "target", true, "TARGET_DIR - Base directory used for pushing or pulling files");
private static final Option ARG_PUSH = new Option("push", "push", false, "PUSH - Push xml config to server");
private static final Option ARG_PULL = new Option("pull", "pull", false, "PULL - pull xml config from server");
public static void main(String[] args) {
CommandLineParser clp = new DefaultParser();
Options options = new Options();
options.addOption(ARG_HELP);
options.addOption(ARG_SERVER);
options.addOption(ARG_USER);
options.addOption(ARG_PASSWORD);
options.addOption(ARG_TARGET);
options.addOption(ARG_PUSH);
options.addOption(ARG_PULL);
try{
CommandLine cl = clp.parse(options, args);
if(cl.getArgList().size() < 5){
ArrayList<String> arguments = new ArrayList<String>(cl.getArgList());
}
else{
printHelp(options);
System.exit(0);
}
}
catch(Exception e){
e.printStackTrace();
}
finally{
System.exit(0);
}
}
When I set a breakpoint at the if statement and check my cl object, the args are 0. But when I look at args passed in the clp.parse(options, args) method they're the arguments that I passed when I ran the program.
Example arguments passed: -s https://localhost:8443 -u admin -p admin -t C:\users\admin -pull
That is the correct behaviour.
According to the CommandLine.getArgList() JavaDoc:
Retrieve any left-over non-recognized options and arguments
Since all the arguments from your command line are recognized as options and arguments there are no left-over arguments.
I want to execute a command with the terminal in linux. Now in order to do this I can't hard code cmd like I do windows. How do I get the terminal name programatically as String from java?
new ProcessBuilder(new String[] {"xfce4-terminal", "--title="+windowTitle, "--hold", "-x", "java", "-jar", decodedPath, "run"}).start();
Notice the string "xfce4-terminal". This changes depending on what distribution of linux they have. What reliable way is there to get the terminal exe for java commands. In my opinion it should by System.getProperty("os.terminal") but, that doesn't exist.
here is a cross platform way to determine the os terminal. supports windows, mac and linux
public static String osName = System.getProperty("os.name");
public static String[] windows_terminals = new String[]
{
"cmd",
"powershell",//seems to freak out and seems to be beta even in 2020 with all it's bugs
};
public static String[] mac_terminals = new String[]
{
"bin/bash"
};
public static String[] linux_terminals = new String[]
{
"/usr/bin/gcm-calibrate",
"/usr/bin/gnome-terminal",
"/usr/bin/mosh-client",
"/usr/bin/mosh-server",
"/usr/bin/mrxvt",
"/usr/bin/mrxvt-full",
"/usr/bin/roxterm",
"/usr/bin/rxvt-unicode",
"/usr/bin/urxvt",
"/usr/bin/urxvtd",
"/usr/bin/vinagre",
"/usr/bin/x-terminal-emulator",
"/usr/bin/xfce4-terminal",
"/usr/bin/xterm",
"/usr/bin/aterm",
"/usr/bin/guake",
"/usr/bin/Kuake",
"/usr/bin/rxvt",
"/usr/bin/rxvt-unicode",
"/usr/bin/Terminator",
"/usr/bin/Terminology",
"/usr/bin/tilda",
"/usr/bin/wterm",
"/usr/bin/Yakuake",
"/usr/bin/Eterm",
"/usr/bin/gnome-terminal.wrapper",
"/usr/bin/koi8rxterm",
"/usr/bin/konsole",
"/usr/bin/lxterm",
"/usr/bin/mlterm",
"/usr/bin/mrxvt-full",
"/usr/bin/roxterm",
"/usr/bin/rxvt-xpm",
"/usr/bin/rxvt-xterm",
"/usr/bin/urxvt",
"/usr/bin/uxterm",
"/usr/bin/xfce4-terminal.wrapper",
"/usr/bin/xterm",
"/usr/bin/xvt"
};
public static String getTerminal()
{
String[] cmds = getTerminals(osName);
for(String cmd : cmds)
{
try
{
Runtime.getRuntime().exec(cmd + " cd " + System.getProperty("user.dir"));
return cmd;
}
catch (Throwable e) {}
}
return null;
}
public static String[] getTerminals(String os)
{
return os.contains("windows") ? windows_terminals : os.contains("mac") ? mac_terminals : os.contains("linux") ? linux_terminals : null;
}
I would like to parse options with picocli in the following format:
application -mode CLIENT -c aaaa -d bbbb
application -mode SERVER -e xxxx -f yyyy
mode is an enum with values { CLIENT, SERVER }
If mode == CLIENT, -c and -d options are mandatory, and -e, -f must not be used.
If mode == SERVER, -e and -f options are mandatory, and -c, -d must not be used.
In other words, I would like to choose the required options based on a key option. Is this possible in picocli?
Yes, this is possible. One way is simple programmatic validation:
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParameterException;
import picocli.CommandLine.Spec;
import java.util.Objects;
import java.util.function.Predicate;
#Command(name = "application", mixinStandardHelpOptions = true)
public class MyApp implements Runnable {
enum Mode {CLIENT, SERVER}
#Option(names = "-mode", required = true)
Mode mode;
#Option(names = "-c") String c;
#Option(names = "-d") String d;
#Option(names = "-e") String e;
#Option(names = "-f") String f;
#Spec CommandSpec spec;
public static void main(String[] args) {
System.exit(new CommandLine(new MyApp()).execute(args));
}
#Override
public void run() {
validateInput();
// business logic here...
}
private void validateInput() {
String INVALID = "Error: option(s) %s cannot be used in %s mode";
String REQUIRED = "Error: option(s) %s are required in %s mode";
if (mode == Mode.CLIENT) {
check(INVALID, "CLIENT", Objects::isNull, e, "-e", f, "-f");
check(REQUIRED, "CLIENT", Objects::nonNull, c, "-c", d, "-d");
} else if (mode == Mode.SERVER) {
check(INVALID, "SERVER", Objects::isNull, c, "-c", d, "-d");
check(REQUIRED, "SERVER", Objects::nonNull, e, "-e", f, "-f");
}
}
private void check(String msg, String param, Predicate<String> validator, String... valuesAndLabels) {
String desc = "";
String sep = "";
for (int i = 0; i < valuesAndLabels.length; i += 2) {
if (validator.test(valuesAndLabels[i])) {
desc = sep + valuesAndLabels[i + 1];
sep = ", ";
}
}
if (desc.length() > 0) {
throw new ParameterException(spec.commandLine(), String.format(msg, desc, param));
}
}
}
Alternatively, if you are willing to change your requirements a little bit, we can use picocli's argument groups for a more declarative approach:
import picocli.CommandLine;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
#Command(name = "application", mixinStandardHelpOptions = true)
public class MyApp2 implements Runnable {
static class ClientArgs {
#Option(names = "-clientMode", required = true) boolean clientMode;
#Option(names = "-c", required = true) String c;
#Option(names = "-d", required = true) String d;
}
static class ServerArgs {
#Option(names = "-serverMode", required = true) boolean serverMode;
#Option(names = "-e", required = true) String e;
#Option(names = "-f", required = true) String f;
}
static class Args {
#ArgGroup(exclusive = false, multiplicity = "1", heading = "CLIENT mode args%n")
ClientArgs clientArgs;
#ArgGroup(exclusive = false, multiplicity = "1", heading = "SERVER mode args%n")
ServerArgs serverArgs;
}
#ArgGroup(exclusive = true, multiplicity = "1")
Args args;
public static void main(String[] args) {
System.exit(new CommandLine(new MyApp2()).execute(args));
}
#Override
public void run() {
// business logic here...
}
}
When invoked with just -serverMode, this second example will show this error message, followed by the usage help message:
Error: Missing required argument(s): -e=<e>, -f=<f>
Usage: application ((-clientMode -c=<c> -d=<d>) | (-serverMode -e=<e> -f=<f>))
...
Note that this declarative approach cannot be achieved with a single -mode option: each argument group needs its own option (I chose -clientMode and -serverMode in this example). This allows the picocli parser to figure out which options must occur together and which are mutually exclusive.
I'm trying to deploy a Java based chaincode in "first-network" sample.
The code is generated with IBM Blockchain Platform plugin for VSCode.
It works in the local environment (Using the VSCode plugin to install, invoke,...), but when I try to test the chaincode in the "first-network" sample, it crashes.
Local Environment:
peer0.org1.example.com
ca.org1.example.com
orderer.example.com
First Network Environment:
cli
peer0.org2.example.com
peer1.org2.example.com
peer0.org1.example.com
peer1.org1.example.com
orderer.example.com
couchdb2
couchdb1
couchdb3
couchdb0
ca.example.com
I have two classes:
SimpleAsset.java
/*
* SPDX-License-Identifier: Apache-2.0
*/
package org.example;
import org.hyperledger.fabric.contract.annotation.DataType;
import org.hyperledger.fabric.contract.annotation.Property;
import org.json.JSONObject;
#DataType()
public class SimpleAsset {
#Property()
private String value;
public SimpleAsset(){
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String toJSONString() {
return new JSONObject(this).toString();
}
public static SimpleAsset fromJSONString(String json) {
String value = new JSONObject(json).getString("value");
SimpleAsset asset = new SimpleAsset();
asset.setValue(value);
return asset;
}
}
SimpleAssetContract.java
/*
* SPDX-License-Identifier: Apache-2.0
*/
package org.example;
import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.Contract;
import org.hyperledger.fabric.contract.annotation.Default;
import org.hyperledger.fabric.contract.annotation.Transaction;
import org.hyperledger.fabric.contract.annotation.Contact;
import org.hyperledger.fabric.contract.annotation.Info;
import org.hyperledger.fabric.contract.annotation.License;
import static java.nio.charset.StandardCharsets.UTF_8;
#Contract(name = "SimpleAssetContract",
info = #Info(title = "SimpleAsset contract",
description = "My Smart Contract",
version = "0.0.1",
license =
#License(name = "Apache-2.0",
url = ""),
contact = #Contact(email = "SimpleAsset#example.com",
name = "SimpleAsset",
url = "http://SimpleAsset.me")))
#Default
public class SimpleAssetContract implements ContractInterface {
public SimpleAssetContract() {
}
#Transaction()
public boolean simpleAssetExists(Context ctx, String simpleAssetId) {
byte[] buffer = ctx.getStub().getState(simpleAssetId);
return (buffer != null && buffer.length > 0);
}
#Transaction()
public void createSimpleAsset(Context ctx, String simpleAssetId, String value) {
boolean exists = simpleAssetExists(ctx,simpleAssetId);
if (exists) {
throw new RuntimeException("The asset "+simpleAssetId+" already exists");
}
SimpleAsset asset = new SimpleAsset();
asset.setValue(value);
ctx.getStub().putState(simpleAssetId, asset.toJSONString().getBytes(UTF_8));
}
#Transaction()
public SimpleAsset readSimpleAsset(Context ctx, String simpleAssetId) {
boolean exists = simpleAssetExists(ctx,simpleAssetId);
if (!exists) {
throw new RuntimeException("The asset "+simpleAssetId+" does not exist");
}
SimpleAsset newAsset = SimpleAsset.fromJSONString(new String(ctx.getStub().getState(simpleAssetId),UTF_8));
return newAsset;
}
#Transaction()
public void updateSimpleAsset(Context ctx, String simpleAssetId, String newValue) {
boolean exists = simpleAssetExists(ctx,simpleAssetId);
if (!exists) {
throw new RuntimeException("The asset "+simpleAssetId+" does not exist");
}
SimpleAsset asset = new SimpleAsset();
asset.setValue(newValue);
ctx.getStub().putState(simpleAssetId, asset.toJSONString().getBytes(UTF_8));
}
#Transaction()
public void deleteSimpleAsset(Context ctx, String simpleAssetId) {
boolean exists = simpleAssetExists(ctx,simpleAssetId);
if (!exists) {
throw new RuntimeException("The asset "+simpleAssetId+" does not exist");
}
ctx.getStub().delState(simpleAssetId);
}
}
I don't know if I'm doing it right. The steps I'm following are:
$ ./byfn.sh up -s couchdb -l java # Deploy the network with Couchdb and Java
$ cp -r SimpleAsset fabric-samples/chaincode/ # This is the chaincodes path in the docker
$ docker exec -it cli bash # Go to the Cli # We go inside the docker
$ /opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode install -n sa01 -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/SimpleAsset/ # Install the SimpleAsset chaincode -> OK!
$ /opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sa01 -l java -v 1.0 -c '{"Args":[]}' -P 'AND ('\''Org1MSP.peer'\'','\''Org2MSP.peer'\'')'
Error: could not assemble transaction, err proposal response was not successful, error code 500, msg chaincode registration failed: container exited with 1
What am I doing wrong? How could I solve this?
There is a problem with the java fabric-shim version 1.4.2 which means if you declare a dependency on that version it will fail to instantiate. Check your pom.xml or build.gradle file to see which version is being used and use version 1.4.4 or later (currently only 1.4.4 is available now but there are plans for further releases)
I would like to provide a search string for my program like:
cmd.execute("getDevices", "-h 1.2.3.4", "-p myPSW", "-u myUser", "-n red|blue&black,-nonprod");
I want to create predicates to search for hostNames that contain red OR blue AND Black, but NOT nonprod. It is unclear to me how to go about parsing this the logical operators along with the Strings in Picocli to create a Predicate. Is there a simple and Straight forward way to parse a String to a predicate?
My CLI is set up as follows:
#Command(name = "HostMagicCLI", mixinStandardHelpOptions = true,
version = "1.0",
description = "Do Stuff With Hosts"
,
subcommands = {TufinDevices.class}
)
public class HostMagicCLI implements Runnable {
public static void main(String[] args) {
CommandLine cmd = new CommandLine(new InterfaceMagicCLI());
cmd.setExecutionStrategy(new RunAll());
cmd.getHelpSectionMap().put(SECTION_KEY_COMMAND_LIST, new MyCommandListRenderer());
cmd.usage(System.out);
cmd.execute("getDevices", "-h1.2.3.4", "-p myPSW", "-u myUser", "-n red|blue&black");
}
#Override
public void run() {
System.out.println("Running..");
}
}
#Command(name = "getDevices", aliases = {"l"}, description = "SpecifyTufin Credentials", subcommands = {InterfaceCommand.class})
class TufinDevices implements Runnable {
.
.//Options to collect user,psw, host etc.
.
#CommandLine.Option(names = {"-n", "--n"}, split = ",", arity = "0..*", description = "Hostname Contains")
String[] hostNameContains;
private void filter(TufinDeviceCollection<TufinDevice> devices) {
if (hostNameContains != null) {
Predicate< ? super TufinDevice> deviceFilter = device -> Arrays.stream(hostNameContains)
.allMatch(input -> device.getHostName().toLowerCase().contains(input.toLowerCase()));
devices = devices.stream()
.sequential()
.filter(deviceFilter)
.collect(Collectors.toCollection(TufinDeviceCollection<TufinDevice>::new));
}
#Override
public void run() {
try {
TufinDeviceCollection<TufinDevice> FETCH_DEVICES = Tufin.FETCH_DEVICES(user.trim(), password.trim(), hostName.trim());
this.filter(FETCH_DEVICES);
} catch (IOException | NoSuchAlgorithmException | KeyManagementException | IPConverter.InvalidIPException ex) {
Logger.getLogger(TufinDevices.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
I suspect you may want to use a library for parsing the string that the end user specifies as the filter expression (the -n parameter). It may be an idea to look at libraries like Spring Expression Language, OGNL, JXPath, there may be others. Alternatively, if it is easy to write such a filter in Groovy or BeanShell, these languages can be called from Java, so you can call that filter from the Java command.
CAUTION:
I notice the example passes parameter to the picocli parser like this:
cmd.execute("getDevices", "-h 1.2.3.4", "-p myPSW", "-u myUser", "-n red|blue&black,-nonprod");
This will probably give an error explaining that "there is no -p myPSW option defined".
In your testing, if you call the execute method directly, make sure to pass parameters separately like this:
cmd.execute("getDevices", "-h", "1.2.3.4", "-p", "myPSW", "-u", "myUser", "-n", "red|blue&black,-nonprod");