Tuesday, June 30, 2009

Setting Maven Properties With Groovy

Goal


While I generally try to configure, rather than script, Maven builds, sometimes the publicly-available plug-ins do not provide enough flexibility to work-around limitations in third-party libraries through configuration alone. Fortunately, GMaven exposes the flexibility of Groovy in a Maven plug-in. This post demonstrates how to use a Groovy script to transform a Maven project property. Such a transformation is sometimes necessary, for example, for transforming a Windows-style path to Unix. For myself, I formalized this solution while trying to install a jar in PostgreSQL automatically during the artifact deployment phase.

Display a Maven Project Property


We can start by creating a simple m2eclipse project, by adding a single property with a default value and by configuring the pom.xml to display this property on the console during a lifecycle event (here, during compilation).

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>timezra.blog.maven.groovy.properties</groupId>
  <artifactId>timezra.blog.maven.groovy.properties</artifactId>
  <name>timezra.blog.maven.groovy.properties</name>
  <version>0.0.1-SNAPSHOT</version>
  <description>An example of setting Maven properties using Groovy.</description>
  <properties>
    <unixy_build_directory>${project.build.directory}</unixy_build_directory>
  </properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <executions>
          <execution>
            <id>show-unixy_build_directory!</id>
            <phase>compile</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <tasks>
                <echo>unixy_build_directory: ${unixy_build_directory}</echo>
              </tasks>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>



If we run the compile goal, we will see the bound Maven property in the build output.

  > mvn compile
  ....
  [INFO] [antrun:run {execution: show-unixy_build_directory!}]
  [INFO] Executing tasks
       [echo] unixy_build_directory: C:\programming\workspaces\blog\timezra.blog.maven.groovy.properties\target
  [INFO] Executed tasks
  ....


Setting up the gmaven-plugin is straightforward, as is re-binding the property with Groovy in the pom.xml.

<project ....>
  ....
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.groovy.maven</groupId>
        <artifactId>gmaven-plugin</artifactId>
        <executions>
          <execution>
            <id>set-unixy_build_directory!</id>
            <phase>compile</phase>
            <goals>
              <goal>execute</goal>
            </goals>
            <configuration>
              <classpath>
                <element>
                  <groupId>commons-lang</groupId>
                  <artifactId>commons-lang</artifactId>
                  <version>2.4</version>
                 </element>
              </classpath>
              <source>
                if (org.apache.commons.lang.SystemUtils.IS_OS_WINDOWS) {
                  project.properties.unixy_build_directory =
                  project.build.directory.replace("\\", "/");
                }
              </source>
            </configuration>
          </execution>
        </executions>
      </plugin>
      ....
    </plugins>
  </build>
</project>


NB: This example takes a Windows file path and transforms the backslashes to forward slashes. If you are not running Windows, then this example is moot. Please, experiment with the bindings in the pom.xml to demonstrate clearly to yourself that the property has really, really, really been re-bound.

When compiling, we will now see output indicating that the Groovy script has mutated the Maven property.

  > mvn compile
  ....
  [INFO] [antrun:run {execution: show-unixy_build_directory!}]
  [INFO] Executing tasks
       [echo] unixy_build_directory: C:/programming/workspaces/blog/timezra.blog.maven.groovy.properties/target
  [INFO] Executed tasks
  ....


Conclusion


With a few lines of configuration and a simple Groovy script, we are able to modify Maven properties as part of a lifecycle event. This tool adds even more power to your Maven builds, but, as stated above, it should be used cautiously and only when absolutely necessary. If there is a solution already in the Maven toolkit, then that is generally better. Not all project requirements fit in the box, however, and this example exposes a simple way to handle the non-ideal.

13 comments:

Verhás Péter said...

Thanks. This is just the tool I needed after maven converted c:\dir to c\:\\dir that obviously does not work

Tim Myer said...

Szevasz, Péter!
Köszönöm a megjegyzést.

I am very happy to help and always appreciate the feedback.

---Tim---

Arul Anand said...

Hi,

Is it possible to pass the value for the property "unixy_build_directory" using the command and override the default value specified.

something like
mvn compile -Dunixy_build_directory=c:/testdirectory/

Tim Myer said...

Hi Anand,
I do not see why not, but I generally would prefer to have Maven figure out what the build directory is rather than to require that a user pass in a hard-coded path. I might as well just have hard-code that property value inside the pom.xml. Setting static Maven properties is not the focus of this blog entry. Setting Maven properties dynamically using Groovy is the focus.
---Tim---

Verhás Péter said...

Google seems to be lacking proper spam filtering these days. I experience it on my other google based tools.

Pity.

Unknown said...

Can an environment variable be set from within GMaven?

If so, please provide an example

Unknown said...

I am unable, using your code, to get the modified property to be "exported" such that the later ant task sees the change.

The following build log snippit show that groovy is running before ant, and that groovy is definitely changing the property.

[INFO] [groovy:execute {execution: set-unixy_build_directory!}]
project.properties.unixy_build_directory = F:/svn2/aie was here/common/acecom/target
[INFO] [antrun:run {execution: show-unixy_build_directory!}]
[INFO] Executing tasks
[echo] unixy_build_directory: F:\svn2\aie was here\common\acecom\target

Tim Myer said...

Hi Robert,

Thank you for your question. Which version of maven are you using, offhand?

---Tim---

Unknown said...

Maven 2.0.9.

I got it to work by removing "unixy_build_directory" from the properties at the top.

${project.build.directory}


Now ant can see the exported groovy value.

Thanks for the help. It's helping me fix an annoying build problem

Tim Myer said...

Hi Robert,

Nice! Thanks for following up.

---Tim---

Fawkes said...

if the unix_build_directory needed to be set in the parent pom, and was required for versioning by the children poms (by versioning, i mean that the child pom builds a module whose version is decided by this property in the parent pom), how would i go abt overriding the value?

I tried the method that youve used, but it doesnt override the value...any help?

Tim Myer said...

Hi Fawkes,

That is an interesting question, and unfortunately I do not have the answer offhand. I will take a look at it in the next few days.
In the meantime, if you find the answer through your own explorations, please let me know!

Thanks,
---Tim---

M K said...

Hi Tim,

I'm having trouble getting this to work in a multi-module project. I am resetting a maven property in sub-module A and attempting to re-use the maven property with the new value in sub-module B, but sub-module B is returning default value set in parent. I added the ant plugin for debugging in sub-module A to verify that the property is indeed being rebounded by gmaven script. I also tried moving the re-binding snippet up to parent module, but sub-module B is still unable to detect the rebounded value. Running Maven 2.2.1. Any ideas? Or is this simply not supported?

Thanks,

Mohammad