I'm having some dependency issues with Gradle. The dependent package is apache storm (org.apache.storm:storm-core:0.9.5) and using Intellij.
I needed the storm package for compiling but not during runtime and so I had my build.gradle configured as below.
apply plugin: 'application'
apply plugin: 'idea'
apply plugin: 'eclipse'
...
configurations {
provided
}
idea {
module{
scopes.PROVIDED.plus += [configurations.provided]
}
}
eclipse {
classpath {
plusConfigurations += [configurations.provided]
}
}
sourceSets {
main {
compileClasspath += configurations.provided
runtimeClasspath += configurations.provided
}
test {
compileClasspath += configurations.provided
runtimeClasspath += configurations.provided
}
}
dependencies {
...
provided 'org.apache.storm:storm-core:0.9.5'
...
}
Similarly the configuration allows Intellij to know of the dependency for linking to the references and the build. My issue is I'd like to run the Storm topology in LocalMode but when I do this the dependency doesn't seem to be pulled in during runtime and I get the error below. How can I keep the package excluded when creating the jar but included when running via Intellij or Eclipse?
Connected to the target VM, address: '127.0.0.1:56593', transport: 'socket'
Exception in thread "main" java.lang.NoClassDefFoundError: backtype/storm/topology/IRichSpout
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2693)
at java.lang.Class.privateGetMethodRecursive(Class.java:3040)
at java.lang.Class.getMethod0(Class.java:3010)
at java.lang.Class.getMethod(Class.java:1776)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
Disconnected from the target VM, address: '127.0.0.1:56593', transport: 'socket'
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: backtype.storm.topology.IRichSpout
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more
I've found a way to do this by configuring Run/Debug configurations to explicitly include the classpath for my application's jar and the apache-storm's jar.
If there's a better to do this I'd be happy to hear about it.
I do it like this. First of all, a bunch of new configurations with cumbersome names:
configurations {
includedInStormJar
excludedFromStormJar.extendsFrom includedInStormJar
compile.extendsFrom excludedFromStormJar
}
Then, within my dependencies section:
excludedFromStormJar "org.apache.storm:storm-core:$stormVersion"
includedInStormJar "org.yaml:snakeyaml:1.15"
// any other cool stuff here
And finally a task which looks pretty similar to the widely known 'fatJar':
task stormJar(type: Jar) {
dependsOn configurations.runtime
from {
(configurations.runtime - configurations.excludedFromStormJar + configurations.includedInStormJar).collect {
it.isDirectory() ? it : zipTree(it)
}
}
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
with jar
}
So when building a jar with 'gradle clean stormJar', it excludes everything related to Storm itself (plus its dependencies) but includes all other stuff with it's dependencies - it might be important since there were times I've had dependency conflicts. Also since compile configuration extends from excludedFromStormJar, IntelliJ's build gets Storm and can run things on an instance of LocalCluster.
Also you might want to check this question:
Making the Storm JARs compile-time only in a Gradle project
Similar approach but in a different and, I have to admit, more elegant way.
Related
...obviously some necessary things are not included from the dependencies.
Once it reaches a call to an external library, it breaks, either with ClassNotFoundException, or without a word.
I started with this skeleton project.
Relevant changes in build.gradle:
application {
mainClassName = 'net.laca.FoKt'
}
(my main function is in fo.kt)
dependencies {
//...
compile "com.sparkjava:spark-core:2.9.3"
implementation 'com.google.code.gson:gson:2.8.6'
implementation fileTree('libs') { include '*.jar' }
}
jar {
archiveBaseName = 'csira'
// Uncommend the last two lines to build a "fat" jar with `./gradlew jar`,
// and run it without Gradle's help: `java -jar build/libs/skeleton.jar`
manifest { attributes 'Main-Class': 'net.laca.FoKt' }
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
versions: Kotlin 1.4.20, Java 11, Gradle 6.7.1
Allegedly it should work this way. As it does if I start it with gradle run.
But when I start it with java -jar build/libs/csira.jar after gradle jar, it doesn't.
Relevant parts of fo.kt:
package net.laca
import spark.Spark.*
import com.google.gson.GsonBuilder
fun main(args: Array<String>) {
before("/*")
{ req, res ->
res.type("application/json")
println("hívás: ${req.requestMethod()} ${req.pathInfo()} " + req.queryString())
println(GsonBuilder().create().toJson(req.queryMap().toMap())) //line 14
//...
}
At GsonBuilder it breaks:
java.lang.NoClassDefFoundError: com/google/gson/GsonBuilder
at net.laca.FoKt$main$1.handle(fo.kt:14)
at spark.FilterImpl$1.handle(FilterImpl.java:73)
at spark.http.matching.BeforeFilters.execute(BeforeFilters.java:48)
at spark.http.matching.MatcherFilter.doFilter(MatcherFilter.java:133)
at ...
...
Caused by: java.lang.ClassNotFoundException: com.google.gson.GsonBuilder
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 19 more
And when I take/comment out the 14th line, and it reaches a call to my own jar in /libs:
get("/whatever")
{
println("before")
com.zz.app.MyScalaClass.apply().myFun()
println("after")
}
then the last thing I see is before, the rest is silence.
It happens because your jar task is configured incorrectly. To understand why, look at your dependencies:
dependencies {
//...
compile "com.sparkjava:spark-core:2.9.3"
implementation 'com.google.code.gson:gson:2.8.6'
implementation fileTree('libs') { include '*.jar' }
}
You are using both the compile and implementation configurations. The former is deprecated and should not be used by the way.
Then look at the jar task:
jar {
archiveBaseName = 'csira'
// Uncommend the last two lines to build a "fat" jar with `./gradlew jar`,
// and run it without Gradle's help: `java -jar build/libs/skeleton.jar`
manifest { attributes 'Main-Class': 'net.laca.FoKt' }
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
The from part instructs Gradle to collect all dependencies from the compile configuration only, and this will ignore the implementation configuration completely.
While you could change "compile" to "implementation" everywhere, the correct way to construct a fat jar is to actually collect from the runtimeClasspath configuration. This one extends other configurations like compile and implementation, but also runtimeOnly which you might find handy in the future.
There is actually also an example of how to do this in the Gradle user guide. To adapt it for your project, it should look like:
jar {
archiveBaseName = 'csira'
manifest { attributes 'Main-Class': 'net.laca.FoKt' }
dependsOn configurations.runtimeClasspath
from {
configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) }
}
}
The extra dependsOn line ensures that the runtimeClasspath configuration is completely resolved before trying to use it. Another difference is that it only collects jar files.
I’m building a Gradle Plugin with Kotlin, in one of the features I’m using Ktor and Coroutines. The plugin must to be included as classpath in third part projects.
Problem comes when I’m trying to use the Plugin in another project, I'm getting:
Caused by: java.lang.NoClassDefFoundError: kotlin/coroutines/Continuation on the consumer project.
I tried to isolate the Coroutines dependencies and apply transitive dependencies for Ktor but no success.
I have seen too different solutions(https://github.com/Kotlin/kotlinx.coroutines/issues/430) like applying ShadowJar to build a FatJar but maybe I’m missing something in the configuration. Once I apply the Shadow Plugin the jar is about 62Mb, even applying minimize size of the jar is 12MB.
The basic conf(based on the samples of Kotlin-DSL) of the plugin would be:
plugins {
`kotlin-dsl`
`maven-publish`
kotlin("jvm") version "1.3.10"
id("com.github.johnrengelman.shadow") version "4.0.3"
}
gradlePlugin {
plugins {
register("greet-plugin") {
id = "greet"
implementationClass = "GreetPlugin"
}
}
dependencies {
api("io.ktor:ktor-client-okhttp:1.0.1")
}
}
val sourcesJar by tasks.registering(Jar::class) {
classifier = "sources"
from(sourceSets.main.get().allSource)
}
val shadowJar: ShadowJar by tasks
shadowJar.apply {
baseName = "test"
classifier = ""
minimize()
}
The complete example is here:
https://github.com/cdsap/testPluginCoroutinesProblem
A more detailed error
java.lang.NoClassDefFoundError: kotlin/coroutines/Continuation
at io.ktor.client.engine.okhttp.OkHttp.create(OkHttp.kt:8)
at io.ktor.client.HttpClientKt.HttpClient(HttpClient.kt:36)
at io.ktor.client.HttpClientKt.HttpClient$default(HttpClient.kt:33)
at GreetPlugin.apply(GreetPlugin.kt:26)
at GreetPlugin.apply(GreetPlugin.kt:12)
I expect the plugin will build properly the coroutines dependency within Ktor and don't have big jar as dependency.
I'm trying to build a Kotlin application but, even with successfully build, I face with the error bellow. What I'm doing wrong?
▶ java -jar build/libs/app-0.1.jar
22:10:02.122 [main] INFO io.micronaut.runtime.Micronaut - No embedded container found. Running as CLI application
Here is my build status:
▶ ./gradlew assemble
BUILD SUCCESSFUL in 3s
14 actionable tasks: 1 executed, 13 up-to-date
That is the part of my gradle.build file:
apply from: "dependencies.gradle"
apply from: "protobuf.gradle"
version "0.1"
group "app"
mainClassName = "app.Application"
dependencies {
compile "ch.qos.logback:logback-classic:1.2.3"
}
jar {
manifest {
attributes "Main-Class": mainClassName
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
I had a similar issue:
I was able to run code from IDE, but failed to run Docker container with the app
And had compile 'io.micronaut:micronaut-http-server-netty:1.1.0' in my build.gradle.
Also I was using shadowJar plugin and there was the issue. Adding:
shadowJar {
mergeServiceFiles()
}
solved the problem. It transforms entries in META-INF/services resources into a single resource. My shadowed jar file contained a lot of entries in this folder.
It is difficult to say for sure without seeing the project but one thing that could cause that issue would be not having a dependency on io.micronaut:micronaut-http-server-netty. A newly created app will have something like this in build.gradle...
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}"
compile "org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}"
compile "io.micronaut:micronaut-runtime"
compile "io.micronaut:micronaut-http-client"
// Make Sure You Have This...
compile "io.micronaut:micronaut-http-server-netty"
kapt "io.micronaut:micronaut-inject-java"
kapt "io.micronaut:micronaut-validation"
kaptTest "io.micronaut:micronaut-inject-java"
runtime "ch.qos.logback:logback-classic:1.2.3"
runtime "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.4.1"
testCompile "org.junit.jupiter:junit-jupiter-api:5.1.0"
testCompile "org.jetbrains.spek:spek-api:1.1.5"
testRuntime "org.junit.jupiter:junit-jupiter-engine:5.1.0"
testRuntime "org.jetbrains.spek:spek-junit-platform-engine:1.1.5"
}
I have a gradle project with a single module.
I have declared the 'provided' configuration to enable provided-scoped dependencies in the parent build.gradle file:
subprojects {
apply plugin: 'maven'
apply plugin: 'java'
apply plugin: 'idea'
configurations {
provided
}
idea {
module {
scopes.PROVIDED.plus += [configurations.provided]
}
}
sourceSets {
main.compileClasspath += configurations.provided
test.compileClasspath += configurations.provided
test.runtimeClasspath += configurations.provided
}
... other stuff...
}
In the module build.gradle I have declared the following dependencies:
dependencies {
testCompile 'org.elasticsearch:elasticsearch:2.3.1:tests'
compile 'org.apache.commons:commons-io:1.3.2'
compile 'org.apache.commons:commons-lang3:3.4'
compile 'org.elasticsearch:elasticsearch:2.3.1'
compile 'org.slf4j:slf4j-api:1.7.12'
provided 'org.slf4j:slf4j-simple:1.7.12'
}
When I expand the Gradle tool window, it declares the second to last dependency there as provided as well, even though it has compile scope:
I would expect to see the dependency listed with (Compile) next to it in this tool window, not (Provided).
So the question is: Why aren't I?
Is it because the implementation of slf4j (slf4j-simple) is provided, and depends on slf4j-api, so automatically makes that one provided as well? How do I stop that? Should I be stopping that? I want the API as a compiled dependency, but I want projects that use this to decide on their own implementation...
I had the very same problem with IntelliJ IDEA Ultimate build 163.12024.16. I wasn't able to fix it. BUT it magically disappeared when I upgraded to build 171.4249.39. (When using the previous build again, the scope goes back to the incorrect "provided" again.)
I have a gradle project imported into idea 14.0.3. The integration tests run fine from the command line. They were also running without any problems in idea 13 from the context menu (running single tests). However, in 14, when I use the context menu in IDE, for some reason the tests that depend on class path resources from src/integTest/resources are failing due to resource not being found. Any idea how I can add this folder in the classpath search in Intellij 14? Has anyone seen this issue before?
If I move the same resource to src/test/resources (or src/main/resources), the tests run fine. So it seems like intellij is not simply looking under src/integTest/resources.
Appreciate the help!!
I have run into this before as well, add the following to your build.gradle file:
// work-around to fix IDE-run test failures (may be fixed in future Gradle versions)
task copyMainResourcesToTest(type: Copy) {
from "${projectDir}/src/main/resources"
into "${buildDir}/classes/test"
}
processTestResources.dependsOn copyMainResourcesToTest
task copyTestResourcesToTest(type: Copy) {
from "${projectDir}/src/test/resources"
into "${buildDir}/classes/test"
}
processTestResources.dependsOn copyTestResourcesToTest
I think this may be resolved in the newest release of Gradle but I have not verified yet. You will want to update the paths for your specific use case.
Looks like this is Bug (IDEA-128966) in IntelliJ 14.
The recommended workaround is something like this:
sourceSets {
integrationTest {
java.srcDir file('src/integTest/java')
resources.srcDir file('src/integTest/resources')
}
if(System.properties.'idea.active') {
main {
resources.srcDir file('src/integTest/resources')
}
}
}
For our projects I changed that to:
if(System.properties.'idea.active') {
test { //Add to test rather than main
That still works and I think it communicates the intent better.
This workaround assumes you have configured your integration tests like this in the build.gradle:
apply plugin: 'idea'
sourceSets {
integrationTest {
java.srcDir file('src/integTest/java')
resources.srcDir file('src/inteTest/resources')
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
task integrationTest(type: Test, dependsOn: jar) {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
build.dependsOn(integrationTest)
Note: This answer was originally deleted by a moderator because of a duplicate answer that I posted on another question. I now deleted my answer there (and added a link to here) because I think it fits this question better.