2017-11-25

Tamaya Mutable Configuration (Extension Module)

Tamaya Mutable Configuration is an extension module. Refer to the extensions documentation for further details.

What functionality this module provides ?

Tamaya Configuration by default is read-only, which covers must of the use cases. But there are many legit scenarios where configuration should be written back to backend systems or the local file system. This module adds this functionality.

Compatibility

The module is based on Java 7, so it can be used with Java 7 and beyond.

Installation

To benefit from configuration mutability support you only must add the corresponding dependency to your module:

<dependency>
  <groupId>org.apache.tamaya.ext</groupId>
  <artifactId>tamaya-mutable-config</artifactId>
  <version>{tamaya_version}</version>
</dependency>

Core Architecture

Accessing MutableConfiguration

The core of the module is the MutableConfigurationProvider singleton, which provides access to MutableConfiguration instance, which extends Configuration. This interface adds additional methods to add/update or remove property values. Hereby each MutableConfiguration manages a transaction like context, which includes a UUID that identifes a change. Backends for writing changes applied umst implement MutablePropertySource, which extends PropertySource. Registrations and ordering policies are exact the same as with ordinary property sources, but mutable property sources can be targeted by config write operations.

The example below shows how a MutableConfiguration can be obtained ,values added, removed and finally changes written back to the backend:

Accessing and changing configuration
MutableConfiguration config = MutableConfigurationProvider
                                      .createMutableConfiguration();
config.put("newKey", "newValue")
      .put("anotherKey", "updatedValue")
      .remove("valueNotValid")
      .store();

In the above scenario we use the system’s default configuration as the backend to be used. We can also pass any Configuration to render it into a mutable instance, e.g.

Explicitly passing the backing configuration
Configuration config = ...;
MutableConfiguration config = MutableConfigurationProvider
                                       .createMutableConfiguration(config);
Note
If a configuration does not contain any MutablePropertySource instances, a ConfigurationException is thrown since it would not be able to accept any changes.

Following we show the possible methods you can use to create a MutableConfiguration. We will show in the following sections more details on the options provided…​

public final class MutableConfigurationProvider {

    private MutableConfigurationProvider(){}

    public static MutableConfiguration createMutableConfiguration();
    public static MutableConfiguration createMutableConfiguration(
                                               ChangePropagationPolicy changePropgationPolicy);
    public static MutableConfiguration createMutableConfiguration(Configuration configuration);
    public static MutableConfiguration createMutableConfiguration(
                                                   Configuration configuration,
                                                   ChangePropagationPolicy changePropgationPolicy);

    [...]
}

As we have not yet shown it, MutableConfiguration is defined as follows:

public interface MutableConfiguration extends Configuration {

    void store();

    ConfigChangeRequest getConfigChangeRequest();
    ChangePropagationPolicy getChangePropagationPolicy();

    MutableConfiguration put(String key, String value);
    MutableConfiguration putAll(Map<String, String> properties);
    MutableConfiguration remove(Collection<String> keys);
    MutableConfiguration remove(String... keys);

}
Targeting specific MutablePropertySources

A Configuration may have multiple MutablePropertySource instances present. These are members of Tamaya’s ordered list of PropertySources to evaluate the configuration. Nevertheless writing back changes requires additional aspects to be considered: * Should changes written target all mutable property sources? Or should a change only target the most significant instance (hereby not writing the change to less significant property sources)? * Or should a change be applied only to specific mutable property source(s), regardless its position in the processing chain?

Therefore a default ChangePropagationPolicy can be applied on a MutableConfiguration instance, which allows to control this aspect:

Explicitly passing the backing configuration
public interface ChangePropagationPolicy {
    /**
     * Method being called when a multiple key/value pairs are added or updated.
     * @param propertySources all property sources, including read-only property sources, of the current configuration,
     *                        never null.
     * @param configChange the configuration change, not null.
     */
    void applyChange(ConfigChangeRequest configChange, Collection<PropertySource> propertySources);
}

By default, changes are applied to all registered MutablePropertySource instances similarly.

The MutableConfigurationProvider singleton also provides the most common change propagation policy implementations:

public final class MutableConfigurationProvider {

    [...]

    public static ChangePropagationPolicy getApplyAllChangePolicy();
    public static ChangePropagationPolicy getApplyMostSignificantOnlyChangePolicy();
    public static ChangePropagationPolicy getApplySelectiveChangePolicy(String... propertySourceNames);
    public static ChangePropagationPolicy getApplyNonePolicy();
}

Some Aspects to consider

Due to Tamaya’s design the effective effect of your changes to the overall configuration, cannot be sometimes a bit tricky to be predicted, since it depends on several aspects:

  1. is the corresponding configuration resource configured as part of the current system’s configuration?

  2. what is the PropertySource's priority within the configuration context? Is it overriding or overridden by other sources?

  3. is the change directly visible to the configuration system? E.g. injected values are normally not updated, whereas injecting a DynamicValue<T> instance allows to detect and react single value changes. Also the PropertySources implementation must be able to detect any configuration changes and adapt its values returned accordingly. Finally values also can be marked as immutable or being cached.

  4. Is configuration cached, or written/collected directly on access?

  5. can the changes applied be committed at all?

So it is part of your application configuration design to clearly define, which property sources may be read-only, which may be mutable, how overriding should work and to which backends finally any changes should be written back.

Configuration Changes

This module does not handle detection of changes to the overall system’s Configuration. This can be done in several ways, e.g. by:

  • using the tamaya-events extension, which can be used to observe the system’s configuration and publishing events when things have been changed.

  • The SPI implementing the MutableConfigurationBackendSpi may inform/update any affected PropertySource, PropertySourceProvider instances about the changes applied.

Supported Backends

Multiple backends are supported. E.g. tamaya-etcd also registers corresponding SPI implementations/backends. This module comes with the following MutablePropertySource implementations:

  • MutablePropertySource resources, targeting local .properties files, using the java.util.Properties format.

  • MutableXmlPropertySource resources, targeting local .xml property files, using the java.util.Properties XML format.

SPIs

The module defines MutableConfigurationProviderSpi, that is used as a delegate by the MutableConfigurationProvider singleton accessor:

SPI: MutableConfigurationProviderSpi
public interface MutableConfigurationProviderSpi {
    /**
     * Creates a new {@link MutableConfiguration} with {@code autoCommit = false} as default.
     *
     * @param configuration the configuration, not null.
     * @param propagationPolicy policy that defines how changes are published to the property
     *                          sources.
     * @return a new mutable configuration instance.
     */
    MutableConfiguration createMutableConfiguration(Configuration configuration,
                                                    ChangePropagationPolicy propagationPolicy);
}

Implementations are registered with the current ServiceContext (using by default the java.util.ServiceLoader service).