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

3 min read 04-10-2024
JavaFX 21 modular application, how to add automatics modules (H2 and Hibernate) to classpath after jlink has linked modules required for the runtime


Building Modular JavaFX Applications with H2 and Hibernate: A Guide to JLink and Automatic Module Resolution

Developing JavaFX applications in a modularized environment can be challenging when incorporating external libraries like H2 and Hibernate. The jlink tool, introduced in Java 9, helps create custom runtime images by packaging only the required modules, but this can lead to issues when these libraries rely on additional, non-modularized components.

This article provides a comprehensive guide on integrating H2 and Hibernate into your modularized JavaFX application, ensuring smooth operation even after using jlink.

Scenario:

Imagine you have a JavaFX application using H2 for database storage and Hibernate for Object-Relational Mapping (ORM). You want to package your application into a lightweight runtime image using jlink. However, after linking your required JavaFX modules, your application fails to find H2 and Hibernate, throwing ClassNotFoundException.

Original Code (Example):

// Your main application class
public class MyApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        // ... JavaFX initialization
        // ... Database and Hibernate setup (using H2 and Hibernate) 
    }

    public static void main(String[] args) {
        launch(args);
    }
}

// jlink command
jlink --module-path modules --add-modules javafx.base,javafx.controls,javafx.graphics,javafx.fxml --output runtimeImage

Understanding the Issue:

The jlink command explicitly specifies which modules to include in the runtime image. It doesn't automatically bundle all dependencies. H2 and Hibernate, if not explicitly added as modules, might be missing from the final runtime image.

Solution: Automatic Module Resolution

Java's Automatic Module Resolution (AMR) can address this challenge. When a module is not found in the module path, the runtime attempts to locate it in the classpath.

Steps:

  1. Configure Your Module Path: Add the paths to your H2 and Hibernate libraries to the module path using the --module-path flag when invoking jlink.
jlink --module-path modules:h2-path:hibernate-path --add-modules javafx.base,javafx.controls,javafx.graphics,javafx.fxml --output runtimeImage
  1. Create Module Info Files: If H2 and Hibernate lack module-info files (required for modularity), manually create them.

    Example (H2 module-info.java):

    module com.h2database {
        requires java.sql;
        exports org.h2;
    }
    

    Example (Hibernate module-info.java):

    module org.hibernate {
        requires java.sql;
        requires java.transaction;
        requires javax.persistence;
        exports org.hibernate;
        exports org.hibernate.boot;
        // ... add other exports as needed
    }
    
  2. Pack Modules: Create a module directory containing your JavaFX modules (e.g., javafx.base) and the newly created H2 and Hibernate modules.

  3. Update the jlink Command: Update the jlink command to include the new module path (which now encompasses H2 and Hibernate).

  4. Run Your Application: Your application should now run correctly within the runtime image.

Important Considerations:

  • Module Dependencies: Ensure you include all necessary dependencies for your H2 and Hibernate modules (e.g., java.sql, java.transaction, javax.persistence) in their module-info.java files.
  • Classpath Resolution: If the module-info files cannot resolve all required classes, consider using the --add-exports flag with jlink to expose necessary classes for H2 and Hibernate.
  • Dependency Management: Use a build tool like Maven or Gradle to manage dependencies and streamline the packaging process.

Benefits of Modularization:

  • Reduced Application Size: jlink generates smaller runtime images by including only the necessary modules, improving deployment efficiency.
  • Enhanced Security: Modularization enhances application security by limiting access to specific resources.
  • Improved Maintainability: Modular architecture allows for easier development and updates, as changes affect only relevant modules.

Conclusion:

This guide demonstrates how to seamlessly integrate H2 and Hibernate into modularized JavaFX applications. By utilizing Automatic Module Resolution and creating appropriate module-info files, you can overcome the challenges posed by jlink and achieve robust, optimized runtime environments. This approach promotes efficient development, improved security, and enhanced maintainability for your JavaFX applications.