// Copyright (C) 2023 Gradle, Inc.
//
// Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 4.0 International License.;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[capabilities]]
= Capabilities

In a dependency graph, it's common for multiple implementations of the same API to be accidentally included, especially with libraries like logging frameworks where different bindings are selected by various transitive dependencies.
[[sec:handling-mutually-exclusive-deps]]
Since these implementations typically reside at different group, artifact, and version (GAV) coordinates, build tools often can't detect the conflict.

To address this, Gradle introduces the concept of **capability**.

[[sec:capabilities]]
== Understanding capabilities

A capability is essentially a way to declare that different components (dependencies) offer the same functionality.

It’s **illegal** for Gradle to include more than one component providing the same capability in a single dependency graph.
If Gradle detects two components providing the same capability (e.g., different bindings for a logging framework), it will fail the build with an error, indicating the conflicting modules.
This ensures that conflicting implementations are resolved, avoiding issues on the classpath.

For instance, suppose you have dependencies on two different libraries for database connection pooling:

[source,kotlin]
----
dependencies {
    implementation("com.zaxxer:HikariCP:4.0.3")  // A popular connection pool
    implementation("org.apache.commons:commons-dbcp2:2.8.0")  // Another connection pool
}

configurations.all {
    resolutionStrategy.capabilitiesResolution.withCapability("database:connection-pool") {
        select("com.zaxxer:HikariCP")
    }
}
----

In this case, both `HikariCP` and `commons-dbcp2` provide the same functionality (connection pooling).
Gradle will fail if both are on the classpath.

Since only one should be used, Gradle’s resolution strategy allows you to *select* `HikariCP`, resolving the conflict.

[[sec:declaring-capabilities-external-modules]]
== Understanding capability coordinates

A **capability** is identified by a `(group, module, version)` triplet.

Every component defines an implicit capability based on its GAV coordinates: group, artifact, and version.

For instance, the `org.apache.commons:commons-lang3:3.8` module has an implicit capability with the group `org.apache.commons`, name `commons-lang3`, and version `3.8`:

[source,kotlin]
----
dependencies {
    implementation("org.apache.commons:commons-lang3:3.8")
}
----

It's important to note that capabilities are **versioned**.

[[sec:declaring-component-capabilities]]
== Declaring component capabilities

To detect conflicts early, it's useful to declare *component capabilities* through rules, allowing conflicts to be caught during the build instead of at runtime.

One common scenario is when a component is relocated to different coordinates in a newer release.

For example, the ASM library was published under `asm:asm` until version `3.3.1`, and then relocated to `org.ow2.asm:asm` starting with version `4.0.`
Including both versions on the classpath is illegal because they provide the same feature, under different coordinates.

Since each component has an implicit capability based on its GAV coordinates, we can address this conflict by using a rule that declares the `asm:asm` module as providing the `org.ow2.asm:asm` capability:

====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/kotlin",files="build.gradle.kts[tags=fix_asm]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/groovy",files="build.gradle[tags=fix_asm]"]
====

With this rule in place, the build will fail if both `asm:asm` ( < = `3.3.1`) and `org.ow2.asm:asm` (`4.0+`) are present in the dependency graph.

NOTE: Gradle won’t resolve the conflict automatically, but this helps you realize that the problem exists. It’s recommended to package such rules into plugins for use in builds, allowing users to decide which version to use or to fix the classpath conflict.

[[sec:selecting-between-candidates]]
== Selecting between candidates

At some point, a dependency graph is going to include either _incompatible modules_, or modules which are _mutually exclusive_.

For example, you may have different logger implementations, and you need to choose one binding.
<<component_capabilities.adoc#capabilities,Capabilities>> help _understand_ the conflict, then Gradle provides you with tools to solve the conflicts.

[[sec:selecting-preferred-capability-provider]]
=== Selecting between different capability candidates

In the relocation example above, Gradle was able to tell you that you have two versions of the same API on classpath: an "old" module and a "relocated" one.
We can solve the conflict by automatically choosing the component which has the highest capability version:

====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/kotlin",files="build.gradle.kts[tags=use_highest_asm]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/groovy",files="build.gradle[tags=use_highest_asm]"]
====

However, choosing the highest capability version conflict resolution is not always suitable.

For a logging framework, for example, it doesn't matter what version of the logging frameworks we use.
In this case, we explicitly select `slf4j` as the preferred option:

====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/kotlin",files="build.gradle.kts[tags=use_slf4j]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/groovy",files="build.gradle[tags=use_slf4j]"]
====

This approach works also well if you have multiple `slf4j` bindings on the classpath; bindings are basically different logger implementations, and you need only one.
However, the selected implementation may depend on the configuration being resolved.

For instance, in testing environments, the lightweight `slf4j-simple` logging implementation might be sufficient, while in production, a more robust solution like `logback` may be preferable.

Resolution can only be made in favor of a module that is found in the dependency graph.
The `select` method accepts only a module from the current set of candidates.
If the desired module is not part of the conflict, you can choose not to resolve that particular conflict, effectively leaving it unresolved.
Another conflict in the graph may have the module you want to select.

If no resolution is provided for all conflicts on a given capability, the build will fail because the module chosen for resolution was not found in the graph.
Additionally, calling `select(null)` will result in an error and should be avoided.

For more information, refer to the link:{javadocPath}/org/gradle/api/artifacts/ResolutionStrategy.html#capabilitiesResolution[capabilities resolution API].
