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

Spring and Helper classes

If you use helper classes and Spring injection it might happen you need to inject beans into static fields of your helper classes.

In this case it can come handy a Spring class called org.springframework.beans.factory.config.MethodInvokingFactoryBean.

Consider the following two classes where Static represents your helper class and NonStatic your injected bean:

package org.agileware.example;

import org.springframework.stereotype.Service;

@Service
public class Static {

	private static NonStatic injected;

	public static void setInjected(NonStatic injected) {
		Static.injected = injected;
	}

	public static final void test() {
		if (injected == null) {
			throw new RuntimeException();
		}
	}
}

package org.agileware.example;

import org.springframework.stereotype.Component;

@Component
public class NonStatic {

	public NonStatic() {
		super();
	}
}

You can inject a NonStatic instance into the Static class by invoking the static method using the following XML Spring configuration snippet:

  <bean id="nonStatic" class="org.agileware.example.NonStatic"/>
  <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="staticMethod" value="org.agileware.example.Static.setInjected"/>
    <property name="arguments">
      <list>
        <ref bean="nonStatic"/>
      </list>
    </property>
  </bean>