This is a simple post, that shows how to leverage Maven profiles and Maven Exec plugin to run your scripts according to the underlying OS.

An example of usage would be with your CI tool with different OS slaves.

To allow us to run arbitrary shell commands we will use maven exec plugin.

In order to get a concrete example and keep things simple we will invoke powershell (in windows) to get top 5 current processes sorted by cpu usage.

For our powershell script we will use:

Get-Process | Sort CPU -descending | Select -first 5 -Property ID,ProcessName,CPU

And for our bash script script:

ps aux | sort -nrk 3,3 | head -n 5

So let’s get on to our pom.xml

So let’s start a new project with maven for this tutorial and lets write the body of our initial pom.xml

<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>com.ilhicas.tutorial</groupId>
  <artifactId>multiple-os-exec</artifactId>
  <packaging>pom</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>multiple-os-exec</name>
  <url>http://maven.apache.org</url>
</project>

Ok, so since we will be using profiles to set properties based on our OS.

Let’s start by adding our <profiles> section, before closing our <project> tag.

<profiles>
    <profile>
      <id>Windows</id>
      <activation>
        <os>
          <family>Windows</family>
        </os>
      </activation>
      <properties>
        <script.executor>powershell.exe</script.executor>
        <script.to.execute>Get-Process | Sort CPU -descending | Select -first 5 -Property ID,ProcessName,CPU</script.to.execute>
      </properties>
    </profile>
    <profile>
      <id>unix</id>
      <activation>
        <os>
          <family>unix</family>
        </os>
      </activation>
      <properties>
        <script.executor>bash</script.executor>
        <script.to.execute>ps aux | sort -nrk 3,3 | head -n 5</script.to.execute>
      </properties>
    </profile>
  </profiles>

So, now we are setting different properties according to the profile that will be activated. The profiles we declared are activated automatically depending on the OS we are using.

Why are we setting this properties? Because now, we are able to use this properties on our lifecycle with our exec plugin goal -> exec for our scenario.

As it stands, we could avoid writing yet another profile and write our <build> with the exec goals we wanted in each profile, but most of the times you would want to use these profiles with others, and combine the properties set by these profile.

Let’s then add another maven profile to show how we may combine them.

<profile>
      <id>our-default-profile</id>
    <activation>
      <property>
        <name>!unsetDefaultProfile</name>
      </property>
    </activation>
</profile>

We will set this profile to be active by default, by using the property unsetDefaultProfile that if not defined will activate this profile, as the <activeByDefault>true</activeByDefault> will conflict if other profiles are set, and they will be set always, either Windows or Unix profile.

So now let’s write our <build> in this newly created profile so we may use maven exec plugin with the exec goal so we may execute the command to list sorted process by CPU consumption depending on our host OS.

      <build>
        <defaultGoal>exec:exec</defaultGoal>
        <plugins>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.6.0</version>
            <executions>
              <execution>
                <id>default-cli</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>test</phase>
                <configuration>
                  <executable>${script.executor}</executable>
                  <commandlineArgs>${script.to.execute}</commandlineArgs>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>

Since the multiple OS execution might be during testing on a CI platform slave/worker/node. We will use the maven test lifecycle to execute our previously defined propertied script.to.execute and executor that are set according to the OS.

So our final pom.xml should be:

<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>com.ilhicas.tutorial</groupId>
  <artifactId>multiple-os-exec</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>multiple-os-exec</name>
  <packaging>pom</packaging>
  <url>http://maven.apache.org</url>
  <profiles>
    <profile>
      <id>Windows</id>
      <activation>
        <os>
          <family>Windows</family>
        </os>
      </activation>
      <properties>
        <script.to.execute>Get-Process | Sort CPU -descending | Select -first 5 -Property ID,ProcessName,CPU</script.to.execute>
        <script.executor>powershell.exe</script.executor>
      </properties>
    </profile>
    <profile>
      <id>unix</id>
      <activation>
        <os>
          <family>unix</family>
        </os>
      </activation>
      <properties>
        <script.to.execute>ps aux | sort -nrk 3,3 | head -n 5</script.to.execute>
        <script.executor>bash</script.executor>
      </properties>
    </profile>
    <profile>
      <id>our-default-profile</id>
      <activation>
        <property>
          <name>!unsetDefaultProfile</name>
        </property>
      </activation>
      <build>
        <defaultGoal>exec:exec</defaultGoal>
        <plugins>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.6.0</version>
            <executions>
              <execution>
                <id>default-cli</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>test</phase>
                <configuration>
                  <executable>${script.executor}</executable>
                  <commandlineArgs>${script.to.execute}</commandlineArgs>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>

So now all we have to do is run the maven command:

mvn test

And get a similar output:

$ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------< com.ilhicas.tutorial:multiple-os-exec >----------------
[INFO] Building multiple-os-exec 1.0-SNAPSHOT
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:exec (default-cli) @ multiple-os-exec ---

   Id ProcessName         CPU
   -- -----------         ---
 7096 idea64      6524,296875
14692 chrome      4437,359375
14896 chrome        3604,6875
18580 SkypeApp     1288,46875
 8592 explorer    1132,421875


[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.401 s
[INFO] Finished at: 2019-04-19T14:27:00+01:00
[INFO] ------------------------------------------------------------------------

Above is an example for windows output.

And that’s it, hope it helped you somehow.

Thank you for reading.

André Ilhicas dos Santos

Devops Padawan, curious about systems automation, learning new languages, paradigms tools each day.

ilhicas ilhicas


Published