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_agent>
or something like
ERROR: Invalid project setup: jenkins/security/MasterToSlaveCallable : Unsupported major.minor version 52.0 ERROR: [JENKINS-18403][JENKINS-28294] JDK 'XXXXXX' not supported to run Maven projects. ERROR: Maven projects have to be launched with a Java version greater or equal to the minimum version required by the master. ERROR: Use the Maven JDK Toolchains (plugin) to build your maven project with an older JDK. ERROR: Retrying with slave Java and setting compile/test properties to point to <path_to_the_java_vm-used_by_your_slave>.
- Any Jenkins environment
- Maven Project Plugin
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 (1.531.1 for LTS) requires Java 6 thus Maven jobs must be launched with a JDK >= 6.
Jenkins >= 1.612 (1.625.1 for LTS) requires Java 7 thus Maven jobs must be launched with a JDK >= 7.
Jenkins >= 2.54 (2.60.1 for LTS) requires Java 8 thus Maven jobs must be launched with a JDK >= 8.
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).
All Maven Jobs have to be configured to use at the minimum the version required by Jenkins like described above. To ease the change you can use the Configuration Slicing Plugin. This will allow your build to properly start but you may face various problems because you will build your project with a moe recent version of your JDK than what you are really targeting.
There are several solutions to avoid this problem.
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 1: This will work if your maven jobs are using the default Maven behavior to configure the java level rather than enforcing the java level configuration in compiler and other plugins.
NOTE 2: Same thing for Java 7 but you use
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.
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 configure (directly or in a parent) the compiler plugin you can just add in your pom (for Java 6):
<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.
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
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 agent.
- You will use the Config File Provider Plugin to define and install a
toolchain.xmlfile 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>
- Configure the Maven Job as explained on How to config a Maven Toolchain
On Maven side:
- You will configure the
maven-toolchain-pluginto 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