Creating Profile Specific Configuration Files With Maven

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.
57 comments… add one
  • Petri Dec 18, 2011 @ 16:46

    Moved the example application to GitHub.

    • Svetlana Jul 25, 2012 @ 20:11

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

      • Petri Kainulainen Jul 25, 2012 @ 20:58

        Svetlana,

        good to hear that I could help you!

    • Sunny Dec 4, 2015 @ 9:09

      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

  • Javix Sep 21, 2012 @ 11:14

    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.

    • Petri Sep 21, 2012 @ 12:07

      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.

  • Lee Theobald Dec 7, 2012 @ 13:12

    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!

    • Petri Dec 7, 2012 @ 14:09

      Hi Lee,

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

  • Wim Anckaert Oct 25, 2013 @ 15:45

    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

  • Guy Nov 20, 2013 @ 17:07

    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!

  • David Jun 24, 2014 @ 11:07

    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>

    • Petri Jun 24, 2014 @ 22:40

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

  • Indra Aug 19, 2014 @ 0:07

    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 ?

    • Petri Aug 20, 2014 @ 20:02

      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?
  • Runnerdave Oct 8, 2015 @ 12:33

    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

    • Petri Oct 8, 2015 @ 12:38

      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.

      • Runnerdave Oct 14, 2015 @ 22:28

        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.

        • Petri Oct 15, 2015 @ 19:03

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

  • Omar Oct 27, 2015 @ 23:52

    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.

    • Petri Oct 29, 2015 @ 10:23

      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).

  • Petr Feb 11, 2016 @ 12:23

    God bless you for this tutorial, it helped me.

    • Petri Feb 12, 2016 @ 23:48

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

  • Marin Mar 24, 2016 @ 22:36

    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!

    • Petri Mar 25, 2016 @ 15:50

      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.

  • Mar Sep 15, 2016 @ 17:29

    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.

    • Petri Sep 15, 2016 @ 23:55

      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.

      • Mar Sep 16, 2016 @ 10:21

        Thank you very much !

        • Petri Sep 16, 2016 @ 14:14

          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

          • Anonymous Sep 16, 2016 @ 16:30

            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.

          • Petri Sep 19, 2016 @ 18:31

            Hi,

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

          • Tester Oct 12, 2022 @ 13:22

            Bro...I am also facing the same...did u get any help

  • Mohak Gupta Sep 30, 2016 @ 10:18

    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.

  • Mukesh Otwani Nov 8, 2016 @ 13:47

    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.

    • Petri Nov 16, 2016 @ 22:14

      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?

  • Naira Kapoor Jan 3, 2017 @ 19:42

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

    • Petri Jan 4, 2017 @ 15:11

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

      • Khan Mar 4, 2017 @ 6:11

        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

  • ersan Apr 8, 2017 @ 15:56

    Hi Petri

    Thanks a lot for the post. It was very useful to me.

    I used same approach as suggested in the post. And tests run in command line as I expect.
    But, I'm not able to run any test in IDE (Intellij 2016). It gives "No tests were found. Empty test suite" error. If I comment out filters and testResources in pom, then IDE runs the tests.
    I use surefire plugin.
    Any idea what it could be?

    thanks

  • Anonymous Apr 18, 2017 @ 7:02

    is there any way in which we can encrypt this property file ?

  • ashwini Feb 5, 2018 @ 14:19

    I am using the above example. instead of logger file I am using server.port=8085 different in different environment files. But its not working. It always picks up default port.

    • Petri Feb 5, 2018 @ 16:38

      It's hard to say what could be wrong because you didn't share your POM file and the property files, but you said that your Maven build always uses the default port. Where to do you specify this default port?

      Also, does your "real" property file contain the following line:

      
      server.port=${server.port}
      
      
      • Tester Oct 12, 2022 @ 13:18

        I am also getting the similar issue sir...I am using dbConfig as main properties file it will get values from xxx.properties or yyy.properties......when I run I am getting value as ${port}...spend lot of time on this but no luck please help me sir

      • tester Oct 12, 2022 @ 13:20

        jfyi my pom.xml is same as urs....hoping my teammate for this from this url

        • Petri Oct 20, 2022 @ 18:19

          Hi,

          Unfortunately it's impossible to say what's wrong because I cannot see your POM file and your properties files. Could you create a demo project which allows me to reproduce your problem and add this project to Github (or Gitlab)?

  • Sandeep Mar 11, 2018 @ 12:17

    Hello sir, Your blog is very useful i'm also working on Maven Thanks it will be beneficial for us.

    • Petri Mar 12, 2018 @ 10:30

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

  • Mitesh Lodhiya Mar 26, 2018 @ 9:54

    Petri Kainulainen
    Thanks Alot

    • Petri Mar 26, 2018 @ 22:14

      You are welcome.

  • Rams Jul 3, 2018 @ 21:40

    Below is my requirement
    I have 4 components with same profile and property file. These 4 components are not dependent on each other. There should be one common place to place the property files and accessible to these 4 components. The same property file copy should be used by the 4 components

    Please suggest me the different ways to place/access the property files

    thank you

    • Petri Jul 5, 2018 @ 11:50

      Hi,

      I assume that you want to use the properties file for configuring the application (and not for passing properties to the Maven module). If so, you should take a look at this StackOverflow answer.

      • rams Jul 5, 2018 @ 21:07

        Hi Petri,

        Thanks for quick response.
        But i am not looking for the application configuration. the property file is having database(JNDI) details which are common for all the four moduels.
        Currently i added the property file in my module as below but cdt-model is not dependancy to my current module. here ${env} is the profile
        ../cdt-model/src/main/resources/${env}/CDT-${env}.properties
        <!-- src/main/resources/${env}/CDT-${env}.properties -->

        Suggest me different options for adding this property file.

  • Rams Jul 3, 2018 @ 22:06

    What about the Test suite for these property file. How can we mock it

    • Petri Jul 5, 2018 @ 11:55

      Hi,

      I have typically created a new Maven profile that is activated when I run my tests. This way I can just create a test specific properties file and I don't have to mock anything.

      Also, if you are using Spring Boot and you can annotate your tests with the @ActiveProfiles annotation, you can use the properties support provided by Spring Boot. For Example, if your active profile is called: integrationTest, you can create a properties file called application-integrationTest.properties and Spring Boot uses this properties file when the integrationTest Spring profile is active (when you run your tests).

  • Anonymous Nov 9, 2018 @ 18:12

    what is in the logs/dev.log file and what is it for?

Leave a Reply