Configurer des applications avec MicroProfile Config

MicroProfile Config est la spécification d'Eclipse MicroProfile définissant un mécanisme d'utilisation souple et extensible de configuration des applications ; au moment de l'écriture de cet article, MicroProfile Config est en version 1.3 et est inclus dans la version 2.2 de MicroProfile.
Au moyen de cette API, on peut accéder à l'ensemble des valeurs configurées pour une application sous la forme d'un ensemble de paires clé/valeur de type chaînes de caractères, et provenant de diverses sources de configuration ; celles-ci peuvent être externes, comme les variables d'environnements, ou internes à l'application, comme des fichiers de propriétés embarqués dans des archives.

MicroProfile Config offre un accès unifié à l'ensemble des propriétés (paires clé/valeur) configurées, en considérant un ordre de précédence rattaché à une source de configuration, une priorité plus élévé prenant le pas sur une priorité plus faible.

Microservice d'exemple

Le projet microprofile-config qui est mentionné par la suite est accessible sur Bitbucket : il s'agit d'un microservice que vous pouvez construire avec Maven, et qui a une dépendance à MicroProfile 2.2 ; ce microservice a été testé avec Payara Micro.

Sources de configuration par défaut

Chaque implémentation de cette spécification doit fournir, par défaut, les trois sources de configuration suivantes, ordonnées selon leur niveau de priorité (propriété ordinal de la classe org.eclipse.microprofile.config.spi.ConfigSource) :

  • SystemProperty, de priorité 400, donne accès aux propriétés systèmes.
  • Environment, de priorité 300, aux variables d'environnement.
  • Properties, de priorité 100, avec autant de sources de configuration de ce type qu'il y a de fichiers de propriétés META-INF/microprofile-config.properties présents dans le classpath.

Il est également possible de créer et d'enregistrer des sources de configuration personnalisées (voir la documentation de ConfigSource), et le projet microprofile-config en donne un exemple (avec une source de configuration appelée Dummy).

Accès à la configuration d'une application

org.eclipse.microprofile.config.Config est l'interface donnant accès à la configuration d'une application, et un objet de ce type s'obtient soit par l'une des méthodes static getConfig de la classe ConfigProvider, soit par injection CDI.

Dans le premier cas, voici un exemple d'obtention de la configuration en utilisant la classe org.eclipse.microprofile.config.ConfigProvider :

1
Config config = ConfigProvider.getConfig();

Dans le second cas, Il est encore plus simple d'obtenir la configuration par injection CDI dans un attribut de classe de type Config :

1
2
@Inject
private Config config;

Une propriété peut alors se lire par la méthode générique getValue, comme dans les exemples suivants, en passant le nom de la propriété en paramètre, ainsi que le type dans lequel la valeur de la propriété doit être convertie :

1
2
String username = config.getValue("user.name", String.class);
Long longValue = config.getValue("my.long.property", Long.class);

Convertisseurs

MicroProfile Config dispose de convertisseurs intégrés pour convertir la valeur chaîne de caractères d'une propriété vers un type cible (voir la documentation de l'interface org.eclipse.microprofile.config.spi.Converter).
Les convertisseurs intégrés concernent les types suivants : boolean, Boolean, int, Integer, long, Long, float, Float, double, Double et Class ; et il est possible d'enregistrer des convertisseurs personnalisés (ceux-ci devant supporter l'interface Converter).

MicroProfile Config supporte également le convertisseur tableau pour chaque convertisseur, permettant de convertir une valeur de propriété constituée d'éléments séparés par le délimiteur ,, vers un tableau ; par exemple (à retrouver dans l'application de démonstration) :

1
String[] tags = config.getValue("tags", String[].class);

Obtenir une valeur de propriété de configuration par injection

Combinée avec l'annotation d'injection @Inject de CDI, le qualificateur @ConfigProperty appartenant au package org.eclipse.microprofile.config.inject, permet d'injecter la valeur d'une propriété de configuration dont le nom est spécifié dans l'élément name.
Dans l'exemple de code ci-dessous, on injecte la valeur de la propriété user.name dans l'attribut username d'une classe :

1
2
3
@Inject
@ConfigProperty(name="user.name")
private String username;

Si la propriété user.name n'existe pas, une exception javax.enterprise.inject.spi.DeploymentException sera levée au moment du déploiement de l'application.

L'annotation @ConfigProperty permet également de pouvoir spécifier une valeur par défaut dans le cas où la propriété demandée n'existe pas :

1
2
3
@Inject
@ConfigProperty(name="user.name", defaultValue="odelia")
private String username;

MicroProfile pourra également opérer pour vous une conversion selon le type cible du point d'injection, comme dans l'exemple suivant où la valeur de la propriété est convertie en Long :

1
2
3
@Inject
@ConfigProperty(name="my.long.property")
private Long injectedLongValue;

Les types cibles tableau, List et Set, sont aussi supportés, si l'on souhaite convertir une valeur de propriété constituée d'éléments séparés par le délimiteur , :

1
2
3
@Inject @ConfigProperty(name="tags") private String[] tags;
@Inject @ConfigProperty(name="tags") private List[] tags;
@Inject @ConfigProperty(name="tags") private Set[] tags;

Les annotations @Inject et @ConfigProperty peuvent aussi s'appliquer sur des points d'injection de type Optional<TYPE> ou javax.inject.Provider<TYPE>, où TYPE est un type pour lequel il existe un convertisseur.
Avec Optional<TYPE>, si la propriété n'existe pas, l'Optional n'aura pas de valeur ; avec javax.inject.Provider<TYPE>, il va être possible de demander par la suite, la dernière valeur d'une propriété en appelant la méthode get() de l'interface Provider (faisant partie de Java EE).
Ceci concerne des propriétés potentiellement mises à jour de manière dynamiquement.

Si l'on effectue une injection avec Provider<String> par exemple :

1
2
3
@Inject
@ConfigProperty(name="a.dynamic.property")
Provider<String> injectedProvider;

On pourra par la suite demander la dernière valeur de la propriété a.dynamic.property, en exécutant ceci :

1
String dynamicValue = injectedProvider.get();

Démonstration dans microprofile-config

Le projet de démonstration microprofile-config déclare une ressource JAX-RS par la classe annotée com.odelia.microprofile.config.boundary.ConfigResource.

Une autre méthode de l'interface Config à mentionner, et utilisée dans l'application d'exemple, est getConfigSources() ; celle-ci donne accès à l'ensemble des sources de configurations enregistrées (un Iterable de ConfigSource).
Si vous déployez l'application microprofile-config dans une instance locale de Payara Micro, et accédez à l'URL http://localhost:8080/microprofile-config/resources/configSources, vous obtiendriez une page listant les sources de configuration avec leurs propriétés.

Dans la capture d'écran ci-dessous, on s'est limité à produire trois propriétés pour les quatre premières sources de configuration :

microprofile-config permet également de renvoyer la valeur d'une propriété en suivant une URL de la forme http://localhost:8080/microprofile-config/resources/property/{name}, comme par exemple http://localhost:8080/microprofile-config/resources/property/com.odelia.microprofile.a.property, sachant que la propriété com.odelia.microprofile.a.property est définie dans le fichier de propriété META-INF/microprofile-config.properties de l'application.

microprofile-config.properties contient également une autre propriété, user.name ayant la valeur odelia ; si l'on cherche à obtenir la valeur de cette propriété par le lien http://localhost:8080/microprofile-config/resources/property/user.name ou http://localhost:8080/microprofile-config/resources/username, on pourra obtenir une valeur différente ; dans mon cas payara, car exécutant Payara Micro dans un conteneur Docker, la propriété user.name est définie par la source de configuration SystemProperty qui est de priorité plus élévée.