Netflix Archaius

repository·2.x·Indexed 25 days ago

https://github.com/netflix/archaius

A configuration library providing a unified interface for accessing static and dynamic configurations, allowing applications to react to changes at runtime without restarts. It features a hierarchical override system (Runtime, Override, System, Environment, Application, and Libraries layers), a Property API for optimized dynamic access, and deep integration with Guice via ArchaiusModule. Includes a bridge to support legacy Archaius 0.x API calls within Archaius 2 environments.

Tokens
4.2K
Snippets
11
Records
19
Agent score
82%

What's inside Archaius

  1. Core concepts of Archaius: Properties and Configurations

    2.x

    Archaius manages configuration through two primary abstractions:

    • Properties: Individual values that your code reads. Properties can be optimized for high-frequency access and can support dynamic updates and change listeners.
    • Configurations: Objects that organize properties into a cohesive unit. Configurations can be combined (e.g., using CompositeConfig) to create an override hierarchy for application bootstrapping.
  2. How the Archaius 0.x to 2 bridge works

    2.x

    The bridge works by calling ConfigurationManager.install() to configure the legacy API with an AbstractConfiguration implementation. This implementation bridges the static API with the Guice-created Archaius 2 top-level Config binding.

    Requirements and Lifecycle:

    • The bridge relies on ConfigurationManager being allowed to bootstrap itself with its default AbstractConfiguration instance.
    • The bridge is instantiated via static injection very early in the Guice bootstrapping process, specifically before most singletons are created.
  3. Use ConfigurationProxyFactory to create configuration proxies

    2.x

    Instead of implementing configuration interfaces manually, you can use ConfigProxyFactory to create a Java Proxy bound to your configuration. This decouples your business logic from the configuration implementation and ensures configuration is fully loaded and available during constructor injection.

    1. Define an interface with @Configuration(prefix="...").
    2. Use proxyFactory.newProxy(YourInterface.class) in a Guice @Provides method.
    @Configuration(prefix="foo")
    interface FooConfiguration {
       int getTimeout();     // maps to "foo.timeout"
       String getName();     // maps to "foo.name"
    }
    
    // In your Guice Module
    public class FooModule extends AbstractModule {
        @Provides
        FooConfiguration getFooConfiguration(ConfigProxyFactory proxyFactory) {
            proxyFactory.newProxy(FooConfiguration.class);
        }
    }
    
    // Usage
    @Singleton
    public class Foo {
        @Inject
        public Foo(FooConfiguration config) {
            // config is ready to use
        }
    }

    You can also override the prefix by passing it as a second argument to newProxy:

    proxyFactory.newProxy(FooConfiguration.class, "otherprefix.foo");
    @Configuration(prefix="foo")
    interface FooConfiguration {
       int getTimeout();     // maps to "foo.timeout"
       
       String getName();     // maps to "foo.name"
    }
    
    public class FooModule extends AbstractModule {
        @Provides
        FooConfiguration getFooConfiguration(ConfigProxyFactory proxyFactory) {
            proxyFactory.newProxy(FooConfiguration.class);
        }
    }
    
    @Singleton
    public class Foo {
        @Inject
        public Foo(FooConfiguration config) {
            this.timeout = timeout;
        }
    }
  4. Load configuration using @ConfigurationSource

    2.x

    You can load configuration into the @LibrariesLayer by annotating an injectable class with @ConfigurationSource.

    Important: @ConfigurationSource processing occurs after the constructor is called. Therefore, you cannot rely on the configuration being available inside the constructor of the annotated class. It is recommended to inject a separate configuration class into your functional code instead of mixing them.

    // The configuration holder
    @Singleton
    @ConfigurationSource({"serviceA"})
    public class ServiceAConfiguration {
        // properties will be loaded here
    }
    
    // The functional class
    @Singleton
    public class ServiceA {
        @Inject
        public ServiceA(ServiceAConfiguration config) {
            config.getTimeout(); // Returns the loaded value
        }
    }
    // Class where configuration is loaded
    @Singleton
    @ConfigurationSource({"serviceA"})
    public class ServiceAConfiguration {
    }
    
    // Class where configuration is used
    @Singleton
    public class ServiceA {
        @Inject
        public ServiceA(ServiceAConfiguration config) {
            config.getTimeout();  // Will return the loaded configuration value
        }
    }
  5. Enable the Archaius 0.x to 2 bridge

    2.x

    To allow legacy Archaius 0.x API calls to be backed by Archaius 2, you must install StaticArchaiusBridgeModule alongside the standard ArchaiusModule in your Guice injector. This enables configurations loaded in either API to be accessible via both.

    Injector injector = Guice.createInjector(
        new ArchaiusModule(),
        new StaticArchaiusBridgeModule()
    )
  6. Customize ArchaiusModule bindings

    2.x

    You can customize the default Archaius configuration by extending ArchaiusModule and overriding configureArchaius(). This allows you to change the configuration name, provide application overrides, or bind custom implementations for remote configuration and cascade strategies.

    Injector injector = Guice.createInjector(new ArchaiusModule() {
        @Override
        protected void configureArchaius() {
            bindConfigurationName().toInstance("foo"); // Use this instead of 'application'.properties
    
            bindApplicationConfigurationOverride().toInstance(MapConfig.builder()
                .put("some.property", "overridevalue")
                .build);
                
            bindRemoteConfig().to(MyRemoteConfigurationLayerImplementation.class);
            
            bindCascadeStrategy().to(MyApplicationCascadeStrategy.class);
        }
    });
  7. Integrate Archaius2 with Guice

    2.x

    To enable Archaius2 in a Guice environment, add the ArchaiusModule when creating your injector. This module provides a complete set of default bindings for all Archaius features. You can customize these features by overriding the configureArchaius() method in ArchaiusModule.

    Injector injector = Guice.createInjector(new ArchaiusModule());
  8. Customize Cascade loading strategy

    2.x

    While @ConfigurationSource uses the default cascading strategy, you can specify a per-class override strategy using the cascading parameter.

    @Singleton
    @ConfigurationSource(value={"serviceA"}, cascading=MyCascadingStrategy.class)
    public class ServiceAConfiguration {
    }
    
    @Singleton
    public static class MyCascadingStrategy extends ConcatCascadeStrategy {
        public MyCascadingStrategy() {
            super(new String[]{"${env}", "${dc}", "${stack}"});
        }
    }
  9. Configure dynamic configuration loading

    2.x

    Archaius supports dynamic configuration where values can change at runtime without application restarts.

    When using a CompositeConfig, adding a DynamicConfig derived configuration will cause the composite to automatically register for change notifications and incorporate new values.

    There are two main ways to implement dynamic loading:

    1. Polling: Use PollingDynamicConfig for sources that require frequent polling of an entire configuration snapshot.
    2. Fine-grained: Implement the Config interface directly for sources that support updates at the individual property level (e.g., ZooKeeper).

    Example of setting up a polling dynamic configuration:

    config.addConfig(new PollingDynamicConfig(
                "REMOTE", 
                new URLConfigReader("http://remoteconfigservice/snapshot"), 
                new FixedPollingStrategy(30, TimeUnit.SECONDS)) 
  10. Use the Property API for optimized dynamic access

    2.x

    For properties expected to change at runtime, use the Property API instead of calling Config directly. Property objects optimize the caching of resolved values from the configuration hierarchy, making them much more efficient for frequent access.

    1. Create a Property Factory

    Use DefaultPropertyFactory.from(config) to create a factory based on your existing configuration.

    2. Create a Property

    Use the factory to define a typed property with a default value.

    3. Access the Value

    Call .get() on the property object to retrieve the most recent cached value.

    4. React to Changes

    Register a com.netflix.archaius.api.PropertyListener to execute logic whenever a property value changes.

    // Create a fast property factory using any Config as the source
    DefaultPropertyFactory factory = DefaultPropertyFactory.from(config);
    
    // Create a Property<Integer> object
    Property<Integer> timeout = factory.getProperty("server.timeout").asInteger(DEFAULT_TIMEOUT_VALUE);
    
    // Access the cached property value
    Thread.sleep(timeout.get());
    
    // React to property change notification
    Property<Integer> timeout = factory
        .getProperty("server.timeout")
        .asInteger() 
        .addListener(new PropertyListener<Integer>() {
            public void onChange(Integer value) {
                socket.setReadTimeout(value);
            }
            
            public void onError(Throwable error) {
            }
        });
  11. Bridge Archaius 1.x configurations to Archaius 2.x

    2.x

    The AbstractConfigurationBridge class (part of the archaius2-archaius1-bridge module) allows you to use Archaius 1.x Configuration objects within an Archaius 2.x environment. It implements Apache Commons Configuration interfaces (AbstractConfiguration, AggregatedConfiguration) while wrapping an Archaius 2.x Config instance.

    Key capabilities include:

    • Property Access: Access properties from the bridged Archaius 2.x Config using standard Apache Commons methods like getString(key, defaultValue) or containsKey(key).
    • Configuration Management: Add or remove named configurations via addConfiguration(AbstractConfiguration config, String name) and removeConfiguration(String name).
    • Dynamic Updates: The bridge listens to Archaius 2.x configuration changes and triggers corresponding Apache Commons PropertyListener events.
    • Property Resolution: Use resolve(String value) to perform interpolation/resolution using the underlying Archaius 2.x configuration logic.