0

How to create a custom runtime image with MSSQL JDBC Driver?

I created a new JavaFX project using and IntelliJ IDEA.

I added to dependency MSSQL JDBC Driver to my pom-file.

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>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>demo</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>5.9.2</junit.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>17.0.6</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>17.0.6</version>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <version>11.2.3.jre17</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <executions>
                    <execution>
                        <!-- Default configuration for running with: mvn clean javafx:run -->
                        <id>default-cli</id>
                        <configuration>
                            <mainClass>com.example.demo/com.example.demo.HelloApplication</mainClass>
                            <launcher>app</launcher>
                            <jlinkZipName>app</jlinkZipName>
                            <jlinkImageName>app</jlinkImageName>
                            <noManPages>true</noManPages>
                            <stripDebug>true</stripDebug>
                            <noHeaderFiles>true</noHeaderFiles>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Then added requires to module-info.java

module com.example.demo {
    requires javafx.controls;
    requires javafx.fxml;
    requires java.sql;
    requires com.microsoft.sqlserver.jdbc;


    opens com.example.demo to javafx.fxml;
    exports com.example.demo;
}

But when I tried to create a custom runtime image , using the JavaFX Maven plugin, I get an error:

Error: automatic module cannot be used with jlink: com.microsoft.sqlserver.jdbc
"C:\Program Files\Windows Portable\Java\jdk-17.0.7\bin\java.exe" 
[INFO] Scanning for projects...
[INFO] 
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> javafx-maven-plugin:0.0.8:jlink (default-cli) > process-classes @ demo >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] <<< javafx-maven-plugin:0.0.8:jlink (default-cli) < process-classes @ demo <<<
[INFO] 
[INFO] 
[INFO] --- javafx-maven-plugin:0.0.8:jlink (default-cli) @ demo ---
Error: automatic module cannot be used with jlink: com.microsoft.sqlserver.jdbc from file:///C:/Users/artyukhev/.m2/repository/com/microsoft/sqlserver/mssql-jdbc/11.2.3.jre17/mssql-jdbc-11.2.3.jre17.jar
[ERROR] Command execution failed.
org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1)
    at org.apache.commons.exec.DefaultExecutor.executeInternal (DefaultExecutor.java:404)
    at org.apache.commons.exec.DefaultExecutor.execute (DefaultExecutor.java:166)
    at org.openjfx.JavaFXBaseMojo.executeCommandLine (JavaFXBaseMojo.java:567)
    at org.openjfx.JavaFXBaseMojo.executeCommandLine (JavaFXBaseMojo.java:434)
    at org.openjfx.JavaFXJLinkMojo.execute (JavaFXJLinkMojo.java:209)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:301)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:211)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:165)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:157)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:121)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:127)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:294)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:960)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:293)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:196)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:77)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:568)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
    at org.codehaus.classworlds.Launcher.main (Launcher.java:47)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.806 s
[INFO] Finished at: 2023-08-17T17:35:22+03:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.openjfx:javafx-maven-plugin:0.0.8:jlink (default-cli) on project demo: Error: Command execution failed. Process exited with an error: 1 (Exit value: 1) -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1)
    at org.apache.commons.exec.DefaultExecutor.executeInternal(DefaultExecutor.java:404)
    at org.apache.commons.exec.DefaultExecutor.execute(DefaultExecutor.java:166)
    at org.openjfx.JavaFXBaseMojo.executeCommandLine(JavaFXBaseMojo.java:567)
    at org.openjfx.JavaFXBaseMojo.executeCommandLine(JavaFXBaseMojo.java:434)
    at org.openjfx.JavaFXJLinkMojo.execute(JavaFXJLinkMojo.java:209)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute(MojoExecutor.java:301)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:211)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:165)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:157)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:121)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:127)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:294)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:960)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:293)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:196)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:47)

Process finished with exit code 1

How to fix it?

Etarus
  • 13
  • 5
  • Please state whether you have that driver as a jmod or a jar – g00se Aug 17 '23 at 15:25
  • If you're willing to use `jpackage`, check out [my answer to a similar question](https://stackoverflow.com/a/76550092/6395627). However, I'm not sure what that solution would look like in Maven. – Slaw Aug 17 '23 at 15:49
  • 1
    @g00se Note whether the driver is packaged in a JMOD file or a JAR file is largely irrelevant, at least with regards to the specified error. The important thing is whether or not the dependency has a `module-info` descriptor, and from looking at [the source repository](https://github.com/microsoft/mssql-jdbc/tree/main), it doesn't look like mssql-jdbc has one. It does appear to have Gradle set the `Automatic-Module-Name` manifest attribute, which is further evidence of no `module-info` descriptor, and which won't help here because `jlink` can only work with _explicit_ modules. – Slaw Aug 17 '23 at 16:01
  • What about repackaging it *as* a module? – g00se Aug 17 '23 at 16:16
  • @g00se repackaging as a module may work or may not. Not all code will just work that way. If it already works as an automatic module repackaging will probably work. Maven tools like moditect do that. The badass jlink gradle plugin does some kind of weird shenanigans to allow non modular code to be packaged with a distribution primarily built by jlink (if might do it through not linking them or repackaging them as a module). JPackageScriptFX uses jpackage maven and scripts to package with unlinked automatic modules. I haven’t used any of the above tools. – jewelsea Aug 17 '23 at 17:04
  • @g00se It's possible. There are tools that do this, as pointed out by jewelsea, but I'm not exactly sure how they work. Though I suspect they rely on [jdeps](https://docs.oracle.com/en/java/javase/20/docs/specs/man/jdeps.html). – Slaw Aug 17 '23 at 18:14
  • 1
    @Slaw, you were right. I created a project on Gradle, added the mssql dependency, and built the custom image without any problems. That's very strange… And maven is more comfortable for me. – Etarus Aug 17 '23 at 20:21
  • @ЭдуардАртюх yeah, the badass gradle plugin offers more functionality around this than the javafx-maven-plugin. It isn’t a core maven limitation, the javafx-maven-plugin could probably be rewritten to do what the badass jlink plugin does, but it would be quite difficult and a lot of work I guess. One traffic is the badass gradle jlink plugin documentation is more complex (and you have to use gradle to use it). – jewelsea Aug 17 '23 at 23:39
  • To create a jlink image using Maven, and also include automatic modules in the distribution: One way to do that is to create a jlink image of all the modular dependencies and then use the assembly plugin to copy the non modular dependencies into the generated image output directory, together with a custom launch script which puts the automatic jars on the class or module path. Zip up the entire assembly for distribution using the assembly plugin. It is tricky and difficult to do right and I haven’t time to write an example answer here at the moment. – jewelsea Aug 18 '23 at 04:38

0 Answers0