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

The “Maven and Github on Windows” hell!

I’m sure this is not the first time I prepare a post like this, but I might have decided to last minute drop it: this time it’s not going to happen.

Sadly I have to use Windows at work and when I have some spare time I do contribute to Open Source Software and I have once again came face to face with the hell caused by trying to run a Maven Release Plugin on a Github hosted project. In this particular case it was a project I started myself called SmartUnit, but you can bet the same applies to any other Github hosted project using Maven and willing to perform releases on the Maven Central Repository.

Where do my problems start? Well, when I run the infamous mvn release:prepare obviously.

First I started encountering failures with the missing commands:

  • gpg.exe must be installed and available on the PATH to be able to sign the packages for the Maven Central Repository
  • git.exe must be installed and available on the PATH to be able to commit the tag and updated pom to Github

Installing the above though does solve only a fraction of the problem as you need the corresponding keys:

  • your GPG key should go into %APPDATA%\gnupg and it’s easier if it is the first key in your GPG key store
  • your SSH key should go into %USERPROFILE%\.ssh and, to avoid further complications, just name the file as rsa_id

As if it wasn’t enough hassle, you have to manually start an SSH agent to provide the SSH key, so don’t forget to (commands should be already on the PATH at this stage):

  1. run the Git’s bash shell with bash
  2. run the SSH agent with eval$(ssh-agent)
  3. add your SSH key with ssh-add ~\.ssh\id_rsa and input the key passphrase

If your key wasn’t already listed among your Github SSH Keys, don’t forget to add it.

Now the Maven release:prepare goal should complete successfully, but problems might arise during the release:prepare one as:

  • the PGP key must be publicly verifiable, so it must be published like on http://pgp.mit.edu/ (you can use gpg --armour --export to obtain the key signature to submit)
  • your Sonatype nexus credentials must be in your Maven settings.xml, under a server directive using an id matching the distribution repository (it should be sonatype-nexus-staging, but it might change over time)

Do you believe it’s all? Well, it’s not! Now you need to:

  1. log into your Sonatype Nexus account
  2. search your newly created Staging Repostory
  3. select the repository and close
  4. wait and refresh the view until it is reported as closed
  5. select the repository and release it!

Only at this point your artifacts will be available on the Maven Central Repository…

This is what I’ve twice learnt about the release process and what I hope I will be able to retrieve here the next time I’ll have to set this thing up again.

Might the force be with you!

Eclipse: annoying JSP errors

A few weeks ago I’ve posted something about annoying Eclipse validation errors regarding minified JavaScript files. Today I’m here to solve the same issue with regards to JSP validations, specifically when those errors are due to non project related files.

I’ve just setup the Maven Cargo Plugin for my project and suddenly I got tons of errors due to JSP files within Tomcat 7 distribution not satisfying JSP validation rules: I hate those distracting errors/warnings and I decided to apply the same solution I used for JavaScript files.

validation-settings

It’s quite easy, but it has to be done on a per project basis: right click on your project then select Properties and in the upcoming pop up window select Validation then click on the ellipses (…) button for JSP Content Validator (you’ll have to repeat the same for the JSP Syntax Validator).

In the new pop up window that will be displayed you will have to add an Exclude Group and then add a rule for the target folder.

jsp-validation-settings

Run a clean build to free up your project from unecessary validation erros!

Java and self signed HTTPS certificates

I encountered this problem while trying to use the Maven Cargo Plugin to deploy a WAR onto a JBoss server having it’s access restricted to HTTPS, but with a self signed certificate.

This configuration creates a set of issues you need to solve in order for this configuration to work, but I believe this same set of steps is needed in other contexts other than the Cargo Plugin and JBoss.

First part of the problem was the server name verification as the certificate used for the SSL encryption was generated referring to a different hostname then the one I’m using to access the server. As a consequence I get a hostname verification error during SSL handshake.

To fix this I decided to go for a Java agent, a library you link to your runtime environment without changing your code. The library is available on Github courtesy of  Peter Liljenberg.

Once you have the JAR you just attach it to your JRE by using the option -javaagent:<PATH_TO_JAR>: for simplicity I’ve added such option within my MAVEN_OPTS as I need that while running Maven.

set MAVEN_OPTS=-javaagent:$MAVEN_REPO\com\sparetimecoders\hostnameverifier-disabler-agent\1.0-SNAPSHOT\hostnameverifier-disabler-agent-1.0-SNAPSHOT.jar

Once this part is fixed you’ll start getting a PKIX error, signalling the certificate you are getting cannot be verified against the trusted certificates list: being the certificate I’m getting a self signed one this is not a surprise.

To add the certificate to the trusted certificates list a simple command is what you need:

keytool -import -alias server-name -file file.cer -keystore $JAVA_HOME/jre/lib/security/cacerts

When prompted, use changeit as keystore password, unless you or the system admin have customized it .

After these changes I’m now able to remotely deploy my WAR artifact on my HTTPS secured JBoss AS.

Artifactory and NamedVirtualHost

Today I needed to publish an Artifactory repository I’ve set up for my company to a first level URL, something like http://artifactory.mycompany.com: it revealed to be not as straightforward as I supposed to.

Once haveing installed Tomcat, deployed the Artifactory WAR and checked that everything was running fine I needed to map the http://artifactory.mycompany.com URL to the Tomcat webapp so I opted for the standard Apache HTTPD VirtualHost section and the common mod_proxy_ajp ProxyPass and ProxyPassReverse directives, but it was not working as expected.

Whenever I was requesting the initial URL I was ending up having http://artifactory.mycompany.com/artifactory/webapp in my web browser address bar and a Tomcat error shown in there referring to a non available resource at http://artifactory.mycompany.com/artifactory/artifactory/webapp (please note the additional artifactory folder).

Apparently Artifactory performs some sort of magic to distinguish the client type and redirects to an appropriate view, but in doing this it forcibly prefix each redirection with the webapp name, invalidating my configuration.

I ended up with the following Apache HTTPD configuration which seems working, but it’s probably sub optimal as I’m not an HTTPD expert:


  ServerName artifactory.mycompany.com
  ServerAlias artifactory.mycompany.com

  ProxyPreserveHost on

  ProxyPass /artifactory ajp://localhost:8009/artifactory
  ProxyPassReverse /artifactory ajp://localhost:8009/artifactory
  ProxyPass / ajp://localhost:8009/artifactory/
  ProxyPassReverse / ajp://localhost:8009/artifactory/

Managing POM versions

Once upon a time there was the release plugin and it was the only tool we had to manage POM versions: release:prepare and a text editor were all we had in our toolbox.

A new hero has come to save us from the evil of POM version management and its name is versions plugin.

I decided to practice a little bit with this champion and I’m now confident it is THE tool, but still a few tricks can be helpful.

First of all, if you want to manage a set of related projects you want to have an aggregating (aka multi-module) POM, something like:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.agileware.project</groupId>
  <artifactId>aggregator</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <modules>
    <module>application</module>
    <module>library1</module>
    <module>library2</module>
  </modules>
</project>

Please note this is not a parent POM because the sub modules will not specify it as their parent, or at least it’s not mandatory!

Let’s say that all sub modules start at version 0.1.0-SNAPSHOT and after a while you want to release library1.

You will need to update all the POMs that have library1 as a dependency to refer to the new 0.2.0-SNAPSHOT version and you can do this witha simple Maven command:

mvn versions:use-latest-versions -DexcludeReactor=false -DallowSnapshots=true

Obviously there are many other goals and options for you to use, providing a lot of control over what is going to be updated and which versions should be considered as target: releases only, snapshots only, no major version updates and so forth.

Git and Maven release:prepare

I don’t know if it happens to me only due to my complex source control configuration (which uses SSH keys like Github), but it happens quite often to me that the release process gets stuck right when it’s time to push changes.

When this happens you are left with almost no choice but to perform a release:rollback and start again, but today I’ve found a good solution to this problem: git reset.

So, if your release preparation process is interrupted for some reason and you want to restart it again, but when you try you get a message saying the git commit command failed, then just do the following:

git reset --soft HEAD~1

This command will remove the commit from your local clone leaving all the Maven changes intact, thus allowing you to recover the process where you left it.

Have fun!

Maven POM version management

A friend and colleague pointed me to a great Maven plugin I wasn’t aware of and that is probably going to save me some headaches: the Codehaus hosted versions-maven-plugin brings to Maven users some very nice functionality!

I’m not going to copy here the full documentation, but just to make you wishing more, here is the goals list:

  • versions:compare-dependencies compares the dependency versions of the current project to the dependency management section of a remote project.
  • versions:display-dependency-updates scans a project’s dependencies and produces a report of those dependencies which have newer versions available.
  • versions:display-plugin-updates scans a project’s plugins and produces a report of those plugins which have newer versions available.
  • versions:display-property-updates scans a projectand produces a report of those properties which are used to control artifact versions and which properies have newer versions available.
  • versions:update-parent updates the parent section of a project so that it references the newest available version. For example, if you use a corporate root POM, this goal can be helpful if you need to ensure you are using the latest version of the corporate root POM.
  • versions:update-properties updates properties defined in a project so that they correspond to the latest available version of specific dependencies. This can be useful if a suite of dependencies must all be locked to one version.
  • versions:update-child-modules updates the parent section of the child modules of a project so the version matches the version of the current project. For example, if you have an aggregator pom that is also the parent for the projects that it aggregates and the children and parent versions get out of sync, this mojo can help fix the versions of the child modules. (Note you may need to invoke Maven with the -N option in order to run this goal if your project is broken so badly that it cannot build because of the version mis-match).
  • versions:lock-snapshots searches the pom for all -SNAPSHOT versions and replaces them with the current timestamp version of that -SNAPSHOT, e.g. -20090327.172306-4
  • versions:unlock-snapshots searches the pom for all timestamp locked snapshot versions and replaces them with -SNAPSHOT.
  • versions:resolve-ranges finds dependencies using version ranges and resolves the range to the specific version being used.
  • versions:set can be used to set the project version from the command line.
  • versions:use-releases searches the pom for all -SNAPSHOT versions which have been released and replaces them with the corresponding release version.
  • versions:use-next-releases searches the pom for all non-SNAPSHOT versions which have been a newer release and replaces them with the next release version.
  • versions:use-latest-releases searches the pom for all non-SNAPSHOT versions which have been a newer release and replaces them with the latest release version.
  • versions:use-next-snapshots searches the pom for all non-SNAPSHOT versions which have been a newer -SNAPSHOT version and replaces them with the next -SNAPSHOT version.
  • versions:use-latest-snapshots searches the pom for all non-SNAPSHOT versions which have been a newer -SNAPSHOT version and replaces them with the latest -SNAPSHOT version.
  • versions:use-next-versions searches the pom for all versions which have been a newer version and replaces them with the next version.
  • versions:use-latest-versions searches the pom for all versions which have been a newer version and replaces them with the latest version.
  • versions:commit removes the pom.xml.versionsBackup files. Forms one half of the built-in “Poor Man’s SCM”.
  • versions:revert restores the pom.xml files from the pom.xml.versionsBackup files. Forms one half of the built-in “Poor Man’s SCM”.