javamavenjava-modulejava-platform-module-systemmodule-path

Java cannot find class in modular jar file, yet javap can


I have a simple hello world that I'm trying to build into a modular jar to test running jlink. I can neither run the jar file nor can I create an image for it with jlink.

I an on Windows, and using Java 21.

Here are the java files in the project.

$ find src/main/java -name '*\.java'
src/main/java/com/foo/bar/demoapp/App.java
src/main/java/module-info.java

package com.foo.bar.demoapp;

public class App
{
    public static void main( String[] args )
    {
        System.out.println("hello hello");
    }
}

module demoapp {
    exports com.foo.bar.demoapp;
}

I'm using Maven 3.9.6, and my pom file is pretty simple. It specifies maven.compiler.(source|target)=21. It sets the compiler plugin version to 3.13.0. It sets the jar plugin to version 3.1.2, and specifies the App class as the mainClass.

<configuration>
<archive>
  <manifest>
    <mainClass>com.foo.bar.demoapp.App</mainClass>
  </manifest>
</archive>
</configuration>

I have verified that the jar file appears to contain the expected contents.

$ unzip -l jars/demo-app-1.0-SNAPSHOT.jar
Archive:  jars/demo-app-1.0-SNAPSHOT.jar
  Length      Date    Time    Name
---------  ---------- -----   ----
      116  2024-04-08 12:30   META-INF/MANIFEST.MF
      552  2024-04-08 12:30   com/foo/bar/demoapp/App.class
     5160  2024-04-08 12:09   META-INF/maven/com.foo.bar/demo-app/pom.xml
       64  2024-04-08 12:30   META-INF/maven/com.foo.bar/demo-app/pom.properties
      283  2024-04-08 12:30   module-info.class

I used javap to verify that the module-info class shows the main-class as required.

$ javap -verbose --module demoapp --module-path jars module-info
Classfile jar:file:///C:/Users/wrobert1/dev/foo/demo-app/jars/demo-app-1.0-SNAPSHOT.jar!/module-info.class
  Last modified Apr 8, 2024; size 283 bytes
  SHA-256 checksum 004814b6bbe32c09ecba1fe2a02289d5e4b33a14426856ca6598e1c256405443
  Compiled from "module-info.java"
module demoapp@1.0-SNAPSHOT
  minor version: 0
  major version: 65
  flags: (0x8000) ACC_MODULE
  this_class: #1                          // "module-info"
  super_class: #0
  interfaces: 0, fields: 0, methods: 0, attributes: 4

... constant pool elided ...

SourceFile: "module-info.java"
Module:
  #6,0                                    // demoapp
  #8                                      // 1.0-SNAPSHOT
  1                                       // requires
    #9,8000                                 // "java.base" ACC_MANDATED
    #11                                     // 21.0.1
  1                                       // exports
    #12,0                                   // com/foo/bar/demoapp
  0                                       // opens
  0                                       // uses
  0                                       // provides
ModuleMainClass: #16                    // com.foo.bar.demoapp.App
ModulePackages:
  #12                                     // com.foo.bar.demoapp

I also used javap to verify that App.class has a main function

$ javap --module demoapp --module-path jars com.foo.bar.demoapp.App
Compiled from "App.java"
public class com.foo.bar.demoapp.App {
  public com.foo.bar.demoapp.App();
  public static void main(java.lang.String[]);
}

Given all of this, when I attempt to run the jar file, I get a ClassNotFoundException.

$ java --module-path jars com.foo.bar.demoapp.App
Error: Could not find or load main class com.foo.bar.demoapp.App
Caused by: java.lang.ClassNotFoundException: com.foo.bar.demoapp.App

And jlink fails for what is likely the same reason.

$ jlink --module-path jars --output linked  --add-modules=java.base --launcher startmeup=demoapp/com.foo.bar.demoapp.App
Error: java.lang.IllegalArgumentException: demoapp does not have main class: com.dhl.raf.demoapp.App

I have been searching for what could be the issue for quite some time now with no luck. I am not sure what I have done wrong here.


Solution

  • While executing the main class with java, one way to work this out would be to state which modules shall be added from the module path to resolve.

    java --module-path jars --add-modules demoapp com.foo.bar.demoapp.App
    

    This shall work in the case of jlink too with something like:

    jlink --module-path jars --output linked --add-modules=java.base,demoapp --launcher startmeup=demoapp/com.foo.bar.demoapp.App
    

    An alternate that you can relate to your approach of using javap would be to specify the module along with the mainClass such as:

    java --module-path jars --module demoapp/com.foo.bar.demoapp.App     
    

    More information about this can be gathered with the java -help command line itself.