Configuration is one of the factors of the 12 factor app. Environment specific configuration should never be part of the compiled code. You should never have to modify and rebuild the code to propagate it through the environments en route to production. In this article I discuss how to create a centralised configuration server using Spring Boot and Spring Cloud.

Spring Boot and Spring Cloud makes short work of standing a configuration service up and creating REST endpoints to use that configuration. Spring Cloud Config allows the configuration to be held within a Git repository thus enabling journaling and subjecting the configuration to the same process flow (i.e. pull requests) as the code base and as such can be linked to the story/requirement for traceability.

You can find the source code for this example in GitHub.

Create a configuration service

Creating a configuration service is ridiculously easy with Spring Boot. Just go to start.spring.io, enter your Group and Artifact, enter ‘config server’ in the Search for dependencies box and select Config Server and hit Generate Project.

springstartio

You’ll get a zip file containing a Maven (or Gradle if you so choose) project. Extract this zip file and open the project in your favourite editor of choice.

Open the ConfigServiceApplication.java file and add the EnableConfigServer annotation to the class:

Here we specify the name of the application, the port and the location of the property files that make up the configuration.

Note: Here we are pointing at a local Git repository in the home directory. You could specify the absolute URL to a GitHub repository such as https://github.com/iancollington/config-service-example-config.

Next we need to create the config-service-example-config  directory and initialise it as a Git repository. You will therefore need to have Git installed.

Open a command prompt/shell and do the following:

Under this directory we can create an application.properties file that will contain properties available to every service/application that requests configuration from the configuration server.

For now lets add a global message:

The configuration server will only read files from the Git repository that have been committed. Therefore we need to commit our properties file:

If we now start the application, either via your favourite IDE or from the command line and go to http://localhost:8888/ you get a standard Spring Boot whitelabel error page containing a 404 not found message.

The config server doesn’t serve anything at the root of the web application.

Endpoints in the configuration server take the form of http://<server>:<port>/<application-name>/<profile>/<label> where

  • <server>  and <port>  are self explanatory
  • <application-name>  is that defined in the bootstrap.properties  or application.properties  file by the spring.application.name  property
  • <profile>  comes from the name of the active Spring Profile if specified via the JVM parameter spring.profiles.active . This is useful for different configuration files between environments. e.g. java -jar -Dspring.profiles.active=dev application.jar  will look for a file named <application-name>-dev.properties
  • <label>  is an optional Git label that defaults to ‘master’

In the next section we will create another application named message-service  as we will specify in the application.properties  file. We will therefore be able to access the configuration at http://localhost:8888/message-service/dev.

Given this URL the config will load the following property files if they exist:

  • application.properties
  • message-service.properties
  • message-service-dev.properties

Therefore, even though we have yet to create this message-service, when a client requests the configuration from the configuration server via URL we will get a JSON payload back containing the global configuration defined in application.properties :

If we had a message-service.properties  in that directory we would also get the properties from that file as well.

Let’s create that service now.

Using the configuration service

To make use of this config service, let’s stand up a simple REST endpoint that returns a message in the payload where the message value is defined in the configuration.

Again, we go to start.spring.io, enter the Group and Artifact, and select our required dependencies; Config Client and Web.

springstartio_message-service

After hitting the Generate project button you’ll get a zip file. Extract the zip file and open the project in your favourite editor.

First we need to update the application.properties  file to set the application name, port and the location of the configuration server.

Now in the main application class we can add a /message endpoint by adding the following code:

Here you can see that we are injecting two strings from the configuration properties.

The first is the local message from the app.local.message  property and the second is from the  app.global.message property. These properties are defined in our configuration service.

The app.global.message  is defined in the global application.properties  file. The app.local.message  property is yet to be defined anywhere so lets do that now.

In the ~/config-service-example-config  directory add a new file named message-service.properties  containing a single line:

Commit the change back to Git:

If you take another look at http://localhost:8888/message-service/dev you’ll find that it has picked up the new message-service.properties  file:

Note: Before you can build and run the message-service code. You’ll need to either remove the generated unit test class or add a src/main/resources/application.properties file containing the app.global.message and app.local.message properties such that the unit test can load the properties on the classpath.

Once you have build the code you can start up the application and point your browser at http://localhost:8080/message you should get the following JSON:

Live reload

So now we have the services running, how do we change the configuration? Do we have to restart the services?

The answer is no but we have to enable reloading on the REST endpoint.

This is easy to achieve by adding a simple annotation:

In order for this to work we also need to add the actuator Spring Starter project to the pom.xml  file:

If you now rebuild the Message Service and run it you should get the same as before when you hit the http://localhost:8080/message endpoint.

So lets change that message in message-service.properties  in our Git repository:

Don’t forget to commit the change!

If you take a look at the configuration service you’ll notice the message has indeed been updated.

If you invoke the endpoint again you’ll notice that the message is still the old one. This is because we have to tell the service to refresh it’s configuration.

This can be done by invoking the /refresh  endpoint on the Message Service via a HTTP POST. This can be done using curl at the command line:

This simply instructs curl to send an empty HTTP POST to the /refresh resource on host localhost on port 8080 .

If you now invoke the message endpoint again you’ll see that the message is now the new one.

More information on Spring Cloud Config can be found in the official documentation at http://cloud.spring.io/spring-cloud-config/spring-cloud-config.html.

Ian Collington
 

Ian Collington is a consultant/contract Java developer at Yobibyte Solutions and lives in Northamptonshire, England.

Click Here to Leave a Comment Below 0 comments

Leave a Reply: