Maven jobs and Java versions compatibility

Issue

Maven jobs are reporting java.lang.UnsupportedClassVersionError: Bad version number in .class file or something like

ERROR: JENKINS-18403 JDK 5 not supported to run Maven; retrying with slave Java and setting compile/test properties to point to <path_to_the_java_vm-used_by_your_slave>

Environment

Explanation

Because java serialized classes are exchanged between Jenkins master and Maven Jobs it is required that the JVM used to launch Maven is superior or equal to the version of Java for which Jenkins Master is built for.

Jenkins >= 1.520 requires Java 6 thus Maven jobs must be launched with Java >= 6.

Jenkins >= 1.612 requires Java 7 thus Maven jobs must be launched with Java >= 7.

This constraint was firstly reported for the Jenkins upgrade from Java 5 to Java 6 as JENKINS-18403 thus the error message

ERROR: JENKINS-18403 JDK 5 not supported to run Maven; retrying with slave Java and setting compile/test properties to point to <path_to_the_java_vm-used_by_your_slave>

This one was wrongly implemented thus for the upgrade to Java 7 you are receiving the same error about JDK 5 while it is JDK 6 (See JENKINS-28294).

Resolution

There are several solutions to avoid this problem.

On Jenkins side

The simplest workaround could be to define the properties -Dmaven.compiler.source=1.6 -Dmaven.compiler.target=1.6 locally in your maven job settings or globally in Jenkins global configuration for MAVEN_OPTS if you have only Java 6 maven jobs. NOTE: This will work only if your maven jobs aren’t enforcing the java level configuration in compiler and others plugins but are using these properties to do the configuration (which is the default behavior of Maven).

In Jenkins, instead of using Maven Jobs you can use FreeStyle jobs with a Maven build step.
This solution requires a manual recreation of jobs.
FreeStyle jobs will offer less features than Maven jobs but they’ll support to launch Maven on any version of java.

At Apache Maven level

Before anything you need to configure the maven compiler plugin to target your oldest version of Java even if you are using a more recent JDK.
If you didn’t configured (directly or in a parent) the compiler plugin you can just add in your pom:

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
...
  <properties>
    <maven.compiler.source>1.6</maven.compiler.source>
    <maven.compiler.target>1.6</maven.compiler.target>
  </properties>
...

If the compiler plugin is already reconfigured in your project or in a parent pom you may have to use another property or declare the configuration options of the plugin in the plugins or pluginManagement settings.

Sadly configuring the compiler options are often not enough to ensure that you will produce binaries compatible with your target JRE. For exemple, you can use APIs (methods) provided by the new JDK and which are not available in the older version.

To avoid this kind of issue there are 2 solutions at Apache Maven level which will allow you to launch Apache Maven with a Java version superior to the one targetted by your application but without risking to produce an incompatible binary.
With theses solutions you’ll have to update your build but you’ll be able to continue to use your Maven Jobs.

The animal-sniffer solution

In your build you add an additionnal control using the Animal Sniffer plugin to avoid to use in your code some APIs provided by the version of Java used to build.
This solution isn’t 100% safe (it controls only the singatures of methods not their semantics) but it covers a large part of classical errors to build an application for an older version of Java.

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>1.4</version>
        <executions>
          <execution>
            <id>check-java-compat</id>
            <goals>
              <goal>enforce</goal>
            </goals>
            <phase>process-classes</phase>
            <configuration>
              <rules>
                <checkSignatureRule implementation="org.codehaus.mojo.animal_sniffer.enforcer.CheckSignatureRule">
                  <signature>
                    <groupId>org.codehaus.mojo.signature</groupId>
                    <artifactId>java16</artifactId>
                    <version>1.0</version>
                  </signature>
                </checkSignatureRule>
              </rules>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>animal-sniffer-enforcer-rule</artifactId>
            <version>1.14</version>
          </dependency>
        </dependencies>
      </plugin>
...
    </plugins>
  </build>
</project>

A full sample is available here

The toolchains solution

About toolchains :

With toolchains, Apache Maven will be able to run on a different (version) of the JVM than the JDK used to build your project.
It will allow to run Apache Maven on the same JVM version than your Jenkins Master (for exemple Java JRE 7) while it will use another JDK to build your application (for example Java JDK 5).
With this strategy the targeted version of the JDL is used

On Jenkins side you will need to perform the following actions:

  • Your Maven Job project will be configured to use a JDK 7. You will use the Tool Environment Plugin to install an additional JDK 6 on your slave.
  • You will use the Config File Provider Plugin to define and install a toolchain.xml file used by maven to define where the JDK6 is installed
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 http://maven.apache.org/xsd/toolchains-1.1.0.xsd">
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>1.6</version>
    </provides>
    <configuration>
      <jdkHome>/home/opt/jdk1.6</jdkHome>
    </configuration>
  </toolchain>
</toolchains>

On Maven side:

  • You will configure the maven-toolchain-plugin to tell to Maven to use a JDK 6 to perform all Java related tasks (javac, javadoc …)
<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-toolchains-plugin</artifactId>
        <version>1.1</version>
        <executions>
          <execution>
            <goals>
              <goal>toolchain</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <toolchains>
            <jdk>
              <version>1.6</version>
            </jdk>
          </toolchains>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

A full sample is available here

Have more questions? Submit a request

2 Comments

  • 0
    Avatar
    Bollapu Harinathareddy

    Hi, 

    I am facing similar issue in Jenkins 2.18 version, will you please is this solution will work or not. I am using maven 3.3.3 and JDK 1.8. 

     

    Thanks in advance Harinatha

     

  • 0
    Avatar
    Arnaud Heritier

    Hi Bollapu,

     

      Yes the principle remains the same. If you have this error this is because you configured a Maven job with a Java version < 7. Jenkins >= 1.612 and thus 2.18 cannot natively use Maven jobs with a Java version inferior to 7. THus the build is trying to use the Java version of your agent which is necessarily superior or equal to 7.

     

    Best regards

Please sign in to leave a comment.