kikinteractive/ice

Name: ice

Owner: Kik Interactive

Description: Guice-based Java8 configuration system

Created: 2016-04-20 17:48:14.0

Updated: 2016-11-01 13:30:32.0

Pushed: 2017-06-30 15:53:39.0

Homepage: null

Size: 159

Language: Java

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

ICE (Interface Config Elements)

A configuration system written in Java 8 that leverages Dependency Injection via Google Guice. It allows you to define your configuration with an annotated interface containing no-arg methods.

Features
Usage
Project Setup

You can use ice with maven by adding the following to your pom.xml:

endencies>
ependency>
<groupId>com.kik.config</groupId>
<artifactId>ice</artifactId>
<version>1.0.4</version>
dependency>
pendencies>
Defining and Using Config

In your application class(es) you can define an interface for your config. This interface has a few rules:

  1. The interface currently needs to be public.
  2. Methods need to be annotated with one of @DefaultValue("some_value_here") or @NoDefaultValue.
  3. @DefaultValue("my_value_as_string") specifies your config entry will have the given value as its default if no overrides are specified in any DynamicConfigSource. The value must be specified in string string form.
  4. @NoDefaultValue specifies that your config entry will have the system default value if it is not overridden in any DynamicConfigSource. i.e.: Objects will default to null. Primitives will default to false, 0, or equivalent. Optional values will default to Optional.empty().
  5. Methods return whatever type is required in your app.
  6. Methods cannot have any arguments.
  7. If your method returns a generic type, you need to include the inner type in the annotation. Eg: returning Optional<Integer> would require something like @NoDefaultValue(innerType=Integer.class)
  8. If you want an Observable of a config value, there's a few extra things:
  9. There must be a non-observable method, with the same name and annotated with @DefaultValue or @NoDefaultValue
  10. The observable-returning method name needs to have the form [otherMethodName]Observable. eg: for an int config entry named “foo”, you would define @DefaultValue("123") Integer foo(); and Observable<Integer> fooObservable();
  11. The system currently doesn't support Observables of other generic types. I.e. no support for Observable<List<String>>
  12. Methods marked default can be defined. They are ignored by the config system.
Examples
Basic Example

An example component class in your application:

ic class ExampleComponent

// Define your configuration
public interface Config
{
    @DefaultValue("3")
    int retryCount();

    @DefaultValue("PT0.5S")
    Duration timeout();
}

// Inject your configuration via Guice
@Inject
Config config;

// Use the config elsewhere ...
void foo() {
    int retries = config.retryCount();
    // ...
}

Your Application's Guice bootstrap will need to include Guice bindings:

ic class MyApplicationModule extends AbstractModule

@Override
protected void configure()
{
    // Bindings for the ice system components
    install(ConfigConfigurator.standardModules());

    // ConfigConfigurator.standardModules() installs
    // FileDynamicConfigSource which can be configured with guice bindings
    // here:

    // FileDynamicConfigSource: config file path
    bind(String.class).annotatedWith(Names.named(FileDynamicConfigSource.FILENAME_NAME))
        .toInstance("/path/to/config/app.config");

    // FileDynamicConfigSource: config reload interval
    bind(Duration.class).annotatedWith(Names.named(FileDynamicConfigSource.POLL_INTERVAL_NAME))
        .toInstance(Duration.ofMinutes(30));

    // Binding for your component
    bind(ExampleComponent.class);

    // Install the generated Module for your component's configuration
    install(ConfigSystem.configModule(ExampleComponent.Config.class));

    // ... other bindings for your application ...
}

The config file named in the example above (/path/to/config/app.config) might look like the following:

foo.app.ExampleComponent$Config.retryCount=5
foo.app.ExampleComponent$Config.timeout=PT0.65S
Example with Observable

An example component defining an Observable:

ic class ExampleWithObservableComponent

private static final Logger log = LoggerFactory.getLogger(Example1.class);

public interface Config
{
    @DefaultValue("true")
    Boolean enabled();

    @DefaultValue("100")
    Integer maxSize();

    Observable<Boolean> enabledObservable();

    Observable<Integer> maxSizeObservable();
}

@Inject
Config config;

public void foo()
{
    rx.Subscription maxSizeSubscription = config.maxSizeObservable()
        .subscribe(maxSize ->
            log.info("Max Size changed to: {}", maxSize));
}

Bindings for the above component class would be the same as the previous example.

Further Examples

Further examples of usage can be found in com.kik.config.ice.example

Tips
Motivations

ICE was developed with a few ideas in mind, some of which were not readily available in other pre-existing configuration libraries. These ideas were:

Author

Kik Interactive Inc.

License

Use of the “ice” configuration system is subject to the Terms & Conditions and the Acceptable Use Policy.

The source for the “ice” configuration system is available under the Apache 2.0 license. See the LICENSE.txt file for details.


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.