In one of the latest blog posts, I have shown you how you can build a Docker image with Java and Maven installed with the SDKMAN! command-line tool. Today I would like to continue the topic and show you, how you can compile your project using two different Java versions in parallel.

Using dockerfile agent

Sample Maven application used in this article can be found here - https://github.com/wololock/simple-java-maven-app.

To keep the example simple, I’m going to use Jenkins Pipeline’s dockerfile agent. It executes given pipeline stage (or stages) in a docker container that gets started from a docker image created using local Dockerfile.

Listing 1. Dockerfile.build (source)
FROM debian:stretch-slim

# Defining default Java and Maven version
ARG JAVA_VERSION="8.0.232-open"
ARG MAVEN_VERSION="3.6.3"

# Defining default non-root user UID, GID, and name
ARG USER_UID="1000"
ARG USER_GID="1000"
ARG USER_NAME="jenkins"

# Creating default non-user
RUN groupadd -g $USER_GID $USER_NAME && \
   useradd -m -g $USER_GID -u $USER_UID $USER_NAME

# Installing basic packages
RUN apt-get update && \
   apt-get install -y zip unzip curl && \
   rm -rf /var/lib/apt/lists/* && \
   rm -rf /tmp/*

# Switching to non-root user to install SDKMAN!
USER $USER_UID:$USER_GID

# Downloading SDKMAN!
RUN curl -s "https://get.sdkman.io" | bash

# Installing Java and Maven, removing some unnecessary SDKMAN files
RUN bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && \
    yes | sdk install java $JAVA_VERSION && \
    yes | sdk install maven $MAVEN_VERSION && \
    rm -rf $HOME/.sdkman/archives/* && \
    rm -rf $HOME/.sdkman/tmp/*"

ENV MAVEN_HOME="/home/jenkins/.sdkman/candidates/maven/current"
ENV JAVA_HOME="/home/jenkins/.sdkman/candidates/java/current"
ENV PATH="$MAVEN_HOME/bin:$JAVA_HOME/bin:$PATH"

The Jenkins Pipeline contains two stages - Build, and Install. The Build stage has two parallel stages inside. The first one compiles the Maven project using OpenJDK 8, while the second one uses OpenJDK 11. Keep in mind that this is just an illustration. You could use the same approach to compile your project using e.g., two different Maven versions, or different JDK vendors.

A pipeline with the parallel Java 8 and Java 11

You have seen the Dockerfile we are going to use to create the build environment docker image. Here is the pipeline’s Jenkinsfile.

Listing 2. Jenkinsfile (source)
pipeline {
    environment {
        DOCKERFILE = "Dockerfile.build"
    }

    stages {
        stage("Build") {
            environment {
                MVN_COMMAND = "mvn clean package"
                TEST_REPORTS = "target/surefire-reports/*.xml"
            }

            parallel {
                stage("openjdk-8.0.232") {
                    agent {
                        dockerfile { (1)
                            filename DOCKERFILE (2)
                            additionalBuildArgs "--build-arg JAVA_VERSION=8.0.232-open -t maven:8.0.232-open" (3)
                        }
                    }
                    steps {
                        sh "${MVN_COMMAND} -P jdk8" (4)
                    }
                    post {
                        always {
                            junit TEST_REPORTS
                        }
                    }
                }

                stage("openjdk-11.0.5") {
                    agent {
                        dockerfile {
                            filename DOCKERFILE
                            additionalBuildArgs "--build-arg JAVA_VERSION=11.0.5-open -t maven:11.0.5-open"
                        }
                    }
                    steps {
                        sh "${MVN_COMMAND} -P jdk11"
                    }
                    post {
                        always {
                            junit TEST_REPORTS
                        }
                    }
                }
            }
        }

        stage("Install") {
            agent {
                dockerfile {
                    filename DOCKERFILE
                    additionalBuildArgs "--build-arg JAVA_VERSION=8.0.232-open"
                    args '-v $HOME/.m2/repository:/home/jenkins/.m2/repository:rw,z' (5)
                }
            }
            steps {
                sh "mvn -DskipTests install -P jdk8"
            }
        }
    }
}

In this example, I used the Declarative Pipeline script. Each parallel stage inside the Build stage uses the dockerfile agent. The Dockerfile name is specified using environment variable DOCKERFILE which stores the Dockerfile.build name. We also pass the additional build arguments - a JAVA_VERSION one especially. In the Maven build command we add a specific profile like jdk8 and jdk11 to configure the Maven compiler plugin accordingly to the Java version.

The Install stage we use OpenJDK 8 only. This stage also uses the dockerfile agent, but this time we mount ~/.m2/repository from the docker host to the container, so the installed artifact will be persisted on the Jenkins node.

Executing the pipeline

jenkins pipeline maven parallel

Szymon Stepniak

Groovista, Upwork's Top Rated freelancer, Toruń Java User Group founder, open source contributor, Stack Overflow addict, bedroom guitar player. I walk through e.printStackTrace() so you don't have to.