Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Apache Tamaya Configuration

DZone's Guide to

Apache Tamaya Configuration

Here's a really cool Apache Tamaya configuration. After JavaOne 2014, Apache Tamaya was born to foster collaboration from Java experts.

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

After JavaOne 2014, when the configuration topic was canceled from the EE8 list, David Blevins and others proposed to start an Apache project for several reasons:

  • Let people with experience in the topic identify a common feature set.
  • Implement the ideas as part of an Apache project to provide the ideas using a free and redistributable licence.
  • Use a common proven organization also capable of generating successful adoption. This was when Apache Tamaya was put to incubation. Following we had several discussions, hangouts, and emails. As a result, Apache Tamaya is now available as a first release 0.1-incubating, ready to be used.

Also to mention is that Mark Struberg and Gerhard Petracek, the guys behind Deltaspike, joined this project and actively contributed to it. I think it is worth it to have a deeper look at the project. This is what this blog is all about.

The Apache Tamaya Project

Like a Java Specification Request

Apache Tamaya is built-up similarly to a Java Specification Request (JSR). It as an API that defines the artifacts users typically interact with and it provides a reference implementation that implements the API so it can be used for real world projects. The reason for doing so are the following:

  1. Separating an API from the implementation gives you a very clear and clean view on the problem. You must isolate the essence of your problem and omit all kind of over-specific aspects. If done in a good way, this leads to a simple and comprehensive API, which at the same time is powerful enough to support at least most or all other requirements (e.g. using extension points or hooks for plugins) in adapted or additional functionality (aka service provider interfaces/SPIs).
  2. The API may be more independent than the reference implementation regarding its compatibility requirements. For example, the Java 7 based API of Apache Tamaya in fact is also compatible with the Java 6 and Java ME platforms.
  3. You can start with a minimal set of functionality on the API and extending it step-by-step as needed. Every extension must be checked, if it is really necessary or if the requirement cannot also be implemented using the existing API/SPI. This ensures your API is really focusing on the minimal aspects, thefore becoming lean and clear.
  4. Last but not least, somehow corresponding to the previous point, adding new functionality does not interfere with the basic API/implementation, making it very easy to add new functionality. The Apache Tamaya project also contains quite a few of so-called extensions that only depend on the API, so the project has already proven being able to cover this aspect very efficiently.

The only difference to a JSR is the current lack of a Technical Compatibility Kit (TCK) that ensures that different implementations of the API are compatible with a common set of rules. Similarly we do not have something like a "specification" (but we have a very extensive documentation, somehow quite similar to a specification, also covering many of the aspects/discussions done during the evaluation phase for the Java EE JSR 2 years ago).

Compatibility

Apache Tamaya currently supports both Java 7 and Java 8. The reasons behind this is that there is still plenty of code, especially in the enterprise context, running on Java 7. And we wanted people to be able to use Apache Tamaya before they move to the Java 8 platform. That said, the API can be added to your maven build quite easily:

<dependency>
  <groupId>org.apache.tamaya</groupId>
  <artifactId>tamaya-java7-api</artifactId>
  <version>0.1-incubating</version>
</dependency>

Or, when using it with Java 8:

<dependency>
  <groupId>org.apache.tamaya</groupId>
  <artifactId>tamaya-api</artifactId>
  <version>0.1-incubating</version>
</dependency>

Similarly the implementation module (called core), can be added, e.g. for Java 7 and beyond:

<dependency>
  <groupId>org.apache.tamaya</groupId>
  <artifactId>tamaya-java7-core</artifactId>
  <version>0.1-incubating</version>
</dependency>

Compatible  with Java 8:

<dependency>
  <groupId>org.apache.tamaya</groupId>
  <artifactId>tamaya-core</artifactId>
  <version>0.1-incubating</version>
</dependency>


The Main Concepts

Configuration Abstraction and Access 

One of the main objectives is to define an abstraction for configuration and define a common way of accessing it using simple Java code. So the first thing is to define a model for configuration:

public interface Configuration {

  default String get(String key) {...}
  default <T> T get(String key, Class<T> type) {...}
  default Configuration with(ConfigOperator operator) {...}
  default <T> T query(ConfigQuery<T> query) {...}
  <T> T get(String key, TypeLiteral<T> type);   Map<String, String> getProperties();

  // not available for Java 7
  default Optional<String> getOptional(String key) {...}
  default <T> Optional<T> getOptional(String key, Class<T> type) {...}
  default <T> Optional<T> getOptional(String key, TypeLiteral<T> type) {...}
  default Optional<Boolean> getBoolean(String key) {...}
  default OptionalInt getInteger(String key) {...}
  default OptionalLong getLong(String key) {...}
  default OptionalDouble getDouble(String key) {...}
}

So looking at this interface some important key decisions can be identified:

  • Configuration entries are accessed using String keys.
  • Configuration values are basically modeled as Strings.
  • Typed access is supported as well using Class or TypeLiteral.
  • Configuration can be accessed key-by-key or by accessing the full properties map (getProperties). Hereby there is a constraint that the returned map may not contain all entries that would also be available when accessing them individually. Reason is that some configuration sources may not be able to list all the entries (aka being scannable). 
  • The methods with, query define so called functional extension points, allowing additional functionality being added as operators/queries that can be applied on a configuration.
  • Finally, only defined in the API version depending on Java 8, are all the methods returning Optional values. These add support for the new Optional artifact introduced with Java 8. Similarly all the default methods were replaced in the Java 7 variant with corresponding abstract base implementations shipped with the reference implementation.

Instances of Configuration can be accessed from a ConfigurationProvider singleton:

Configuration config = ConfigurationProvider.getConfiguration();

Hereby  always a valid instance must be returned. It is not required that always the same instance is returned. Especially when running in a contextual environment, such as Java EE, each context may return different configurations, also reflecting the configuration resources deployed in the different Java EE artifacts. Similarly also OSGI based environments have their own classloader hierarchies, that may require isolation of configuration along the classloader bounderies.

Functional Extension Points

In the previous section we already mentioned the methods with and query. These take as an argument a ConfigurationOperator or a ConfigurationQuery<T>, which are defined as follows:

@FunctionalInterface
public interface ConfigOperator {
    Configuration operate(Configuration config);
}

@FunctionalInterface
public interface ConfigQuery<T> {
    T query(Configuration config);
}

So basically ConfigOperator acts as a mapping that derives a Configuration from another Configuration, whereas a ConfigurationQuery<T> can return any kind of result. Both constructs allow adding functionality in multiple ways without having to deal with it on the Configuration interface, e.g. aspects like:

  • Filtering of configuration for specific use cases, e.g. recombining entries, or removing entries out of scope for a certain use case
  • Masking of entries or sections for security reasons
  • Creating typed objects based on configuration
  • Statistical details on a given configuration, e.g. the defined sections
  • Configuration validation and documentation
  • Conversion of configuration, e.g. to a JSON representation
  • And much more.

For running examples you may consider having a look at the tamaya-functions extension module, which already implements quite a few of aspects.

A Minimalistic Example

To clarify things a bit more let's create a small example, which just uses the base mechanism provided with Tamaya's core implementation. Let's assume we build a small node, with a microservice performing a simple compound interest rate calculation (I will omit the financial details refer to https://en.wikipedia.org/wiki/Compound_interest for more details).

We assume that the interest rate is something that is configured for this component, so in our component we simply add the following code:

BigDecimal interestRate = ConfigurationProvider.getConfiguration()
                .get("com.mycomp.ratecalculator.rate", BigDecimal.class);

When using Java 8 we could also easily combine it with a default value:

BigDecimal interestRate = ConfigurationProvider.getConfiguration()
                .getOptional("com.mycomp.ratecalculator.rate", bBigDecimal.class)
                .orElse(BigDecimal.of(0.05d));

Given that we can easily implement our business logic, also using the JSR 354 type (see http://javamoney.org/):

public class MyRateCalculator implements RateCalculator{

  private BigDecimal interestRate = ConfigurationProvider.getConfiguration()
                .getOptional("com.mycomp.ratecalculator.rate", BigDecimal.class)
                .orElse(BigDecimal.of(0.05d));

  public MonetaryAmount calcRate(MonetaryAmount amt, int periods){
    ...
  }

}

Now given you have built your logic in a similar way you can deploy the calculator

  • ... as part of a Desktop application.
  • ... as part of a Java EE application.
  • ... in an OSGI container.
  • ... easily as a standalone micro-service (with an appropriate API, e.g. REST).

Making Tamaya Support Optional

Basically you can even use the Tamaya optional module to integrate with Tamaya only as an optional dependency. This extension module is a very simple module, adding basically only one class to your dependency path, which

  • Ensures Tamaya API is on your classpath
  • Optionally checks if a Configuration is accessible from a given context.
  • Delegates Configuration request to Tamaya, or - if not availalbe - to a delegate passed from your logic, when creating the delegate:
import org.apache.tamaya.ext.optional.OptionalConfiguration;

private BigDecimal interestRate = Optional.ofNullable(
                 OptionalConfiguration.of(
                    (k) -> MyConfigMechanism.get(k)) // String get(String key);                  )
                .get("com.mycomp.ratecalculator.rate", BigDecimal.class))
                .orElse(BigDecimal.of(0.05d));

This allows you to support Tamya Configuration, but you can still use your own default configuration logic as default, if Tamaya is not loaded in your target environment.

What Else?

From an API perspective there is not much more needed. The TypeLiteral class used before is the same, which is also known well from Java EE (CDI) and the only other artifact not mentioned is the ConfigException class. Of course, this functionality per se is very minimalistic, but it exactly does, what it is supposed to: it provides a minimalistic access APIfor configuration. And why we think this is so important? Here is why:

  1. Everybody writing components typically writes some configuration logic, but everybody does it different: different formats, locations, key schemes, overridings etc. Also Apache Tamaya neither wants to define what you configure, or where your configuration is located and how it can be overridden. But we define a common API for accessing the configuration.
  2. Given that components from different teams can be more easily integrated within a project, but also within a concrete enterprise context, since all components refer to the same configuration mechanism.
  3. Even better, when using Tamaya overriding rules of configuration can be more or less ignored, since the mechanisms of Tamaya (I will present the corresponding SPI in the next blog here) already provide these mechanisms, so they can be adapted as needed.
  4. Similarly the formats used for configuration and also the fact that configuration may be locally stored in the file system or be remotely distributed is not of importance anymore.

This per se should render Apache Tamaya to very interesting and crucial piece of any application or module architecture. Additionally its SPI brings additional benefits, especially within bigger entprise contexts. We will look at the SPI  and the extensions in the next blog posts here. So stay tuned!

As always comments are welcome. If anybody out there is also thinking of contributing to the project please get in contact with us under mailto:dev@tamaya.incbuator.apache.org.

And of course, help us spread the word writing tweets, blogs, adopting it, using it, loving it! 

Want to Hear More?

Want to know more about Apache Tamaya? Visit our project site or even better join and see us at 

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
java ,java se ,apache ,tamaya

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}