Micronaut Configuration - Reading from Property Sources

Let's start by creating a service. Take a look at this post if you would like to know how to create one.

Now that we have a Java service with a controller, let's go ahead and modify the index() method of the controller to return a greeting message. The controller code will look as follows.

package com.nareshak;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;

@Controller("/hello")
public class HelloController {

    @Get("/")
    @Produces(MediaType.TEXT_PLAIN)
    public String index() {
        return "Hello";
    }
}

Let's invoke the endpoint to make sure it works as we intended.

➜  ~ curl -i http://localhost:8080/hello/
HTTP/1.1 200 OK
Date: Sun, 14 Apr 2019 11:38:16 GMT
content-type: text/plain
content-length: 5
connection: keep-alive

Hello

That works as expected. Now suppose we want to configure the message instead of hard coding to 'Hello'. By default, when the CLI generates the project, a configuration file(property source) is created for you with the name application.yml under source -> main -> resources. Let's make an entry for our message to greet.

micronaut:
  application:
    name: hello-service

greeting:
  message: Hello from Micronaut

In addition to yml format, Micronaut also supports .properties, .json and .groovy formats.

Now let's change the controller to read the message from the property source.

package com.nareshak;

import io.micronaut.context.env.Environment;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;

import javax.inject.Inject;

@Controller("/hello")
public class HelloController {

    @Inject
    private Environment environment;

    @Get("/")
    @Produces(MediaType.TEXT_PLAIN)
    public String index() {
        String message = environment.getProperty("greeting.message", String.class).get();
        return message;
    }
}

Here we have obtained the value for 'greeting.message' from the property source using environment.getProperty() method. This method (the one that accepts a config key and the expected type as arguments) returns java.util.Optional<T> (where T is the expected type - String, in the above example). Hence I invoked the get() method to obtain the actual message string. You have to be absolutely sure that the value is present in the property source for the supplied key. Otherwise you will end up with a java.util.NoSuchElementException.

A safer approach is to invoke the orElse method on the Optional type. Let's refactor the code to make use of it.

String message = environment.getProperty("greeting.message", String.class).orElse("Hello");

You would be happy to know that there is a flavour of getProperty method which takes the default value as the third argument. Time to refactor again.

String message = environment.getProperty("greeting.message", String.class, "Hello");

You would be happier to know that Micronaut does provide a more declarative approach to achieve this in the form of @Value annotation. What are we waiting for? Let's dive into make our code more declarative!

package com.nareshak;

import io.micronaut.context.annotation.Value;
import io.micronaut.context.env.Environment;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;

import javax.inject.Inject;

@Controller("/hello")
public class HelloController {

    @Inject
    private Environment environment;

    @Value("${greeting.message}")
    private String message;

    @Get("/")
    @Produces(MediaType.TEXT_PLAIN)
    public String index() {
        return message;
    }
}

That code is more declarative indeed. But wait, what if there is no value supplied in the property source for 'greeting.message'? The default value can be supplied after a ':' as follows.

@Value("${greeting.message:Hello}")
    private String message;