Skip to content

mwitkow/java-flagz

Repository files navigation

FlagZ - modern flag library for Java/Scala

Travis Build MIT License

A modern, annotation-based flags library for Java and Scala that's tailored for large codebases.

Key features

  • No more passing around config classes, and punching-through parameters.
  • Like args4j or JCommander uses @FlagInfo annotation-based mapping of flag names to class fields.
  • Unlike args4j or JCommander allows flags to be specified anywhere on the classpath.
  • Support for simple types, e.g. Boolean, Integer, String, Double...
  • Support for generic container types, e.g. List<String>, Map<String, Integer>, Set<Double>
  • All flags are thread-safe and dynamically modifiable at runtime through:
    • JMX MBeans - a standard Java mechanism for server debugging/tuning - see JmxSampleApp example
    • etcd - a distributed key-value store, allowing for multiple servers to have their dynamic flags changed in sync - see EtcdSampleApp
  • Compatibility with existing System.properties-based libraries through @FlagProperty annotation that syncs a flag with a property name.
  • withValidator - All flags can have a set of validators attached, that prevent bad values (e.g. out of range) from being set.
  • withNotifier - All flags have callabacks that are triggered when flags are modified dynamically.
  • Extensible - just extend FlagField and define your own types, e.g. JSON flags, protobuf flags.
  • Scala support

Paint me a code picture

Let's say your project is more than just a simple CLI utility and consists of multiple packages.

You can define parameters that relate to given functionality inside the relevant package.

package com.example.rpc.client
...

public class MyRpcClientFactory {
  @FlagInfo(name="rpc_client_max_threadpool_size", help="Max threadpool size for RPC client callbacks")
  private static final Flag<Integer> maxThreadPool = Flagz.valueOf(10);
  
  @FlagInfo(name="rpc_client_default_timeout_s", help="Default timeout (seconds) for RPCs.")
  private static final Flag<Double> defaultRpcTimeoutSec = Flagz.valueOf(1.5);
  ...
}

All @FlagInfo-annotated fields on the classpath will automatically be discovered. Let's say that the server's main looks like:

package com.example.superserver
...

public class Main {
  @FlagInfo(name="daemonize", altName="d", help="Whether to fork off the process,")
  private static final Flag<Boolean> daemonize = Flagz.valueOf(false);

  @FlagInfo(name="bind_addr", help="Bind addresses to listen on")
  private static final Flag<List<String>> bindList = Flagz.valueOf(ImmutableList.of("0.0.0.0:80"));

  ...
  public static void main(String[] args) {
    Flagz.parse(args);
    ...
    for (bind in bindList.Get()) {
      server.BindTo(bind);
    }
    if (daemonize.Get()) {
      server.Fork()
    }
    ...
  }
  
  ...
}

This means you can start the server as:

$ java com.example.superserver.Main --rpc_client_max_threadpool_size=15 -d

This will start the server in daemon mode, and change the the maxThreadPool field in MyRpcClientFactory without the need for punching through massive configuration objects.

Installing

Maven Packages

To use this flag library, include this in your pom.xml:

<repositories>
  ...
  <repository>
    <id>mwitkow-github-repo</id>
    <url>https://raw.github.com/mwitkow/maven-repos/master</url>
  </repository>
  ...
</repositories>
<dependencies>
  ...
  <dependency>
    <groupId>org.flagz</groupId>
    <artifactId>flagz</artifactId>
    <version>2.2</version>
  </dependency>
  ...
</dependencies>

Development

Obviously git-clone this repo. Then install bazel>=0.3.2, which is a fantastic build system that this project uses. Bazel takes care of downloading all other dependencies (Scala, etcd), the only system requirement is JDK>=8.0.

To run all the tests:

bazel test //...

Publishing (internal stuff)

The way that bazel BUILD files are laid out in this project is along the lines of old Maven packages. This means that each target is a maven-deliverable.

To build all of them using:

bazel build //flagz-java/src/main/java/org/flagz
bazel build //flagz-etcd/src/main/java/org/flagz
bazel build //flagz-scala/src/main/scala/org/flagz

The command line will output you the relevant .jar file containing the distributable. TODO(mwitkow) Add a script for Maven publishing.

Sample Apps

The samples/ directory contains two sample JVM apps that show case what's going on.

You can run the JMX example

bazel run -- //samples/src/main/java/org/flagz/samples:jmx

or (with a running etcd instance) the etcd demo:

bazel run -- //samples/src/main/java/org/flagz/samples:etcd --flagz_etcd_directory=/foo

Status

At Improbable we use flagz-etcd and flagz-scala to dynamically reconfigure our simulation runtime environment, allowing our engineers to quickly iterate on tuning parameters and launch new functionality hidden behind flags.

As such, the code is considered production quality.

License and Copyright

flagz is released under the MIT License. See the LICENSE file for details.

The project was completely written by @mwitkow's as a thread-safe and dynamic version of kennyyu/flags, with only the public interfaces remaining from the old project.