I'm new to Gradle and I'm facing a problem ( using Gradle 2.14)
My project has 3 subprojects: assembly, sprinBootProject, E2E
The assembly module is responsible of zipping the jar of the spring boot application along with properties files.
The E2E module is an 'end to end' tests module for testing the spring boot application.
I'm using the distribution plugin in assembly/build.gradle like this:
apply plugin: 'distribution'
distributions {
main {
baseName "project-assembly"
contents{
from project(':springBootProject').fileTree(dir: 'build/libs', include: ['*.jar'])
from project(':springBootProject').fileTree(dir: 'build/resources/main', includes: ['*.properties', 'conf/*.properties'])
}
}
}
but I need to make sure that springBootProject will be evaluated before the assembly subproject, so I used dependsOn like that:
distZip.dependsOn(':springBootProject:build')
when I added this line I see that the E2E module is failing in compileTestJava task, so this line is effecting how the spring boot application is generated (I think)
E2E/build.gradle:
dependencies {
testCompile project(":springBootProject")
}
so my question is am I doing it right? why when I added the dependsOn line the E2E module is effected, and how can I force Gradle to evaluate the spring boot application before the assembly module?
I figure it out.
Gradle will evaluate modules by alphabetic order
so the evaluation will be: assembly -> E2E -> springBootProject
when adding this line to assembly/build.gradle:
distZip.dependsOn(':springBootProject:build')
the order of the evaluation changed to be: assembly -> springBootProject -> E2E
now in 'springBootProject' module I am using the spring boot plugin in order to generate an executable jar, this executable jar is generated by the task :springBootProject:bootRepackage which uses the jar generated by the :springBootProject:jar task and convert it to executable.
now because the 'E2E' module depends on 'springBootProject' it will use it's jar, but now this jar is executable, and because of that I had a compilation error in 'E2E'.
so I have to make sure that the 'E2E' module gets the original jar and not the executable one.
so I added this line in springBootProject/build.gradle:
bootRepackage.mustRunAfter(':E2E:test')
which seem to solve the problem
Related
I have a gradle project P which has module A and B. Module A has this jackson dependencies:
...
dependencies {
...
compile 'com.fasterxml.jackson.core:jackson-core:2.12.0-rc1'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.12.0-rc1'
compile 'com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1'
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.12.0-rc1'
...
}
...
and module B uses module A, and has no need for including this dependencies because jackson usage is encapsulated in module A. But when code executed from module B reaches a statement that invokes code from Module A using it, I get exception:
java.lang.NoClassDefFoundError: com/fasterxml/jackson/dataformat/xml/XmlMapper
If I add the same dependencies to Module's B gradle.build file, the code works.
The question is, why would I include them if Module A does not use the library?
Shouldn't dependencies in Module A be compiled, packaged, so that when Module A is used elsewhere, its code works (using its included dependencies such as jackson library as in this example)
Disclaimer: I don't know Gradle, but this sounds like a problem common to Maven and Gradle.
The fact that you can build the module, but not run the module, means that somewhere you are not bringing the transitive dependencies into the Spring Boot fat jar. Jackson doesn't do anything weird with metadata files, classloaders, etc. It plays well with others in a fat jar.
Given that you haven't shared much of your build files, the easiest way to figure out if something has excluded the Jackson XML module is to just run jar -xvf target/app.jar and inspect the output to see if it's in there.
If it's not, look for a Gradle equivalent of the Maven dependency plugin's dependency-tree target that will show you the whole transitive dependency tree. If it's being excluded you'll definitely see it missing from a dependency dump.
I'm having a problem on a larger multi-project build that I was migrating from Gradle 4 to 5, and replicated the issue on a smaller, more concise build to demonstrate the issue.
I have 2 projects in the build. One is a dependency (basic library) used by the other.
demo (root project)
|- build.gradle
|
|--- demo-web
|---|- build.gradle
|
|--- demo-dependency
|---|- build.gradle
Snippet of demo-web: build.gradle
...
dependencies {
implementation project(':demo-dependency')
...
}
...
The dependency project defines one class that is used in the web project, DownstreamThing.
The web project attempts to use that to construct its own object, but during a build on the root project level, it fails.
> ./gradlew build
> Task :demo-web:test
com.example.demo.ThingTest > testThing FAILED
java.lang.NoClassDefFoundError at ThingTest.java:12
Caused by: java.lang.ClassNotFoundException at ThingTest.java:12
ThingTest.java
#Test
public void testThing() {
DownstreamThing t = new DownstreamThing(); //line 12, ClassNotFoundException
assertTrue(t != null);
}
I didn't have any trouble with this in Gradle 4, but only in Gradle 5. Why is the dependency not being found during the test task?
Full source for the example is here: https://bitbucket.org/travelsized/gradle-problem/src/master/
I believe he reason you are getting that exception is because you have applied the Spring Boot plugin to the demo-dependency project. What this plugin does is to repackage the jar file to a fat jar that needs a special classloader to load the content.
You can still use the Spring Boot dependencies (e.g. the starters) in the dependency project without the plugin. So if you can, just remove it.
If there is a particular reason why you have it, you will need to keep the original jar file so that is used as the actual dependency. For Spring Boot 1.5.x, you can do that with something like this:
bootRepackage {
classifier = "boot"
}
bootJar {
enabled = true
}
But do note that I don't think Spring Boot 1.5 is fully compatible with Gradle 5 and later (at this time we are on 6.0), so you might need to either downgrade Gradle or upgrade Spring Boot.
Bjørn Vester's answer got me pointed in the right direction. The spring-boot plugin was causing the jar tasks to go awry. I needed to make sure that the bootJar task was disabled for the dependency while the jar task was enabled.
The changes in the configuration to do this between versions of Gradle and the Spring Boot plugin made this get lost in the upgrades.
Previously, I could specify a classifier for the jar post-boot:
bootRepackage {
classifier = 'exec'
}
Now, I need to enable and disable the appropriate tasks:
bootJar {
enabled = false
}
jar {
enabled = true
archiveBaseName = "demo-dependency"
}
In the larger project, I previously had a jar task that specified the archiveBaseName, but didn't explicitly enable it to override the bootJar task. Once I made the above changes (while keeping the boot plugins in place), things started working.
We're currently deploying our Flink applications as a fat-jar using the maven-shade-plugin. Problem is, each application jar ends up being approximately 130-140 MB which is a pain to build and deploy every time. Is there a way to exclude dependencies and just deploy a thin jar to the cluster which comes up to about 50 kB?
You can place the dependency JARs in the cluster beforehand in Flink's lib (see Avoid Dynamic Classloading) and just upload the thin JAR on each job submission.
Here's how we do it with Gradle!
We have two sub-projects:
job: For the stream job that we want to run
runtime: For additional runtime dependencies (e.g. a custom FileSystem implementation)
We create a new gradle configuration for dependencies that are provided at runtime:
configurations {
provided,
compile.extendsFrom provided
}
and then mark the provided dependencies as:
provided("org.apache.flink:flink-java:1.6.0") // flink java v1.6.0
Then, we modify the jar task to build a jar without any provided dependencies:
jar {
dependsOn configurations.runtime
from {
(configurations.runtime - configurations.provided).collect {
it.isDirectory()? it : zipTree(it)
}
} {
exclude 'META-INF/*.RSA'
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
}
manifest {
attributes 'Main-Class': 'com.example.Entrypoint'
}
}
The result is a jar with required dependencies (compile) bundled which we then deploy using the Web UI.
As for the custom runtime dependencies, we build a custom Docker image and push the built artifact (runtime.jar, built using same configuration as above) to the libs/directory in Flink. You can do that manually too if you are not using Docker.
And, lastly, in our particular case there is no direct dependency defined between our job and the runtime dependency (which is discovered using reflection).
I have a multi-module project:
Root project 'platform'
+--- Project ':api'
+--- Project ':common'
and in the :common module I include all the dependencies, in the :api module, I only have
apply(plugin = "org.springframework.boot")
dependencies {
implement(project(":common"))
}
The problem is that when I build the :api module, from the jar file I can't see any of the dependencies inside the jar file, there's no BOOT-INF/libs/ only BOOT-INF/classes/.
When I run the jar with java -jar, it says NoClassFound for one of the class in the :common module.
gradle :api:bootRun works fine.
Is there any other config I should do?
I'm using gradle 4.9 kotlin dsl and spring boot plugin 1.5.15.RELEASE
The reason is that the spring boot 1.5 gradle plugin is only targeted for gradle 2 & 3, which does not support the implementation configuration (introduced in gradle 4).
Based on this documentation, it describes that by default only compile and runtime configurations are included. That being said, it is possible to include a custom configuration to make it work.
If you look at the hierarchy of gradle configurations for gradle 5 Illustrated here, runtimeClasspath is the root of implementation, and therefore has all the dependencies you need for your runnable jar.
This means that for spring boot 1.5, you can point it to a custom configuration to get it to correctly build the runnable jar:
build.gradle:
bootRepackage {
customConfiguration = 'runtimeClasspath'
}
build.gradle.kts:
import org.springframework.boot.gradle.repackage.RepackageTask
// more of the build file
tasks {
"bootRepackage"(RepackageTask::class) {
setCustomConfiguration("runtimeClasspath")
}
}
Turns out it's because of spring-boot-plugin 1.5.x somehow doesn't recognize the implement(project(:common)), by changing to compile(project(:common)) it works fine.
I have a java gradle project. I have a dependency.
dependencies {
compile project(":mymodule")
compile 'org.springframework:spring-context:4.1.2.RELEASE'
}
Where can I find and use exact jar file name of both my custom module and spring dependency jar to process it further in composing application's libs, folders, etc?
Stick this in a new task:
project.configurations.compile.each{ println it}
Or, for just one specific dependency:
println project.configurations.compile.find {it.name.startsWith("something") }
However, if you're looking to create distributable packages including dependencies, you really should look into the gradle application plugin.