Apache Tamaya — Extension: Mutable Configuration
Tamaya Mutable Configuration (Extension Module)
Overview
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 some 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>{tamayaVersion}</version> </dependency>
Core Architecture
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 changes applied are managed in a transaction like context, called ConfigChangeContext. Each context defines a UUID that identifes a change. Backends for writing changes applied are of type MutablePropertySource, similarly extending the PropertySource SPI with methods for writing changes back. Registrations and ordering policies are like with ordinary property sources, with one important difference. Mutable property source can be targeted by write operations.
Summarizing a MutableConfiguration can be obtained as follows:
Accessing and changing a configurationMutableConfiguration config = MutableConfigurationProvider.createMutableConfiguration(); config.set("newKey", "newValue") .set("anotherKey", "updatedValue") .remove("valueNotValid") .commit();
In the above scenario we use the overall system’s 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 configurationConfiguration config = ...; MutableConfiguration config = MutableConfigurationProvider.createMutableConfiguration(config);
Note | If a configuration does not contain any MutablePropertySource instances, a MutableConfiguration built from it will not be able to accept any changes. |
Following is the complete listing of the MutableConfigurationProvider accessor:
public final class MutableConfigurationProvider { private MutableConfigurationProvider(){} public static MutableConfiguration getMutableConfiguration(); public static MutableConfiguration getMutableConfiguration(Configuration configuration); [...] }
Hereby MutableConfiguration is defined as follows:
public interface MutableConfiguration extends Configuration { UUID startTransaction(); void commitTransaction(); void rollbackTransaction(); UUID getTransactionId(); boolean getAutoCommit(); void setAutoCommit(boolean autoCommit); void setChangePropagationPolicy(ChangePropagationPolicy changePropagationPolicy); ChangePropagationPolicy getChangePropagationPolicy(); boolean isWritable(String keyExpression); boolean isRemovable(String keyExpression); boolean isExisting(String keyExpression); List<MutablePropertySource> getMutablePropertySources(); List<MutablePropertySource> getPropertySourcesThatCanWrite(String keyExpression); List<MutablePropertySource> getPropertySourcesThatCanRemove(String keyExpression); List<MutablePropertySource> getPropertySourcesThatKnow(String keyExpression); MutableConfiguration put(String key, String value); MutableConfiguration putAll(Map<String, String> properties); MutableConfiguration remove(Collection<String> keys); MutableConfiguration remove(String... keys); }
Targeting the right MutablePropertySources
A Configuration may have multiple MutablePropertySource present. These are members of Tamaya’s oredered list of PropertySources to evaluate the configuration. Nevertheless writing back changes requires additional aspects to be considered: * Should changes being written back to all mutable property sources? Or should a key that could be added or removed on a more significant instance not be written/removed on less significant property source instances? * Should a change be applied only to a specific mutable property source, regardless its position in the processing chain?
Therefore a ChangePropagationPolicy can be set on a MutableConfiguration instance, which allows to control this aspect:
Explicitly passing the backing configurationpublic interface ChangePropagationPolicy { void applyChanges(Collection<PropertySource> propertySources, UUID transactionID, Map<String,String> changes); void applyChange(Collection<PropertySource> propertySources, UUID transactionID, String key, String value); void applyRemove(Collection<PropertySource> propertySources, UUID transactionID, String... keys); }
By default, changes are applied to all registered MutablePropertySources similarly.
Also the MutableConfigurationProvider provides access to the most commonly used change propagation policies:
public final class MutableConfigurationProvider { private MutableConfigurationProvider(){} public static MutableConfiguration getMutableConfiguration(); public static MutableConfiguration getMutableConfiguration(Configuration configuration); 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 easily predicted, since it depends on several aspects:
-
is the corresponding configuration resource configured as part of the current system’s configuration?
-
what is the PropertySource's ordinal? Is it overriding or overridden by other sources?
-
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.
-
Is configuration cached, or written/collected directly on access?
-
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. To support such fine granular scenarios a MutableConfiguration also offers methods to determine if a key is writable at all or can be removed or updated:
Checking for mutabilityMutableConfiguration config = MutableConfigurationProvider.createMutableConfiguration(); if(config,isWritable("mycluster.shared.appKey")){ config.set("newKey", "newValue"); } if(config,isRemovable("mycluster.myapp.myKey")){ config.remove("mycluster.myapp.myKey"); } config.commit();
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. the etcd integration module of Tamaya also registers corresponding SPI implementations/backends. By default this module comes with the following MutablePropertySource implementations:
-
MutablePropertySource resources, targeting local .properties files, following the java.util.Properties format.
-
MutableXmlPropertySource resources, targeting local .xml property files, following the java.util.Properties XML format.
SPIs
The module defines MutableConfigurationProviderSpi, that is used as a delegate by the MutableConfigurationProvider singleton accessor:
SPI: MutableConfigurationProviderSpipublic interface MutableConfigurationProviderSpi { MutableConfiguration createMutableConfiguration(Configuration configuration); }
Implementations are registered with the current ServiceContext, by default as a java.util.ServiceLoader service.
As convenience the following base classes are provided:
-
org.apache.tamaya.mutableconfig.propertysource.AbstractMutablePropertySource simplifying implementation of MutablePropertySource.