Using Maven for multiple deployment environment (production/development)

I have a web app in Maven, with the default directory structure. No problem there. The default directory structure has some property files that point to my localhost database.

Currently I create an Ant script to create different war files - one for production and one for development, using these commands:

ant deploy-dev
ant deploy-prod
ant deploy-sit
ant deploy-uat

So basically they create a war file and then update the war file by plugging in the properties file

Is there something like that in maven (different war created depending on the configuration)?

if so, how do i do that?

i tried mvn warbut it just creates a war


Solution 1:

I prefer use maven profiles for this situation. For example we have directory structure:

src/main/resources
|
+- local
|  |
|  `- specific.properties
+- dev
   |
   `- specific.properties

In pom.xml define two profiles:

<profiles>
    <profile>
        <id>local</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <build>
            <resources>
                <resource>
                    <directory>src/main/resources/local</directory>
                </resource>
            </resources>
        </build>
    </profile>
    <profile>
        <id>dev</id>
        <build>
            <resources>
                <resource>
                    <directory>src/main/resources/dev</directory>
                </resource>
            </resources>
        </build>
    </profile>
</profiles>

In that case, I dont need to update every time pom.xml for new files. In IDE simply switch profiles, or use -P flag from command line.

UPD: what to do if some properties are the same for configurations? Make the configuration like this:

<profiles>
    <profile>
        <id>local</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                </resource>
                <resource>
                    <directory>src/main/config/local</directory>
                </resource>
            </resources>
        </build>
    </profile>
    <profile>
        <id>dev</id>
        <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                </resource>
                <resource>
                    <directory>src/main/config/dev</directory>
                </resource>
            </resources>
        </build>
    </profile>
</profiles>

Common part will be stored in src/main/resources and other configs will be in appropriate folders in config directory.

Solution 2:

FYI best practice is to not have to rebuild your artifact for different environments - as that does not lead to re-produce-able builds, and other things could potentially change when rebuilding. I.e. using resource-filtering, as suggested above, only works when re-building your project.

When you graduate an artifact from dev to test or acceptance test to production - you do not want to have to rebuild.

What you want to do, is actually have your configuration dynamic, dependent on run-time variables. I.e. different spring setups or properties files for different environments e.g:

db-dev.properties
db-test.properties
db-prod.properties

Then you can switch between these configurations using run-time variables and Spring's PropertyPlaceholderConfigurer.

You can also actually use different spring configuration files as well, as I've done in the past, for more complex setups.

I also suggest you leave your 'default' setup as production - so that if you deploy to production, you don't need to worry if you forget to set the environment variable.

Solution 3:

If you want to delete ant from your process, I would look at using build profiles with filters.

In this scenario, plug your properties files into the src/main/resources tree structure. Then parameterize the properties file with filter properties like this:

jdbc.url=${filtered.jdbc.property}

Then inside src/main/filters create filter files based upon profiles. so you could have dev-filters.properties sit-filters.properties, etc. These contain:

filtered.jdbc.property=jdbc url here

Then you setup build profiles for each region where you set an env property pointing to the particular region your building. You can then setup the resources filter to use ${env}-filters.properties for each build. Additionally, you can setup the war plugin to add the env property to your artifact so that you actually store 4 different artifacts in your repository under a different classifier.

You then simply build the app with each profile. You have to call the build for each profile, but it does work well.

Example of some settings in the POM:

<build>
  <filters>
    <filter>src/main/filters/filter-${env}-application.properties</filter>
  </filters>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
  <plugins>
    <plugin>
      <artifactId>maven-war-plugin</artifactId>
      <version>2.1-beta-1</version>
      <executions>
        <execution>
          <phase>package</phase>
          <goals>
            <goal>war</goal>
          </goals>
          <configuration>
            <classifier>${env}</classifier>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

<profiles>
  <profile>
    <id>LOCAL</id>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
    <properties>
      <env>LOCAL</env>
    </properties>
  </profile>
  <profile>
    <id>DEV</id>
    <properties>
      <env>DEV</env>
    </properties>
  </profile>
  <profile>
    <id>UAT</id>
    <properties>
      <env>UAT</env>
    </properties>
  </profile>
  <profile>
    <id>PROD</id>
    <properties>
      <env>PROD</env>
    </properties>
  </profile>
</profiles>

Also, props to this blog post which is where I originally found the steps to accomplish this.

Solution 4:

I have handled this using Spring's PropertyPlaceholderConfigurer and including property files on the classpath and one on the filesystem:

<context:property-placeholder 
    location="classpath*:META-INF/spring/*.properties,file:myapp*.properties"/>

If there is a myapp*.properties file in the current directory when the app starts (or tests are run etc.) it will override properties from files baked into the war/ear/whatever.

Solution 5:

There's this article about it on maven 2 using build profiles. It looks like it just delegates to ant anyways via the antrun plugin so you might even be able to get away with re-using your existing build.xml files.