Fork me on GitHub

Programming, Internet & more

Create deployment profiles with maven

A common software development project with more than one developer typically has the requirement to deploy to different servers. On the one hand, there are the developers which would like to deploy locally on their machines to test the application. On the other hand, there is hopefully a test-server, maybe a staging-server and of course there is a production-server.

Mostly the different servers need to have a slightly different configuration of the application. That could be different database connection configurations as well as different logging configurations or debug informations which should only be visible during development.

If you use maven to build your project, you can easily create different profiles for the different servers. In this tutorial I would like to show you how to do this.

The target of this tutorial is to have two configurations (development & production), where each configuration points to another database.

Requirements

*You should have maven installed.
*You should have experience with maven and know how to build your project with maven.

Creating profiles

The first thing we have to do is to create the different profiles for each environment in our pom.xml. I want to create 2 very simple profiles, one for local development and one for production server. Therefore I add the following to my pom.xml:

<project>
...

<profiles>
 <profile>
   <id>profile-development</id>
   <activation>
     <activeByDefault>true</activeByDefault>
     <property>
       <name>environment</name>
       <value>dev</value>
     </property>
   </activation>
 </profile>
 <profile>
   <id>profile-production</id>
   <activation>
    <property>
      <name>environment</name>
      <value>prod</value>
    </property>
   </activation>
 </profile>
</profiles>

...
</project>

Now I have defined my 2 profiles. The id is the profile name to identify the profile. The other interesting thing here is how the profiles could be activated. I defined that the profiles should be activated by a variable named environment. This variable could be passed to maven while creating a new build. For example to activate the profile profile-production I could start maven like this:

mvn clean compile package -Denvironment=prod

The property activeByDefault in the pom.xml indicates which profile should be active by default, thus when no environment variable is given.

To check which profile is currently active you can use the maven command help:active-profiles like this:

mvn clean compile package -Denvironment=prod help:active-profiles

This should give you the following output:

The following profiles are active:

 - profile-production (source: pom)

The last thing to complete configuration of the profiles is to define which properties should be applied to each profile. I would like to define a typical database connection as well as an enviroment property. My property definitions:

<properties>
 <environment>prod</environment>
 <db.username>myProdUser</db.username>
 <db.password>secretProdPasswd</db.password>
 <db.url>jdbc:mysql://production-database/myDb</db.url>
 <db.dialect>org.hibernate.dialect.MySQL5Dialect</db.dialect>
 <db.driver>com.mysql.jdbc.Driver</db.driver>
</properties>

Now my complete profile definition looks like this:

<project>
...
<profiles>
 <profile>
   <id>profile-development</id>
   <properties>
    <environment>dev</environment>
    <db.username>myDevUser</db.username>
    <db.password>secretDevPasswd</db.password>
    <db.url>jdbc:mysql://localhost/myDb</db.url>
    <db.dialect>org.hibernate.dialect.MySQL5Dialect</db.dialect>
    <db.driver>com.mysql.jdbc.Driver</db.driver>
   </properties>
   <activation>
    <activeByDefault>true</activeByDefault>
    <property>
        <name>environment</name>
        <value>dev</value>
    </property>
   </activation>
 </profile>
 <profile>
   <id>profile-production</id>
   <properties>
    <environment>prod</environment>
    <db.username>myProdUser</db.username>
    <db.password>secretProdPasswd</db.password>
    <db.url>jdbc:mysql://production-database/myDb</db.url>
    <db.dialect>org.hibernate.dialect.MySQL5Dialect</db.dialect>
    <db.driver>com.mysql.jdbc.Driver</db.driver>
   </properties>
   <activation>
    <property>
        <name>environment</name>
        <value>prod</value>
    </property>
   </activation>
 </profile>
</profiles>

Creating the properties file

I have defined my database connection in a file called db.properties. This file contains the database configuration keys as well as placeholders which should be replaced during build by maven. The file looks like this:

db.username=${db.username}
db.password=${db.password}
db.url=${db.url}
db.dialect=${db.dialect}
db.driver=${db.driver}

Notice that the syntax of the placeholders is exactly the same as for maven properties in pom.xml. A placeholder must have the same name as we defined before in our profile properties section. For example:

${db.username}

in db.properties will be matched to the defined property

<db.username>myDevUser</db.username>

in our profiles section of our pom.xml if the active profile is profile-development.

Enabling replacement

To finally make the replacement work, we have tell maven that we want to replace placeholders during build. This is called filtering in maven. So, we have to enable filtering in the build section of the pom.xml

<build>
 <finalName>${project.name}</finalName>
 <resources>
  <resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
  </resource>
 </resources>
</build>

With this configuration, maven will replace all placeholders found in any file which is in the src/main/resources directory during build. You can, of course, define multiple directories here.

Which profile was run during build?

If you have a build server and you build for multiple environments from there, sometimes you would like to know which profile was active during the build of a concrete version of your application.

To solve this, I have included another property in the profiles section named environment. I have also a seperate properties file which is called version.properties. This file contains the environment as well as the version of the application and will be replaced during build by maven.

So, if you ever wonder which profile was active during the packaging of your application, you can simply check this file out. That has the advantage, that you don’t have to compare your database connection settings which your profile definition, to know which profile was active.

Conclusion

To test the replacement you can simply run the maven build command.

mvn clean compile

Now, if you look into on of your property-files in your target folder all placeholders should be replaced by the defined profile variables.

Source-Code

You can find a full working example at GitHub.

Category: maven, tutorials

2 Comments

  • Dieter says:

    Hi,

    While this is certainly a good tutorial for explaining how maven profiles work, perhaps it would be a good idea to mention that this can be considered a bad practice.

    Indeed, by specifying maven profiles you are taking configuration differences away from your code, certainly, but not from your application. The only way you can deploy to different machines in this way is to actually generate different artifacts.

    Sadly, this can lead to big and very hard to find errors as this configuration must always lead to the usage of different artifacts on different environment. If we define these properties at Application Server level, this problem does not present itself and we can safely deploy (“promote”) the same artifact over all our different environments.

    While it necessitates maintaining these properties in your application server, I would say it is a much better approach, as you won’t have to rebuild your code from scratch if any configuration property should ever change. A simple Application Server restart would be enough, or even just changing the properties at Application Server level could be sufficient depending on your application.

    Say I have specified a username/password configuration for a certain service in my application. This username/password configuration is certain to change somewhere in the future, and that will mean my application no longer works. Putting this property as part of the AS configuration means that it is a simple matter of alerting the AS admin to the change in username/password and he can adapt his configuration to work. If this configuration is injected in any scope other than application scope, the application will continue to function as expected, instead of requiring a completely new release of the product.

    Kind regards,
    Dieter

  • kamal says:

    I need to run a profile based on boolean value in properties….any suggestions..

    Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *