> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/continuous-integration/troubleshooting-and-resources/tutorials-and-code-samples/ci-java.md).

# Java

You can build and test a Java application using a Linux platform on [Harness Cloud](/continuous-integration/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md) or a [self-managed Kubernetes cluster](/continuous-integration/use-harness-ci/use-harness-ci/set-up-build-infrastructure/k8s-build-infrastructure.md) build infrastructure.

This guide assumes you've [created a Harness CI pipeline](/continuous-integration/use-harness-ci/use-harness-ci/prep-ci-pipeline-components.md).

### Install dependencies <a href="#install-dependencies" id="install-dependencies"></a>

Use [Run steps](/continuous-integration/use-harness-ci/use-harness-ci/run-step-settings.md) to install dependencies in the build environment.

```yaml
              - step:
                   type: Run
                   name: build
                   identifier: build
                   spec:
                     connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                     image: maven:3.8-jdk-11
                     shell: Sh
                     command: |-
                       mvn clean package dependency:copy-dependencies
                   - step:
                       type: Run
                       name: check dependencies
                       identifier: check_dependencies
                       spec:
                         connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                         image: maven:3.8-jdk-11
                         shell: Sh
                         command: |-
                           mvn dependency-check:check -U -DskipTests
                         reports:
                           type: JUnit
                           spec:
                             paths:
                               - /harness/target/*.xml
```

{% hint style="info" %}
In addition to Run steps, [Plugin steps](/continuous-integration/use-harness-ci/use-harness-ci/use-drone-plugins/explore-ci-plugins.md) are also useful for installing dependencies.

You can use [Background steps](/continuous-integration/use-harness-ci/use-harness-ci/manage-dependencies/background-step-settings.md) to run dependent services that are needed by multiple steps in the same stage.
{% endhint %}

### Cache dependencies <a href="#cache-dependencies" id="cache-dependencies"></a>

{% tabs %}
{% tab title="Cache Intelligence" %}
Cache your Java dependencies with [Cache Intelligence](/continuous-integration/use-harness-ci/use-harness-ci/caching-ci-data/cache-intelligence.md).

Add `caching.enabled.true` to your `stage.spec`:

```yaml
- stage:
    spec:
      caching:
        enabled: true
```

{% endtab %}

{% tab title="Save and Restore Cache steps" %}
You can use built-in steps to:

* [Save and Restore Cache from S3](/continuous-integration/use-harness-ci/use-harness-ci/caching-ci-data/saving-cache.md)
* [Save and Restore Cache from GCS](/continuous-integration/use-harness-ci/use-harness-ci/caching-ci-data/save-cache-in-gcs.md)

{% hint style="info" %}
**MAVEN CACHE KEY AND PATH REQUIREMENTS**

If you're using Maven, you must reference `pom.xml` in the `key` value for your **Save Cache** and **Restore Cache** steps, for example:

```yaml
spec:
  key: cache-{{ checksum "pom.xml" }}
```

Additionally, you must include `/root/.m2` in the `sourcePaths` for your **Save Cache** step, for example:

```yaml
spec:
  sourcePaths:
    - /root/.m2
```

{% endhint %}

Here's an example of a pipeline with **Save Cache to S3** and **Restore Cache from S3** steps.

```yaml
            steps:
              - step:
                  type: RestoreCacheS3
                  name: Restore Cache From S3
                  identifier: Restore_Cache_From_S3
                  spec:
                    connectorRef: AWS_Connector
                    region: us-east-1
                    bucket: your-s3-bucket
                    key: cache-{{ checksum "pom.xml" }}
                    archiveFormat: Tar
              - step:
                  type: Run
                  ...
              - step:
                  type: BuildAndPushDockerRegistry
                  ...
              - step:
                  type: SaveCacheS3
                  name: Save Cache to S3
                  identifier: Save_Cache_to_S3
                  spec:
                    connectorRef: AWS_Connector
                    region: us-east-1
                    bucket: your-s3-bucket
                    key: cache-{{ checksum "pom.xml" }}
                    sourcePaths:
                      - /root/.m2
                    archiveFormat: Tar
```

{% endtab %}
{% endtabs %}

### Build and run tests <a href="#build-and-run-tests" id="build-and-run-tests"></a>

You can use **Run** or **Test** steps to [run tests in CI pipelines](/continuous-integration/use-harness-ci/use-harness-ci/run-tests/run-tests-in-ci.md).

{% tabs %}
{% tab title="Run step" %}
This example uses two [Run steps](/continuous-integration/use-harness-ci/use-harness-ci/run-step-settings.md) to build and test with Maven.

```yaml
- step:
    type: Run
    name: build
    identifier: build
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: maven:3.8-jdk-11
      shell: Sh
      command: |
        mvn clean package dependency:copy-dependencies
- step:
    type: Run
    name: run test
    identifier: run_test
    spec:
      shell: Sh
      command: |-
        mvn test
      reports:
        type: JUnit
        spec:
          paths:
            - target/surefire-reports/*.xml
```

{% endtab %}

{% tab title="Test step (Test Intelligence)" %}
You must use the **Test** step for your unit tests if you want to leverage Harness' [Test Intelligence](/continuous-integration/use-harness-ci/use-harness-ci/run-tests/ti-overview.md) feature.

```yaml
              - step:
                  type: Test
                  name: RunTestsWithIntelligence
                  identifier: RunTestsWithIntelligence
                  spec:
                    command: |-
                      mvn test
                      mvn package -DskipTests
                    shell: Sh
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: maven:3.8-jdk-11
                    intelligenceMode: true
                    reports:
                      - "target/surefire-reports/*.xml"
```

{% endtab %}
{% endtabs %}

#### Visualize test results <a href="#visualize-test-results" id="visualize-test-results"></a>

If you want to [view test results in Harness](/continuous-integration/use-harness-ci/use-harness-ci/run-tests/viewing-tests.md), make sure your test commands produce reports in JUnit XML format and that your steps include the `reports` specification.

```yaml
reports:
  type: JUnit
  spec:
    paths:
      - target/surefire-reports/*.xml
```

#### Test splitting <a href="#test-splitting" id="test-splitting"></a>

Harness CI supports [test splitting (parallelism)](/continuous-integration/use-harness-ci/use-harness-ci/run-tests/speed-up-ci-test-pipelines-using-parallelism.md) for both **Run** and **Test** steps.

### Specify version <a href="#specify-version" id="specify-version"></a>

{% tabs %}
{% tab title="Harness Cloud" %}
Java is pre-installed on Hosted Cloud runners. For details about all available tools and versions, go to [Platforms and image specifications](/continuous-integration/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md#platforms-and-image-specifications).

If your application requires a specific Java version, add a **Run** step to install it.

<details>

<summary>Install a specific version of Java</summary>

```yaml
- step:
    type: Run
    name: Install Java
    identifier: install_java
    spec:
      shell: Sh
      command: |-
        mkdir -p $HOME/java
        curl -fsSL "https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jdk/hotspot/normal/eclipse" | tar xz -C $HOME/java --strip-components=1
        export JAVA_HOME=$HOME/java
        export PATH=$JAVA_HOME/bin:$PATH
        java -version
```

</details>

<details>

<summary>Install multiple Java versions</summary>

1. Add the [matrix looping strategy](/harness-ai/use-harness-platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism.md) configuration to your stage.

```yaml
strategy:
  matrix:
    javaVersion:
      - "17"
      - "21"
```

2. Reference the matrix variable in your steps.

```yaml
- step:
    type: Run
    name: Install Java
    identifier: install_java
    spec:
      shell: Sh
      command: |-
        mkdir -p $HOME/java
        curl -fsSL "https://api.adoptium.net/v3/binary/latest/<+matrix.javaVersion>/ga/linux/x64/jdk/hotspot/normal/eclipse" | tar xz -C $HOME/java --strip-components=1
        export JAVA_HOME=$HOME/java
        export PATH=$JAVA_HOME/bin:$PATH
        java -version
```

</details>
{% endtab %}

{% tab title="Self-managed" %}
Specify the desired [Java Docker image](https://hub.docker.com/_/eclipse-temurin) tag in your steps. There is no need for a separate install step when using Docker.

<details>

<summary>Use a specific Java version</summary>

```yaml
- step:
    type: Run
    name: Java Version
    identifier: java_version
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: maven:3.9-eclipse-temurin-17
      shell: Sh
      command: |-
        java -version
        mvn --version
```

</details>

<details>

<summary>Use multiple Java versions</summary>

1. Add the [matrix looping strategy](/harness-ai/use-harness-platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism.md) configuration to your stage.

```yaml
strategy:
  matrix:
    javaVersion:
      - "17"
      - "21"
```

2. Reference the matrix variable in the `image` field of your steps.

```yaml
- step:
    type: Run
    name: Java Version
    identifier: java_version
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: maven:3.9-eclipse-temurin-<+matrix.javaVersion>
      shell: Sh
      command: |-
        java -version
        mvn --version
```

</details>
{% endtab %}
{% endtabs %}

### Full pipeline examples <a href="#full-pipeline-examples" id="full-pipeline-examples"></a>

Here's a YAML example of a pipeline that:

1. Tests a Java code repo.
2. Builds and pushes an image to Docker Hub.

This pipeline uses [Harness Cloud build infrastructure](/continuous-integration/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md), [Cache Intelligence](/continuous-integration/use-harness-ci/use-harness-ci/caching-ci-data/cache-intelligence.md), and [Test Intelligence](/continuous-integration/use-harness-ci/use-harness-ci/run-tests/ti-overview.md).

If you copy this example, replace the placeholder values with appropriate values for your Harness project, connector IDs, account/user names, and repo names.

<details>

<summary>Pipeline YAML</summary>

```yaml
pipeline:
  name: Build java
  identifier: Build_java
  projectIdentifier: default
  orgIdentifier: default
  properties:
    ci:
      codebase:
        connectorRef: YOUR_CODE_REPO_CONNECTOR_ID
        repoName: YOUR_REPO_NAME
        build: <+input>
  stages:
    - stage:
        name: Build
        identifier: Build
        description: ""
        type: CI
        spec:
          caching:
            enabled: true
          cloneCodebase: true
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
          execution:
            steps:
              - step:
                  type: Test
                  name: RunTestsWithIntelligence
                  identifier: RunTestsWithIntelligence
                  spec:
                    command: |-
                      mvn test
                      mvn package -DskipTests
                    shell: Sh
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: maven:3.8-jdk-11
                    intelligenceMode: true
                    reports:
                      - "target/surefire-reports/*.xml"
              - step:
                  type: BuildAndPushDockerRegistry
                  name: BuildAndPushDockerRegistry_1
                  identifier: BuildAndPushDockerRegistry_1
                  spec:
                    connectorRef: YOUR_DOCKER_CONNECTOR_ID
                    repo: YOUR_DOCKER_HUB_USERNAME/DOCKER_REPO_NAME
                    tags:
                      - <+pipeline.sequenceId>
```

</details>

### Next steps <a href="#next-steps" id="next-steps"></a>

Now that you have created a pipeline that builds and tests a Java app, you could:

* Create [triggers](/harness-ai/use-harness-platform/triggers.md) to automatically run your pipeline.
* Add steps to [build and upload artifacts](/continuous-integration/use-harness-ci/use-harness-ci/build-and-upload-artifacts.md).
* Add a step to [build and push an image to a Docker registry](/continuous-integration/use-harness-ci/use-harness-ci/build-and-upload-artifacts/build-and-push/build-and-push-to-docker-registry.md).
