Backward compatible configuration key values for MicroProfile Config

Introduction

With MicroProfile config, you can define the application configuration using key-value pairs which can be retrieved from various resources.
You can use it for defining the configuration in a very flexible way and this for is useful for your applications but also for frameworks which need some config.

But one day, you like to change the key for whatever good reason. But can you do this easily? If you have written the application, it probably is. But what if you have written a little framework. Do the developers read the release notes where you have stated the changes?

The backward compatibility struggle

When your configuration parameter is required, the change will quickly be detected by the developer. They upgrade to your new version and get an exception that the key is not defined. Annoying maybe but not that dramatic.
The scenario where the parameter is optional is a much greater threat. The developer has defined a custom value, overriding your default but by changing the key, the default value is picked up again. Unless the developer has written your release not notice that a change of the key name is required.

So we need a way to define the fact that the key config.key.old is now config.key.new and ideally the value for the old parameter should be picked up.

The Alias config ConfigSource

The solution for the above-described problem can be solved with the tools we have at our disposal within MicroProfile Config itself.
We can define a Config Source which will be consulted at the end of the chain. As you probably know, you can define multiple Config Sources. Each will be asked to provide a value for the key. If a source cannot supply the value, the next source is contacted.
When our ConfigSource is contacted at the end of the chain, we can see if the developer (of the framework in this case) has defined an alias for this parameter key. In this case, we define that the search for a value for config.key.new, should also be tried with key-value config.key.old. So our special ConfigSource just asks for the value of the config parameter with the old key. If there is a value found with this key value, it is returned. If nothing comes up, it returns null as required to have the default value then be selected.

The Atbash Alias config ConfigSource

The Alias config ConfigSource concept is thus fairly simple. The Atbash config extension contains this feature since his latest release (version 0.9.3)

The configuration is also fairly simple. We only need to configure the mapping from the old to the new key value. This can be done by adding a properties file on the classpath. The file much have the structure: Alias.<something>.properties and must be located within the config path. This file needs to be created by the framework developer in case he is changing one of the configuration key values.

In our example here, the contents of the config/alias.demo.properties should be

config.key.new=config.key.old

Do you want some more information and an example? Have a look at the demo in the Atbash demo repository.

And another nice thing, it works with Java 8 and Java 11 classpath.

Conclusion

By adding a ConfigSource at the end of the chain, we can make the key values of our configuration parameters backward compatible. In case the developer still uses the old values of the key, we can look up the new key value and put a warning in the log. This makes sure that the application still works and informs the developer of the changed name.

Have fun.

Extensible Resource API

Introduction

There are various scenarios where you like to use a resource, like a classpath resource, file or URL, and want to make it configurable for the developer. If you are creating a little framework for example which needs some data that needs to be adjustable depending on the application you are using it in, the resource should be easily configurable.
This is where this Atbash Resource API can be very handy.

Reading an InputStream

There are various sources which can give you an InputStream to the resource you are pointing to. File and URL are the two well-known classes for this. But getting them is different if you are using FileInputStream for example. And it is again different when you want to read a resource from the classpath.

The Resource API wants to uniform the way on how you can obtain an InputStream. The be.atbash.util.resource.ResourceUtil#getStream(java.lang.String) method takes a String, the Resource Reference, pointing to the resource you want to open and it will find out how it should retrieve the InputStream.
The prefix is the most important indicator of how the resource should be approached. By default the prefixes http:, classpath: and file: are supported. But other types can be implemented by the developer if needed.

ResourceReader

The Resource API is extensible so that other types of resources can be accessed. To do this, implement the be.atbash.util.resource.ResourceReader interface. The load() method tries to open the resource and returns the InputStream. The method is allowed to return null when the type of resource can’t be handled by this ResourceReader or when the resource doesn’t exist.
Each ResourceReader implementation should have the be.atbash.util.ordered.Order annotation on the implementation class so that the implementations can be tried in a certain order. Your custom implementation will be picked up by the Service Loader mechanism.
The implementations will then be consulted based upon the order, from low to high, to see if it can handle the resource reference. The method canRead() from ResourceReader is used for the verification of the resource reference existence.

With MicroProfile Config

The Resource API can be used with MicroProfile config. You can define a configuration parameter pointing to the default resource (like classpath) And the developer can then overwrite these values by specifying another resource using one of the supported MicroProfile methods.
Since you use the ResourceUtil#getStream(), any resource like file and URL can be supported.

Extending

As mentioned above, the ResourceReader can be used to create a custom implementation to read from specific resource. But it can also be very handy during testing. You can define a custom ResourceReader which reads some data from a Map for instance. That way you can easily point to different resources during testing.

You can have a look at the Atbash demos where a little demo is prepared. The class be.atbash.demo.utils.resource_api.spi.MapBasedResourceReader implements the ResourceReader interface.

Conclusion

This Resource API is a small and simple extensible API to get an InputStream from a resource like a file, URL or a resource on the classpath. It saves the developer to verify where the resource is located and calling the correct code. You can easily extend it by implementing the ResourceReader interface which can be very handy during testing.
And the last nice quality it has, it runs on Java 7, 8 and 11 (classpath option)

You can also have a look at some documentation here.

Have fun.