Combine Maven profile properties

I have fought with this problem a couple of times, but now I found a way out.

Let’s say you have a Maven POM and, depending on the active profiles, you want to set a property, like:

<profiles>
  <profile>
    <id>ci</id>
    <properties>
      <spring.profiles.active>something</spring.profiles.active>
    </properties>
  </profile>
  <profile>
    <id>dev</id>
    <properties>
      <spring.profiles.active>another</spring.profiles.active>
    </properties>
   </profile>
</profiles>

You should know by now that using mvn package -Pdev,ci is not going to produce the outcome you would expect: the spring.profiles.active Maven property will have a value of something, the last one of the activated profiles.

You actually have no straightforward way to concatenate properties in Maven, but the following will do:

<plugin>
  <groupId>org.codehaus.gmavenplus</groupId>
  <artifactId>gmavenplus-plugin</artifactId>
  <version>1.6</version>
  <dependencies>
    <dependency>
      <groupId>org.codehaus.groovy</groupId>
      <artifactId>groovy-all</artifactId>
      <version>2.4.9</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
  <executions>
    <execution>
      <id>excludedGroups</id>
      <phase>initialize</phase>
      <goals>
        <goal>execute</goal>
      </goals>
      <configuration>
        <scripts>
          <script><![CDATA[             def value = ""             (project.activeProfiles).each{ profile -> value += profile.properties.springProfiles + "," }
            project.properties.springProfiles = value.substring(0, value.length() - 1)
          ]]></script>
        </scripts>
      </configuration>
    </execution>
  </executions>
</plugin>

The plugin, activated at the very beginning of POM processing, will create a property springProfiles with a value resulting by concatenating the properties defined in the active profiles.

The same configuration, with a slight variation, is also usable to concatenate JUnit categories:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <configuration>
        <excludedGroups>${excludedGroups}</excludedGroups>
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.codehaus.gmavenplus</groupId>
      <artifactId>gmavenplus-plugin</artifactId>
      <version>1.6</version>
      <dependencies>
        <dependency>
          <groupId>org.codehaus.groovy</groupId>
          <artifactId>groovy-all</artifactId>
          <version>2.4.9</version>
          <scope>runtime</scope>
        </dependency>
      </dependencies>
      <executions>
        <execution>
          <id>excludedGroups</id>
          <phase>initialize</phase>
          <goals>
            <goal>execute</goal>
          </goals>
          <configuration>
            <scripts>
              <script><![CDATA[                 def value = ""                 (project.activeProfiles).each{ profile -> value += profile.properties.excludedGroups + "," }
                project.properties.excludedGroups = value.substring(0, value.length() - 1)
              ]]></script>
            </scripts>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

<profiles>
  <!-- Exclude any tests in `SlowTest` category when profile 'skipSlow' is specified. -->
  <profile>
    <id>skipSlow</id>
    <properties>
      <excludedGroups>SlowTest.class</activeByDefault>
    </properties>
  </profile>

  <!-- Skip any tests in 'WindowsTest' category when not running on Windows. -->
  <profile>
    <id>skipWindowsTests</id>
    <activation>
      <os><family>!windows</family></os>
    </activation>
    <properties>
      <excludedGroups>WindowsTest.class</excludedGroups>
    </properties>
  </profile>
</profiles>

Launch the above as mvn test -PskipSlow on a non-Windows machine to have both SlowTest.class and WindowsTest.class JUnit categories excluded.

Happy concatenation!

Advertisements

Smarter Eclipse quality friendly config

If you haven’t realized I’ve some sort of addiction to software quality then this should be the first time you read my blog: it doesn’t mind because you are reading it now!

Here is another of my famous (!!!) tips for a better Eclipse IDE configuration and this time I’m trying to help all those software developers out there that, willing or not, smash their faces against tests being them unit or automated ones. If you don’t write such tests don’t get desperate: this tip might still help you!

With introduction of static imports more and more libraries have converted or integrated their APIs with commodoty static methods, with testing libraries being one of the most populated category.

If you use JUnit, Mockito and/or any other library using static methods for building complex object structures you will certainly know the code you write should NOT look like the following:

  @Test
  public void someTestMethod() {
    Mockito.when(mockedObject.someMethod(Matchers.any(String.class))).thenReturn(new Object());
    // some code here
    Mockito.verify(mockedObject).someMethod("one");
    Assert.assertEquals(expectedValue, mockedObject.someMethod());
  }

Good modern code should look like the following instead:

  @Test
  public void someTestMethod() {
    when(mockedObject.someMethod(any(String.class))).thenReturn(new Object());
    // some code here
    verify(mockedObject).someMethod("one");
    assertEquals(expectedValue, mockedObject.someMethod());
  }

Our favourite Eclipse IDE can be instructed to help you write those neat lines of code, actually recognizing you are using static methods and automatically add the corresponding static imports.

This not widely known preference is available in the form of a configurable list of packages and types candidate for static methods and attributes scanning, reachable at Window > Preferences > Java > Editor > Content Assist > Favorites (yes, not the easiest to find, I agree).

After configuring your favourite libraries in that list you can start forgetting about the Assert, Mockito and Matchers classes: I just start typing the method name and the IDE does all the borying stuff for me!

This is what I’ve configured at the moment in my STS
static-imports

Enjoy!