2019-11-17

Apache 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:

  • A service loader implementation, configurations and further logic required to run in an OSGi environment.

  • Numerous converters, including an EnumConverter, that is a converter implementation that can automatically select the currect enumeration values based on a configured entry.

  • A configurable `BannerManager`to print Tamaya’s start logo on load.

Basically tamaya-core leverages the base implementations contained in Tamaya’s SPI support module, so it is quite small in size. All required components are implemented and registered, so basically the Core module with its trasitive dependencies (API and spi-support) is a complete configuration solution. Nevertheless it is also very minimalistic, but fortunately it can be easily extended/accommodated with additional features as needed.

These extensions are managed in the extensions repository of the Tamaya project. Some of the most commonly used extensions are

  • 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)

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

Default PropertyConverters

The tamaya-core module provides several PropertyConverter implementations, which are automatically registered:

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

java.time.Duration

DurationConverter

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

java.io.File

FileConverter

value → new File(value)

java.ui.lang.Float

FloatConverter

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

java.time.Instant

InstantConverter

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

java.ui.lang.Integer

IntegerConverter

1, 0xD3, MIN_VALUE, MAX_VALUE

java.time.LocalDate

LocalDateConverter

<Date as defined by LocalDate.parse(String)

java.time.LocalDateTime

LocalDateTimeConverter

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

java.time.LocalTime

LocalTimeConverter

<Time as defined by LocalTime.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.time.OffsetDateTime

OffsetDateTimeConverter

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

java.time.OffsetTime

OffsetTimeConverter

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

java.util.Optional

OptionalConverter

java.nio.Path

PathConverter

java.ui.lang.Short

ShortConverter

1, 0xD3, MIN_VALUE, MAX_VALUE

java.util.Supplier

SupplierConverter

java.net.URI

URIConverter

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

java.net.URL

URLConverter

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

Enums

EnumConverter

<Enum item name>

Registering PropertyConverters

Additional PropertyConverter classes 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) to enable auto-discovery.

Alternatively you can also use a ConfigurationBuilder 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 ConfigurationBuilder 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.spisupport.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
Similarly to property converters the property sources shown here are defined within the "tamaya-spisupport* module and automatically registered with the tamaya-core implementation using Tamaya’s auto-discovery mechanisms.
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 combines the previous with the next value.

The resulting raw value(s) are then finally handed over to the registered filters and finally converted to the target type as required by the user API.

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.