Friday, December 5, 2008

Maven Profiles: Something you need to know

An issue came up today in our CI environment and I learned something new about maven profiles. Something that everyone who uses maven should know. Perhaps, like your environment, our project uses maven2 to build and we take advantage of multiple profiles (see our example here). A common use for profiles is to define new modules and properties. For example:
<profiles>
<profile>
<id>integration-tests</id>
<modules>
<module>integration</module>
</modules>
<properties>
<app.port>8080</app.port>
</properties>
</profile>
<profiles>

This profile is activated when you run mvn -P integration-tests install. But sometimes you want a profile on all the time. Maven profiles have the ability to be actived by default. To do this just do the following:
<profiles>
<profile>
<id>integration-tests</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>integration</module>
</modules>
<properties>
<app.port>8080</app.port>
</properties>
</profile>
<profiles>

Then you can just run mvn install and that module and property will already be active.

activeByDefault.....Sometimes
Turns out that perhaps maven should have renamed it to activeByDefaultSometimes. Reading their documentation I learned something important about maven profiles:
"This profile will automatically be active for all builds unless another profile in the same pom is activated using one of the previously described methods. All profiles that are active by default are automatically deactivated when a profile in the pom is activated on the command line or through its activation config."
So in my example, if I have more than one profile defined in my pom and I run that new profile, then my integration-tests profile is deactivated. For example, when I run mvn -P SecondProfile, the module and property defined in the integration-tests profiles are not available to my build. One trick to see which profiles are activated when running maven is to run mvn help:active-profiles.

In general I have never been a huge fan of profiles. They feel clumsy, clutter up the cmd line, and are not well known among your team. As their documentation states, "adding profiles to your build has the potential to break portability for your project". Instead of using Profiles, attach to known phases. For example, see my second example from this previous post. In this example, I use the maven-antrun-plugin to attach to the install phase. Now this will run anytime I run mvn install. No clumsy profile I have to memorize and run. Given that, I am not saying you should avoid Profiles all together. Just use them as a last resort.

13 comments:

  1. I agree with you about the phases. I think they provide the first gateway into "making your build system unreadable by humans".

    We have used the phases to do some of our work, my favorite is "pre-integration-test" where you can deploy your code to whatever it needs to live in before you run your integration test.

    However, I think the default maven2 phases leave a lot of thing unaccounted for. I wonder if there is a way to define your own list?

    ReplyDelete
  2. yeah the pre-integration-test phase is a good phase. A lot of our problems exist because a lot of these features of maven2 were not known at the time we started created all these profiles.

    I have also wanted to the ability to define my own phases. Not sure it exists in maven2 but might be a good suggestion for maven3.

    ReplyDelete
  3. I was skeptical at first, as we use activeByDefault profiles. But there is a difference between having them in your pom -vs- having them in your settings.xml.

    I elaborated on this further (sorry, shameless plug) - http://blog.gorshing.net/2009/01/02/maven-default-profiles/

    ReplyDelete
  4. @gorshing
    Yeah defining them in your settings.xml file is another possible solution. However, I don't see it has The solution, because it requires all of the development team and CI environments to modify their local settings. I work on a distributed team across multiple locations and timezones, so not something we want to require.

    I also think internal projects should work more like open source projects as well. And I hate it when I check out an open source projects code and I can't build it. So being able to just check code out and build it with no local modifications is important to me.

    ReplyDelete
  5. Hi James,
    Thanks for this excellent, helpful post. I've been using profiles and classifiers to generate different binaries for development, QA, production, etc. This gives me jars like myapp-1.0.0-dev.jar, myapp-1.0.0-qa.jar, etc. It's exactly what I want but implementing it with profiles and classifiers is extremely brittle just under the covers. Your post makes me feel that way even more. Can you suggest another facility/feature in Maven that might be better for this purpose?
    Thanks,
    -Noah

    ReplyDelete
  6. @Noah
    One possibility would be to have separate modules+pom for each.
    For example a folder structure like this:

    /myapp
    /jar
    pom.xml
    /qa
    pom.xml
    pom.xml

    The jar pom produces a jar that contains everything needed by every one else (the fat jar). Then the qa module has a dependency on the jar artifact, and it slims it down to what it needs producing my-qa.jar.

    This may not fit what your want. Just an idea. We have done similar things in the past like this.

    ReplyDelete
  7. The formatting on that last comment didn't work. This might look better.

    /myapp
    ../jar
    ....pom.xml
    ../qa
    ....pom.xml
    pom.xml

    ReplyDelete
  8. Hi James,
    This could actually work for me. The nice part is that calling "mvn install" at the top level would build and install all the configurations at once. Right now I have to do "mvn install -Pdev", "mvn install -Pqa", etc.

    I'll give it a try - thanks!
    -Noah

    ReplyDelete
  9. Sorry to bother you again, I forgot to ask. Which facility in Maven would I use to "slim down" the fat JAR and alter it in general?

    Thanks,
    -Noah

    ReplyDelete
  10. Good question.
    I was thinking in the context of WARs and not JARs, so not exactly sure.
    But my assumption would be whatever you are currently putting in your profile logic.
    But it all depends on the difference between your dev and qa jar?

    ReplyDelete
  11. I'm actually not slimming down - was just borrowing your terminology. The only differences between the JARs are what properties files I insert into them. You're right that with WARs, this is easier to do. Not sure how to construct a JAR in one subproject (the "JAR" subproject, using your terminology) and add in different properties files in the "dev" and "qa" subprojects. Am checking the docs for any methods that would work.

    ReplyDelete
  12. Not sure there exists an ability to add a file to an existing jar.
    But in your qa module, you could unpackage the jar, add the new files, then jar is back up again.
    Possible look at the jar plugin, dependency plugin, or use the ant run plugin (or my preference the gmaven plugin with ant) with the zip task to unzip and zip.
    Google maven groovy plugin or gmaven on my blog and you can find some examples.

    ReplyDelete
  13. I kinda thought there wasn't anything like that native to Maven... looks like a good excuse to finally use Groovy :-) Thanks again for the help here! Looking forward to checking out more of your posts.

    Best,
    -Noah

    ReplyDelete