When we are developing an application, it is usually a good practice to externalize some aspects of the application so we can change its runtime behavior without the need to change the code. This is what we call Configuration. A few examples may include the application language, the currency symbol, the username and password to connect to the database or a system path to store uploaded files.
In a Monolithic Architecture, you may get away from Configuration. It is not a big deal to perform a couple more builds to change some parameters. In the Microservices world, it is impossible.
A Microservices deployment will often run hundreds of microservice instances and use third-party services like a registry or an API Gateway. The connection information among microservices and third-party services are usually unknown at development time. Only at runtime are we able to apply the correct information to set up our application – this is especially true when using the Cloud services. To address the need to dynamically apply and modify configuration settings in a microservices deployment, a standard mechanism is needed.
It is no coincidence that the first specification defined for the MicroProfile initiative was precisely about Configuration. So, what do we get with MicroProfile Config?
Familiarize yourself with the API
The MicroProfile Configuration API is fairly simple and straightforward. To get you started you only need to know a handful of classes.
@org.eclipse.microprofile.config.inject.ConfigProperty
The ConfigProperty annotation allows you to use CDI to inject a configuration value into a CDI aware bean. Of course, you need to combine it with @Inject
to perform the actual injection.
The annotation also allows you to define a name and a defaultValue
. The name is the key to the configuration property to look up the configuration value. The defaultValue
allows you to set predefined value if the configuration property cannot be found.
@Inject
@ConfigProperty(name = "application.currency")
private String currency;
@Inject
@ConfigProperty(name = "application.list.maxSize", defaultValue="10")
private Integer maxSize;
Notice that you are using a different type for each of the configuration annotations. It is smart enough to check the type being injected and do the conversion for you. This works for the majority of common Java types, like String
, Integer
, Long
, Boolean
, and even Java 8 Optional
. It also works for native types. The default converters should cover most of the use cases. For the rare occasion that they are not enough, you can also implement your own Converter – a topic that will be covered in a future post.
What if I don’t have CDI available?
Not a problem. You can statically call the method org.eclipse.microprofile.config.ConfigProvider.getConfig()
which will return you an instance of org.eclipse.microprofile.config.Config
. From this Config
object, you can call getValue
and getOptionalValue
to retrieve configuration values.
final Config config = ConfigProvider.getConfig();
config.getValue("application.currenty", String.class);
config.getOptionalValue("application.list.maxSize”, Integer.class);
The Config instance could also be injected into a CDI aware bean with @Inject.
@Inject
final Config config;
Config Sources
We now know how to retrieve configuration values, but where are they coming from? The answer is simple. A >ConfigSource
!
A ConfigSource
is exactly what its name says: a source for configured values. The Config API uses all configured implementations of the type org.eclipse.microprofile.config.spi.ConfigSource to look up the property in question.
A few ConfigSource
types are already set up for you by default.
ConfigSource /META-INF/microprofile-config.properties
This is a key value pair file that you can place in your application WAR file (or JAR file) to load configuration values.
ConfigSource System Properties
This source will read any key-value pair passed down to the JVM startup prefixed with -D
to load configuration values.
ConfigSource Environment Variables
This source will load the configuration values from the OS environment values.
What if you have the same configuration key in multiple sources?
ConfigSource
types have a priority. The priority will be used to order the ConfigSource
implementations to search for the configuration key. It starts with the highest one in priority and it falls back to the next ConfigSource
if no key is found. Once it is found, the search ends and the config value from that ConfigSource
is used. The value in the ConfigSource
with the highest ordinal takes precedence.
By default, the priority order of the supplied ConfigSource types is Environment Variables, System Properties and finally microprofile-config.properties
. This allows you to override configuration in runtime. You can provide a list of all your configurations in microprofile-config.properties
and then override a value using an Environment Variable.
Examples
Currently, MicroProfile Config is supported by all the major vendors involved in the MicroProfile initiative and, of course, also available in TomEE, version 7.1.0 and the newest TomEE 8.0.
You can find a few samples around MicroProfile Config in TomEE. Please check the following Github project and try it out:
https://github.com/ivanjunckes/tomee/tree/master/examples/mp-config-example
Instructions to run the sample can be found in the project README file.
Resources
For additional information, please check:
MicroProfile Config Specification
Final Words
MicroProfile Config is a fairly small and simple specification, and at the same time, it is extremely powerful. Use it to customize your application and externalize configuration that you can change to adapt the runtime behavior for your needs.
In the future, we will dive deeper into MicroProfile Config. We will show you how to create your own ConfigSource to extract configuration values from a Database table and how to add Converters to convert values to nonstandard types. Stay tuned.
If you like to be involved or just follow the discussion around MicroProfile Config, just signup into the MicroProfile mailing list. It’s public and free!
Any hint, how to do the unit tests with such property reading?