A modern, annotation-based flags library for Java and Scala that's tailored for large codebases.
- 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
- JMX MBeans - a standard Java mechanism for server debugging/tuning - see
- 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
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.
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>
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 //...
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.
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
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.
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.