Gradle: Override transitive dependency by version classifier - java

One of the dependencies declared in my project has a transitive dependency on 'com.google.guava:guava:15.0'. But my application deployed on WAS/Weblogic doesn't work due to a CDI issue which has been fixed in 'com.google.guava:guava:15.0:cdi1.0'. (same version, but with classifier) I need to tell gradle to use this jar during build and packaging. I am trying to figure out how we can override this transitive dependency with a jar specific version classifier.
Tried the following approaches:
Added the dependency explicitly: compile 'com.google.guava:guava:15.0:cdi1.0'. But both jars got included in the resultant WAR.
Added the dependency explicitly and defined a resolution strategy:
configurations.all {
resolutionStrategy {
force 'com.google.guava:guava:15.0:cdi1.0'
}
}
Even this didn't work.
Defined a resolution strategy to check and change the version.
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group + ":" + details.requested.name == 'com.google.guava:guava') {
details.useVersion "15.0:cdi1.0"
//details.useTarget "com.google.guava:guava:15.0:cdi1.0"
}
}
}
Even this didn't work.
Need your suggestions on how this issue can be tackled.

Currently classifiers are not yet taken into account when it comes to resolutionStrategies. A workaround for you might excluding the transitive Guava library when declaring your dependencies and adding the Guava cdi1.0 version explicitly:
dependencies {
compile ("org.acme:someDependency:1.0"){
exclude group: 'com.google.guava', module: 'guava'
}
compile "com.google.guava:guava:15.0:cdi1.0"
}

I came across a more elegant approach which is simply:
compile ("com.google.guava:guava:15.0:cdi1.0") {
force = true
}
Explanation
Setting force = true for a dependency tells gradle to use the specified version in case of a version conflict

implementation( group: 'commons-codec', name: 'commons-codec'){
version{
strictly "[1.15]"
}
}
This works for me with gradle 6.6.1
The documentation link for strictly can found here https://docs.gradle.org/current/userguide/rich_versions.html#rich-version-constraints

Gradle 4.5.1 has the function DependencySubstitutions. Here an example to replace a dependency:
configurations.each {
c -> c.resolutionStrategy.dependencySubstitution {
all { DependencySubstitution dependency ->
if (dependency.requested.group == 'org.json') {
dependency.useTarget 'com.vaadin.external.google:android-json:0.0.20131108.vaadin1'
}
}
}
}

This will not work if the same dependency is pointed by some other jar. Sureshot way to exclude the dependency
configurations {
all*.exclude group: 'com.google.guava', module:'guava-jdk5'
}

Since force = true is deprecated, relevant solution is to use strictly(...) version, e.g.:
dependencies {
// no need to exclude transitive spring-data-relational from this dependency
implementation("org.springframework.data", "spring-data-r2dbc", "1.1.0.RC1")
implementation("org.springframework.data", "spring-data-relational").version {
strictly("2.0.0.RC1")
}
}
P.S. tested on Gradle 6.3

Try this:
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
// https://docs.gradle.org/current/userguide/resolution_rules.html
if (details.requested.group == 'com.google.guava' && details.requested.name == 'guava') {
details.useVersion '15.0:cdi1.0'
}
}
}

try this its working perfectly in my case in App level in build.gradle file
android {
configurations {
all*.exclude module: 'conceal'
all*.exclude module: 'bcprov-jdk15on'
}
}

Related

Generate a single JAR files with multiple JARs and wsdl files combined together

I am trying to generate a JAR (salesforce-soap-connection.jar) file that I want to use as a dependency (library) in my project.
Currently, I generate this jar file using the command below and then push the generated jar file in my repo.
java -classpath ./sf/force-wsc-45.0.0.jar:./sf/js-1.7R2.jar:./sf/ST4-4.3.1.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/lib/tools.jar:./sf/antlr-4.7.2-complete.jar com.sforce.ws.tools.wsdlc ./sf/enterprise.wsdl ./sf/salesforce-soap-connection.jar
Basically, I want to do this using Gradle. (Instead of running the above command, do exactly what the command does using Gradle)
The jar files that are used in the above command should be downloaded from maven repository and only enterprise.wsdl will be present in project/src/main/resources folder.
The generated jar file should be kept in project/lib folder.
This is my build.gradle file. Please suggest what to do next…
plugins {
id 'scala'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.scala-lang:scala-library:2.11.8'
implementation 'joda-time:joda-time:2.1'
implementation 'org.joda:joda-convert:2.1.1'
implementation 'org.slf4j:slf4j-api:1.7.36'
implementation 'org.apache.logging.log4j:log4j-api:2.6.2'
implementation 'org.apache.logging.log4j:log4j-core:2.6.2'
implementation 'com.google.cloud:google-cloud-storage:1.52.0'
implementation 'com.google.cloud:google-cloud-bigquery:1.110.0'
implementation 'org.jooq:jooq:3.12.0'
implementation 'org.jooq:jooq-scala_2.11:3.10.5'
implementation 'com.google.apis:google-api-services-bigquery:v2-rev459-1.25.0'
implementation 'org.json:json:20180813'
implementation 'com.google.code.gson:gson:2.8.3'
implementation 'org.yaml:snakeyaml:1.25'
implementation 'com.github.tototoshi:scala-csv_2.11:1.3.6'
implementation 'org.json4s:json4s-native_2.11:3.6.10'
implementation 'org.apache.commons:commons-dbcp2:2.0.1'
implementation 'commons-io:commons-io:2.6'
implementation fileTree('lib')
}
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest {
attributes "Main-Class": "com.org.loader.main"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
I was able to solve the issue with gradle JavaExec type task.
JavaExec Executes a Java application in a child process.
Similar to Exec, but starts a JVM with the given classpath and application class.
I have used toCopy as a configuration so that it is only salesForceJarGenerate task and not included in project external dependencies.
transitive = false is used to stop given dependency to not download or exclude its transitive dependencies.
In salesForceJarGenerate task:-
classpath option is to include dependencies/jars that are needed to be provided for the soap-connector.jar
main option is to provide the main class that will convert .ml files to java files.
args is the arguments.
agrs "{location of your enterprise.xml file}" , "output location of your jar file" , "location where the intermediate java files will be stored"
More on this can be found on official documentation here!
here is my final build.gradle
plugins {
id 'scala'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.scala-lang:scala-library:2.11.8'
implementation 'joda-time:joda-time:2.1'
implementation 'org.joda:joda-convert:2.1.1'
implementation 'org.slf4j:slf4j-api:1.7.36'
implementation 'org.slf4j:slf4j-simple:2.0.0'
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.12.1'
implementation 'org.apache.logging.log4j:log4j-api:2.6.2'
implementation 'org.apache.logging.log4j:log4j-core:2.6.2'
implementation 'com.google.cloud:google-cloud-storage:1.52.0'
implementation 'com.google.cloud:google-cloud-bigquery:1.110.0'
implementation 'org.jooq:jooq:3.12.0'
implementation 'org.jooq:jooq-scala_2.11:3.10.5'
implementation 'com.google.apis:google-api-services-bigquery:v2-rev459-1.25.0'
implementation 'org.json:json:20180813'
implementation 'com.google.code.gson:gson:2.8.3'
implementation 'org.yaml:snakeyaml:1.25'
implementation 'com.github.tototoshi:scala-csv_2.11:1.3.6'
implementation 'org.json4s:json4s-native_2.11:3.6.10'
implementation 'org.apache.commons:commons-dbcp2:2.0.1'
implementation 'commons-io:commons-io:2.6'
implementation 'com.force.api:force-wsc:54.0.0'
implementation fileTree('lib')
}
configurations {
toCopy
}
dependencies {
toCopy('com.force.api:force-wsc:45.0.0') {
transitive = false
}
toCopy('rhino:js:1.7R2') {
transitive = false
}
toCopy('org.antlr:ST4:4.3.1') {
transitive = false
}
toCopy('org.antlr:antlr-complete:3.5.2') {
transitive = false
}
toCopy('jdk.tools:jdk.tools:1.8') {
transitive = false
}
}
//generates salesforce-soap-connection.jar with the given enterprise.xml file
task salesForceJarGenerate(type: JavaExec) {
classpath = configurations.toCopy
main = "com.sforce.ws.tools.wsdlc"
args "${projectDir}/src/main/resources/enterprise.xml", "${projectDir}/lib/salesforce-soap-connection.jar", "$buildDir/salesforce-gen/"
}
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest {
attributes "Main-Class": "com.org.loader.main"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
compileScala.dependsOn salesForceJarGenerate
Now the generated jar file can be added as a dependency
by adding the below in dependencies block.
implementation fileTree("location(directory) of generated jar file")

Duplicate class org.apache.commons

I am trying to run a build on Android. I'm in the process of building a React-Native bridge for a Java library. However, I am getting a Duplicate class error which looks like follows:
Duplicate class org.apache.commons.lang3.builder.CompareToBuilder
found in modules commons-lang3-3.9.jar (org.apache.commons:commons-lang3:3.9)
and creditCardNfcReader-1.0.3-runtime.jar (com.github.pro100svitlo:creditCardNfcReader:1.0.3)
There are an entire list of these errors being printed out. There is also a slightly different error which looks like this:
Duplicate class bolts.BoltsExecutors$ImmediateExecutor found in
modules bolts-tasks-1.4.0.jar (com.parse.bolts:bolts-tasks:1.4.0)
and jetified-bolts-android-1.1.2.jar (com.parse.bolts:bolts-android:1.1.2)
My dependencies currently look like this in my build.gradle file:
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.facebook.react:react-native:+"
implementation 'com.github.pro100svitlo:creditCardNfcReader:1.0.3'
addUnimodulesDependencies()
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
}
I have tried to exclude modules and groups from the dependencies with no luck yet. Here are a few approaches I have tried as recommended from several sources:
1.
implementation ("com.facebook.react:react-native:+") {
exclude group: 'com.parse.bolts', module: 'bolts-tasks'
}
2.
implementation ("com.facebook.react:react-native:+") { exclude module: 'bolts-tasks'}
3.
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.facebook.react:react-native:+"
implementation 'com.github.pro100svitlo:creditCardNfcReader:1.0.3'
addUnimodulesDependencies()
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
}
configurations { runtime.exclude group: '*' }
I would really appreciate any help with finding a solution to this problem. Thanks in advance.
The exclude method of the configuration closure for a dependency excludes transitive dependencies. So, if your module dependency depends on other modules, you can exclude them from your build. You can check out the transitive dependencies of the 'com.facebook.react:react-native:+' module on its maven repo.
https://mvnrepository.com/artifact/com.facebook.react/react-native/0.20.1
Try specify the version of dependency instead of use '+' to avoid conflict.
Here is some reference to check on resolving the plugins and dependency jar conflicts.
jarjar package reference

How to resolve gradle dependency conflicts on library root dependency?

In my project I have two dependencies with okio as a transitive dependency conflict. In theory, gradle should solve it by choosing the highest version, but that didn't work.
I have been trying everything, since exclude until force the version from okio lib, but nothing works. Looking on external libraries path, I realized that one of the dependencies contains the okio as a path of the dependency, and I believe that this is the problem. But how can I solve this?
This is a simple gradle example with my two dependencies. Commented lines are my failed attempts to solve the problem:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.72'
}
repositories {
mavenCentral()
}
//configurations.all {
// resolutionStrategy.force('com.squareup.okio:okio:2.4.3')
//}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// -> dependency one
implementation "com.squareup.retrofit2:retrofit:2.7.2"
// implementation ("com.squareup.retrofit2:retrofit:2.7.2"){
// exclude group: 'com.squareup.okio'
// }
// -> dependency two
implementation "com.eternitywall:java-opentimestamps:1.18"
// implementation ("com.eternitywall:java-opentimestamps:1.18") {
// exclude group: 'com.squareup.okio'
// }
// implementation "com.squareup.okio:okio:2.4.3"
// implementation "com.squareup.okio:okio"
// constraints {
// implementation("com.squareup.okio:okio:2.4.3") {
// because 'transitive version conflict'
// }
// }
}
To get the error, just have a Main.kt file with the code:
import okhttp3.OkHttpClient
fun main(args: Array<String>) {
OkHttpClient.Builder()
}
And the error obtained is:
Exception in thread "main" java.lang.NoSuchMethodError: 'boolean okio.ByteString.startsWith(okio.ByteString)'
at okio.Options.of(Options.java:64)
at okhttp3.internal.Util.<clinit>(Util.java:73)
at okhttp3.OkHttpClient.<clinit>(OkHttpClient.java:124)
at okhttp3.OkHttpClient$Builder.<init>(OkHttpClient.java:449)
at MainKt.main(Main.kt:4)
And finally, this is the library root that I mentioned from dependence with the included okio
I will be very grateful if you can help me.
Thanks in advance!!
Well, it seems that the only solution is to separate the app into modules, placing each dependency in a different module. Even though I was reluctant it was the only way I found.

How to define different dependencies for subprojects in a multi-projects Gradle ? (Java)

I have a distributes projects with different sub-projects and I want to accomplish the following:
(root)
client
module A
module B
module C
model
I want to put
protoc {
artifact = 'com.google.protobuf:protoc:3.5.0'
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:1.7.0"
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
} }
dependencies {
compile "com.google.api.grpc:proto-google-common-protos:1.0.0"
compile "io.grpc:grpc-netty:1.7.0"
compile "io.grpc:grpc-protobuf:1.7.0"
compile "io.grpc:grpc-stub:1.7.0"
}
for module A, B and C.
For now I have the following in my root build.gradle
subprojects{
apply plugin: 'java'
sourceCompatibility = 1.8
group 'project'
version '0.0.1-SNAPSHOT'
jar {
manifest {
attributes 'Main-Class': "com.project.${project.name}.App"
}
doFirst {
from { configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } }
}
}
repositories {
mavenCentral()
mavenLocal()
}
dependencies {
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.9.5'
}
}
So every sub-project use java plugin, and has the defines dependencies and jar task.
How can I only put the first block for some sub-projects ?
I tried using a variable like in Multi-project Builds - Gradle but I couldn't access it in subprojects block.
Thank you in advance. I'm really interested in using Gradle correctly and it's a bit hard to get into it outside of simple Android/Java projects. Feel free to include any documentations I should read :)
Edit:
Thank you. I wouldn't have posted here if I hadn't search before. Apparently I was missing the keyword "subset" who would have gave me the solution you linked.
A solution is described here: https://discuss.gradle.org/t/configure-only-subset-of-subprojects/5379/3
You can run configure() with a list of projects.
project.ext {
subprojectList = subprojects.findAll{
it.name == 'subprojectA' ||
it.name == 'subprojectB' ||
it.name == 'subprojectC'
}
}
configure(project.subprojectList) {
// insert your custom configuration code
}
or
configure([project(':a'), project(':b'), project(':c')]) {
// configuration
}

How to shade a transitive dependency in Gradle?

Is there a way to shadow a particular (transitive) dependency in Gradle? My situation: I have a project that depends directly on com.amazonaws:aws-java-sdk-emr:1.10.33 and org.apache.hadoop:hadoop-aws:2.7.1, but hadoop-aws in turns depends on com.amazonaws:aws-java-sdk-emr:1.7.4 which screws the final JAR, but I need both anyway.
Is it currently possible to do something like this?
shadowJar {
relocate('com.amazonaws', 'shadowedstuff.awsjdk') {
include(dependency('com.amazonaws:aws-java-sdk:1.7.4'))
}
}
Or a not-so-dirty workaround for it?
Thanks!
NOTE: shading the aws-sdk which my projects depends on directly is not an option. This is a simplification and in the original setup some reflection is going on.
Yes, you can use the shadow plugin for Gradle to which has a very similar syntax to your example:
// Configuring Filtering for Relocation
shadowJar {
relocate('junit.textui', 'a') {
exclude 'junit.textui.TestRunner'
}
relocate('junit.framework', 'b') {
include 'junit.framework.Test*'
}
}
Apologies if I've misunderstood your situation and it is in fact more complex but it looks like the exclusion can just be provided in the dependency declaration?
dependencies {
...
compile('org.apache.hadoop:hadoop-aws:2.7.1') {
exclude group: 'com.amazonaws', module: 'aws-java-sdk'
}
...
}
You can try the resolutionStrategy (https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.ResolutionStrategy.html) to force a particular version.
Example from a projet:
configurations {
testImplementation.extendsFrom compileOnly
all {
resolutionStrategy {
// Force jackson 2.4.4 for Spark
force 'com.fasterxml.jackson.core:jackson-core:2.4.4', 'com.fasterxml.jackson.core:jackson-databind:2.4.4', 'com.fasterxml.jackson.core:jackson-annotations:2.4.4'
force 'com.google.guava:guava:23.6-jre'
}
}
}

Categories

Resources