package hip.util; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import joptsimple.*; import org.apache.hadoop.util.ToolRunner; import java.io.IOException; import java.io.PrintStream; import java.util.Map; /** * A command-line tool for making calls in the HAServiceProtocol. * For example,. this can be used to force a service to standby or active * mode, or to trigger a health-check. */ public final class Cli { /** * Output stream for errors. */ private final PrintStream errOut = System.err; private final OptionParser parser; private final String[] args; private OptionSet options; private Cli(final OptionParser parser, final String[] args) { this.parser = parser; this.args = args; } /** * Write the usage to stderr. * * @throws IOException if writing fails */ public void printUsage() throws IOException { parser.printHelpOn(errOut); errOut.println(); ToolRunner.printGenericCommandUsage(errOut); } /** * Parse the arguments. * * @return 0 if the arguments were parsed, otherwise non-zero * @throws IOException on error */ public int runCmd() throws IOException { boolean parseException = false; options = null; try { options = parser.parse(args); } catch (OptionException e) { e.printStackTrace(); parseException = true; } if (parseException || options.has("help")) { printUsage(); return 1; } return 0; } public String getArgValueAsString(ArgInfo opt) { return (String) options.valueOf(opt.getArgName()); } public Integer getArgValueAsInt(ArgInfo opt) { String s = getArgValueAsString(opt); if (s == null) { return null; } return Integer.valueOf(s); } public String getArgValueAsString(ArgGetter opt) { return (String) options.valueOf(opt.getArgInfo().getArgName()); } public Integer getArgValueAsInt(ArgGetter opt) { String s = getArgValueAsString(opt); if (s == null) { return null; } return Integer.valueOf(s); } public boolean doesArgExist(ArgInfo opt) { return options.has(opt.getArgName()); } public boolean doesArgExist(ArgGetter opt) { return options.has(opt.getArgInfo().getArgName()); } public static CliBuilder builder() { return new CliBuilder(); } public static interface ArgInfo { String getArgName(); String getArgDescription(); boolean isRequired(); boolean hasArg(); } public static interface ArgGetter { ArgInfo getArgInfo(); } public static class ArgInfoImpl implements ArgInfo { private final String argName; private final boolean hasArgument; private final boolean isRequired; private final String description; public ArgInfoImpl(String argName, boolean hasArgument, boolean isRequired, String description) { Preconditions.checkNotNull(argName); this.argName = argName.toLowerCase().replaceAll("_", "-"); this.hasArgument = hasArgument; this.isRequired = isRequired; this.description = description; } @Override public String getArgName() { return argName; } @Override public String getArgDescription() { return description; } @Override public boolean isRequired() { return isRequired; } @Override public boolean hasArg() { return hasArgument; } } public static class ArgBuilder { private String argName; private boolean hasArgument; private boolean isRequired; private String description; public static ArgBuilder builder() { return new ArgBuilder(); } public ArgBuilder setArgName(String argName) { this.argName = argName; return this; } public ArgBuilder hasArgument(boolean hasArgument) { this.hasArgument = hasArgument; return this; } public ArgBuilder required(boolean isRequired) { this.isRequired = isRequired; return this; } public ArgBuilder description(String description) { this.description = description; return this; } public ArgInfo build() { return new ArgInfoImpl(argName, hasArgument, isRequired, description); } } public static class CliBuilder { private String[] args; private ImmutableMap.Builder<String, ArgInfo> opts = ImmutableMap.builder(); public CliBuilder setArgs(String[] args) { this.args = args; return this; } public CliBuilder addOption(ArgInfo argInfo) { opts.put(argInfo.getArgName(), argInfo); return this; } public CliBuilder addOptions(ArgInfo... argInfos) { for (ArgInfo argInfo : argInfos) { addOption(argInfo); } return this; } public CliBuilder addOptions(ArgGetter... getters) { for (ArgGetter gettter : getters) { addOption(gettter.getArgInfo()); } return this; } public Cli build() { OptionParser parser = new OptionParser(); Map<String, ArgInfo> options = opts.build(); for (ArgInfo opt : options.values()) { OptionSpecBuilder builder = parser.accepts(opt.getArgName()); if (opt.hasArg()) { ArgumentAcceptingOptionSpec<String> optSpec = builder.withRequiredArg(); if (opt.isRequired()) { optSpec.required(); } optSpec.describedAs(opt.getArgDescription()); } } parser.accepts("help").forHelp(); parser.allowsUnrecognizedOptions(); return new Cli(parser, args); } } }