Note
|
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website. |
Learn how to provide external configuration to microservices using MicroProfile Config.
You will learn how to externalize and inject both static and dynamic configuration properties for microservices using MicroProfile Config.
You will learn to aggregate multiple configuration sources, assign prioritization values to these sources, merge configuration values, and create custom configuration sources.
The application that you will be working with is an inventory
service which stores the information about various JVMs running on different hosts. Whenever a request is made to the inventory
service to retrieve the JVM system properties of a particular host, the inventory
service will communicate with the system
service on that host to get these system properties. You will add configuration properties to simulate if a service is down for maintenance.
You can access the following two microservices to test their availability:
-
http://localhost:9080/system/properties retrieves the information for a specific host
-
http://localhost:9080/inventory/systems retrieves the information for a list of all previously registered hosts
In addition, you can access a third microservice, which retrieves and aggregates all of the configuration properties and sources that are added throughout this guide. This is available at:
Now, navigate to the start
directory to begin.
MicroProfile Config combines configuration properties from multiple sources, each known as a ConfigSource. Each ConfigSource has a specified priority, defined by its config_ordinal
value.
A higher ordinal value means that the values taken from this ConfigSource will override values from ConfigSources with a lower ordinal value.
The following four sources are the default configuration sources:
-
A
<variable name="…" value="…"/>
element in the server.xml file has a default ordinal of 500. -
System properties has a default ordinal of 400. (e.g.
bootstrap.properties
file) -
Environment variables have a default ordinal of 300. (e.g.
server.env
file) -
The
META-INF/microprofile-config.properties
configuration property file on the classpath has a default ordinal of 100.
Access the src/main/resources/META-INF/microprofile-config.properties
local configuration file. This configuration file is the default configuration source for an application that uses MicroProfile Config.
microprofile-config.properties
link:finish/src/main/resources/META-INF/microprofile-config.properties[role=include]
The MicroProfile Config API is included in the MicroProfile dependency that is specified in your pom.xml
file. Look for the dependency with the microprofile
artifact ID. This dependency provides a library that allows you to use the MicroProfile Config API to externalize configurations for your microservices. The mpConfig
feature is also enabled in the src/main/liberty/config/server.xml
file.
pom.xml
link:finish/pom.xml[role=include]
server.xml
link:finish/src/main/liberty/config/server.xml[role=include]
Now navigate to the src/main/resources/META-INF/microprofile-config.properties
local configuration file to check some static configuration. This configuration file is the default configuration source for an application that uses MicroProfile Config.
The io_openliberty_guides_port_number
property that has already been defined in this file, determines the port number of the REST service.
microprofile-config.properties
link:finish/src/main/resources/META-INF/microprofile-config.properties[role=include]
To use this configuration property,
Create theInventoryConfig.java
class.src/main/java/io/openliberty/guides/inventory/InventoryConfig.java
InventoryConfig.java
link:finish/src/main/java/io/openliberty/guides/inventory/InventoryConfig.java[role=include]
Inject the io_openliberty_guides_port_number
property, and add the getPortNumber()
class method to the InventoryConfig.java
file.
The @Inject
annotation injects the port number directly, the injection value is static and fixed on application starting.
The getPortNumber()
method directly returns the value of portNumber
because it has been injected.
Note that three default config sources mentioned above are static and fixed on application starting, so the properties within them cannot be modified while the Liberty is running. However, you can externalize configuration data out of the application package, through the creation of custom configuration sources, so that the service updates configuration changes dynamically.
CustomConfigSource.json
link:finish/resources/CustomConfigSource.json[role=include]
Custom configuration sources can be created by implementing the org.eclipse.microprofile.config.spi.ConfigSource
interface and using the java.util.ServiceLoader
mechanism.
A CustomConfigSource.json
JSON file has already been created in the resources
directory. This JSON file simulates a remote configuration resource in real life. This file contains 4 custom config properties and has an ordinal of 150
. To use these properties in the application, the data object needs to be transformed from this JSON file to the configuration for your application.
To link this JSON file to your application and to implement the ConfigSource
interface,
Create theCustomConfigSource
class.src/main/java/io/openliberty/guides/config/CustomConfigSource.java
CustomConfigSource.java
link:finish/src/main/java/io/openliberty/guides/config/CustomConfigSource.java[role=include]
The getProperties()
method reads the key value pairs from the resources/CustomConfigSource.json
JSON file and writes the information into a map.
Finally, register the custom configuration source.
Create the configuration file.
src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource
org.eclipse.microprofile.config.spi.ConfigSource
link:finish/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource[role=include]
Add the fully qualified class name of the configuration source into it.
Now that the custom configuration source has successfully been set up, you can enable dynamic configuration injection of the properties being set in this ConfigSource. To enable this dynamic injection,
Replace theInventoryConfig.java
class.src/main/java/io/openliberty/guides/inventory/InventoryConfig.java
InventoryConfig.java
link:finish/src/main/java/io/openliberty/guides/inventory/InventoryConfig.java[role=include]
Inject the io_openliberty_guides_inventory_inMaintenance
property, and add the isInMaintenance()
class method.
The @Inject
and @ConfigProperty
annotations inject the io_openliberty_guides_inventory_inMaintenance
configuration property from the CustomConfigSource.json
file. The Provider<>
interface used, forces the service to retrieve the inMaintenance value just in time. This retrieval of the value just in time makes the config injection dynamic and able to change without having to restart the application.
Every time that you invoke the inMaintenance.get()
method, the Provider<>
interface picks up the latest value of the io_openliberty_guides_inventory_inMaintenance
property from configuration sources.
CustomConfigSource.json
link:finish/resources/CustomConfigSource.json[role=include]
Configuration values are purely Strings. MicroProfile Config API has built-in converters that automatically converts configured Strings into target types such as int
, Integer
, boolean
, Boolean
, float
, Float
, double
and Double
. Therefore, in the previous section, it is type-safe to directly set the variable type to Provider<Boolean>
.
To convert configured Strings to an arbitrary class type, such as the Email
class type,
Replace thesrc/main/java/io/openliberty/guides/config/Email.java
Email.java
link:finish/src/main/java/io/openliberty/guides/config/Email.java[role=include]
To use this Email
class type, add a custom converter by implementing the generic interface org.eclipse.microprofile.config.spi.Converter<T>
. The Type parameter of the interface is the target type the String is converted to.
Create theCustomEmailConverter
class.src/main/java/io/openliberty/guides/config/CustomEmailConverter.java
CustomEmailConverter.java
link:finish/src/main/java/io/openliberty/guides/config/CustomEmailConverter.java[role=include]
This implements the Converter<T>
interface.
To register your implementation,
Create the configuration file.
src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter
org.eclipse.microprofile.config.spi.Converter
link:finish/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter[role=include]
Add the fully qualified class name of the custom converter into it.
To use the custom Email
converter,
Replace theInventoryConfig
class.src/main/java/io/openliberty/guides/inventory/InventoryConfig.java
InventoryConfig.java
link:finish/src/main/java/io/openliberty/guides/inventory/InventoryConfig.java[role=include]
Inject the io_openliberty_guides_email
property, and add the getEmail()
method.
To use externalized configuration in the inventory
service,
Replace theInventoryResource
class.src/main/java/io/openliberty/guides/inventory/InventoryResource.java
InventoryResource.java
link:finish/src/main/java/io/openliberty/guides/inventory/InventoryResource.java[role=include]
To add configuration to the inventory
service, the InventoryConfig
object is injected to the existing class.
The port number from the configuration is retrieved by the inventoryConfig.getPortNumber()
method and passed to the manager.get()
method as a parameter.
To determine whether the inventory service is in maintenance or not (according to the configuration value), inventoryConfig.isInMaintenance()
class method is used. If you set the io_openliberty_guides_inventory_inMaintenance
property to true
in the configuration, the inventory service returns the message, ERROR: Service is currently in maintenance
, along with the contact email. The email configuration value can be obtained by calling inventoryConfig.getEmail()
method.
CustomConfigSource.json
link:finish/resources/CustomConfigSource.json[role=include]
While the Liberty is running, the following two microservices should be available to access:
You can find the service that retrieves configuration information that is specific to this guide at the following location:
The config_ordinal
value of the custom configuration source is set to 150
. It overrides configuration values of the default microprofile-config.properties
source, which has a config_ordinal
value of 100
.
CustomConfigSource.json
link:finish/resources/CustomConfigSource.json[role=include]
microprofile-config.properties
link:finish/src/main/resources/META-INF/microprofile-config.properties[role=include]
Play with this application by changing configuration values for each property in the resources/CustomConfigSource.json
file. Your changes are added dynamically, and you do not need to restart the Liberty. Refresh http://localhost:9080/config to see the dynamic changes.
For example, change io_openliberty_guides_inventory_inMaintenance
from false
to true
, then try to access http://localhost:9080/inventory/systems again. The following message displays: ERROR: Service is currently in maintenance
.
Create theConfigurationIT
class.src/test/java/it/io/openliberty/guides/config/ConfigurationIT.java
ConfigurationIT.java
link:finish/src/test/java/it/io/openliberty/guides/config/ConfigurationIT.java[role=include]
microprofile-config.properties
link:finish/src/main/resources/META-INF/microprofile-config.properties[role=include]
The testInitialServiceStatus()
test case reads the value of the io_openliberty_guides_inventory_inMaintenance
configuration property in the META-INF/microprofile-config.properties
file and checks the HTTP response of the inventory service. If the configuration value is false
, the service returns a valid response. Otherwise, the service returns the following message: ERROR: Service is currently in maintenance
.
Because the io_openliberty_guides_inventory_inMaintenance
configuration property is set to false
by default, the testPutServiceInMaintenance()
test case first checks that the inventory service is not in maintenance in the beginning. Next, this test switches the value of the io_openliberty_guides_inventory_inMaintenance
configuration property to true
. In the end, the inventory service returns the following message: ERROR: Service is currently in maintenance
.
The testChangeEmail()
test case first puts the inventory
service in maintenance, then it changes the email address in the configuration file. In the end, the inventory
service should display the error message with the latest email address.
In addition, a few endpoint tests have been provided for you to test the basic functionality of the inventory
and system
services. If a test failure occurs, then you must have introduced a bug into the code. Remember that you must register the custom configuration source and custom converter in the src/main/resources/META-INF/services/
directory. If you don’t complete these steps, the tests will fail. These tests run automatically as a part of the integration test suite.
You see the following output:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.config.ConfigurationIT
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.92 s - in it.io.openliberty.guides.config.ConfigurationIT
Running it.io.openliberty.guides.system.SystemEndpointIT
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.017 s - in it.io.openliberty.guides.system.SystemEndpointIT
Running it.io.openliberty.guides.inventory.InventoryEndpointIT
[WARNING ] Interceptor for {http://client.inventory.guides.openliberty.io/}SystemClient has thrown exception, unwinding now
Could not send Message.
[err] The specified host is unknown.
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.077 s - in it.io.openliberty.guides.inventory.InventoryEndpointIT
Results:
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
The warning and error messages are expected and result from a request to a bad or an unknown hostname. This request is made in the testUnknownHost()
test from the InventoryEndpointIT
integration test.
To see whether the tests detect a failure, remove the configuration resetting line in the setup()
method of the ConfigurationIT.java
file. Then, manually change some configuration values in the resources/CustomConfigSource.json
file. Rerun the tests. You will see a test failure occur.
You just built and tested a MicroProfile application with MicroProfile Config in Open Liberty.
Feel free to try one of the related guides. They demonstrate new technologies that you can learn and expand on top what you built in this guide.