I released the starter package of my Test With Spring course. Take a look at the course >>

Creating Profile Specific Configuration Files With Maven

Three pints of beer

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:

  1. Configure the development, production, and testing profiles.
  2. Configure the locations of the properties files that contains the configuration of each Maven profile.
  3. 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:

  1. 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’.
  2. Create the production profile. Specify a property called build.profile.id and set its value to ‘prod’.
  3. 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:

  • If we need to use different configuration files in different environments, using Maven profiles is one way to solve that problem.
  • If we need to replace placeholders found from our resource files with the actual property values, we can solve this problem by using resource filtering.

About the Author

Petri Kainulainen is passionate about software development and continuous improvement. He is specialized in software development with the Spring Framework and is the author of Spring Data book.

About Petri Kainulainen →

39 comments… add one
  • Moved the example application to GitHub.

    Reply
    • very helpfull, thanks
      I created huge config.prop, works fine

      Reply
      • Svetlana,

        good to hear that I could help you!

        Reply
    • Hi Petri , i don’t exactly how much its relative but i m stuck in problem where i want to move all properties used under from pom.xml to pom.properties file i did it.but its not able to read it

      Reply
  • I created exactly the same project structure but the config.properties file could not be found:

    [INFO] Scanning for projects…
    [INFO]
    [INFO] ————————————————————————
    [INFO] Building java_cukes 1.0-SNAPSHOT
    [INFO] ————————————————————————
    [INFO]
    [INFO] — maven-clean-plugin:2.4.1:clean (default-clean) @ java_cukes —
    [INFO]
    [INFO] — maven-resources-plugin:2.5:resources (default-resources) @ java_cukes —
    [debug] execute contextualize
    [INFO] ————————————————————————
    [INFO] BUILD FAILURE
    [INFO] ————————————————————————
    [INFO] Total time: 0.656s
    [INFO] Finished at: Fri Sep 21 10:13:33 CEST 2012
    [INFO] Final Memory: 3M/15M
    [INFO] ————————————————————————
    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:2.5:resources (default-resources) on proj
    ect java_cukes: Error loading property file ‘C:\Documents and Settings\…..\java
    _cukes\profiles\dev\config.properties’ -> [Help 1]

    Any idea ? Regards.

    Reply
    • Hi Javix,

      as the error states, the Maven Resources plugin tries to look for the properties file from the file path ‘C:\Documents and Settings\…..\java_cukes\profiles\dev\config.properties’ and cannot find it. If the file path is correct, I would move the project to a directory which does not have white space in its path. I am not sure if this is an issue anymore but I remember facing similar problems in the past. Let me know if this did the trick.

      Reply
  • Hey Petri,

    Just a quick thanks for pointing out this feature. Our Maven POM profiles were starting to make the POM quite full & bloated. Being able to split a lot of that environment specific configuration out into separate files is exactly what we needed (and thought wasn’t possible via Maven).

    So thanks a bunch!

    Reply
    • Hi Lee,

      It is great to hear that I could help you out.

      Reply
  • Hello,

    quick thx for this example, it helped me further to configured binaries.

    I was wondering why you have duplicated each tag in the profiles.
    I have moved the build tag out of the profiles.
    Was there any particular reason for it?

    kind regards

    Reply
  • When the project is a web-app, I like to take the properties out of the application completely and put them inside the e.g. the Tomcat “context.xml” so the the app reads them from its environment, rather than having to remember to compile a different version.

    In context.xml:

    Read the values in the app:
    JndiTemplate template = new JndiTemplate();
    String sts = template.lookup(“java:comp/env/Status”);

    Cheers!

    Reply
  • You might be interested in this plugin as an alternative:
    https://github.com/sofdes/config-generation-maven-plugin

    Its very configurable but has sensible defaults. Available from Maven Central:-

    com.ariht
    config-generation-maven-plugin
    0.9.10

    generate/goal>

    Reply
    • That plugin looks very interesting. I will have to take a closer look at it. Thanks for the tip!

      Reply
  • Thanks a lot Petri. This is really very helpful page. Thanks for sharing this. I have one question with the described approach . will it be applicable to placeholders in xml files ? I have weblogic-properties where i m putting the placeholder that can be substituted as per the selected profile but its not working ?

    Reply
    • Yes, you can use this approach with XML files as well. You just have to put your XML files to the correct directory (this example uses the src/main/resources directory). Also, if you include only specific files, you will have to change the configuration to include your XML file.

      I have a few questions about your problem:

      • Which directory contains the weblogic-properties.xml file?
      • Could you add our pom.xml file to Pastebin and add the link here?
      Reply
  • Hi Petri,

    I have been reading your articles for the last week they are great! I tried to run this example though but the jar does not contain the log4j jar, do you know if I have to specify something else when I call the command?
    Thanks

    Reply
    • Hi,

      I tried to run this example though but the jar does not contain the log4j jar, do you know if I have to specify something else when I call the command?

      Yes. You need to declare the Log4j dependency in your pom.xml file. Take a look at the dependencies section of this pom.xml file.

      Reply
      • Thanks, I declared and it compiled, but then to build it you need to also include the assembly plugin as you explain in another of your tutorials, after that it was all good, thanks a lot.

        Reply
        • You are welcome. I am happy to hear that you were able to solve your problem.

          Reply
  • Hi, tank you for this tutorial, it’s very good, I had a small problem when i was running the project, maven was throwing a exception saying “FileNotFoundException: /Users/xxxx/Documents/codigo/web/main/resources/profiles/dev/config.properties” it was fixed changing this
    profiles/${build.profile.id}/config.properties
    for this
    src/main/resources/profiles/${build.profile.id}/config.properties.

    Reply
    • Hi Omar,

      Hi, tank you for this tutorial, it’s very good,

      You are welcome!

      I had a small problem when i was running the project, maven was throwing a exception saying “FileNotFoundException: /Users/xxxx/Documents/codigo/web/main/resources/profiles/dev/config.properties” it was fixed changing this profiles/${build.profile.id}/config.properties for this src/main/resources/profiles/${build.profile.id}/config.properties.

      The example assumes that the profiles directory is found from the root directory of the project. If you create the profiles directory to some other directory, you need to change the value of that property.

      However, If you create the profiles directory under the src/main/resources directory, Maven will add all profile specific configuration files to the created binary (unless you exclude them).

      Reply
  • God bless you for this tutorial, it helped me.

    Reply
    • Thank you for your kind words. I really appreciate them.

      Reply
  • Since in my case (as in yours) there are only three files, I just configured it as three separate files: profiles/${build.profile.id}.properties, instead of three separate directories.

    It works great – very helpful, thanks!

    Reply
    • Thank you for your kind words. I really appreciate them. Also, I agree that adding separate directories is not necessary if you have only a few profile specific files.

      Reply
  • Hi Petri,
    How can I use the property from the config file in java class ( ${} obviously is not working and also I am not using Spring).
    Thanks in advance.

    Reply
    • Hi Mar,

      Take a look at this blog post. It explains how you can read property files from the file system and from the classpath.

      Reply
      • Thank you very much !

        Reply
        • Petri,
          I am currently implementing the same logic in a new project and now I have problem that it couldn’t read the property value -> it prints it as ${test}, not the value ot test property.
          Do you have an idea what I do wrong or what I have forgotten to do?
          Thanks

          Reply
          • The problem was the type of the project when I create maven project with netbeans and when with eclipse, which is completely strange….anyway I resolved the issue.

          • Hi,

            Good to hear that you were able to solve your problem.

  • Hi,

    I have two profiles in my pom.xml and want to build both of them together. i.e. I want my application to build jboss7 as well as was8.5 profiles together. But when I run command, giving both profiles, only was8.5 gets executed as it is written in the end of pom.xml.

    Is there any way by which I can generate both ear’s together.

    Reply
  • Hi Petri,

    Very nice information on profiles in Maven. I have also one requirement where I have to change pom.xml at run time. Is it possible in any way?

    My Scenario is

    I have 2 xml file (testng1.xml and testng2.xml) and I am running 2 xml files via pom.xml but sometimes I have to run either 1 or 2 xml so as of now we are commenting another file and run.

    So any idea or solution for this so that based on our parameter it can run required xml file.

    Any help will be highly appreciated.

    Reply
    • Hi,

      It’s a bit hard to answer to your question because I assume that the plugin you are using to run your unit tests might have support for your use case. Are you by any chance using the Maven Surefire plugin?

      Reply
  • Thanks for this article, helped me in configuring Maven with Selenium.

    Reply
    • You are welcome! I am happy to hear that this blog post was useful to you.

      Reply
      • I tried using the example you provided but the final artifacts that were generated replaced the config.properties values in properties files defined in the main/resources folder within jar created by pom not in the zip file as intended in assembly.xml.

        I am looking for replacing the contents of properties files defined in my main/resources folder with config.properties and the clean install to generate a zip file as defined in assembly.xml

        Reply

Leave a Comment