Using Jenkins to build a Java/Maven project

This tutorial shows you how to build a simple Java application using Maven, whose overall build process will be orchestrated in Jenkins.

If you are a Java/Maven developer who is new to CI/CD concepts, or you might be familiar with these concepts but don’t know how to implement building your project using Jenkins, then this tutorial is for you.

The simple Java application (which you’ll obtain from a sample repository on GitHub) outputs the string "Hello world!" and is accompanied by a unit test to check that the main application works as expected.

Duration: This tutorial takes 20-40 minutes to complete (assuming you’ve already met the prerequisites below). The exact duration will depend on the speed of your machine and whether or not you’ve already downloaded and run Jenkins in Docker from another introductory tutorial.

You can stop this tutorial at any point in time and continue from where you left off.

Prerequisites

For this tutorial, you will require:

  • A macOS, Linux or Windows machine with:

    • 256 MB of RAM - more than 512MB is recommended

    • 10 GB of drive space for Jenkins and your Docker image

  • The following software installed:

    • Docker - navigate to Get Docker at the top of the website to access the Docker download that’s suitable for your platform

    • Git and optionally GitHub Desktop

Download and run Jenkins in Docker

In this tutorial, we’ll be running Jenkins as a Docker container from a Docker image of Jenkins with Blue Ocean (pre-bundled). Read more about these concepts in the Docker documentation’s Getting Started, Part 1: Orientation and setup page and the Docker overview page for an extensive overview of Docker’s capabilities.

Tip: If you’ve already run though this procedure (perhaps from another introductory tutorial), then you can skip this section and jump to the next.

  1. Open up a terminal/command prompt window.

  2. Pull the latest Docker image for Jenkins with Blue Ocean (pre-bundled) by running the command:
    docker pull jenkinsci/blueocean
    This command downloads the latest Docker image for Jenkins, with Blue Ocean, that will run Jenkins as a container in your locally running Docker instance (also known as your Docker host).

  3. Create the following Docker data volume that will be used to persistently store all data in the Jenkins home directory (explained in a bit more detail later) by running the command:
    docker volume create jenkins-data
    Note: This data volume allows you to shut down Docker (for example, part way through the tutorial) without you losing any work you’ve already done.

  4. To run Jenkins, start the downloaded Jenkins/Blue Ocean Docker image as a Docker container using the appropriate command:

    • On macOS and Linux:

      docker run \
        --rm \  (1)
        -u root \ (2)
        -p 8080:8080 \ (3)
        -v jenkins-data:/var/jenkins_home \ (4)
        -v /var/run/docker.sock:/var/run/docker.sock \
        -v "$HOME":/home \ (5)
        jenkinsci/blueocean
      1 Automatically removes the Docker container (which is the instantiation of this Jenkins/Blue Ocean Docker image) when it is shut down. This keeps things tidy if you need to quit Jenkins.
      2 Sets the default user within the container to be root. This is not recommended practice for running Docker containers in production. However, for the sake of this tutorial, this is fine.
      3 Makes the Jenkins/Blue Ocean Docker container accessible through port 8080.
      4 Maps the jenkins-data data volume (which you created in the previous step) with the /var/jenkins_home directory in the container.
      5 Maps the $HOME directory on the host (i.e. your local) machine (usually the /Users/<your-username> directory) to the /home directory in the container.
    • On Windows:
      The following command is effectively the same command shown for macOS/Linux above but the -v "$HOME":/home is replaced with -v "%HOMEPATH%":/home (assuming that your user home directory on Windows is on the C drive). Also, if you’re using the Windows command prompt, replace the backslash symbols \ at the end of each line with carat symbols instead ^, giving you:

      docker run --rm ^
        -u root ^
        -p 8080:8080 ^
        -v jenkins-data:/var/jenkins_home ^
        -v /var/run/docker.sock:/var/run/docker.sock ^
        -v "%HOMEPATH%":/home ^
        jenkinsci/blueocean

      If you have some experience with Docker, you can add an option like --name jenkins-tutorials, which would give the Jenkins/Blue Ocean Docker container the name "jenkins-tutorials". This makes it easier to refer to the container if, for instance you wish to access the container through a terminal/command prompt.

  5. After the 2 sets of asterisks appear in the terminal/command prompt window, browse to http://localhost:8080 and wait until the Unlock Jenkins page appears.
    Unlock Jenkins page

  6. From your terminal/command prompt window again, copy the automatically-generated alphanumeric password (between the 2 sets of asterisks).
    Copying initial admin password

  7. On the Unlock Jenkins page, paste this password in the Administrator password field and click Continue.

  8. On the Customize Jenkins page, click Install suggested plugins. The Getting Started page is displayed, showing the progression of Jenkins being configured and the suggested plugins being installed. (This process may take a few minutes.)

  9. When the Create First Admin User page appears, specify your details in the respective fields and click Save and Finish.

  10. Click Start using Jenkins to and you’re now ready to begin using Jenkins.

Throughout the remainder of this tutorial, you can stop the Jenkins/Blue Ocean Docker container by typing Ctrl-C in the terminal/command prompt window from which you ran the docker run ... command above.

To restart the Jenkins/Blue Ocean Docker container, run the same docker run ... command you ran in step 4 (above).

If you don’t wish to run Jenkins in Docker, you can run Jenkins locally on your machine by:

  1. Ensuring you have a Java 8 Runtime Environment (JRE) or Java Development Kit (JDK) installed on your machine (in addition to meeting the prerequisites above).

  2. Downloading the latest stable Jenkins WAR file to an appropriate directory on your machine.

  3. Opening up a terminal/command prompt window to the download directory.

  4. Running java -jar jenkins.war.

  5. Browsing to http://localhost:8080.

  6. Following the instructions to complete the installation.

This process does not automatically install the Blue Ocean features, which would need to installed separately via the Manage Jenkins > Manage Plugins page in Jenkins. Read more about the specifics for installing Blue Ocean on the Getting Started with Blue Ocean page.

Fork and clone the sample repository on GitHub

Obtain the simple "Hello world!" Java application from GitHub, by forking the sample repository of the application’s source code into your own GitHub account and then cloning this fork locally.

  1. Ensure you are signed in to your GitHub account. If you don’t yet have a GitHub account, sign up for a free one on the GitHub website.

  2. Fork the simple-java-maven-app on GitHub into your local GitHub account. If you need help with this process, refer to the Fork A Repo documentation on the GitHub website for more information.

  3. Begin cloning your forked simple-java-maven-app repository (on GitHub) locally to your machine by opening up a terminal/command line window to the directory in which the repository will be cloned. In this tutorial, we’ll assume that simple-java-maven-app will be cloned within the following directories on these platforms, which you should cd into now:

    • macOS - /Users/<your-username>/Documents/GitHub

    • Linux - /home/<your-username>/GitHub

    • Windows - C:\Users\<your-username>\Documents\GitHub

      where <your-username> is your user account’s name on your operating system.

      Note:

      • On a Windows machine, use a Git bash command line window (as opposed to the usual Microsoft command prompt).

      • Alternatively, and in particular if you have the GitHub Desktop app installed on your machine, in GitHub, you can click the green Clone or download button on your forked repository and follow the instructions to clone the repository locally. If you use GitHub Desktop, you can omit the following step.

  4. Run the following command to continue/complete cloning your forked repo:
    git clone https://github.com/YOUR-GITHUB-ACCOUNT-NAME/simple-java-maven-app
    where YOUR-GITHUB-ACCOUNT-NAME is the name of your GitHub account.

Create your Pipeline project in Jenkins

  1. Go back to Jenkins, log in again if necessary and click create new jobs under Welcome to Jenkins!

  2. In the Enter an item name field, specify the name for your new Pipeline project. In this tutorial, we’ll assume simple-java-maven-app.

  3. Scroll down and click Pipeline, then click OK at the end of the page.

  4. ( Optional ) On the next page, specify a brief description for your Pipeline in the Description field (e.g. An entry-level Pipeline demonstrating how to build a simple Java Maven project using Jenkins.)

  5. Click the Pipeline tab at the top of the page to scroll down to the Pipeline section.

  6. From the Definition field, choose the Pipeline script from SCM option. This option instructs Jenkins to obtain your Pipeline from Source Control Management (SCM), which will be your locally cloned Git repository.

  7. From the SCM field, choose Git.

  8. In the Repository URL field, specify the directory path of your locally cloned repository above, which is from your user account/home directory on your host machine, mapped to the /home directory of the Jenkins/Blue Ocean container - i.e.

    • For macOS - /home/Documents/GitHub/simple-java-maven-app

    • For Linux - /home/GitHub/simple-java-maven-app

    • For Windows - /home/Documents/GitHub/simple-java-maven-app

  9. Click Save to save your new Pipeline project. You’re now ready to begin creating your Jenkinsfile, which you’ll be checking into your locally cloned Git repository.

Create your initial Pipeline as a Jenkinsfile

You’re now ready to create your Pipeline that will automate building your Java/Maven project in Jenkins. Your Pipeline will be created as a Jenkinsfile, which will be committed to your locally cloned Git repository (simple-java-maven-app).

This is the foundation of "Pipeline-as-Code", which treats the continuous delivery pipeline a part of the application to be versioned and reviewed like any other code. Read more about Pipeline and what a Jenkinsfile is in the Pipeline and Using a Jenkinsfile sections of the User Handbook.

First, we’ll create an initial Pipeline to download a Maven Docker image and run it as a Docker container (which will build your simple Java application). We’ll also add a "Build" stage to the Pipeline that begins orchestrating this whole process.

  1. Using your favorite text editor or IDE, create and save new text file with the name Jenkinsfile at the root of your local simple-java-maven-app Git repository.

  2. Copy the following Declarative Pipeline code and paste it into your empty Jenkinsfile:

    pipeline {
        agent {
            docker {
                image 'maven:3-alpine' (1)
                args '-v /root/.m2:/root/.m2' (2)
            }
        }
        stages {
            stage('Build') { (3)
                steps {
                    sh 'mvn -B -DskipTests clean package' (4)
                }
            }
        }
    }
    1 This image parameter (of the agent section’s docker parameter) downloads the maven:3-apline Docker image (if it’s not already available in your Docker host) and runs this image as a separate container. This means that:
    • You’ll have separate Jenkins/Blue Ocean and Maven containers running locally in Docker.

    • The Maven container becomes the agent that Jenkins uses to build your Pipeline project. However, this container is short-lived - its lifespan is only that of the duration of your Pipeline’s execution.

    2 This args parameter creates a reciprocal mapping between the /root/.m2 (i.e. Maven repository) directories in the short-lived Maven Docker container and that of your Docker host’s filesystem. Explaining the details behind this is beyond the scope of this tutorial. However, the main reason for doing this is to ensure that the artifacts necessary to build your Java application (which Maven downloads while your Pipeline is being executed) are retained in the Maven repository beyond the lifespan of the Maven container. This prevents Maven from having to download the same artifacts during successive runs of your Jenkins Pipeline, which you’ll be conducting later on. Be aware that unlike the Docker data volume you created for jenkins-data above, the Docker host’s filesystem is effectively cleared out each time Docker is restarted. This means you’ll lose the downloaded Maven repository artifacts each time Docker restarts.
    3 Defines a stage called Build that appears on the Jenkins UI.
    4 This sh step (of the steps section) runs the Maven command to cleanly build your Java application (without running any tests).
  3. Save your amended Jenkinsfile and commit it to your local simple-java-maven-app Git repository. E.g. Within the simple-java-maven-app directory, run the commands:
    git add .
    then
    git commit -m "Add initial Jenkinsfile"

  4. Go back to Jenkins again, log in again if necessary and click Open Blue Ocean on the left to access Jenkins’s Blue Ocean interface.

  5. In the This job has not been run message box, click Run, then quickly click the OPEN link which appears briefly at the lower-right to see Jenkins building your Pipeline project. If you weren’t able to click the OPEN link, click the row on the main Blue Ocean interface to access this feature.

    Note: You may need to wait several minutes for this first run to complete. After making a clone of your local simple-java-maven-app Git repository itself, Jenkins:

    1. Initially queues the project to be built on the agent.

    2. Downloads the Maven Docker image and runs it in a container on Docker.
      Downloading Maven Docker image

    3. Executes the Build stage (defined in the Jenkinsfile) on the Maven container. During this time, Maven will download many artifacts necessary to build your Java application, which will ultimately be stored in Jenkins’s local Maven repository (in the Docker host’s filesystem).
      Downloading Maven artifacts

    The Blue Ocean interface turns green if Jenkins built your Java application successfully.
    Initial Pipeline runs successfully

  6. Click the X at the top-right to return to the main Blue Ocean interface.

    Main Blue Ocean interface

Add a test stage to your Pipeline

  1. Go back to your text editor/IDE and ensure your Jenkinsfile is open.

  2. Copy and paste the following Declarative Pipeline syntax immediately under the Build stage of your Jenkinsfile:

            stage('Test') {
                steps {
                    sh 'mvn test'
                }
            }

    so that you end up with:

    pipeline {
        agent {
            docker {
                image 'maven:3-alpine'
                args '-v /root/.m2:/root/.m2'
            }
        }
        stages {
            stage('Build') {
                steps {
                    sh 'mvn -B -DskipTests clean package'
                }
            }
            stage('Test') { (1)
                steps {
                    sh 'mvn test' (2)
                }
            }
        }
    }
    1 Defines a new stage called Test that appears on the Jenkins UI.
    2 This sh step (of the steps section) runs the Maven command to run a unit test on your simple Java application.
  3. Save your amended Jenkinsfile and commit it to your local simple-java-maven-app Git repository. E.g. Within the simple-java-maven-app directory, run the commands:
    git stage .
    then
    git commit -m "Add 'Test' stage"

  4. Go back to Jenkins again, log in again if necessary and ensure you’ve accessed Jenkins’s Blue Ocean interface.

  5. Click Run at the top left, then quickly click the OPEN link which appears briefly at the lower-right to see Jenkins building your amended Pipeline project. If you weren’t able to click the OPEN link, click the top row on the Blue Ocean interface to access this feature.

    Note: You’ll notice from this run that Jenkins no longer needs to download the Maven Docker image. Instead, Jenkins only needs to run a new container from the Maven image downloaded previously. Also, if Docker had not restarted since you last ran the Pipeline above, then no Maven artifacts need to be downloaded during the "Build" stage. Therefore, running your Pipeline this subsequent time should be much faster.
    If your amended Pipeline ran successfully, here’s what the Blue Ocean interface should look like. Notice the additional "Test" stage. You can click on the previous "Build" stage circle to access the output from that stage.
    Test stage runs successfully (with output)

  6. Click the X at the top-right to return to the main Blue Ocean interface.

Add a final deliver stage to your Pipeline

  1. Go back to your text editor/IDE and ensure your Jenkinsfile is open.

  2. Copy and paste the following Declarative Pipeline syntax immediately under the Test stage of your Jenkinsfile:

            stage('Deliver') {
                steps {
                    sh './jenkins/scripts/deliver.sh'
                }
            }

    so that you end up with:

    pipeline {
        agent {
            docker {
                image 'maven:3-alpine'
                args '-v /root/.m2:/root/.m2'
            }
        }
        stages {
            stage('Build') {
                steps {
                    sh 'mvn -B -DskipTests clean package'
                }
            }
            stage('Test') {
                steps {
                    sh 'mvn test'
                }
            }
            stage('Deliver') { (1)
                steps {
                    sh './jenkins/scripts/deliver.sh' (2)
                }
            }
        }
    }
    1 Defines a new stage called Deliver that appears on the Jenkins UI.
    2 This sh step (of the steps section) runs the shell script deliver.sh located in the jenkins/scripts directory from the root of the simple-java-maven-app repository. As a general principle, it’s a good idea to keep your Pipeline code (i.e. the Jenkinsfile) as tidy as possible and place more complex build scripting steps into separate shell script files like the deliver.sh file. This ultimately makes maintaining your Pipeline code easier, especially if your Pipeline gains more complexity.
  3. Save your amended Jenkinsfile and commit it to your local simple-java-maven-app Git repository. E.g. Within the simple-java-maven-app directory, run the commands:
    git stage .
    then
    git commit -m "Add 'Deliver' stage"

  4. Go back to Jenkins again, log in again if necessary and ensure you’ve accessed Jenkins’s Blue Ocean interface.

  5. Click Run at the top left, then quickly click the OPEN link which appears briefly at the lower-right to see Jenkins building your amended Pipeline project. If you weren’t able to click the OPEN link, click the top row on the Blue Ocean interface to access this feature.
    If your amended Pipeline ran successfully, here’s what the Blue Ocean interface should look like. Notice the additional "Deliver" stage. Click on the previous "Test" and "Build" stage circles to access the outputs from those stages.
    Deliver stage runs successfully
    Here’s what the output of the "Deliver" stage should look like, showing you the execution results of your Java application at the end.
    Deliver stage output only

  6. Click the X at the top-right to return to the main Blue Ocean interface, which lists your previous Pipeline runs in reverse chronological order.
    Main Blue Ocean interface with all previous runs displayed