What causes a "java.lang.ClassNotFoundException: com.sun.jna.Callback" Error using maven javafx:jlink?

102 Views Asked by At

After making a JavaFX Application, the hardest part has been trying to export it. I am using openjfx 19 and openjdk 11 and want to export the Application using the javafx-maven-plugin javafx:jlink.

In my Application, I make use of org.pcap4j, which in turn needs com.sun.jna to work. Both of the modules are non-modular. As JLink is not possible for non-modular jars, I injected module-info.class files in both jars. After that javafx:jlink worked, but running the application caused the ClassNotFoundException Error shown below. What could be causing this error?

I don't have a lot of experience with Java Modules and have been trying to export this Application for almost 3 weeks now, so I would be very grateful for some help.

JavaFX Plugin in pom.xml

<plugin>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-maven-plugin</artifactId>
        <version>0.0.8</version>
        <executions>
          <execution>
            <id>default-cli</id>
            <configuration>
              <mainClass>${mainclass}</mainClass>
              <launcher>app</launcher>
              <jlinkZipName>app</jlinkZipName>
              <jlinkImageName>app</jlinkImageName>
              <noManPages>true</noManPages>
              <stripDebug>true</stripDebug>
              <noHeaderFiles>true</noHeaderFiles>
              <stripDebug>true</stripDebug>
              <compress>2</compress>
              <launcher>${jlink-image-name}</launcher>
              <jlinkImageName>${jlink-image-name}</jlinkImageName>
              <mainClass>${mainclass}</mainClass>
            </configuration>
          </execution>
        </executions>
      </plugin>

Dependencies in pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<!-- groupId etc. -->

<properties>
    <classifier>linux</classifier>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <javafx-graphics.version>19</javafx-graphics.version>
    <!-- etc. -->
</properties>


<!-- Openjfx -->
    <dependency>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-graphics</artifactId>
      <version>${javafx-graphics.version}</version>
      <classifier>${classifier}</classifier>
    </dependency>
    <dependency>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-controls</artifactId>
      <version>${javafx-graphics.version}</version>
      <classifier>${classifier}</classifier>
    </dependency>
<!-- Pcap4j (non-modular) -->
    <dependency>
      <groupId>org.pcap4j</groupId>
      <artifactId>pcap4j-core</artifactId>
      <version>1.7.4</version>
    </dependency>
<!-- com.sun.jna needed for Pcap4j (non-modular) -->
    <dependency>
      <groupId>net.java.dev.jna</groupId>
      <artifactId>jna</artifactId>
      <version>4.0.0</version>
    </dependency>
<!-- logging etc. -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.8.0-beta2</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.8.0-beta2</version>
      <scope>test</scope>
    </dependency>
<!-- ... -->

Making JNA modular

# got to project folder (IntelliJ IDEA)
jdeps --generate-module-info target //.../net/java/dev/jna/jna/4.0.0/jna-4.0.0.jar

javac --patch-module jna=//.../net/java/dev/jna/jna/4.0.0/jna-4.0.0.jar target/jna/module-info.jna

# go to folder with module-info.class
jar uf //.../net/java/dev/jna/jna/4.0.0/jna-4.0.0.jar module-info.class

Making Pcap4j modular

# go to project folder

jdeps --module-path //path/to/jna-4.0.0.jar://path/to/pcap4j-core-1.7.4-sources.jar
  ://path/to/slf4j-simple-1.8.0-beta2.jar
  ://path/to/slf4j-api-1.8.0-beta2.jar 
  --generate-module-info target //path/to/pcap4j-core-1.7.4.jar

javac --module-path //path/to/jna-4.0.0.jar
  ://path/to/slf4j-simple-1.8.0-beta2.jar
  ://path/to/pcap4j-core-1.7.4-sources.jar
  ://path/to/slf4j-api-1.8.0-beta2.jar
  ://path/to/pcap4j-core-1.7.4.jar 
  --patch-module pcap4j.core=//path/to/pcap4j-core-1.7.4.jar target/pcap4j.core/module-info.java 

# go to folder with module-info.class
jar uf //path/to/pcap4j-core-1.7.4.jar module-info.class

Error when running application After using javafx:jlink, a app.zip folder is created. Unzipping the folder and running ./bin/app should work to run the Application, but the following error occurs:

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#noProviders for further details.
Exception in Application start method
Exiting
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source)
    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(Unknown Source)
    at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: com/sun/jna/Callback
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(Unknown Source)
    at java.base/java.lang.ClassLoader.defineClass(Unknown Source)
    at java.base/java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(Unknown Source)
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassInModuleOrNull(Unknown Source)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(Unknown Source)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
    at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
    at pcap4j.core/org.pcap4j.core.PcapHandle$Builder.build(Unknown Source)
    
        etc.etc.

    at javafx.graphics@19/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(Unknown Source)
    at javafx.graphics@19/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(Unknown Source)
    at javafx.graphics@19/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(Unknown Source)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics@19/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(Unknown Source)
    at javafx.graphics@19/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
    at javafx.graphics@19/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
    at javafx.graphics@19/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(Unknown Source)
    ... 1 more
Caused by: java.lang.ClassNotFoundException: com.sun.jna.Callback
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
    at java.base/java.lang.ClassLoader.loadClass(Unknown Source)

Module info of the javafx applicationn

module MyApp {

    requires javafx.controls;
    requires com.google.gson;
    requires org.slf4j;
    requires java.desktop;
    requires static com.sun.jna;
    requires pcap4j.core;


    opens ch.my.app to javafx.graphics, com.google.gson;
    exports ch.my.app;
    exports ch.my.app.xx;
    export etc.etc.;
}

Some ideas

I first thought this might have something to do with the naming of the module, or with requires transitive/static specifications in module-info.java. Therefore I tried changing module jna { to 'module com.sun.jna` and injecting the new module-info in the jna-jar instead of the following:

module jna {
    requires java.desktop;

    exports com.sun.jna;
    exports com.sun.jna.ptr;
    exports com.sun.jna.win32;
}

And changing requires static jna to requires static com.sun.jna in the code below:

module pcap4j.core {
    requires org.slf4j;

    requires transitive java.sql;
    requires static jna;

    exports org.pcap4j;
    exports org.pcap4j.core;
    exports org.pcap4j.packet;
    exports org.pcap4j.packet.constant;
    exports org.pcap4j.packet.factory;
    exports org.pcap4j.packet.namednumber;
    exports org.pcap4j.util;

}

Both did not have any effect on the error.

When using requires transitive jna in the pcap4j.core module-info.java, The following error occured:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007fa9f0807604, pid=192519, tid=192548
#
# JRE version: OpenJDK Runtime Environment (11.0.19+7) (build 11.0.19+7-post-Ubuntu-0ubuntu118.04.1)
# Java VM: OpenJDK 64-Bit Server VM (11.0.19+7-post-Ubuntu-0ubuntu118.04.1, mixed mode, tiered, compressed oops, g1 gc, linux-amd64)
# Problematic frame:
# C  [jna10034915907313375652.tmp+0x7604]
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E" (or dumping to /path/to/app/core.192519)
#
# An error report file with more information is saved as:
# /path/to/app/hs_err_pid192519.log
#
# If you would like to submit a bug report, please visit:
#   https://bugs.launchpad.net/ubuntu/+source/openjdk-lts
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
Aborted (core dumped)

1

There are 1 best solutions below

0
jewelsea On

Rather than trying to patch an obsolete version of jna to make it modular, use a modern version which is already built to be modular and work with the java platform module system (JPMS).

In Maven the JNA developers provide a version compatible with JPMS as an artifact with a -jpms suffix.

 <dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna-platform-jpms</artifactId>
    <version>5.14.0</version>
</dependency>

You should also require the jna modules in your module-info:

module com.example.demo {
    requires javafx.controls;
    requires com.sun.jna;
    requires com.sun.jna.platform;

    exports com.example.demo;
}

I tried this with the answer to:

Using the following jlink configuration in Maven and everyting worked fine. Linking the application using jlink worked. Executing the application using the launcher script and image created by jlink also worked.

The add-opens arguments in the example here are specific to the linked question and not required in general for JNA usage.

<plugin>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-maven-plugin</artifactId>
    <version>0.0.8</version>
    <executions>
        <execution>
            <id>default-cli</id>
            <configuration>
                <mainClass>com.example.demo/com.example.demo.TransparentApplication</mainClass>
                <launcher>app</launcher>
                <jlinkZipName>app</jlinkZipName>
                <jlinkImageName>app</jlinkImageName>
                <noManPages>true</noManPages>
                <stripDebug>true</stripDebug>
                <noHeaderFiles>true</noHeaderFiles>
                <options>--add-opens javafx.graphics/javafx.stage=com.example.demo --add-opens javafx.graphics/com.sun.javafx.tk.quantum=com.example.demo</options>
            </configuration>
        </execution>
    </executions>
</plugin>

I note that you say you are using JNA because you are using PCAP, which I know little about in this kind of usage respect. So, although this answer works for jlinking a JNA application that I tried, I don't know if the solution will also be appropriate for your target application.

My test system was JavaFX 21.0.1, OpenJDK 21 and Windows 11 Pro.

Build output (I clicked on the javafx:jlink target in the Maven window of my IDE to generate this):

[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------------< com.example:TransparentApp >---------------------
[INFO] Building TransparentApp 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> javafx-maven-plugin:0.0.8:jlink (default-cli) > process-classes @ TransparentApp >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ TransparentApp ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 4 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ TransparentApp ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 7 source files with javac [debug target 21 module-path] to target\classes
[INFO] 
[INFO] <<< javafx-maven-plugin:0.0.8:jlink (default-cli) < process-classes @ TransparentApp <<<
[INFO] 
[INFO] 
[INFO] --- javafx-maven-plugin:0.0.8:jlink (default-cli) @ TransparentApp ---
Warning: The 0 argument for --compress is deprecated and may be removed in a future release
[INFO] Building zip: C:\dev\InclusionApp\target\app.zip
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  10.360 s
[INFO] Finished at: 2024-01-05T03:02:56-08:00
[INFO] ------------------------------------------------------------------------