Search code examples
javafxjava-modulejlinkjava-21

JavaFX 21 modular application, how to add automatics modules (H2 and Hibernate) to classpath after jlink has linked modules required for the runtime


How to in a JavaFX 21 modular application using JLink (in order to package as a native executable),

put the automatic modules H2 and Hibernate, in the class path, as explained in this video,

do you have a corresponding code example?

I tried to add the 2 jars in resources, and change the corresponding requires module-info to static :
My module-info :

module module-name {
    requires static com.h2database;
    requires static org.hibernate.orm.core;
    opens package-name.model to org.hibernate.orm.core;
    opens package-name.interfaces to org.hibernate.orm.core;
}

I would like to understand how to manage this problem linked to automatic modules, using the class path in my modular application.


Thanks @jewelsea

When I remove requires and opens to, then hibernate is not visible and the build failed.

I'm using Maven and my jpackage command is :

jpackage --dest output --name %1 --type msi --module-path "%PATH_TO_FX_JMODS%;./target/%jarName%" --add-modules %4 --module %5 --win-shortcut --win-menu --win-menu-group %1 --java-options -Xmx2048m --vendor %3 --copyright "Copyright © %year% %3" --icon src/main/resources/package-name/images/icon.ico --app-version %2

Thanks @jewelsea

This is what I understood:

Development and Packaging:
  • it worked because I used H2 and Hibernate as automatic modules
Runtime (via jpackage)
  • it didn't work because jpackage uses jlink which is unable to work with automatic module dependencies
Solutions :
  • @jewelsea does not recommend doing a modular project if it is not completely modular
    1. I change my project to a non-modular application according to the recommendations of openjfx.io
    2. I transform the automatic modules into modules if possible, to have a completely modular application :
      • Manually
        • Generate module-info (jdeps)
        • Modifiy module-info (cat, exports...)
        • Build module-info (javac)
        • Update the jar manually (--file)
      • Badass jlink for Gradle (@jewelsea specifies that it may not work with all software)
        • Which combines non-modular dependencies into a single JAR archive for which the module-info.java file is generated. Making it easy to create a runtime image with jlink.
      • Moditect for Maven (@jewelsea does not recommend it)
        • Analyze an existing Java project and generate the corresponding module-info.java file, automatically detecting dependencies and package exports

Solution

  • Update to address question edit

    After this answer was created, the question was edited to note that the developer was using jpackage and not jlink.

    A key difference between jpackage and jlink is that the way to deal with non-modular dependencies when you use jpackage is different than how you need to work around things with jlink (because jlink is unable to work with automatic module dependencies, but jpackage is able to work with non-modular dependences).

    The original answer remains specific to techniques to use software libraries that are placed on the classpath rather than the module path after jlink has linked modules required for the runtime.

    If however, you are going to use jpackage with software that is not completely modular, I don't recommend making your project modular at all. Delete the module-info and follow the instructions for non-modular applications and runtimes in the openjfx.io getting started documentation. jpackage can handle packaging non-modular applications just fine, but a full description of how to do this for your particular application and build structure is out of the scope of what I would discuss here.

    Original Answer

    Classes on the classpath are not modules

    If the jar files for the automatic modules are on the classpath and not the module path, they aren't automatic modules, they are just classes loaded from the classpath. Therefore, they should not be referenced as modules either in module info or as modules referred to by command line arguments.

    Access to classes on the classpath

    Development

    When you develop, you should ensure that the required software (the libraries that are not proper modules) is on the classpath. Generally, tools such as Maven and Gradle integrated with IDEs will ensure that automatically.

    Packaging

    When you build your deployment package you should include the jar files to be placed on the classpath in your distribution (e.g. using the Maven assembly tool). You can place them in a lib directory that you have the assembly create. The assembly can zip everything up in the jlink image (e.g. a javafx image created by the javafx-maven-plugin), including the libs to be placed on the classpath.

    Runtime

    The script used to launch the application then needs to add the arguments to the java command to locate the lib files in the lib directory at runtime and place them on the classpath (e.g. java -cp lib). The required VM options can be specified via the <options> element of the <launcher> element in the javafx-maven-plugin jlink configuration.

    Tool support

    Badass jlink for Gradle

    If you use Gradle, the badass jlink plugin does some weird stuff (e.g. repackaging software and creating fake module info), to allow automatic modules to be converted to an actual module that can be linked by jlink. It won't work for all software but might work for you, if you want to use that. If you need more info read the (very complex) badass jlink plugin instructions.

    Moditect for Maven

    Tools such as moditect are available for Maven which accomplish similar things to the badass jlink plugin but in a more manual manner. Moditect is not a tool I recommend, but it is an option if you wish to pursue it.

    jpackage alternative

    Consider whether it would be better to use jpackage instead of jlink. jpackage has specific arguments for handling packaging of non-modular software, including automatic modules.