2017-09-12

Tamaya Core Implementation

Overview

Tamaya Core provides an implementation of the Tamaya Configuration API and adds additional functionality and building blocks for supporting SPI implementations.

Tamaya Core contains the following artifacts:

  • Implementations of Configuration, ConfigurationContext, ConfigurationContextBuilder, ConfigurationProviderSpi

  • A java.util.ServiceLoader based ServiceContext implementation. Hereby it implements component priorization based on the @Priority annotations.

  • A PropertyConverterManager that loads and stores references to all the preconfigured PropertyConverter instances hereby providing type conversion for all important types.

  • A simple default configuration setup using the current classpath and an optional staging variable.

  • It collects all PropertySource and PropertySourceProvider instances registered with the ServiceLoader and registers them in the global ConfigurationContext

  • It provides a ConfigurationContextBuilder implementation (DefaultConfigurationContextBuilder) and allows changing the current ConfigurationContext.

The overall size of the library is very small. All required components are implemented and registered, so basically the Core module is a complete configuration solution. Nevertheless it is also very minimalistic, but fortunately is flexible enough to be extended/accommodated with additional features as needed, such as

  • placeholder and resolution mechanisms (org.apache.tamaya.ext:tamaya-resolver)

  • dynamic resource path lookup, e.g. with ant styled patterns (org.apache.tamaya.ext:tamaya-resources)

  • configuration injection and configuration templates (org.apache.tamaya.ext:tamaya-injcetion-api)

  • abstraction for reusable formats (org.apache.tamaya.ext:tamaya-formats)

  • integration with other existing solutions (e.g. org.apache.tamaya.ext:tamaya-spring)

  • configuration and configuration isolation targeting Java EE (org.apache.tamaya.ext:tamaya-injection-ee)

  • dynamic configuration and configuration updates (org.apache.tamaya.ext:tamaya-events)

  • remote configuration (org.apache.tamaya.ext:tamaya-etcd, org.apache.tamaya.ext:tamaya-consul, org.apache.tamaya.ext:tamaya-hazelcast)

  • and more

For details about the extension modules available and their functionality refer to the extension user guide.

Default PropertyConverters

The Core module provides several PropertyConverter implementations by default. Find below the listing of converters automatically registered with the Core module:

Target Type Class Name Supported Formats

java.math.BigDecimal

BigDecimalConverter

1.2345, 0xFF

java.math.BigInteger

BigIntegerConverter

0xFF, 1234

java.ui.lang.Boolean

BooleanConverter

true, false, T, F, 1 ,0

java.ui.lang.Byte

ByteConverter

0xFF, MIN_VALUE, MAX_VALUE, 123

java.ui.lang.Character

CharConverter

0xFF, 'a', 'H', 123

java.ui.lang.Class

ClassConverter

<fully qualified class name>

java.util.Currency

CurrencyConverter

CHF, 123

java.ui.lang.Double

DoubleConverter

1, 0xFF, 1.2334, NaN, NEGATIVE_INFITIY, POSITIVE_INFINITY, MIN_VALUE, MAX_VALUE

Enums

EnumConverter

<Enum item name>

java.ui.lang.Float

FloatConverter

1, 0xFF, 1.2334, NaN, NEGATIVE_INFITIY, POSITIVE_INFINITY, MIN_VALUE, MAX_VALUE

java.ui.lang.Integer

IntegerConverter

1, 0xD3, MIN_VALUE, MAX_VALUE

LocalDate

LocalDateConverter

<Date as defined by LocalDate.parse(String)

LocalTime

LocalTimeConverter

<Time as defined by LocalTime.parse(String)

LocalDateTime

LocalDateTimeConverter

<LocalDateTime as defined by LocalDateTime.parse(String)>

java.ui.lang.Long

LongConverter

1, 0xD3, MIN_VALUE, MAX_VALUE

java.ui.lang.Number

NumberConverter

1, 0xFF, 1.2334, NaN, NEGATIVE_INFITIY, POSITIVE_INFINITY

java.ui.lang.Short

ShortConverter

1, 0xD3, MIN_VALUE, MAX_VALUE

java.net.URI

URIConverter

http://localhost:2020/testresource?api=true

java.net.URL

URLConverter

http://localhost:2020/testresource?api=true

ZoneId

ZoneIdConverter

Europe/Zurich

Registering PropertyConverters

Additional PropertyConverters can be implemented easily. It is recommended to register them using the java.util.ServiceLoader, meaning you add a file under META-INF/service/org.apache.tamaya.spi.PropertyConverter containing the fully qualified class names of the converters to be registered (one line each).

Alternatively you can also use a ConfigurationContextBuilder to add additional converters programmatically.

Component Loading and Priorization

Tamaya Core in general loads all components by default using the java.util.ServiceLoader mechanism. This means that new components must be registered by adding a file under META-INF/service/<myInterfaceName> containing the fully qualified implementation class names of the components to be registered (one line per each). The ServiceLoader itself does not provide any functionality for overriding or ordering of components. Tamaya Core adds such a functionality with the possibility to add @Priority annotations to the components registered. By default, and if no annotation is added 0 is assumed as priority. Hereby higher values preceed lower values, meaning

  • if a singleton component is accessed from the current ServiceContext the component with the higher value effectively overrides/replaces any component with lower values.

  • if a collection of components is obtained from the ServiceContext the components are ordered based on their priorities, where the ones with higher priority are before components with lower priority.

  • if priorities match Tamaya Core additionally sorts them using the simple class name. This ensures that ordering is still defined and predictable in all scenarios.

Note
Sorting the property sources based on their ordinal value is only the default ordering principle applied. By implementing your own implementation of ConfigurationProviderSpi you can apply a different logic:

Registering Property Sources

PropertySource implementations that provide configuration properties are registered as components as described in the previous section. Hereby the precedence (ordering) of property sources is not hard-coded. Instead a Comparator<PropertySource> can be passed to a ConfigurationContextBuilder to perform automatic ordering of the property sources registered. The default implementation hereby implements the following logic:

  1. It checks for an property entry tamaya.ordinal if present the value is parsed into an int value and used as the ordinal val value.

  2. It checks for an explicit method int getOrdinal(), if found its value is taken as an ordinal.

  3. It checks for a @Priority annotation, if present the priority value is used as an ordinal.

  4. If none of the above works, 0 is assumed as ordinal value.

  5. If multiple PropertySource instances share the same ordinal value, they are ordered based on their fully qualified class names.

Custom implementations of the property source comparator can be applied by calling ConfigurationContextBuilder.sortPropertySources(Comparator<PropertySource>). The default comparator can be replaced by passing the fully qualified comparator class name as system property:

-Dproperty-source-comparator=a.b.c.MyComparatorClass

The ladder allows to adapt the ordering of auto-discovered property sources, even if the value returned by int getOrdinal() cannot be changed.

Configuration Setup in Core

Tamaya Core provides a minimal configuration setting, that allows you to configure SE applications already easily. Basically configuration is built up by default as follows:

  1. Read environment properties and add them prefixed with env.

  2. Read all files found at META-INF/javaconfiguration.properties and META-INF/javaconfiguration.xml

Overview of Registered Default Property Sources and Providers

The Tamaya Core implementation provides a couple of default PropertySource implementations, which are automatically registered. They are all in the package org.apache.tamaya.core.propertysource and org.apache.tamaya.core.provider:

Type Class Name Ordinal Used

META-INF/javaconfiguration.properties

JavaConfigurationProvider

100

META-INF/javaconfiguration.xml

JavaConfigurationProvider

100

JNDI Entries

JNDIPropertySource

200

Environment Properties

EnvironmentPropertySource

300

System Properties

SystemPropertySource

1000

Note
JNDIPropertySource is provided by the tamaya-jndi extension module.

Abstract Class PropertiesFilePropertySource

The abstract class PropertiesFilePropertySource can be used for implementing a PropertySource based on a URL instance that points to a .properites file. It requires a URL to be passed on the constructor:

PropertiesFilePropertySource(URL url);

Abstract Class PropertiesPropertySource

The abstract class PropertiesPropertySource can be used for implementing a PropertySource based on a Properties instance. It requires a PropertySource to be passed on the constructor:

PropertiesPropertySource(Properties properties);

Abstract Class BasePropertySource

The abstract class BasePropertySource can be used for implementing custom PropertySource classes. It requires only one method to implemented:

Implementing a PropertySource using BasePropertySource
public class MyPropertySource extends BasePropertySource{

    public String getName(){
        // return a unique name for the property source, e.g. based on the underlying resource. This name also
        // allows to access the property source later
    }

    public Map<String, String> getProperties(){
        // Get a map with all properties provided by this property source
        // If the property source is not scannable, the map returned may be empty.
        // In the ladder case the +boolean isScannale()+ must be overridden, since
        // by default property sources are assumed to be scannable.
    }

}

By default the ordinal of the property sources will be 1000, unless the key tamaya.ordinal as defined in PropertySource.TAMAYA_ORDINAL is present in the current PropertySource. Of course it is also possible to override the inherited protected void initializeOrdinal(final int defaultOrdinal), or directly int getOrdinal().

Default PropertySourceProvider in Core

With org.apache.tamaya.core.provider.JavaConfigurationProvider there is also a default PropertySourceProvider present that loads all .properties files found at META-INF/javaconfiguration.properties and META-INF/javaconfiguration.xml.

Replacing the property value evaluation policy

Tamaya’s core implementation allows to replace the complete logic how a configuration value or the current configuration properties are calculated from a given ConfigurationContext by implementing the ConfigValueEvaluator interface:

/**
 * Component SPI which encapsulates the evaluation of a single or full <b>raw</b>value
 * for a {@link ConfigurationContext}.
 */
public interface ConfigValueEvaluator {

    /**
     * Evaluates single value using a {@link ConfigurationContext}.
     * @param key the config key, not null.
     * @param context the context, not null.
     * @return the value, or null.
     */
    PropertyValue evaluteRawValue(String key, ConfigurationContext context);

    /**
     * Evaluates all property values from a {@link ConfigurationContext}.
     * @param context the context, not null.
     * @return the value, or null.
     */
    Iterable<PropertyValue> evaluateRawValues(ConfigurationContext context);

}

The default implementation DefaultConfigValueEvaluator implements the following logic:

  1. Collect all PropertySources from the context.

  2. Access PropertyValue get(String) (single key access)/ Map<String,PropertyValue> getProperties() (config map access) from each property source and combine the previous with the next value using the PropertyValueCombinationPolicy in place.

The resulting raw value(s) are then finally handed over to the registered filters and finally converted to String/Map<String,String> as required by the user API.

To replace this default behaviour you must register your implementation with the current ServiceContext.

Adding Extensions

Tamaya Core only implements the API. Many users require/wish additional functionality from a configuration system. Fortunately there are numerous extensions available that add further functionality. Loading extensions hereby is trivial: you only are required to add the corresponding dependency to the classpath.

For detailed information on the extensions available refer to the extensions documentation.