The benefits of using Microprofile-Config
MicroProfile-Config allows us to provide configurations to our applications without referencing the configuration source from within the application and provides the possibility to inject configurations into CDI beans. MicroProfile-Config abstracts the application code from the underlying configuration source and therefore configurations can be provided to the application by different configurations sources. As with all MicroProfiles, MicroProfile-Opentracing is also not restricted to be used only in microservices but can also be useful in ordinary WAR/EAR deployments which run on an application server.
How to integrate MicroProfile-Config into an application
The MicroProfile-Config API and it’s implementations are supplied via Maven Dependencies, although these dependencies are provided in different ways, depending on the used application runtime. The following sections explain how MicroProfile-Opentracing is supplied by different application runtimes.
Integration in Wildfly/EAP
The cited dependency has to be added in provided scope. No runtime dependency is needed, because Wildfly/EAP has MicroProfile capabilities already installed as JBoss modules.
<dependency> <groupId>org.eclipse.microprofile.config</groupId> <artifactId>microprofile-config-api</artifactId> <version>${version.microprofile.config}</version> <scope>provided</scope> </dependency>
Integration with Thorntail
Thorntail provides a Maven Bill-of-Materials (bom) which includes the Thorntail fraction for MicroProfile-Config.
<dependencyManagement> <dependencies> <dependency> <groupId>io.thorntail</groupId> <artifactId>bom</artifactId> <version>${version.thorntail}</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>io.thorntail</groupId> <artifactId>microprofile-config</artifactId> </dependency> </dependencies>
Integration with Quarkus
Quarkus, like Thorntail, provides a Maven-BOM which provides all Extensions supported by the specific Quarkus version.
<dependencyManagement> <dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-bom</artifactId> <version>${quarkus.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Does not need a dependency defined. It uses Smallrye implementation of the MicroProfile-Config as default for configurations --> </dependencies>
How to provide configurations to an application
This section shows how to provide configurations to an application in different environments via different configuration sources.
Supported Configuration Sources
The specification demands the following configuration sources to be supported by any implementation and processed in the given order:
- System Properties
- Environment variables
- META-INF/microprofile-config.properties
Configurations provided by a configuration source of a higher ordinal will overlay configurations provided by a configuration source of a lower ordinal.
Custom configuration sources can be implemented as well and will be discovered via SPI. For instance, the Smallrye implementation provides a DirectoryConfigSource which processes configurations from files located in a configured directory (nested directories are not supported), in which the filename represents the configuration name and the file content the configuration value. This is very useful in cases where the application runs in a Linux Container in the cloud.
How to provide configuration for Wildfly/EAP
Configurations for an application running on a Wildfly/EAP can be provided as system properties defined in the standalone.xml, environment variables defined in the host system or configurations provided by the Smallrye subsystem.
System properties in standalone.xml: <system-properties> <property name="conax.request.dir" value="/tmp/cmnt/req"/> </system-properties> Properties in the subsystem in standalone.xml: <subsystem xmlns="urn:wildfly:microprofile-config-smallrye:1.0"> <config-source name="props"> <property name="prop1" value="foo"/> <property name="prop2" value="bar"/> </config-source> </subsystem> Directory configuration in the subsystem in standalone.xml: <subsystem xmlns="urn:wildfly:microprofile-config-smallrye:1.0"> <config-source name="file-props"> <dir path="/etc/config/numbers-app"/> </config-source> </subsystem> Configuring directory source via the JBoss-CLI: /subsystem=microprofile-config-smallrye/config-source=file-props \ :add(dir={path=/etc/config/numbers-app}) Configuring properties via the JBoss-CLI: /subsystem=microprofile-config-smallrye/config-source=props \ :add(properties={"prop1" = "foo", "prop2" = "bar"})
How to provide configurations for Thorntail
Configurations for an application running on Thorntail might be provided via system properties defined during startup, via environment variables defined by the host Linux Container or via configurations defined in YAML files within the application (supports stages). The YAML files might also be put outside the application and provided via startup arguments.
There are multiple ways to configure Thorntail via YAML files which are explained in the following list.
- src/main/resources/microprofile-config.properties
The configuration file which is enforced by the specification to be supported by any implementation - src/main/resources/project-defaults.yaml
This file should contain the default configurations applicable for all stages - src/main/resources/project-stages.yaml
This file should contain configurations for multiple stages separated via ‘– – -’, which indicates that this YAML file contains actually multiple YAML files. The stages can be selected via startup arguments or a system property like this:
// Activates all stages contained in project-stages.yaml
java -jar app-thorntail.jar -Slocal,test
// Activates one single stage in project-stages.yaml
java -Dthorntail.project.stage=test -jar app-thorntail.jar - src/main/resources/project-test.yaml
Configuration file for the test stage. The configuration file can be activated via a startup argument like, for example:
// Activates all stages contained in project-stages.yaml
java -jar app-thorntail.jar -Stest - /some_dir/external-config.yaml
Contains the configuration outside the application which can be provided via startup arguments:
java -jar app-thorntail.jar -s /some_dir/external-config.yaml
The cited example shows a YAML file project-stages.yaml which contains configurations for the stages ‘local’, ‘test’ and ‘cloud’.
Staged MicroProfile-Config configuration: # Configurations for all stages thorntail: microprofile: config: config-sources: STATIC_CONFIG: properties: info: "The application info..." — — - project: stage: local thorntail: microprofile: config: config-sources: LOCAL_CONFIG: properties: prop: "local-prop" — — - project: stage: test thorntail: microprofile: config: config-sources: TEST_CONFIG: properties: prop: "test-prop" — — - project: stage: cloud thorntail: microprofile: config: config-sources: CLOUD_CONFIG: Dir: “/config/app”
How to provide configurations for Quarkus
Configurations for an application running with Quarkus can be provided via system properties defined during startup, environment variables defined by the hosting Linux Container or via configurations defined in a properties file within the application.
There are multiple ways to configure Quarkus which are explained in the following list.
- src/main/resources/microprofile-config.properties
The configuration file which is enforced by the specification to be supported by any implementation - src/main/resources/application.properties
The Quarkus specific configuration file - Via system properties during startup
java -Dproperty=value -jar app-quarkus.jar
I am not familiar with Quarkus yet, and just used it to illustrate that a JEE application can run oin multiple application runtimes.
How to implement advanced behaviours for configuration
Configurations provided for an application via MicroProfile-Config are simple String variables, but the demand for other variable types such as Integer, Double or Boolean in configurations might arise. The need for support of the standard Java Types definitely exists.
Configuration Converters
If you have configuration properties of a specific format then you can convert them to any Java Type you wish via custom converters. Custom converters are resolved by MicroProfile-Config via SPI.
The following code snippet illustrates how to map a configuration property of the form <PROPERTY_NAME>=<USERNAME>;<FIRST_NAME> to a custom Java Model. // The user model public class User { public final String username; public final String firstname; public User(String username, String firstname) { this.username = username; this.firstname = firstname; } } // Converts a property to a User model. // E.g user.extern=het;thomas public class UserConverter implements Converter<User> { @Override public User convert(String value) { final String[] properties = value.split(";"); final String username = properties[0]; final String firstname = properties[1]; return new User(username, firstname); } }
How to provide a configuration within an application
MicroProfile-Config integrates into CDI and makes the configurations injectable via the CDI container. Some applications even spread the configurations through the whole application where it becomes hard over time to find usages of configurations in the application code. System.getProperty(“prop”) is mostly used to retrieve configurations within applications whereby the configuration source is limited to system properties and always the current configuration value is retrieved. For instance, if we have a request and we would replace a configuration property, an inconsistency during the request could happen because some parts of the application use the old configuration value and others the new configuration value.
With MicroProfile-Config and CDI it is now possible to implement a scoped configuration class which bundles all configurations, has no reference to the underlying configuration source and reloads the configurations whenever the bean scope is created.
Configuration classes
The following code snippet illustrates a configuration class which exists in the RequestScope (@RequestScoped) and bundles all configurations for the application. Each request has its own bean and keeps the state consistent over the request lifecycle. The injection of the configurations is performed via a CDI Producer Method, provided by the MicroProfile-Config implementation, because @ConfigProperty is a CDI qualifier annotation.
@RequestScoped public class Configuration { @Inject @ConfigProperty(name = "info.text", defaultValue=“...”) private String infoText; @Inject @ConfigProperty(name = "google.apiKey") private String apiKey; // Getters for accessing the configuration properties }
Be aware that the reload of configuration properties depends on the used configuration source and implementation vendor because the specifications don’t specify reload behaviours for configuration sources.
Sample Application
I have implemented a simple sample application which demonstrates the usage of MicroProfile-Config in a JEE application which is deployable as a microservice for Thorntail and Quarkus or as a WAR deployment in Wildfly.
Useful Links
- Overview of all MicroProfile Projects
- MicroProfile-Config Github repository:
- Thorntail Home (Be aware that the doc is not updated regularly anymore)
- Quarkus Home
- Wildfly Home
- Quarkus Github repository
// Autor:in
Thomas