Vert.x命令行接口API
Vert.x内核模块提供一个用向程序传递命令行参数的API。此API也可以打印出命令行工具可获取选项的详细帮助信息。既使此特性与Vert.x核心主题相差甚远,此AP在fat jar的Launcher类中使用,并且也在vertx命令行工具中使用。此外此API支持多语言(在支持的语言中使用)并且在Vert.x Shell中被使用。
Vert.x CLI提供了用于描述命令行接口的模型和一个解析器。此解析器支持不同的语法:
-
类POSIX选项(如tar–zxvf foo.tar.gz)
-
类GNU长选项(如du–human-readable –max-depth=1)
-
类似Java属性(如. java-Djava.awt.headless=true -Djava.net.useSystemProxies=true Foo)
-
伴有附加的值的短选项(例gcc -O2 foo.c)
-
但一连字号的长选项(如ant –projecthelp)
使用CLI API有三步骤:
-
命令行接口的定义
-
用户命令行的解析
-
查询和提问
定义阶段
每个命令号接口必须定义一组将被使用的选项和参数。CLI AIP使用Option和Argument类描术选项和参数:
CLI cli = CLI.create("copy")
.setSummary("A command line interface to copy files.")
.addOption(new Option()
.setLongName("directory")
.setShortName("R")
.setDescription("enables directory support")
.setFlag(true))
.addArgument(new Argument()
.setIndex(0)
.setDescription("The source")
.setArgName("source"))
.addArgument(new Argument()
.setIndex(0)
.setDescription("The destination")
.setArgName("target"));
如我所见,可以用CLI.create方法创建一个CLI。传入的字符串是一个CLI名称。一旦创建,就可以设置概述和描述了。概术规定为一行较短文件,然而描述可包含更加详细信息。每个选项和参数在调用CLI对象的addArgument和addOption方法被添加。
选项
一个选项是一个用关键字标识的存在与用户命令行的命令行参数。选项至少必须有一个长名称和一个知名称。长名称通常使用—前缀,然而短名称用于单一的中划线(-)。选项能得到关于用法的可显示信息。选项可接收0,1或多个值。一个选项接收0个值是一个标识,要用setFlag进行声名。默认的,选项接收单一值,可以用setMultiValued方法配置选项接收多个值。
CLI cli = CLI.create("some-name")
.setSummary("A command line interface illustrating the optionsvaluation.")
.addOption(new Option()
.setLongName("flag").setShortName("f").setFlag(true).setDescription("aflag"))
.addOption(new Option()
.setLongName("single").setShortName("s").setDescription("asingle-valued option"))
.addOption(new Option()
.setLongName("multiple").setShortName("m").setMultiValued(true)
.setDescription("a multi-valued option"));
选项可以被标记为强制要求的,一个强制要求的选项没有在命令行上设置,将会在解析过程中抛出异常:
CLI cli = CLI.create("some-name")
.addOption(new Option()
.setLongName("mandatory")
.setRequired(true)
.setDescription("a mandatory option"));
非强制选项有一个默认值,如果用户在命令行上没有设置,将使用此默认值。
CLI cli = CLI.create("some-name")
.addOption(new Option()
.setLongName("optional")
.setDefaultValue("hello")
.setDescription("an optional option with a default value"));
选项使用setHidden方法可以隐藏。隐藏选项不会在用法中显示,但是仍然能在用户命令行中使用(仅用于超级用户)。
如果选项值限定为固定集合,可以设置不同的接收选项:
CLI cli = CLI.create("some-name")
.addOption(new Option()
.setLongName("color")
.setDefaultValue("green")
.addChoice("blue").addChoice("red").addChoice("green")
.setDescription("a color"));
选项也可以用JSON形式初始化。
参数
与选项不同,参数没有键,仅用他们的索引进行标识。例如java com.acme.Foo
com.acme.Foo是一个参数。参数没有名称,用基于0的索引进行标识,第一个参数索引为0:
CLI cli = CLI.create("some-name")
//will have the index 0
.addArgument(new Argument()
.setDescription("the first argument")
.setArgName("arg1"))
//will have the index 1
.addArgument(new Argument()
.setDescription("the second argument")
.setArgName("arg2"));
argName是可选反并在用户信息中使用。
作为选项,Argument可以:
-
用setHidden被隐藏。
-
用setRequired被强制要求
-
用setDefaultValue设置默认值
-
用setMultiValued设置接收多个值,仅最后一个参数允许有多个值。
参数可以从JSON格式初始化。
用法信息生成
一旦CLI实例配置完成,就可以生成用法信息了:
CLI cli = CLI.create("copy")
.setSummary("A command line interface to copy files.")
.addOption(new Option()
.setLongName("directory")
.setShortName("R")
.setDescription("enables directory support")
.setFlag(true))
.addArgument(new Argument()
.setIndex(0)
.setDescription("The source")
.setArgName("source"))
.addArgument(new Argument()
.setIndex(0)
.setDescription("The destination")
.setArgName("target"));
StringBuilder builder = new StringBuilder();
cli.usage(builder);
产生的用法信息如下:
Usage: copy [-R] source target
A command line interface to copy files.
-R,--directory enables directorysupport
如果需要调试用法信息,检查UsageMessageFormatter类。
解析阶段
一旦CLI实例配置好,就可以解析用户命令行了,并分析每个选项和参数:
CommandLine commandLine =cli.parse(userCommandLineArguments);
parse方法返回一个包含值的CommandLine对象。默认的,CommandLine对象验证用户命令行并且检查每个强制选项和参数是否被设置,检查收到的每个选项的的值的个数。也可以传入false作为parse方法第二个参数禁用验证。这对于检查一个选项或者参数是否存在,和传入的命令行是不有效是有帮助的。也可以用CommandLine的isValid方法检查是否有效。
查询/提问阶段
一旦解析,就可得到由pase方法返回CommnadLine对象中选项和参数的值:
CommandLine commandLine =cli.parse(userCommandLineArguments);
String opt =commandLine.getOptionValue("my-option");
boolean flag =commandLine.isFlagEnabled("my-flag");
String arg0 = commandLine.getArgumentValue(0);
如果有一个选项标识为“help”,并启用“help”选项,那么验证不会失败,但是会给予是否检查帮助信息的机会。
CLI cli = CLI.create("test")
.addOption(
newOption().setLongName("help").setShortName("h").setFlag(true).setHelp(true))
.addOption(
new Option().setLongName("mandatory").setRequired(true));
CommandLine line =cli.parse(Collections.singletonList("-h"));
// The parsing does not fail and let you do:
if (!line.isValid() &&line.isAskingForHelp()) {
StringBuilder builder = new StringBuilder();
cli.usage(builder);
stream.print(builder.toString());
}
输入选项和参数
上面说明的Option和Argument类不必输入,意示着仅需要字符串值。
TypedOption和TypedArgument让你指定类型,所以原如字符串值被转成指定类型。在CLI定义中用TypedOption和TypedArgument替代Optiont和Argument:
CLI cli = CLI.create("copy")
.setSummary("A command line interface to copy files.")
.addOption(new TypedOption<Boolean>()
.setType(Boolean.class)
.setLongName("directory")
.setShortName("R")
.setDescription("enables directory support")
.setFlag(true))
.addArgument(new TypedArgument<File>()
.setType(File.class)
.setIndex(0)
.setDescription("The source")
.setArgName("source"))
.addArgument(new TypedArgument<File>()
.setType(File.class)
.setIndex(0)
.setDescription("The destination")
.setArgName("target"));
然后你可以象下面这样接收转换后的值:
CommandLine commandLine =cli.parse(userCommandLineArguments);
boolean flag =commandLine.getOptionValue("R");
File source = commandLine.getArgumentValue("source");
File target =commandLine.getArgumentValue("target");
Vert.x CLI也可以转换成类:
-
有一个单一字符串参数的构造器,与File和JsonObject类似
-
俱有静态from和fromString方法
-
具有静态valueOf方法,如基本类型和枚举类型
此外,也可以实现自己的转换器Converter并且让CLI使用此转换器:
CLI cli = CLI.create("some-name")
.addOption(new TypedOption<Person>()
.setType(Person.class)
.setConverter(new PersonConverter())
.setLongName("person"));
对于布尔值,布尔值可以被解析成true:on,yes,1,true。如果一个选项用enum作为类型,CLI将会自动计算选择的集合。
使用注解
也可以用注解定义CLI。在类或者setter方法上使用注解进行定义:
@Name("some-name")
@Summary("some short summary.")
@Description("some long description")
public class AnnotatedCli {
privateboolean flag;
privateString name;
privateString arg;
@Option(shortName = "f", flag =true)
publicvoid setFlag(boolean flag) {
this.flag = flag;
}
@Option(longName = "name")
publicvoid setName(String name) {
this.name = name;
}
@Argument(index = 0)
publicvoid setArg(String arg) {
this.arg = arg;
}
}
注解完成,可以定义CLI并且注入值:
CLI cli = CLI.create(AnnotatedCli.class);
CommandLine commandLine =cli.parse(userCommandLineArguments);
AnnotatedCli instance = new AnnotatedCli();
CLIConfigurator.inject(commandLine, instance);
Vert.x启动器
Vert.x启动器(Launcher)被用于fat jar中的启动类,Launcher也被用在命令行工具中。Launcher执一个命令的集合,如run,bare,star…。
扩展Vert.x启动器
仅可以在Java中,通过实现自己的Command扩展命令集:
@Name("my-command")
@Summary("A simple hello command.")
public class MyCommand extends DefaultCommand {
privateString name;
@Option(longName = "name", required = true)
publicvoid setName(String n) {
this.name = n;
}
@Override
publicvoid run() throws CLIException {
System.out.println("Hello " + name);
}
}
并需要实现一个CommandFactory:
public class HelloCommandFactory extendsDefaultCommandFactory<HelloCommand> {
publicHelloCommandFactory() {
super(HelloCommand.class);
}
}
然后创建src/main/resources/META-INF/services/io.vertx.core.spi.launcher.CommandFactory文件,并加一行用于指定完整有效的工厂名称
io.vertx.core.launcher.example.HelloCommandFactory
生成包含命令的jar包。并确定包含SPI文件
(META-INF/services/io.vertx.core.spi.launcher.CommandFactory)。
然后将包含命令的jar放到fat-jar的类路径或者Vert.x发布的lib目录中,然后可以这样执行:
vertx hello vert.x
java -jar my-fat-jar.jar hello vert.x
在fat ja中使用Launcher
为了在fat-jar中使用Launcher类,可以将MwgkANIFEST文件中的Main-Class设置为io.vertx.core.Launcher。此外设置MANIFEST中Main-Verticle到主verticle。
默认,Launcher执行run命令。然而,也可以在MAINFEST中设置Main-Command作为默认命令。如果启动fat-jar没有提供命令时请用默认命令。
Launcher子类
也可以创建一个Launcher的子类启动应用。此类被设计成极易扩展。一个Launcher的子类可以:
-
在beforeStartingVertx方法定制vert.x配置
-
重写afterStartingVertx获取通过run,bare命令创建的实例。
-
用getMainVerticle和getDefaultCommand命令配置默认的verticle和命令。
-
用register和unregister方法添加/移除命令
启动和退出码
在用Launcher类作为主类时,将会用到下面退出码:
-
0,进程平滑结束或者一个未捕获的异常被抛出
-
1,通用错误
-
11,Vert.x不能初始化
-
12,大量进程不能启动,发现和关闭,此错误被用于start,stop命令
-
14,系统配置不满足系统要求(如没找到java)
-
15,主verticle不能被布署。