OpenLiberty / draft-guide-microprofile-config-apis

Other
0 stars 0 forks source link

// 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:

[source, role="no_copy"]

The defaultServer server is ready to run a smarter planet.

Point your browser to the following URLs:

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:

[role="no_copy"]


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.

// file 0 query/pom.xml [source, XML, linenums, role='code_column']

include::finish/query/pom.xml[]

// file 1 query/server.xml [source, XML, linenums, role='code_column']

include::finish/query/src/main/liberty/config/server.xml[]

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.

// file 2 [role="code_command hotspot file=2", subs="quotes"]

Replace the microprofile-config.properties file.

query/src/main/resources/META-INF/microprofile-config.properties

query/microprofile-config.properties [source, properties, linenums, role="code_column hide_tags=properties,roleAndQuery"]

include::finish/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.

// file 3 [role="code_command hotspot file=3", subs="quotes"]

Replace the SystemResource class.

query/src/main/java/io/openliberty/guides/query/SystemResource.java

query/SystemResource.java [source, java, linenums, role="code_column hide_tags=copyright"]

include::staging/query/src/main/java/io/openliberty/guides/query/SystemResource.java[]

// file 4 system/server.xml [source, XML, linenums, role='code_column']

include::finish/system/src/main/liberty/config/server.xml[]

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.

// file 0 [role="code_command hotspot file=0", subs="quotes"]

Replace the microprofile-config.properties file.

query/src/main/resources/META-INF/microprofile-config.properties

query/microprofile-config.properties [source, properties, linenums, role="code_column hide_tags=roleAndQuery"]

include::finish/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,

// file 1 [role="code_command hotspot file=1", subs="quotes"]

Replace the SystemResource class.

query/src/main/java/io/openliberty/guides/query/SystemResource.java

query/SystemResource.java [source, java, linenums, role="code_column hide_tags=copyright"]

include::finish/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.

// file 0 [role="code_command hotspot file=0", subs="quotes"]

Replace the microprofile-config.properties file.

query/src/main/resources/META-INF/microprofile-config.properties

query/microprofile-config.properties [source, properties, linenums, role="code_column"]

include::finish/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,

// file 1 [role="code_command hotspot file=1", subs="quotes"]

Create the ConfigResource class.

query/src/main/java/io/openliberty/guides/query/ConfigResource.java

query/ConfigResource.java [source, java, linenums, role="code_column hide_tags=copyright,configSystemBean,getSystemConfig,importConfigProperties"]

include::finish/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:

// =========================================================================================== == 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,

// file 0 [role="code_command hotspot file=0", subs="quotes"]

Create the ConfigSystemBean class.

query/src/main/java/io/openliberty/guides/query/ConfigSystemBean.java

query/ConfigSystemBean.java [source, java, linenums, role="code_column hide_tags=copyright"]

include::finish/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,

// file 1 [role="code_command hotspot file=1", subs="quotes"]

Replace the ConfigResource class.

query/src/main/java/io/openliberty/guides/query/ConfigResource.java

query/ConfigResource.java [source, java, linenums, role="code_column hide_tags=copyright"]

include::finish/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.

// file 0 [role="code_command hotspot file=0", subs="quotes"]

Create the QueryEndpointIT class.

query/src/test/java/it/io/openliberty/guides/query/QueryEndpointIT.java

QueryEndpointIT.java [source, java, linenums, role="code_column hide_tags=copyright"]

include::finish/query/src/test/java/it/io/openliberty/guides/query/QueryEndpointIT.java[]

// file 1 query/pom.xml [source, XML, linenums, role='code_column']

include::finish/query/pom.xml[]

See the following descriptions of the test cases:

=== 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:

[source, role="no_copy"]


T E S T S

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:

Tests run: 5, Failures: 0, Errors: 0, Skipped: 0

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"]