When we are writing software that is deployed to different environments, we often have to create different configuration files for each environment. If we are using Maven, we can do this by using build profiles.

This blog post describes how we can create a build script that uses different configuration for development, testing, and production environments.

The requirements of our build process are:

Each profile must have its own configuration file. The name of that configuration file is always config.properties.

The configuration files must be found from the profiles/[profile name] directory.

The development profile must be active by default.

Let’s start by taking a quick look at our example application.

The Example Application

The example application of this blog post has only one class that writes ‘Hello World!’ to a log file by using Log4j. The source code of the HelloWorldApp class looks follows:

import org.apache.log4j.Logger; public class HelloWorldApp { private static Logger LOGGER = Logger.getLogger(HelloWorldApp.class); public static void main( String[] args ) { LOGGER.info("Hello World!"); } }

The properties file that configures Apache Log4j is called log4j.properties, and it is found from the src/main/resources directory. Our log4j.properties file looks as follows:

log4j.rootLogger=DEBUG, R log4j.appender.R=org.apache.log4j.FileAppender log4j.appender.R.File=${log.filename} log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

Our Log4j configuration file looks pretty ordinary, but it has one thing that doesn’t really make any sense. The value of the log4j.appender.R.File property is ${log.filename}.

Our goal is to create a build script that replaces this placeholder with the actual log file path. But before we can create that build script, we have to create the profile specific configuration files. Let’s move on and find out how we can do that.

Creating the Profile Specific Configuration Files

Because we have to create a build script that uses different configuration in development, production, and test environments, we have to create three configuration files that are described in the following:

The profiles/dev/config.properties file contains the configuration that is used in the development environment.

The profiles/prod/config.properties file contains the configuration that is used in the production environment.

The profiles/test/config.properties file contains the configuration that is used in the test environment.

These properties files configure the file path of the log file that contains the log of our example application.

The configuration file of the development profile looks as follows:

log.filename=logs/dev.log

The configuration file of the production profile looks as follows:

log.filename=logs/prod.log

The configuration file of the testing profile looks as follows:

log.filename=logs/test.log

We have now created the properties files that specify the location of our log file. Our next step is create a build script that replaces the placeholder found from the src/main/resources/log4j.properties file with the actual property value. Let’s see how we can do that.

Creating the Build Script

We can create a Maven build script that replaces the placeholder found from the src/main/resources/log4j.properties file with the actual property value by following these steps:

Configure the development, production, and testing profiles. Configure the locations of the properties files that contains the configuration of each Maven profile. Configure the location of our resources and enable resource filtering.

First, we have configure the development, production, and testing profiles in our pom.xml file. We can do this by following these steps:

Create the development profile and configure it to be active by default. Specify a property called build.profile.id and set its value to ‘dev’. Create the production profile. Specify a property called build.profile.id and set its value to ‘prod’. Create the testing profile. Specify a property called build.profile.id and set its value to ‘test’.

We can finish these steps by adding the following XML to our pom.xml file:

<!-- Profile configuration --> <profiles> <!-- The configuration of the development profile --> <profile> <id>dev</id> <!-- The development profile is active by default --> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <!-- Specifies the build.profile.id property that must be equal than the name of the directory that contains the profile specific configuration file. Because the name of the directory that contains the configuration file of the development profile is dev, we must set the value of the build.profile.id property to dev. --> <build.profile.id>dev</build.profile.id> </properties> </profile> <!-- The configuration of the production profile --> <profile> <id>prod</id> <properties> <!-- Specifies the build.profile.id property that must be equal than the name of the directory that contains the profile specific configuration file. Because the name of the directory that contains the configuration file of the production profile is prod, we must set the value of the build.profile.id property to prod. --> <build.profile.id>prod</build.profile.id> </properties> </profile> <!-- The configuration of the testing profile --> <profile> <id>test</id> <properties> <!-- Specifies the build.profile.id property that must be equal than the name of the directory that contains the profile specific configuration file. Because the name of the directory that contains the configuration file of the testing profile is test, we must set the value of the build.profile.id property to test. --> <build.profile.id>test</build.profile.id> </properties> </profile> </profiles>

Second, we have to configure Maven to load the property values from the correct config.properties file. We can do this by adding the following XML to the build section of our POM file:

<filters> <!-- Ensures that the config.properties file is always loaded from the configuration directory of the active Maven profile. --> <filter>profiles/${build.profile.id}/config.properties</filter> </filters>

Third, we have to configure the location of our resources directory and enable resource filtering. We can do this by adding the following XML to the build section of our POM file:

<resources> <!-- Placeholders that are found from the files located in the configured resource directories are replaced with the property values found from the profile specific configuration file. --> <resource> <filtering>true</filtering> <directory>src/main/resources</directory> </resource> </resources>

We have now configured our build script to replace the placeholders found from our resources (files that are found from the src/main/resources directory) with the actual property values. Let’s move on and find out what this really means.

What Did We Just Do?

We have now created a build script that replaces the placeholders found our resources with the property values found from the profile specific configuration file.

In other words, if we compile our project by running the command: mvn clean compile -P test at the command prompt, the log4j.properties file found from the target/classes directory looks as follows (the relevant part is highlighted):

log4j.rootLogger=DEBUG, R log4j.appender.R=org.apache.log4j.FileAppender log4j.appender.R.File=logs/test.log log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

As we can see, the placeholder that was the value of the log4j.appender.R.File property was replaced with the actual property value that was read from the profiles/test/config.properties file.

Let’s move on and summarize what we learned from this blog post.

Summary

This blog post has taught us two things: