6

I have mixed Java & Kotlin code in a demo project which I want to run from command line. For only java, I am able to run the program with java -jar foo.jar but when I use any class from Kotlin code, it generates java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics

I have tried different solutions and checked the jar file. It contains both the classes but I guess kotlin-runtime is missing from jar.

Here is build.gradle file

plugins {
    id 'java-library'
    id 'org.jetbrains.kotlin.jvm'
}

sourceSets {
    main.java.srcDirs += 'src/main/kotlin/'
    test.java.srcDirs += 'src/test/kotlin/'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    testImplementation 'junit:junit:4.12'
}

jar {
    manifest {
        attributes 'Main-Class': 'com.mallaudin.App'
    }
    from {
        configurations.compile.collect {
            it.isDirectory()? it: zipTree(it)
        }
    }
}

Content of generated jar file

.
├── com
│   └── mallaudin
│       ├── App.class
│       └── User.class
└── META-INF
    ├── basics.kotlin_module
    └── MANIFEST.MF

Content of MANIFEST.MF

Manifest-Version: 1.0
Main-Class: com.mallaudin.App

Exception I get when I run jar form command line

allaudin@geek ~/Desktop/KotlinLab (master) $ java -jar basics/build/libs/basics.jar 
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
    at com.mallaudin.User.<init>(User.kt)
    at com.mallaudin.App.main(App.java:5)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
    ... 2 more
mallaudin
  • 4,744
  • 3
  • 36
  • 68

2 Answers2

3

The issue is in the way you build your fat jar. While you use the more recent and recommended implementation scope for your dependencies, you are only adding to the fat jar the content of the compile configuration.

You should replace the configuration to be used in the fat jar by runtimeClasspath which is the full set of runtime components.

That is:

jar {
  manifest {
    attributes 'Main-Class': 'com.mallaudin.App'
  }
  from {
    configurations.runtimeClasspath.collect {
      it.isDirectory()? it: zipTree(it)
    }
  }
}  

Have a look at the documentation to understand the different dependency configurations for a java project.

Louis Jacomet
  • 13,661
  • 2
  • 34
  • 43
0

So, I have found a solution. In build.gradle file I have replaced implementation configuration with compile and it works now. I believe it should work with implementation too but it doesn't. Now the jar looks like this

.
├── com
├── kotlin
├── META-INF
└── org
mallaudin
  • 4,744
  • 3
  • 36
  • 68
  • 1
    Hi, I'm glad it works now, but that's what I said in my previous comment already ;-) – Erik Pragt Dec 16 '18 at 13:46
  • 1
    But why it doesn't work with `implementation`? Do you have any idea? – mallaudin Dec 16 '18 at 14:59
  • I'm pretty sure it will work with implementation too, but then you need to change this line: `configurations.compile` to also include implementation: `configurations.implementation`. Right now, you only include compilation scope, cause that was the 'old' way of doing things. – Erik Pragt Dec 17 '18 at 06:18
  • `configurations.implementation` is not allowed. I have checked it already. – mallaudin Dec 17 '18 at 07:45
  • I'm having the same problem. Switching from `implementation` to `api` didn't help. – Maroun May 22 '20 at 20:17