# The Java Platform Plugin

version 6.5-milestone-1

The Java Platform plugin brings the ability to declare platforms for the Java ecosystem. A platform can be used for different purposes:

- a description of modules which are published together (and for example, share the same version)

- a set of recommended versions for heterogeneous libraries. A typical example includes the Spring Boot BOM

- sharing a set of dependency versions between subprojects

A platform is a special kind of software component which doesn’t contain any sources: it is only used to reference other libraries, so that they play well together during dependency resolution.

Platforms can be published as Gradle Module Metadata and Maven BOMs.

| The `java-platform` plugin cannot be used in combination with the `java` or `java-library` plugins in a given project. Conceptually a project is either a platform, with no binaries, or produces binaries.

## Usage

To use the Java Platform plugin, include the following in your build script:

Example 1. Using the Java Platform plugin
build.gradle

    plugins {
        id 'java-platform'
    }

build.gradle.kts

    plugins {
        `java-platform`
    }

## API and runtime separation

A major difference between a Maven BOM and a Java platform is that in Gradle dependencies and constraints are declared and scoped to a configuration and the ones extending it. While many users will only care about declaring constraints for compile time dependencies, thus inherited by runtime and tests ones, it allows declaring dependencies or constraints that only apply to runtime or test.

For this purpose, the plugin exposes two configurations that can be used to declare dependencies: `api` and `runtime`. The `api` configuration should be used to declare constraints and dependencies which should be used when compiling against the platform, whereas the `runtime` configuration should be used to declare constraints or dependencies which are visible at runtime.

Example 2. Declaring API and runtime constraints
build.gradle

    dependencies {
        constraints {
            api 'commons-httpclient:commons-httpclient:3.1'
            runtime 'org.postgresql:postgresql:42.2.5'
        }
    }

build.gradle.kts

    dependencies {
        constraints {
            api("commons-httpclient:commons-httpclient:3.1")
            runtime("org.postgresql:postgresql:42.2.5")
        }
    }

Note that this example makes use of constraints and not dependencies. In general, this is what you would like to do: constraints will only apply if such a component is added to the dependency graph, either directly or transitively. This means that all constraints listed in a platform would not add a dependency unless another component brings it in: they can be seen as recommendations.

| For example, if a platform declares a constraint on `org:foo:1.1`, and that nothing else brings in a dependency on `foo`, `foo` will not appear in the graph. However, if `foo` appears, then usual conflict resolution would kick in. If a dependency brings in `org:foo:1.0`, then we would select `org:foo:1.1` to satisfy the platform constraint.

By default, in order to avoid the common mistake of adding a dependency in a platform instead of a constraint, Gradle will fail if you try to do so. If, for some reason, you also want to add dependencies in addition to constraints, you need to enable it explicitly:

Example 3. Allowing declaration of dependencies
build.gradle

    javaPlatform {
        allowDependencies()
    }

build.gradle.kts

    javaPlatform {
        allowDependencies()
    }

## Local project constraints

If you have a multi-project build and want to publish a platform that links to subprojects, you can do it by declaring constraints on the subprojects which belong to the platform, as in the example below:

Example 4. Declaring constraints on subprojects
build.gradle

    dependencies {
        constraints {
            api project(":core")
            api project(":lib")
        }
    }

build.gradle.kts

    dependencies {
        constraints {
            api(project(":core"))
            api(project(":lib"))
        }
    }

The project notation will become a classical `group:name:version` notation in the published metadata.

## Sourcing constraints from another platform

Sometimes the platform you define is an extension of another existing platform.

In order to have your platform include the constraints from that third party platform, it needs to be imported as a `platform` dependency:

Example 5. Importing a platform
build.gradle

    javaPlatform {
        allowDependencies()
    }

    dependencies {
        api platform('com.fasterxml.jackson:jackson-bom:2.9.8')
    }

build.gradle.kts

    javaPlatform {
        allowDependencies()
    }

    dependencies {
        api(platform("com.fasterxml.jackson:jackson-bom:2.9.8"))
    }

## Publishing platforms

Publishing Java platforms is done by applying the `maven-publish` plugin and configuring a Maven publication that uses the `javaPlatform` component:

Example 6. Publishing as a BOM
build.gradle

    publishing {
        publications {
            myPlatform(MavenPublication) {
                from components.javaPlatform
            }
        }
    }

build.gradle.kts

    publishing {
        publications {
            create<MavenPublication>("myPlatform") {
                from(components["javaPlatform"])
            }
        }
    }

This will generate a BOM file for the platform, with a `<dependencyManagement>` block where its `<dependencies>` correspond to the constraints defined in the platform module.

## Consuming platforms

Because a Java Platform is a special kind of component, a dependency on a Java platform has to be declared using the `platform` or `enforcedPlatform` keyword, as explained in the managing transitive dependencies section. For example, if you want to share dependency versions between subprojects, you can define a platform module which would declare all versions:

Example 7. Recommend versions in a platform module
build.gradle

    dependencies {
        constraints {
            // Platform declares some versions of libraries used in subprojects
            api 'commons-httpclient:commons-httpclient:3.1'
            api 'org.apache.commons:commons-lang3:3.8.1'
        }
    }

build.gradle.kts

    dependencies {
        constraints {
            // Platform declares some versions of libraries used in subprojects
            api("commons-httpclient:commons-httpclient:3.1")
            api("org.apache.commons:commons-lang3:3.8.1")
        }
    }

And then have subprojects depend on the platform to get recommendations:

Example 8. Get recommendations from a platform
build.gradle

    dependencies {
        // get recommended versions from the platform project
        api platform(project(':platform'))
        // no version required
        api 'commons-httpclient:commons-httpclient'
    }

build.gradle.kts

    dependencies {
        // get recommended versions from the platform project
        api(platform(project(":platform")))
        // no version required
        api("commons-httpclient:commons-httpclient")
    }