Enterprise Java

Pimp your config with configuration meta-data in Spring Boot

There were many updates released in the Spring Boot 1.3.0 but one of them stood out to me because I was not aware of this before and the state it got to makes it one really useful feature (unfortunately available only in Spring Boot as of writing this). I am talking about configuration meta-data and the processing related to this area of framework / application. As I will demonstrate further, there are several ways you can make use of it and the framework also allows you to leverage the benefits of automated processing. If you feel the need to take the matters into your own hands don’t worry – there is a way of manual entry as well if you want to use some of the features allowing you to precisely tailor your setup. Let’s talk configuration.

Configuration meta-data in Spring Boot

Let’s face it – we have all been there. The application you are working on must be configurable however when it comes to actual documentation things get bit weird. There are usually several ways the team handles this nifty task. Whether it is described and managed in a project wiki, part of the comments in the property files, written down in Javadoc comments or it does not exists at all, we can all agree that this is far from the desired state of the affairs. There are several challenges involved in this like making the documentation available to all the stakeholders (like devops team), versioning and keeping it up to date (especially updates that are not backwards compatible) or simply making it clear what options are available or deprecated and what do they mean to the application.

Project setup

First step is to set everything up. As mentioned earlier, you will need to use Spring Boot 1.3.0 or newer together with special dependency ensuring recompilation of the file containing metadata that are processed by other tools like IDEs later on. The artifact is called spring-boot-configruation-processor and it should be marked as optional.

Spring boot dependency in pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.1.RELEASE</version>
    <relativePath/>
</parent>
 
<dependencies>
    ...
 
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
 
    ...
</dependencies>

The second step of this setup is to enable configuration properties and actually create a class that contains them. This is a fairly simple task (especially if you have prior experience with Spring Boot). Let’s call this class MyProperties.

Configuration class enabling configuration properties

@Configuration
@EnableConfigurationProperties({MyProperties.class})
public class ApplicationConfiguration {
 
    @Autowired
    private MyProperties myProperties;
 
    public MyProperties getMyProperties() {
        return myProperties;
    }
 
    public void setMyProperties(MyProperties myProperties) {
        this.myProperties = myProperties;
    }
}

Configuration metadata out-of-the-box

MyProperties class reflects properties prefixed with the word my. Now that we have everything setup and ready to go, let’s take a look at how this mechanism works for two most basic cases. Consider these two configuration properties – a single String property (property) and a property using an enum value (copyOption). Both of these properties are described using standard Javadoc, plus in case of StandardCopyOption, each enum value has its own Javadoc comment. Spring Boots support for configuration metadata tries to leverage the maximum from the code since it is expected from developer to comment their configuration properties properly (really useful and handy in case of enums).

Sample configuration properties class

@ConfigurationProperties(prefix = "my")
public class MyProperties {
 
    /**
     * String property used to configure my app.
     */
    private String property;
 
    /**
     * Configuration for file operations.
     */
    private StandardCopyOption copyOption;
 
    public String getProperty() {
        return property;
    }
 
    public void setProperty(String property) {
        this.property = property;
    }
 
    public StandardCopyOption getCopyOption() {
        return copyOption;
    }
 
    public void setCopyOption(StandardCopyOption copyOption) {
        this.copyOption = copyOption;
    }
    
}

Now it is time to see the magic happen. The Spring dependency mentioned earlier ensures that the metadata is generated during the build of your project. In order to get something out of this whole setup, you need to know how does your IDE support this Spring Boot feature. For example, as far as I know, Eclipse IDEs on save action/event triggers a build which takes care of keeping the metadata up-to-date. When it comes to IntelliJ IDEA, you need to trigger the build manually since there is no saving / on save event. Once the build is over, you can explore the target folder (in case of using maven) and look for newly added file target\classes\META-INF\spring-configuration-metadata.json. Given the code above, you should see something similar to this:

Contents of target\classes\META-INF\spring-configuration-metadata.json

{
  "groups": [{
    "name": "my",
    "type": "com.jakubstas.s3downloader.MyProperties",
    "sourceType": "com.jakubstas.s3downloader.MyProperties"
  }],
  "properties": [
    {
      "name": "my.copy-option",
      "type": "java.nio.file.StandardCopyOption",
      "description": "Configuration for file operations.",
      "sourceType": "com.jakubstas.s3downloader.MyProperties"
    },
    {
      "name": "my.property",
      "type": "java.lang.String",
      "description": "String property used to configure my app.",
      "sourceType": "com.jakubstas.s3downloader.MyProperties"
    }
  ],
  "hints": []
}

This file is now available to either a tool to be read and processed, or to a team member to examine to see how to configure the application. That being said, once I open application.properties in IntelliJ IDEAs editor and start typing the prefix for each of my properties, I am greeted by a familiar automatic code completion window that has the notion of what each of my properties is used for (based on the Javadoc comment):

simple-properties

In case of the enum property, I am also able to see each of the enums values with their respective Javadoc comment:

simple-enum-property

Default value selection

One of the most basic use cases for configuration documentation is selecting a sensible default value for your configuration properties so that it is as easy as possible to configure your application. Let’s take a look at how to achieve this in this setup. In order to allow any manual entry into the spring-configuration-metadata.json file, developer must first create a new separate file that is later picked up by the build process. It is another json file called additional-spring-configuration-metadata.json that is supposed to be created in META-INF folder and must follow syntax described in Appendix B. Configuration meta-data.

That way, once I decide on my defaults and available options (I want to present two predefined options yet still allow for any other String values to be used in my String property), I can create that file with following content:

Contents of additional-spring-configuration-metadata.json

{
  "properties": [
    {
      "name": "my.copy-option",
      "type": "java.lang.String",
      "sourceType": "java.nio.file.StandardCopyOption",
      "defaultValue": "replace_existing"
    },
    {
      "name": "my.property",
      "defaultValue": "something"
    }
  ],
  "hints": [
    {
      "name": "my.property",
      "values": [
        {
          "value": "nothing",
          "description": "Does nothing."
        },
        {
          "value": "something",
          "description": "Does something."
        }
      ]
    }
  ]
}

This results in pretty much what one would expect and can already see in some of the Spring Boot dependencies. In case of the String property, both of the options are presented with their respective descriptions. The default option has been bolted.

default-string-property

The behavior of enum property is slightly different since the IDE doesn’t bolt the option, but places it at the top of the list.

default-enum-property

And finally, let’s take a look at what you get when you haven’t selected any particular property yet. In this case, both properties show their descriptions from Javadoc and their default values.

default-properties

Conclusion

This was a short introduction into the way how you can make your configuration shine. This post only scrapped the surface of what is possible to achieve and it still managed to show a great potential of this feature. I encourage you to give this feature a try and see for yourself if the setup like this works for you and your team. I will go into more detail and advanced features of this configuration support in the next post. Stay tuned!

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button