Language Guide: Java (with Maven)
This guide will help you get started with a Java application building with Maven on CircleCI.
- Overview
- Sample configuration: version 2.1:
- For 2.0 Configuration (recommended for CircleCI Server only):
- See also
Overview
This is an example application showcasing how to run a Java app on CircleCI 2.1. This application uses the Spring PetClinic sample project. This document includes pared down sample configurations demonstrating different CircleCI features including workspaces, dependency caching, and parallelism.
Sample configuration: version 2.1:
A basic build with an orb:
version: 2.1
orbs:
maven: circleci/maven@0.0.12
workflows:
maven_test:
jobs:
- maven/test # checkout, build, test, and upload test results
This config uses the language-specific orb to replace any executors, build tools, and commands available. Here we are using the maven orb, which simplifies building and testing Java projects using Maven. The maven/test command checks out the code, builds, tests, and uploads the test result. The parameters of this command can be customized. See the maven orb docs for more information.
For 2.0 Configuration (recommended for CircleCI Server only):
version: 2.0
jobs:
build:
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- run: ./mvnw package
Version 2.0 configs without workflows will look for a job named build
. A job
is a essentially a series of commands run in a clean execution environment.
Notice the two primary parts of a job: the executor and steps. In this case, we
are using the docker executor and passing in a CircleCI convenience image.
Using a workflow to build then test
A workflow is a dependency graph of jobs. This basic workflow runs a build job followed by a test job. The test job will not run unless the build job exits successfully.
version: 2.0
jobs:
test:
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- run: ./mvnw test
build:
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- run: ./mvnw -Dmaven.test.skip=true package
workflows:
version: 2
build-then-test:
jobs:
- build
- test:
requires:
- build
Caching dependencies
The following code sample details the use of caching.
version: 2.0
jobs:
build:
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "pom.xml" }} # appends cache key with a hash of pom.xml file
- v1-dependencies- # fallback in case previous cache key is not found
- run: ./mvnw -Dmaven.test.skip=true package
- save_cache:
paths:
- ~/.m2
key: v1-dependencies-{{ checksum "pom.xml" }}
The first time this build ran without any dependencies cached, it took 2m14s. Once the dependencies were restored, the build took 39 seconds.
Note that the restore_cache
step will restore whichever cache it first matches.
You can add a restore key here as a fallback. In this case, even if pom.xml
changes, you can still restore the previous cache. This means the job will only
have to fetch the dependencies that have changed between the new pom.xml
and the
previous cache.
Persisting build artifacts to workspace
The following configuration sample details persisting a build artifact to a workspace.
version: 2.0
jobs:
build:
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- run: ./mvnw -Dmaven.test.skip=true package
- persist_to_workspace:
root: ./
paths:
- target/
test:
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- attach_workspace:
at: ./target
- run: ./mvnw test
workflows:
version: 2
build-then-test:
jobs:
- build
- test:
requires:
- build
This persist_to_workspace
step allows you to persist files or directories to be used by
downstream jobs in the workflow. In this case, the target directory produced by
the build step is persisted for use by the test step.
Splitting tests across parallel containers
version: 2.0
jobs:
test:
parallelism: 2 # parallel containers to split the tests among
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- run: |
./mvnw \
-Dtest=$(for file in $(circleci tests glob "src/test/**/**.java" \
| circleci tests split --split-by=timings); \
do basename $file \
| sed -e "s/.java/,/"; \
done | tr -d '\r\n') \
-e test
- store_test_results: # We use this timing data to optimize the future runs
path: target/surefire-reports
build:
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- run: ./mvnw -Dmaven.test.skip=true package
workflows:
version: 2
build-then-test:
jobs:
- build
- test:
requires:
- build
Splitting tests by timings is a great way to divide time-consuming tests across multiple parallel containers. You might think of splitting by timings as requiring 4 parts:
- a list of tests to split
- the command:
circleci tests split --split-by=timings
- containers to run the tests
- historical data to intelligently decide how to split tests
To collect the list of tests to split, simply pull out all of the Java test
files with this command: circleci tests glob "src/test/**/**.java"
. Then use sed
and tr
to translate this newline-separated list of test files into a
comma-separated list of test classes.
Adding store_test_results
enables CircleCI to access the historical timing data
for previous executions of these tests, so the platform knows how to split tests
to achieve the fastest overall runtime.
Storing code coverage artifacts
version: 2.0
jobs:
test:
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- run: ./mvnw test verify
- store_artifacts:
path: target/site/jacoco/index.html
workflows:
version: 2
test-with-store-artifacts:
jobs:
- test
The Maven test runner with the JaCoCo plugin
generates a code coverage report during the build. To save that report as a
build artifact, use the store_artifacts
step.
A configuration
The following code sample is the entirety of a configuration file combining the features described above.
version: 2.0
jobs:
test:
parallelism: 2
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "pom.xml" }}
- v1-dependencies-
- attach_workspace:
at: ./target
- run: |
./mvnw \
-Dtest=$(for file in $(circleci tests glob "src/test/**/**.java" \
| circleci tests split --split-by=timings); \
do basename $file \
| sed -e "s/.java/,/"; \
done | tr -d '\r\n') \
-e test verify
- store_test_results:
path: target/surefire-reports
- store_artifacts:
path: target/site/jacoco/index.html
build:
docker:
- image: circleci/openjdk:stretch
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "pom.xml" }}
- v1-dependencies-
- run: ./mvnw -Dmaven.test.skip=true package
- save_cache:
paths:
- ~/.m2
key: v1-dependencies-{{ checksum "pom.xml" }}
- persist_to_workspace:
root: ./
paths:
- target/
workflows:
version: 2
build-then-test:
jobs:
- build
- test:
requires:
- build
The configuration above is from a demo Java app, which you can access here. If you want to step through it yourself, you can fork the project on GitHub and download it to your machine. Go to the Projects page in CircleCI and click the Follow Project button next to your forked project. Finally, delete everything in .circleci/config.yml. Nice! You just set up CircleCI for a Java app using Gradle and Spring.
See also
- See the Deploy document for example deploy target configurations.
- See the Debugging Java OOM errors document for details on handling Java memory issues.