Micronaut Environment Specific Configuration

In my previous post I described the anti-pattern of rebuilding your artifacts for every environment and explained why it is a bad idea. I also mentioned that modern frameworks support environment specific out of the box. In this post let's see this in action using Micronaut.

I will be using the code creating in post on reading from property sources.

Here is what we configured in application.yml

micronaut:
  application:
    name: hello-service

greeting:
  message: Hello from Micronaut

Suppose we want the value of 'greeting.message' to be different in different environments. It is quite easy to achieve in Micronaut as it readily provides ways to achieve such configurations. The convention is to have property sources for each environment. Let's stick to yml configuration for now. The default property source here is application.yml. Now if you have a UAT environment, you specify the corresponding values in 'application-uat.yml'. Similarly, you define the values for production in 'application-prod.yml'. To generalise, you create a file 'application-.yml' for each of your environments.

Here is the controller we are going to invoke.

package com.nareshak;

import io.micronaut.context.annotation.Value;
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 {

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

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

Let's produce a fat jar of the service using Gradle.

➜  hello-service ./gradlew build

> Task :compileJava
Note: Creating bean classes for 1 type elements

BUILD SUCCESSFUL in 6s
12 actionable tasks: 10 executed, 2 up-to-date

The above command produces the fat jar under 'build/libs'. Let's switch to the directory where fat jar is present and run the jar file.

➜  hello-service cd build/libs
➜  libs ls
hello-service-0.1.jar
➜  libs java -Dmicronaut.environments=uat -jar hello-service-0.1.jar
17:34:19.135 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [uat]
17:34:20.002 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 973ms. Server Running: http://localhost:8080

We can instruct Micronaut to activate specific environments by providing the value of "micronaut.environments" system property. One can specify multiple environment values separated by commas. Note the startup INFO log listing all the active environments.

Let's invoke the endpoint and make sure the controller is getting the value from 'uat' property source.

➜  ~ curl -i http://localhost:8080/hello
HTTP/1.1 200 OK
Date: Thu, 18 Apr 2019 12:04:24 GMT
content-type: text/plain
content-length: 26
connection: keep-alive

Hello from UAT Environment

Inf you wish to access the active environments programmatically, you could take the following approach.

package com.nareshak;

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

import javax.inject.Inject;
import java.util.Set;

@Controller("/info")
public class InfoController {

    @Inject
    Environment environment;

    @Get("/")
    public Set<String> index() {
        return environment.getActiveNames();
    }
}

Let' invoke the endpoint.

➜  ~ curl -i http://localhost:8080/info
HTTP/1.1 200 OK
Date: Thu, 18 Apr 2019 12:23:03 GMT
content-type: application/json
content-length: 7
connection: keep-alive

["uat"]

I hope this encourages you to avoid rebuilding your jars per environment and you move a step ahead in your journey of continuous delivery.

Show Comments