javakotlinjarjvm

ClassNotFoundException when running jar file which compiled with external libraries (Kotlin with JVM)


Here is my step for reproducing the issue:

com/example/Library.kt

package com.example

class Library {
    fun getValue(): Int {
        return 123
    }
}

MyCode.kt

import com.example.Library

fun main() {
    println("Hello World")
    println(Library().getValue())
}

I can compile both files with command below, without any error:

kotlinc com/example/Library.kt -d Library.jar
kotlinc -cp Library.jar MyCode.kt -d MyCode.jar

File structure after compile:

$ tree
.
├── Library.jar
├── MyCode.jar
├── MyCode.kt
└── com
    └── example
        └── Library.kt

However, I cannot execute MyCode.jar

$ java -cp Library.jar -jar MyCode.jar
Hello world
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/Library
        at MyCodeKt.main(MyCode.kt:5)
        at MyCodeKt.main(MyCode.kt)
Caused by: java.lang.ClassNotFoundException: com.example.Library
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
        ... 2 more

Neither works using kotlin instead of java:

$ kotlin -cp Library.jar MyCode.jar
Hello world
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/Library
        at MyCodeKt.main(MyCode.kt:5)
        at MyCodeKt.main(MyCode.kt)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at org.jetbrains.kotlin.runner.AbstractRunner.run(runners.kt:70)
        at org.jetbrains.kotlin.runner.Main.run(Main.kt:194)
        at org.jetbrains.kotlin.runner.Main.main(Main.kt:204)
Caused by: java.lang.ClassNotFoundException: com.example.Library
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
        ... 7 more

Can anyone please tell me what is the problem here?
(Maven / Gradle is not an option, I am trying to build a fast tool to compile users' codes)


Solution

  • The reason for the error is given by the kotlin -help command:

      app.jar                    Runs the given JAR file as 'java -jar' would do
    

    So we need to look at what java -jar does. For that, see the Java documentation:

    -jar jarfile
    Executes a program encapsulated in a JAR file. The jarfile argument is the name of a JAR file with a manifest that contains a line in the form Main-Class:classname that defines the class with the public static void main(String[] args) method that serves as your application's starting point. When you use -jar, the specified JAR file is the source of all user classes, and other class path settings are ignored. If you're using JAR files, then see jar.

    The relevant sentence is the one I've made bold. So kotlin MyCode.jar ignores the -cp argument. The fix is to either run it the way you've given in your own answer, or put the Library.class file inside your MyCode.jar.