JCommander:Java外部参数解析利器

最近需要把项目交给别人进行运维,为了不让接手之人涉及太多繁琐细节,我把一些定义在final类中的不可变量抽取出来,把项目变成可外部配置的。用配置文件可以达到这个目的,但由于配置之间有相互依赖关系,比如:

public static boolean local = false;
public static String host =  (local) ? "127.0.0.1" : "172.16.3.142";
public static int port = (local) ? 6379 : 6380; 

原本只需要改变local, 用配置文件的话与local值有依赖关系的地方都要面临修改。

后来打算用命令行参数实现可外部动态配置,如果自己动手实现完善的命令行参数解析,可不是一项little job。 比较了几款开源的工具,还是选择了JCommander。主要原因是被它官网的slogan打动了:

Because life is too short to parse command line parameters.

项目是maven构建的,使用JCommander的方式十分简单:

<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
</dependency>

使用JCommander的方式也很简单,给需要外部传参的变量加Parameter标注:

 @Parameter(names = { "-topologyName"}, description = "Topology name.")
 private static String TOP_NAME = "sz-train";

一般类型参数后面都要跟值,JCommander会根据对应变量做类型检查和转换,不合法时会抛出异常错误。

boolean类型有点特殊,后面不需要跟一个值,输入-local之后,local值即为true:

@Parameter(names = { "-local"}, description = "Local model, default cluster Model.")
public static boolean LOCAL_MODE = false;

如果一个boolean变量的默认值为true,而想通过参数设置为false,可以指定元数:

@Parameter(names = { "-log", "-verbose"}, description = " Wheather to write system log.", arity = 1)
public static boolean LOG = true;

默认情况下,参数是可选的,如果要求必须指定参数,可以设置required:

@Parameter(names = "-operator", required = true)
private String operator;

有时仅仅依靠JCommander的类型检查还不够,还需要自定义检查器提前发现不合法的输入:

@Parameter(names = { "-redisPort"}, description = "Redis port.", validateWith = PortValidator.class)
public static int REDIS_PORT=(LOCAL_MODE) ? 6379:6380;

public static class PortValidator implements IParameterValidator {
           public void validate(String name, String value)
            throws ParameterException {
          Pattern pattern = Pattern.compile("[1-9]\\d*");
          Matcher matcher = pattern.matcher(value);
          if (matcher.matches()) {
              int n = Integer.parseInt(value);
              if (n < 65536) {
                  return;
              }
          }
          throw new ParameterException("Parameter " + name 
                    + " should be a number(0~65535) (found " + value +")");
        }
      }

如果是一个写日志的目录,可以提前发现该目录是否可写,这是配置文件无法做到的:

@Parameter(names = { "-logDir"}, description = "Dir to write log file.", 
                                          validateWith = DirValidator.class)
public static String LOG_DIR = "/logs/your_project/"; 

public static class DirValidator implements IParameterValidator {
           public void validate(String name, String value)
            throws ParameterException {
            File file = new File(value);
            if (!file.isDirectory() || !file.canWrite()) {
                  throw new ParameterException("Parameter " + name 
                         + " should be a writable folder(found " + value +")");
                  }
            }
      }

对于IP地址,不仅可以通过正则表达式进行匹配,还可以进行简单的网络连通性探测等。相比基于文本的配置文件,JCommander显示出了强大的优势。

但更多的项目可能还是更适合用配置文件的方式进行外部配置,如果配置文件如果可以吸收JCommander的特点,那就perfect了。我理想中的配置文件应该有如下特性:
- 配置文件本身是programmable
- 可以进行上下文联系
- 自动类型检查和转换
- 可以自定义语义检查器
- 所使用的弱语言可以方便嵌入

Tags// ,
More Reading
Newer// PL Meets AI