Chapter 4. Configuration

In this chapter, you’ll learn the following about setting configuration parameters:

  • How to configure a Quarkus service

  • How to inject configuration parameters in the service

  • How to apply values depending on the environment

  • How to correctly configure the logging system

  • How to create customizations for the configuration system

4.1 Configuring the Application with Custom Properties

Problem

You want to configure the Quarkus application with custom properties.

Solution

Quarkus makes use of a number of the Eclipse MicroProfile specifications. One of those is the Configuration specification; however, to simplify configuration, Quarkus uses just one file for all configurations, application.properties, which must be placed in the root of the classpath.

This file can be used to configure Quarkus properties such as logging or default path, Quarkus extensions like data source or Kafka, or custom properties that you define for the application. You are going to see all of them in the book, but in this recipe, you’ll see the latter one.

Open the src/main/resources/application.properties file and add the following property:

greeting.message=Hello World

You can inject the property value defined in application.properties by using the org.eclipse.microprofile.config.inject.ConfigProperty annotation in a field.

Open org.acme.quickstart.GreetingResource.java and inject greeting.message property value:

@ConfigProperty(name = "greeting.message") 1
String message; 2

@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
    return message; 3
}
1

Injects the value of greeting.message property

2

Places fields in package-protected scope

3

Returns the configured value

Tip

For performance reasons when using GraalVM and reflection, we encourage you to use protected-package scope on fields that will be injected at runtime. You can read more about it in the Quarkus CDI Reference Guide.

In a new terminal window, make a request to /hello to see that the output message is the configured value in application.properties:

curl http://localhost:8080/hello

Hello World

If you want to make a configuration field not mandatory and provide a default value, you can use the defaultValue attribute of @ConfigProperty annotation.

Open the org.acme.quickstart.GreetingResource.java file and inject the greeting.upper-case property value:

@ConfigProperty(name = "greeting.upper-case",
                defaultValue = "true") 1
boolean upperCase;
@GET
@Path("/optional")
@Produces(MediaType.TEXT_PLAIN)
public String helloOptional() {
    return upperCase ? message.toUpperCase() : message;
}
1

Sets the default of greeting.upper-case property to true

And in a terminal window, make a request to /hello/optional to see that the output message is in upper case:

curl http://localhost:8080/hello/optional

HELLO WORLD

Multivalue properties are supported—you need to define only the field type as one of Arrays, java.util.List or java.util.Set, depending on your requirements/preference. The delimiter for the property value is a comma (,) and the escape character is the backslash (\).

Open the src/main/resources/application.properties file and add the following property with three values:

greeting.suffix=!!, How are you???

Open org.acme.quickstart.GreetingResource.java and inject greeting.suffix property values:

@ConfigProperty(name = "greeting.suffix")
List<String> suffixes;
@GET
@Path("/list")
@Produces(MediaType.TEXT_PLAIN)
public String helloList() {
    return message + suffixes.get(1);
}

And in a terminal window make a request to /hello/list to see that the output message contains the second suffix:

curl http://localhost:8080/hello/list

Hello World How are you?

The YAML format is also supported for configuring the application. In this case, the file is named application.yaml or application.yml.

To start using the YAML configuration file, you need to add the config-yaml extension:

./mvnw quarkus:add-extension -Dextensions="config-yaml"

Given the following configuration file using the properties format:

greeting.message=Hello World

%staging.quarkus.http.port=8182

quarkus.http.cors=true
quarkus.http.cors.methods=GET,PUT,POST

The equivalent in YAML format follows:

greeting:
  message: Hello World 1
"%staging": 2
  quarkus:
    http:
      port: 8182
quarkus:
  http:
    cors:
      ~: true 3
      methods: GET,PUT,POST
1

Simple properties are set as a structure

2

Profiles are supported wrapped in quotation marks

3

When there are subkeys the ~ is used to refer to the unprefixed part

Discussion

Eclipse MicroProfile Configuration comes with the following built-in converters to map a configuration value into a Java object:

  • boolean and java.lang.Boolean; the values for true are true, 1, YES, Y, and ON, while any other value is considered false

  • byte and java.lang.Byte

  • short and java.lang.Short

  • int and java.lang.Integer

  • long and java.lang.Long

  • float and java.lang.Float

  • double and java.lang.Double

  • char and java.lang.Character

  • java.lang.Class based on the result of the call of Class.forName

If a built-in converter or custom converter does not exist, then the following methods are checked in the target object. If a built-in converter or custom converter does exist, the discovered/found method is used to instantiate the converter object and the string argument is passed for conversion:

  • Target type has public static T of(String) method

  • Target type has public static T valueOf(String) method

  • Target type has public constructor with a String parameter

  • Target type has public static T parse(CharSequence) method

4.2 Accessing Configuration Properties Programmatically

Problem

You want to access configuration properties programmatically instead of injecting them using the org.eclipse.microprofile.config.inject.ConfigProperty annotation.

Solution

Inject the org.eclipse.microprofile.config.Config class in the object for which you want to access properties programmatically.

The Eclipse MicroProfile Configuration spec allows you to inject org.eclipse.microprofile.config.Config to get properties programmatically instead of injecting directly with ConfigProperty.

Open org.acme.quickstart.GreetingResource.java and inject Config class:

@Inject 1
Config config;
@GET
@Path("/config")
@Produces(MediaType.TEXT_PLAIN)
public String helloConfig() {
    config.getPropertyNames().forEach( p -> System.out.println(p)); 2

    return config.getValue("greeting.message", String.class); 3
}
1

Use Inject CDI annotation to inject the instance

2

You can now access the list of properties

3

Property needs to be cast to final type

You can access the Config class without using CDI by calling ConfigProvider.getConfig() method.

4.3 Overwriting Configuration Values Externally

Problem

You want to overwrite any configuration value at runtime.

Solution

You can overwrite any property at runtime by setting it as a system property or environment variable.

Quarkus lets you overwrite any configuration property by setting a configuration as a system property (-Dproperty.name=value) and/or as an environment variable (export PROPERTY_NAME=value). System properties have more priority than environment variables.

Examples of externalizing these properties can be a database URL, username, or password because they are known only in the target environment. But you need to know that there is a trade-off because the more runtime properties are available, the less build time prework Quarkus can do.

Let’s package the application used in Recipe 4.1 and override the greeting.message property by setting a system property:

./mvnw clean package -DskipTests

java -Dgreeting.message=Aloha -jar target/getting-started-1.0-SNAPSHOT-runner.jar

In a new terminal window, validate that the property has been overridden from Hello World to Aloha by running:

curl localhost:8080/hello

Aloha

In the case of environment variables, three naming conventions for a given property name are supported. This is because some operating systems allow only alphabetic characters and underscores (_) but no other characters, like dots (.). To support all possible cases, the following rules are used:

  1. Exactly match (greeting.message).

  2. Replace nonalphanumeric characters to underscore (greeting_message).

  3. Replace nonalphanumeric characters to underscore and convert the rest to upper case (GREETING_MESSAGE).

Here is the application.properties file:

greeting.message=Hello World

You can override its value using any of the following environment variable names because all of them are equivalent:

export greeting.message=Aloha
export greeting_message=Aloha
export GREETING_MESSAGE=Aloha

There is also a special place where you can put the application.properties file outside the application itself, inside a directory named config where the application runs. Any runtime properties defined in that file will override the default configuration.

Important

config/application.properties works in development mode as well, but you need to add it on your build tool output directory to make it work (in case of the Maven, the target directory; in case of Gradle, build), so you need to be aware of the need to re-create it when running the clean task.

Apart from environment variables and the application.properties file, you can also place a .env file in the current working directory to override configuration values, following the environment variables format (GREETING_MESSAGE=Aloha).

4.4 Configuring with Profiles

Problem

You want to overwrite configuration values depending on the environment in which you are running Quarkus.

Solution

Quarkus supports the notion of configuration profiles. These allow you to have multiple configuration values for the same property in the same file and enable different values to suit the environment in which you are running the service.

The syntax for configuration profiles is %{profile}.config.key=value.

Discuss

Quarkus comes with three built-in profiles.

dev

Activated when in development mode (i.e., quarkus:dev).

test

Activated when running tests.

prod

The default profile when not running in development or test mode; you don’t need to set it in application.properties, as it is implicitly set.

Open src/main/resources/application.properties file and set to start Quarkus at port 8181 in development mode:

%dev.quarkus.http.port=8181

After this change, start the service to again check that the listening port is 8181 instead of the default one (8080):

./mvnw compile quarkus:dev

INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed
    in 671ms
INFO  [io.quarkus] (main) Quarkus 1.4.1 started in 1.385s. Listening on:
    http://0.0.0.0:8181
INFO  [io.quarkus] (main) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (main) Installed features:
    [cdi, hibernate-validator, resteasy]

Notice that now the listening address is http://0.0.0.0:8181 instead of the default one.

Finally, rollback to 8080 port, remove %dev.quarkus.http.port=8181 line in application.properties to align with the port that is used in the rest of the book.

4.5 Changing Logger Configuration

Problem

You want to change the default logging configuration.

Solution

Quarkus uses a unified configuration model in which all configuration properties are placed in the same file. In the case of Quarkus, this file is application.properties, and you can configure many aspects of logging there.

For example, if you want to change the logging level, you just set quarkus.log.level to the minimum log level.

Open src/main/resources/application.properties and add the following content:

quarkus.log.level=DEBUG

Now start the application to see that a lot of new logging messages are printed in the console:

./mvnw compile quarkus:dev

...
[INFO] --- quarkus-maven-plugin:0.22.0:dev (default-cli) @ getting-started ---
Listening for transport dt_socket at address: 5005
DEBUG [org.jbo.logging] (main) Logging Provider: \
    org.jboss.logging.JBossLogManagerProvider
INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
DEBUG [io.qua.run.con.ConverterSupport] (main) Populate SmallRye config builder
    with converter for class java.net.InetSocketAddress of priority 200
DEBUG [io.qua.run.con.ConverterSupport] (main) Populate SmallRye config builder
    with converter for class org.wildfly.common.net.CidrAddress of priority 200
Note

We had to span multiple lines for formatting in the book; we have used the backslash to indicate this.

You can also enable storing logs in a file by using quarkus.log.file.enable property. The output is written by default to a file named quarkus.log:

quarkus.log.file.enable=true
Note

While you are in development and working out of the source directory, your logging file will be in target directory.

4.6 Adding Application Logs

Problem

You want to add log lines to your application.

Solution

Most of the time, your applications need to write their own logging messages and not rely solely on the default logs provided by Quarkus. Applications may use any of the supported APIs for logging, and the logs will be merged.

Quarkus supports these logging libraries:

  • JDK java.util.logging

  • JBoss logging

  • SLF4J

  • Apache Commons Logging

Let’s see how to use JBoss Logging to log content. Open org.acme.quickstart.GreetingResource.java and log a message when an special endpoint is called:

private static org.jboss.logging.Logger logger =
                org.jboss.logging.Logger.getLogger(GreetingResource.class); 1

@GET
@Path("/log") 2
@Produces(MediaType.TEXT_PLAIN)
public String helloLog() {
    logger.info("I said Hello"); 3
    return "hello";
}
1

Creates the logger instance

2

Endpoint subpath is /log

3

Logs at info level

Now start the application:

./mvnw compile quarkus:dev

In a new terminal, window make a request to /hello/log:

curl http://localhost:8080/hello/log

If you inspect the terminal where you started Quarkus, you’ll see the next logline:

INFO  [org.acm.qui.GreetingResource] (executor-thread-1) I said Hello

Discussion

Logging is done on a per-category basis. A configuration that applies to a category also applies to all subcategories of that category, unless there is a more specific matching subcategory configuration.

Categories are represented by class location (i.e., the package, or subpackages, where they are defined). For example, if you want to set Undertow security logging to trace level, you need to set the quarkus.log.category."io.undertow.request.security".level=TRACE property in application.properties.

Following the previous example, let’s restrict log lines from classes residing in org.acme.quickstart (and subclasses) so the minimum log level is WARNING:

quarkus.log.category."org.acme.quickstart".level=WARNING 1
1

Double quotes are mandatory to set the category

If you repeat the request to http://localhost:8080/hello/log, logline is no longer written down.

4.7 Advanced Logging

Problem

You want to centrally log all your services.

Solution

When working with microservice architectures and Kubernetes, logging is an important thing to take into consideration because each service is logging individually; but as a developer or operator, you might want to have all the logs centralized in one place so they can be consumed as a whole.

Quarkus logging also supports JSON and GELF output.

These logs can be written in JSON format instead of plain text for machine processing by registering the logging-json extension:

./mvnw quarkus:add-extension -Dextensions="logging-json"

Use the GELF extension to produce logs in GELF format and send them using either TCP or UDP.

Graylog extended log format (GELF) is understood by three of the most centralized logs systems that are used nowadays:

  • Graylog (MongoDB, Elasticsearch, Graylog)

  • ELK (Elasticsearch, Logstash, Kibana)

  • EFK (Elasticsearch, Fluentd, Kibana)

To start logging in GELF format, all you need to do is add the logging-gelf extension:

./mvnw quarkus:add-extension -Dextensions="logging-gelf"

Logging code is not changing, so the same interfaces are used:

private static org.jboss.logging.Logger logger =
                org.jboss.logging.Logger.getLogger(GreetingResource.class); 1

@GET
@Path("/log") 2
@Produces(MediaType.TEXT_PLAIN)
public String helloLog() {
    logger.info("I said Hello"); 3
    return "hello";
}
1

Creates the logger instance

2

Endpoint subpath is /log

3

Logs at info level

The GELF handler must be configured in application.properties:

quarkus.log.handler.gelf.enabled=true 1
quarkus.log.handler.gelf.host=localhost 2
quarkus.log.handler.gelf.port=12201 3
1

Enables extension

2

Sets host where log messages are sent

3

Sets the endpoint port

Important

If you are using Logstash (ELK), you need to enable the Input plug-in that understands the GELF format:

input {
  gelf {
    port => 12201
  }
}
output {
  stdout {}
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
  }
}
Important

If you are using Fluentd (EFK), you need to enable the Input plug-in that understands the GELF format:

<source>
  type gelf
  tag example.gelf
  bind 0.0.0.0
  port 12201
</source>

<match example.gelf>
  @type elasticsearch
  host elasticsearch
  port 9200
  logstash_format true
</match>

Discussion

Quarkus logging also supports syslog format by default without the requirement of adding any extension. Syslog format can be used in Fluentd as an alternative to GELF format in Quarkus:

quarkus.log.syslog.enable=true
quarkus.log.syslog.endpoint=localhost:5140
quarkus.log.syslog.protocol=udp
quarkus.log.syslog.app-name=quarkus
quarkus.log.syslog.hostname=quarkus-test
Important

You need to enable the Input plug-in that understands the syslog format in Fluentd:

<source>
  @type syslog
  port 5140
  bind 0.0.0.0
  message_format rfc5424
  tag system
</source>

<match **>
  @type elasticsearch
  host elasticsearch
  port 9200
  logstash_format true
</match>

If you are using Kubernetes, the simplest way to log is to log to the console and install into the cluster a central log manager that collects all log lines.

See Also

To learn more about advanced logging topics, visit the following website:

4.8 Configuring with Custom Profiles

Problem

You want to set different configuration values for the custom profiles you’ve created.

Solution

So far, you’ve seen that Quarkus comes with built-in profiles so that you can set different configuration values for the same property and enable them to suit the environment. But with Quarkus, you can also set your own profiles.

The only thing you need to do is specify which profile you want to enable by either using the quarkus.profile system property or the QUARKUS_PROFILE environment variable. If both are set, the system property takes precedence over the environment variable.

Then the only thing you need to do is create the property with the profile name and set the current profile to that name. Let’s create a new staging profile that overwrites the listening port of Quarkus.

Open src/main/resources/application.properties file and set to start Quarkus at port 8182 when the staging profile is enabled:

%staging.quarkus.http.port=8182

Then start the application with staging profile enabled:

./mvnw -Dquarkus.profile=staging compile quarkus:dev

INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed
    in 640ms
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 1.300s. Listening on:
    http://0.0.0.0:8182
INFO  [io.quarkus] (main) Profile staging activated. Live Coding activated.
INFO  [io.quarkus] (main) Installed features: [cdi, hibernate-validator,
    resteasy]

In this case, the system property approach is used, but you could also set it using the QUARKUS_PROFILE environment variable.

Discussion

If you want to set the running profile in tests, you only need to set the quarkus.test.profile system property to the given profile in your build script—for example, in Maven:

<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
    <systemPropertyVariables>
        <quarkus.test.profile>foo</quarkus.test.profile>
        <buildDirectory>${project.build.directory}</buildDirectory>
    </systemPropertyVariables>
</configuration>

or, in Gradle:

test {
    useJUnitPlatform()
    systemProperty "quarkus.test.profile", "foo"
}

Also, you can change the default production profile. The built-in profile in Quarkus is prod, so when you are running your application without any profile, this is the default one where the values are taken. But you can change that at build time so that, without specifying any profile, your profile is the default one when the application is running.

The only thing you need to do is build the application using the quarkus.profile system property with the profile value you want set as the default:

./mvnw package -Pnative -Dquarkus.profile=prod-kubernetes`
./target/getting-started-1.0-runner 1
1

The command will run with the prod-kubernetes profile enabled by default

4.9 Creating Custom Sources

Problem

You want to load configuration parameters from any other source instead of/apart from application.properties file.

Solution

Quarkus uses the Eclipse MicroProfile Configuration spec to implement all the logic regarding configuration. The specification offers o⁠r⁠g⁠.⁠e⁠c⁠l⁠i⁠p⁠s⁠e⁠.⁠m⁠i⁠c⁠r⁠o⁠p⁠r⁠o⁠f⁠i⁠l⁠e​.⁠c⁠o⁠n⁠f⁠i⁠g⁠.⁠s⁠p⁠i⁠.⁠C⁠o⁠n⁠f⁠i⁠g⁠S⁠o⁠u⁠r⁠c⁠e Java SPI interface to implement a custom way to load configuration properties instead of/apart from the default one provided by Quarkus.

For example, you could load configuration properties from a database, an XML file, or a REST API.

Let’s create a simple in-memory config source that gets configuration properties from Map populated at instantiation time. Create a new class called org.acme.quickstart.InMemoryConfigSource.java:

package org.acme.quickstart;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.microprofile.config.spi.ConfigSource;

public class InMemoryConfigSource implements ConfigSource {

    private Map<String, String> prop = new HashMap<>();

    public InMemoryConfigSource() { 1
        prop.put("greeting.color", "red");
    }

    @Override
    public int getOrdinal() { 2
        return 500;
    }

    @Override
    public Map<String, String> getProperties() { 3
        return prop;
    }

    @Override
    public String getValue(String propertyName) { 4
        return prop.get(propertyName);
    }

    @Override
    public String getName() { 5
        return "MemoryConfigSource";
    }

}
1

Populates map with a property

2

Used to determine the importance of the values; the highest ordinal takes precedence over the lower-priority ordinal

3

Gets all properties as Map; in this case it is direct

4

Gets the value for a single property

5

Returns the name of this config source

Then you need to register this as a Java SPI. Create the services folder at src/main/resources/META-INF. Next, create a file inside services named org.eclipse.microprofile.config.spi.ConfigSource with the following content:

org.acme.quickstart.InMemoryConfigSource

Finally, you can modify the org.acme.quickstart.GreetingResource.java class to inject this property:

@ConfigProperty(name = "greeting.color") 1
String color;

@GET
@Path("/color")
@Produces(MediaType.TEXT_PLAIN)
public String color() {
    return color;
}
1

Inject the value of the property defined in the InMemoryConfigSource

And in a terminal window make a request to /hello/color to see that the output message is the configured value in the custom source:

curl http://localhost:8080/hello/color

red

Discussion

Each ConfigSource has a specified ordinal, which is used to set the importance of the values taken from the ConfigSource in the case of multiple config sources defined for the same application. A higher ordinal ConfigSource is used over a ConfigSource with a lower value. Using the defaults in the following list as a reference, a system property will be used over everything, and the application.properties file in the src/main/resources directory will be used if no other ConfigSources are found:

  • System properties to 400

  • Environment variables to 300

  • application.properties at config directory to 260

  • application.properties at project to 250

4.10 Creating Custom Converters

Problem

You want to implement a custom converter.

Solution

You can convert a property from String to any kind of object by implementing the org.eclipse.microprofile.config.spi.Converter Java SPI.

Quarkus uses the Eclipse MicroProfile Configuration spec to implement all the logic regarding configuration. The specification offers the org.eclipse.microprofile.config.spi.Converter Java SPI interface to implement the conversion of configuration values to a custom type.

For example, you could transform a percentage value (i.e., 15%) to a Percentage type, wrapping the percentage as double type.

Create a new POJO class org.acme.quickstart.Percentage.java:

package org.acme.quickstart;

public class Percentage {

    private double percentage;

    public Percentage(double percentage) {
        this.percentage = percentage;
    }

    public double getPercentage() {
        return percentage;
    }

}

And then create a class org.acme.quickstart.PercentageConverter.java that converts from String representation to Percentage:

package org.acme.quickstart;

import javax.annotation.Priority;

import org.eclipse.microprofile.config.spi.Converter;

@Priority(300) 1
public class PercentageConverter implements Converter<Percentage> { 2

    @Override
    public Percentage convert(String value) {

        String numeric = value.substring(0, value.length() - 1);
        return new Percentage (Double.parseDouble(numeric) / 100);

    }

}
1

Sets the priority; in this specific case it might be optional

2

Generic type that sets the type to convert to

Then you need to register this as a Java SPI. Create the services folder at src/main/resources/META-INF. Next, create a file inside the services folder named org.eclipse.microprofile.config.spi.Converter with the following content:

org.acme.quickstart.PercentageConverter

Then, you can modify the org.acme.quickstart.GreetingResource.java class to inject this property:

@ConfigProperty(name = "greeting.vat")
Percentage vat;

@GET
@Path("/vat")
@Produces(MediaType.TEXT_PLAIN)
public String vat() {
    return Double.toString(vat.getPercentage());
}

Lastly, you will need to add a new property into the application.properties file in your src/main/resources directory:

greeting.vat = 21%

And in a terminal window, make a request to /hello/vat to see that the output message is the transformed vat as double:

curl http://localhost:8080/hello/vat

0.21

Discussion

By default, if no @Priority annotation can be found on a converter, it is registered with a priority of 100. Quarkus converters are registered with a priority of 200, so if you want to replace a Quarkus converter, you should use a higher value; if you don’t need to replace a Quarkus converter, then the default one is perfectly fine.

A list of Quarkus core converters has been shown in Recipe 4.1.

4.11 Grouping Configuration Values

Problem

You want to avoid setting the common prefix of a configuration property over and over again.

Solution

You can group common properties (those with the same prefix) using the @⁠i⁠o​.⁠q⁠u⁠a⁠r⁠k⁠u⁠s⁠.⁠a⁠r⁠c⁠.⁠c⁠o⁠n⁠f⁠i⁠g⁠.⁠C⁠o⁠n⁠f⁠i⁠g⁠P⁠r⁠o⁠p⁠e⁠r⁠t⁠i⁠e⁠s annotation.

When you are creating ad hoc configuration properties in your application, typically these properties will have the same prefix (i.e., greetings). To inject all these properties, you can use the @ConfigProperty annotation (as shown in Recipe 4.1), or you can use the io.quarkus.arc.config.ConfigProperties annotation to group properties together.

Using the application.properties file:

greeting.message=Hello World
greeting.suffix=!!, How are you???

let’s implement a class that maps the configuration properties into Java objects using the io.quarkus.arc.config.ConfigProperties annotation. Create a new class org.acme.quickstart.GreetingConfiguration.java:

package org.acme.quickstart;

import java.util.List;
import java.util.Optional;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;

import io.quarkus.arc.config.ConfigProperties;

@ConfigProperties(prefix = "greeting") 1
public class GreetingConfiguration {

    public String message; 2
    public String suffix = "!"; 3
}
1

Sets this as a configuration POJO with a common prefix

2

Maps the greeting.message property

3

The default value for greeting.suffix in case the property is not set

One of the important things to notice in the preceding code is that the prefix attribute is not mandatory. If it is not set, then the prefix to be used will be determined by the class name (removing the suffix part Configuration). In this case, the prefix attribute could be auto-resolved to greeting.

Then you can inject this configuration POJO to start consuming the configuration values.

You can modify the org.acme.quickstart.GreetingResource.java class to inject this class:

@Inject 1
GreetingConfiguration greetingConfiguration;

@GET
@Path("/configurations")
@Produces(MediaType.TEXT_PLAIN)
public String helloConfigurations() {
    return greetingConfiguration.message + greetingConfiguration.suffix;
}
1

The configuration is injected with the CDI @Inject annotation

And in a terminal window make a request to /hello/configurations to see that the configuration values are populated inside Java, for instance:

curl http://localhost:8080/hello/configurations

Hello World!!, How are you???

As you can now see, you don’t need to annotate every field by using @ConfigProperty—you just leverage the class definition to get the property name or the default value.

Discussion

Furthermore, Quarkus supports nested object configuration so that you can also map subcategories by using inner classes.

Suppose we add a new property named greeting.output.recipients in application.properties:

greeting.output.recipients=Ada,Alexandra

You could use an inner class to map it into the configuration object. Modify the class org.acme.quickstart.GreetingConfiguration.java. Then add a new inner class representing the subcategory output and register it as a field:

public OutputConfiguration output; 1

public static class OutputConfiguration {
    public List<String> recipients;
}
1

Name of the subcategory is the field name (output)

Then you can access the greetingConfiguration.output.recipients field to get the value. You can also annotate the fields with Bean Validation annotations to validate at start-up time that all configuration values are valid. If they are not valid, the application will fail to start and will indicate the validation errors in the log.

4.12 Validating Configuration Values

Problem

You want to validate that configuration values are correct.

Solution

Use the Bean Validation specification to validate that a property value is valid when it is injected using the @ConfigProperty annotation on a class.

The Bean Validation spec allows you to set constraints on objects using annotations. Quarkus integrates the Eclipse MicroProfile Configuration spec with the Bean Validation spec so you can use them together to validate that a configuration value meets certain criteria. This verification is executed at boot time, and if there is any violation, an error message is shown in the console and the boot process is aborted.

The first thing you need to do is to register the Quarkus Bean Validation dependency. You can do it manually by editing your pom.xml or by running the next Maven command from the root directory of the project:

./mvnw quarkus:add-extension -Dextensions="quarkus-hibernate-validator"

After that, you will need to create a configuration object, which you learned about in the previous recipe. In the next example, a constraint on the greeting.repeat configuration property is set so that repetitions outside of the range 1–3 inclusive cannot be set.

To validate integer range, the following Bean Validation annotations are used: j⁠a⁠v⁠a⁠x​.⁠v⁠a⁠l⁠i⁠d⁠a⁠t⁠i⁠o⁠n⁠.⁠c⁠o⁠n⁠s⁠t⁠r⁠a⁠i⁠n⁠t⁠s⁠.⁠M⁠a⁠x and javax.validation.constraints.Min. Open org.acme.quickstart.GreetingConfiguration.java and add Bean Validation annotations:

@Min(1) 1
@Max(3) 2
public Integer repeat;
1

Min value accepted

2

Max value accepted

Open src/main/resources/application.properties file and set the greeting.repeat configuration property to 7:

greeting.repeat=7

Start the application, and you’ll see an error message notifying that a configuration value is violating one of the defined constraints:

./mvnw compile quarkus:dev

Discussion

In this example, you’ve seen a brief introduction to Bean Validation specification, as well as some annotations you can use to validate fields. However, more constraints are supported by Hibernate Validation and the Bean Validation implementation used, such as @Digits, @Email, @NotNull, and @NotBlank.

Get Quarkus Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.