Sunday, February 11, 2007

Maven Profiles

I was struggling with some different configuration files for my development and production environment. As a "weekend-free-time-developer-now-that-I-changed-into-management", my development environment is restricted. So I use the same instance of MySQL for the production database and run some tests on the same server with a different schema and configuration.

Using maven as a build/management tool improved my a lot my performance, but these different environments still gave me some headache. So I decided to leave laziness aside and configure some profiles in my pom files.

Maven profiles allow you to prepare different sets of configuration. There are for different types of profiles, defined in different files and with different purposes:

  1. Per Project: Defined in the POM itself (pom.xml).
  2. Per User: Defined in the Maven-settings (%USER_HOME%/.m2/settings.xml).
  3. Global: Defined in the global maven-settings (%M2_HOME%/conf/settings.xml).
  4. Profile descriptor: a descriptor located in project basedir (profiles.xml)


I decided to use the type number 1, which is a Per-Project configuration type, and unlike number 4 (which has a restricted scope) which is also a Per-Project configuration type it has the ability to change a lot of the project settings because it is in the same pom file as the projects' specifications themselves.

For starters, I wanted to have different jboss' data source xml files for production and development environments. As Maven works with a hierarchy of configuration files I figures the less intrusive way to set the build configuration was to have different folders for each environment. So where I previously had /src/main/resources/WEB-INF now I would have /src/main/resources/development/WEB-INF and /src/main/resources/production/WEB-INF. With the original single path, Maven did not need any special configuration to find my data source xml files (I love "convention over configuration"). With the 2 new folders, I had to add another layer to the hierarchy of Maven's configuration files and this layer would be the profiles.

In my pom.xml, I added a profiles section, obviously enough marked by the tag <profiles>. Here is what I added to my original pom.xml.
  <profiles>
    <profile>
      <id>env-dev</id>
      <build>
        <resources>
          <resource>
            <directory>src/main/resources/development</directory>
          </resource>
        </resources>
      </build>
      <activation>
        <property>
          <name>env-dev</name>
        </property>
      </activation>
    </profile>

    <profile>
      <id>env-prod</id>
      <build>
        <resources>
          <resource>
            <directory>src/main/resources/production</directory>
          </resource>
        </resources>
      </build>
      <activation>
        <property>
          <name>env-prod</name>
        </property>
      </activation>
    </profile>
        
  </profiles>

Now, you can tell maven to build based on different profiles. Where you normally would enter a command like mvn install now you should change to:

mvn install -P env-dev for development environment

OR

mvn install -P env-prod for production environment

After doing this, I was wondering... Which profile will maven choose to execute the build if I do not explicitly tell it?

That is very easy to check! Run the command:
mvn help:active-profiles

It will show you that there are no active profiles, but it will (wrongly IMHO) add BOTH (production and development) folders to your final build, and will not realize that those are actual resource folders.

One simple solution for this issue is to tell maven which profile should be used by default. I modified the profiles tag content to use the production environment as the default one, so this will force me during development to always add the "-P env-dev" while I am testing/developing and this should make me used to always tell maven what I really want. The way to tell maven which one is the default active profile is in bold below.

    <profile>
      <id>env-prod</id>
      <build>
        <resources>
          <resource>
            <directory>src/main/resources/production</directory>
          </resource>
        </resources>
      </build>
      <activation>
        <activeByDefault>true</activeByDefault>
        <property>
          <name>env-prod</name>
        </property>
      </activation>
    </profile>

Now, if you run mvn help:active-profiles again you will see that the env-prod is being used, which is the same as running mvn help:active-profiles -P env-prod.

$ mvn help:active-profiles
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'help'.
[INFO] ----------------------------------------------------------------------------
[INFO] Building Fisio EJB Component
[INFO] task-segment: [help:active-profiles] (aggregator-style)
[INFO] ----------------------------------------------------------------------------
[INFO] [help:active-profiles]
[INFO]
Active Profiles for Project 'com.jc.fisio:fisio-ejb:ejb:0.1-SNAPSHOT':

The following profiles are active:

- env-prod (source: pom)



[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 second
[INFO] Finished at: Sun Feb 11 18:13:13 BRST 2007
[INFO] Final Memory: 2M/5M
[INFO] ------------------------------------------------------------------------

Working with profiles is quite interesting, because it allows you to have multiple active profiles at the same time, providing a number of different combinations. It can be used to may purposes, like setting different application servers, web servers, snapshot version etc.

6 comments:

Seonhyu Kim said...

Good post, thanks ^^

Anonymous said...

I need to put a lib folder that contains 2 jars in the project. I don't want to create a local repository for the 2 jar files.

The 2 jar files are not available in maven repository. Is there a way to do it, or is it better to stick with ant for my situation?

Daniel said...

You could add the two jars to your local repository. Maven always keep copies of the jar files of your dependencies in a local repository where your are building your application.

Check this other post. I guess it might help you: http://thedevelopercorner.blogspot.com/2007/02/install-jar-file-into-mavens-local.html

Anonymous said...

Thanks for the link.

jack smith said...

Its really a good information on profiles, Yesterday i was asked about maven profiles in Interview, i failed to answer the question. :-) bad luck.
Today after i read this article i think i am pretty much comfortable.

Thanks for the information.

Anonymous said...

Hi,
I had the same problem with different resources and no idea; now solved the problem and every runs fine...

Thanks!

matt