// Copyright (c) 2023 IBM Corporation and others. // Licensed under Creative Commons Attribution-NoDerivatives // 4.0 International (CC BY-ND 4.0) // https://creativecommons.org/licenses/by-nd/4.0/ // // Contributors: // IBM Corporation :projectid: microprofile-config-apis :page-layout: guide-multipane :page-duration: 25 minutes :page-releasedate: 2023-01-31 :page-description: Learn how to optimize configuration for microservices with MicroProfile Config advanced features. :page-tags: ['MicroProfile'] :page-permalink: /guides/{projectid} :page-related-guides: ['microprofile-config-intro', 'microprofile-config-profile'] :common-includes: https://raw.githubusercontent.com/OpenLiberty/guides-common/prod :source-highlighter: prettify :page-seo-title: Optimizing configuration for microservices with MicroProfile Config advanced features and functionality :page-seo-description: A tutorial and example on how to manage configurations for your Java microservices with property expressions, retrieval of multi-valued properties as lists, configuration source APIs, and bulk-extraction of configuration properties by using Eclipse MicroProfile Config. :guide-author: Open Liberty = Optimizing configuration for microservices
[.hidden] NOTE: This repository contains the guide documentation source. To view the guide in published form, view it on the https://openliberty.io/guides/{projectid}.html[Open Liberty website].
Learn how to optimize configuration management for microservices with advanced features like property expressions, retrieval of multi-valued properties as lists, configuration source APIs, and bulk-extraction of configuration properties by using MicroProfile Config.
:win: WINDOWS :mac: MAC :linux: LINUX
// ================================================================================================= // What you'll learn // ================================================================================================= == What you'll learn
You'll learn how to improve flexibility and maintainability of your application's configurations by using https://openliberty.io/docs/latest/reference/feature/mpConfig-3.0.html[MicroProfile Config^].
MicroProfile Config streamlines the configuration process for microservices by providing a flexible mechanism for application configuration. Applications can use the https://openliberty.io/docs/latest/reference/javadoc/microprofile-5.0-javadoc.html?package=org/eclipse/microprofile/config/package-frame.html&class=org/eclipse/microprofile/config/package-summary.html[MicroProfile Config API^] to eliminate the need for repetitive injection statements and simplify the handling of complex configurations with bulk-extraction of configuration properties, dynamic property expressions, and retrieval of multi-valued properties as lists. Additionally, the configuration source APIs allow for fine-grained control and access to the metadata and values of configuration properties, enabling greater flexibility and customization in managing application configurations.
This guide builds on the basics of the https://openliberty.io/guides/microprofile-config-intro.html[Separating configuration from code in microservices^] guide and the https://openliberty.io/guides/microprofile-config.html[Configuring microservices^] guide. If you are not familiar with externalizing the configuration of microservices, it will be helpful to read https://openliberty.io/docs/latest/external-configuration.html[this document^] and complete those guides before proceeding.
The application that you will be working with is a query
service, which fetches information about the running JVM from a system
microservice. You will use MicroProfile Config to manage configurations for the query
service.
You will learn how to use property expressions in property values and how to retrieve property as a list by using the @ConfigProperty
annotation. You will also learn how to use the configuration source APIs to retrieve information about a single specified configuration property or the metadata of the underlying application configuration. Finally, you will learn how to aggregate configuration properties into a CDI bean and inject it using the @ConfigProperties
annotation.
// ================================================================================================= // Getting Started // =================================================================================================
[role=command] include::{common-includes}/gitclone.adoc[]
// ================================================================================================= === Try what you'll build
The finish
directory contains the finished implementation for the services in the application. Try the finished application before you build your own.
To try out the application, run the following commands to navigate to the finish/system
directory and deploy the system
service to Open Liberty:
[role='command']
cd finish/system
mvn liberty:run
Open another command-line session and run the following commands to navigate to the finish/query
directory and deploy the query
service to Open Liberty:
[role='command']
cd finish/query
mvn liberty:run
After you see the following message in both command-line sessions, both of your services are ready:
Point your browser to the following URLs:
The http://localhost:9085/query/systems/localhost URL returns the current OS and Java properties in JSON format. The URL retrieves the system property information for the localhost
hostname by making a request to the system
service at the \http://localhost:<system.httpPort>/<system.contextRoot>/property/{property}
URL.
The http://localhost:9085/query/config/contact URL returns the source name, source ordinal, and value of the query.contactEmail
property.
The http://localhost:9085/query/config URL returns all registered configuration sources and properties of the running query
service.
The http://localhost:9085/query/config/system URL returns all the configuration properties that are prefixed with system.
After you finish checking out the application, leave the system
service running and stop the query
service by pressing CTRL+C
in the command-line session where you ran the query
service. Alternatively, you can run the following goal from the finish/query
directory in another command-line session:
[role='command']
mvn liberty:stop
// ================================================================================================= == Defining a property with expressions
In MicroProfile Config, property expressions provide a way to set and expand variables in property values. This is useful when the value of one property depends on the value of another property. For example, defining a server.baseURL
property as \http://${server.host}:${server.port}
would automatically update the value of server.baseURL
when the values of server.host
or server.port
change. To use property expressions, define a property with a ${expression}
syntax, where expression
is the name of another configuration property. When the property is loaded, the ${expression}
syntax is replaced with the value of the referenced property.
You can learn more about property expressions and their syntax at the https://download.eclipse.org/microprofile/microprofile-config-3.0.2/microprofile-config-spec-3.0.2.html#property-expressions[MicroProfile Config specification^].
// static guide instructions:
ifndef::cloud-hosted[]
Navigate to the start
directory to begin.
endif::[]
// cloud-hosted guide instructions: ifdef::cloud-hosted[] To begin, run the following command to navigate to the start directory:
cd /home/project/guide-microprofile-config-apis/start
endif::[]
When you run Open Liberty in https://openliberty.io/docs/latest/development-mode.html[dev mode^], dev mode listens for file changes and automatically recompiles and deploys your updates whenever you save a new change. Run the following commands to navigate to the query
directory and start the query
service in dev mode:
[role='command']
cd query
mvn liberty:dev
After you see the following message, your Liberty instance is ready in dev mode:
Dev mode holds your command-line session to listen for file changes. Open another command-line session to continue, or open the project in your editor.
The MicroProfile Config API is included in the MicroProfile dependency that is specified in your [hotspot file=0]query/pom.xml
file. Look for the dependency with the [hotspot=mpconfig file=0]microprofile
artifact ID. This dependency provides a library that allows you to use the MicroProfile Config API to externalize configurations for your microservices. The [hotspot=mpconfig file=1]mpConfig
feature is also enabled in the [hotspot file=1]query/src/main/liberty/config/server.xml
configuration file.
microprofile-config.properties
file.query/src/main/resources/META-INF/microprofile-config.properties
Define a property called [hotspot=userPassword file=2]system.userPassword
that references two other properties, [hotspot=user file=2]system.user
and [hotspot=password file=2]system.password
.
The ${system.user}
and ${system.password}
expressions are replaced with their respective values when the [hotspot=userPassword file=2]system.userPassword
property is loaded, resulting in alice:alicepwd
.
To use this configuration property, add it to the SystemResource
class.
SystemResource
class.query/src/main/java/io/openliberty/guides/query/SystemResource.java
Injects the [hotspot=userPassword file=3]system.userPassword
property instead of the separate system.user
and system.password
properties. Then, use the [hotspot=authHeader file=3]systemUserPassword
variable to construct the authorization header for the system
service.
Note that only registered users can access the system microservice. To authenticate a user, a [hotspot=basicRegistry file=4]basicRegistry
element is configured in the system
Liberty configuration file that is at [hotspot file=4]system/src/main/liberty/config/server.xml
. This basicRegistry
element is a simple case for learning purposes, and for more information about different user registries, see the https://openliberty.io/docs/latest/user-registries-application-security.html[User registries documentation^].
Because you are running the query
service in dev mode, the changes that you made were automatically picked up. You're now ready to check out your application in your browser.
Point your browser to the http://localhost:9085/query/systems/localhost URL. You can see the current OS name and Java version in JSON format.
// =========================================================================================== == Injecting property as a list
Configuration values are just Strings. MicroProfile Config API has built-in converters that automatically convert configured Strings into target types. When injecting a multi-valued property, MicroProfile Config API assumes that the property value is a comma-separated list of items. It then splits the value into individual items and returns them as a List of Strings.
Retrieving a configuration property as a list can be beneficial in situations where the configuration property contains multiple values that need to be accessed and processed within the application. For example, imagine an application that needs to connect to multiple databases. By defining these values as a list of items in the ConfigSource and retrieving them as a list in the application, you can easily access and process each value without having to manually split the values into separate variables.
microprofile-config.properties
file.query/src/main/resources/META-INF/microprofile-config.properties
Define the [hotspot=properties file=0]system.properties
property as a comma-separated list of system properties.
To use this configuration property,
SystemResource
class.query/src/main/java/io/openliberty/guides/query/SystemResource.java
Inject the system.properties
property by using the [hotspot=systemPropertiesInject file=1]@Inject
and [hotspot=systemPropertiesProperty file=1]@ConfigProperty
annotations with a variable type of [hotspot=listStringType file=1]List<String>
.
The [hotspot=listStringType file=1]systemProperties
variable retrieves the list of system properties from the configuration and passes it to the [hotspot=getSystemPropertiesMethod file=1]getSystemProperties()
method.
The [hotspot=getSystemPropertiesMethod file=1]getSystemProperties()
then iterates through this list of system properties by using the [hotspot=systemProperties file=1]systemProperties
variable to retrieve its value from the system
service.
Point your browser to the http://localhost:9085/query/systems/localhost URL. You can see the list of system properties that are retrieved from the system.properties
property in JSON format.
// =========================================================================================== == Using configuration source APIs
MicroProfile Config provides https://openliberty.io/docs/latest/reference/javadoc/microprofile-5.0-javadoc.html?package=org/eclipse/microprofile/config/package-frame.html&class=org/eclipse/microprofile/config/package-summary.html[configuration source APIs^] for obtaining detailed information about your application configuration. These APIs can be used to retrieve various useful information about a single specified configuration property by using the ConfigValue
API class, or to look up the metadata of all underlying configurations of your microservice by using the Config
API class.
The configuration source APIs offer a powerful toolset for managing and utilizing your microservice configuration in a flexible and efficient manner. They enable you to add configurations from various sources, such as the server.xml
, server.env
, and bootstrap.properties
files, or ConfigSources for different environments, such as development
, testing
, and production
. With these APIs, you can easily determine which sources and properties are loaded in your current environment and make changes accordingly. These APIs can also be useful for debugging and troubleshooting purposes.
To explore the capabilities of these APIs, you will first add more configuration properties to the ConfigSource.
microprofile-config.properties
file.query/src/main/resources/META-INF/microprofile-config.properties
Add the [hotspot=role file=0]role
and [hotspot=query file=0]query.*
properties to the [hotspot file=0]microprofile-config.properties
file.
Note that the [hotspot=contactEmail file=0]query.contactEmail
property contains a composed property expression where the ${role}
inner expression is expanded first. The ${query.${role}}
expression provides the admin
default value if the expression doesn't find a value. In this case, the property role
is defined as developer
and the property query.contactEmail
is expanded to alice@ol.guides.com
.
To use the configuration source APIs to retrieve information about these configuration properties,
ConfigResource
class.query/src/main/java/io/openliberty/guides/query/ConfigResource.java
Inject the [hotspot=contactEmail file=1]query.contactEmail
property as a [hotspot=configValue file=1]ConfigValue
type, and add the [hotspot=getContactConfig file=1]getContactConfig()
class method.
The [hotspot=configValue file=1]ConfigValue
metadata object holds additional information after the lookup of the [hotspot=queryContactEmail file=1]query.contactEmail
property. The [hotspot=getSourceName file=1]getSourceName()
, the [hotspot=getSourceOrdinal file=1]getSourceOrdinal()
methods determines the ConfigSource name and the ordinal value of the ConfigSource that loaded the property lookup, respectively. The [hotspot=getValue file=1]getValue()
methods returns the value of the property lookup.
Then, inject the [hotspot=config file=1]Config
metadata object, and add the [hotspot=getConfigSourcesClassMethod file=1]getConfigSources()
and the [hotspot=getConfigProperties file=1]getConfigProperties()
class methods.
The [hotspot=getConfigSources file=1]Config.getConfigSources()
method in the Config API returns all the registered ConfigSources for the current service. These ConfigSources contain the configuration properties that are available to the service.
The [hotspot=getPropertyNames file=1]Config.getPropertyNames()
method in the Config API finds all configuration property names by searching through all the registered ConfigSources.
Now, check out the following endpoints that you just created:
The http://localhost:9085/query/config/contact URL returns the source name, source ordinal, and value of the query.contactEmail
property.
The http://localhost:9085/query/config URL returns all registered configuration sources and properties of the running query
service.
// =========================================================================================== == Injecting configuration as a bulk through @ConfigProperties annotation
MicroProfile Config provides a practical way to aggregate related configuration properties into a single CDI bean by using the @ConfigProperties
annotation. This annotation is applied to a plain old Java object (POJO) that serves as a holder for configuration properties, making it easier to retrieve. Injecting the annotated POJO as a CDI bean into a separate POJO provides a cleaner and more organized approach to managing configuration properties.
To define a CDI bean for the configuration properties that share the system.
prefix,
ConfigSystemBean
class.query/src/main/java/io/openliberty/guides/query/ConfigSystemBean.java
Annotate the [hotspot=ConfigSystemBean file=0]ConfigSystemBean
class with the [hotspot=prefix file=0]@ConfigProperties
annotation.
The [hotspot=prefix file=0]@ConfigProperties
annotation is used to specify that the [hotspot=ConfigSystemBean file=0]ConfigSystemBean
class is a holder for configuration properties. The [hotspot=prefix file=0]prefix
attribute of the annotation specifies the common prefix of the configuration properties, which in this case is system
.
To use the ConfigSystemBean
class,
ConfigResource
class.query/src/main/java/io/openliberty/guides/query/ConfigResource.java
Inject the ConfigSystemBean
CDI bean by using the [hotspot=inject file=1]@Inject
and [hotspot=ConfigProperties file=1]@ConfigProperties
annotations to create the [hotspot=systemConfig file=1]systemConfig
field variable, and add the [hotspot=getSystemConfig file=1]getSystemConfig()
class method.
The [hotspot=getSystemConfig file=1]getSystemConfig()
method retrieves the values of the [hotspot=systemProperties file=1]system.*
configuration properties by using the [hotspot=configSystemBean file=1]systemConfig
bean without having to inject them one by one and returns a Properties object that contains the values of the system.*
configuration properties.
Check out the endpoint that you created at the http://localhost:9085/query/config/system URL. You see all the configuration properties that are prefixed with system.
.
// =========================================================================================== == Testing the application
You will implement several endpoint tests to test the basic functionality of the query
microservice. If a test failure occurs, then you might have introduced a bug into the code.
QueryEndpointIT
class.query/src/test/java/it/io/openliberty/guides/query/QueryEndpointIT.java
See the following descriptions of the test cases:
[hotspot=testQuerySystem file=0]testQuerySystem()
verifies the /query/systems/{hostname}
endpoint.
[hotspot=testQueryConfigContact file=0]testQueryConfigContact()
verifies the /query/config/contact
endpoint.
[hotspot=testQueryConfig file=0]testQueryConfig()
verifies the /query/config
endpoint.
[hotspot=testQueryConfigSystem file=0]testQueryConfigSystem()
verifies the /query/config/system
endpoint.
[hotspot=testUnknownHost file=0]testUnknownHost()
verifies that an unknown host or a host that does not expose their JVM system properties is correctly handled with a fail message.
=== Running the tests
Because you started Open Liberty in dev mode, you can run the tests by pressing the enter/return
key from the command-line session where you started the query
service. If the tests pass, you see a similar output to the following example:
Running it.io.openliberty.guides.query.QueryEndpointIT Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.703 s - in it.io.openliberty.guides.query.QueryEndpointIT
Results:
When you are done checking out the application, exit dev mode by pressing CTRL+C
in the command-line sessions where you ran the system
and query
services.
== Great work! You're done!
You just learned how to use Microfile Config APIs to optimize configuration management for your microservices.
Feel free to try one of the related guides. They demonstrate new technologies that you can learn to expand on what you built in this guide.
include::{common-includes}/attribution.adoc[subs="attributes"]