0

Basically, when I'm running my modular project on eclipse, it works fine but when I use jlink to create a runtime image, it throws an error for mysql connection.

Caused by: java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/timp_db
        at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:708)
        at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:230)
        at projectTMIP/database.DBConnect.getConnection(DBConnect.java:14)
        ... 56 more 

this is my module-info.java look like

module projectTMIP {
    requires javafx.controls;
    requires java.sql;
    requires javafx.fxml;
    requires javafx.graphics;

    opens model to javafx.graphics, javafx.fxml;
    opens controller to javafx.fxml;

    exports controller;

    requires transitive javafx.base;
}

this is the command i use on eclipse terminal navigated on my root project.

jlink --module-path "path/to/jmod";"path/to/mysql/connector.jar";bin --add-modules projectTMIP --output output/image --launcher run=projectTMIP/model.Main

I expected to produce a runtime image where it would include the mysqlconnector.jar

  • 2
    You can't. The MySQL driver is not modular. Your module setup indicates that you are loading the driver as an [automatic module](https://stackoverflow.com/questions/46741907/what-is-an-automatic-module). That is not compatible with `jlink`, at least not without performing [certain hacks on the connector jar](https://stackoverflow.com/questions/52518105/using-jlink-with-automatic-modules) which may or may not work. Request the MySQL developers to create a modular distribution of their connector. – jewelsea Jun 24 '23 at 21:15
  • @jewelsea Is there any workaround or any approach I should do instead of jlink to create a standalone executable file? – Kenneth Arias Jun 25 '23 at 00:14
  • If you use `jpackage` to create the executable file, then there should be a workaround. Since JDBC drivers are service providers, they can be found by named modules via the service-loader mechanism even if the provider is on the class-path (i.e., in an unnamed module). Build your run-time image as normal (either via `jlink` directly or via `jpackage`, which uses `jlink` behind the scenes). Then have `jpackage` include the MySQL JDBC driver on the class-path with the `--input` option. – Slaw Jun 25 '23 at 05:38
  • no problem using automatic modules. the problem is that the driver is loaded dynamically (reflection or service loader) and they do not declare that you are using the corresponding services. so see what the `uses` directive is for in the module declaration. – mr mcwolf Jun 25 '23 at 05:38
  • @mrmcwolf Note `jlink` can't link automatic modules (unless something has changed recently). – Slaw Jun 25 '23 at 05:39
  • @Slaw, hey. I use the command jpackage --type app-image --name Test --input "path/to/driver" --main-jar "path/to/app.jar" --main-class model.Main --runtime-image "path/to/runtime/image" it did create my app-image and it did include the connector.jar but its still not working? Anything I am missing with the command? – Kenneth Arias Jun 25 '23 at 10:07

1 Answers1

3

Warning: Using the jpackage tool may make it more complicated to deploy your application, as the tool can only create executables/installers for the operating system it's executing on.


The jlink tool only works with explicit modules. In other words, the module must have a module-info descriptor. So, you cannot include the MySQL JDBC driver in the run-time image, because that library is not modular. You could try to "hack" the dependency to make it modular, but that can be difficult and error prone.

That said, the jpackage tool works with modular and non-modular applications. This is important in this case, because JDBC drivers are service providers, meaning named modules can load them via the service-loader mechanism even if the provider is in the unnamed module. In order to include the MySQL JDBC driver with your code, configure jpackage to put all needed explicit modules in a custom run-time image (it uses jlink behind the scenes), and then put the driver on the class-path via the --input option.

Example

Here's a very basic example showing what I described above. Note all tool invocations in this example are executed directly on the command line rather than via an IDE or a build tool.

The example does not include JavaFX as it's not important to what's being demonstrated.

Source Code

module-info:

module sample {
    requires java.sql;
}

sample.Main:

package sample;

import java.sql.DriverManager;

public class Main {

    public static void main(String[] args) throws Exception {
        try (var con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test")) {
            System.out.println("Made connection!");
        }
    }
}

Project Layout

C:\...\PROJECT
│   
├───lib
│       mysql-connector-j-8.0.33.jar
│       
└───src
    └───sample
        │   module-info.java
        │   
        └───sample
                Main.java

Creating Executable

First compile the project:

javac --module-source-path src --module sample -d out/modules

Then package the application:

jpackage --type app-image --name TestApp --module-path out/modules --add-modules java.naming --module sample/sample.Main --input lib --dest out --win-console

Couple notes:

  1. I included --win-console because I wanted to see the console output when running the executable (and I'm on Windows).

  2. I included --add-modules java.naming because it appears the MySQL JDBC driver requires that module to run. You may or may not find you need to include more modules; if you do, simply add them to the --add-modules argument or, if it makes more sense, add appropriate requires directives to your code's module-info descriptor.

Running Executable

I do not currently have a MySQL server available for testing, so the output I show below is an exception stack trace. But if you look at the exception, you'll see the error is that a connection could not be made. That fact, plus the fact that MySQL JDBC driver code shows up in the stack trace, shows that the MySQL JDBC driver was found successfully.

The command (Windows):

.\out\TestApp\TestApp.exe

The output (with no running MySQL server):

Exception in thread "main" com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
        at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:175)
        at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
        at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:825)
        at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:446)
        at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:239)
        at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:188)
        at java.sql/java.sql.DriverManager.getConnection(Unknown Source)
        at java.sql/java.sql.DriverManager.getConnection(Unknown Source)
        at sample/sample.Main.main(Unknown Source)
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
        at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(Unknown Source)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Unknown Source)
        at java.base/java.lang.reflect.Constructor.newInstance(Unknown Source)
        at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:62)
        at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)
        at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:150)
        at com.mysql.cj.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:166)
        at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:89)
        at com.mysql.cj.NativeSession.connect(NativeSession.java:121)
        at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:945)
        at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:815)
        ... 6 more
Caused by: java.net.ConnectException: Connection refused: connect
        at java.base/sun.nio.ch.Net.connect0(Native Method)
        at java.base/sun.nio.ch.Net.connect(Unknown Source)
        at java.base/sun.nio.ch.Net.connect(Unknown Source)
        at java.base/sun.nio.ch.NioSocketImpl.connect(Unknown Source)
        at java.base/java.net.SocksSocketImpl.connect(Unknown Source)
        at java.base/java.net.Socket.connect(Unknown Source)
        at com.mysql.cj.protocol.StandardSocketFactory.connect(StandardSocketFactory.java:153)
        at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:63)
        ... 9 more
Failed to launch JVM

Application Layout

Here's the layout of the application generated by the above jpackage command:

C:\...\PROJECT\OUT\TESTAPP
│   TestApp.exe
│   
├───app
│       .jpackage.xml
│       mysql-connector-j-8.0.33.jar
│       TestApp.cfg
│       
└───runtime
    │   release
    │   
    ├───bin
    │   │   api-ms-win-core-console-l1-1-0.dll
    │   │   api-ms-win-core-console-l1-2-0.dll
    │   │   api-ms-win-core-datetime-l1-1-0.dll
    │   │   api-ms-win-core-debug-l1-1-0.dll
    │   │   api-ms-win-core-errorhandling-l1-1-0.dll
    │   │   api-ms-win-core-fibers-l1-1-0.dll
    │   │   api-ms-win-core-file-l1-1-0.dll
    │   │   api-ms-win-core-file-l1-2-0.dll
    │   │   api-ms-win-core-file-l2-1-0.dll
    │   │   api-ms-win-core-handle-l1-1-0.dll
    │   │   api-ms-win-core-heap-l1-1-0.dll
    │   │   api-ms-win-core-interlocked-l1-1-0.dll
    │   │   api-ms-win-core-libraryloader-l1-1-0.dll
    │   │   api-ms-win-core-localization-l1-2-0.dll
    │   │   api-ms-win-core-memory-l1-1-0.dll
    │   │   api-ms-win-core-namedpipe-l1-1-0.dll
    │   │   api-ms-win-core-processenvironment-l1-1-0.dll
    │   │   api-ms-win-core-processthreads-l1-1-0.dll
    │   │   api-ms-win-core-processthreads-l1-1-1.dll
    │   │   api-ms-win-core-profile-l1-1-0.dll
    │   │   api-ms-win-core-rtlsupport-l1-1-0.dll
    │   │   api-ms-win-core-string-l1-1-0.dll
    │   │   api-ms-win-core-synch-l1-1-0.dll
    │   │   api-ms-win-core-synch-l1-2-0.dll
    │   │   api-ms-win-core-sysinfo-l1-1-0.dll
    │   │   api-ms-win-core-timezone-l1-1-0.dll
    │   │   api-ms-win-core-util-l1-1-0.dll
    │   │   api-ms-win-crt-conio-l1-1-0.dll
    │   │   api-ms-win-crt-convert-l1-1-0.dll
    │   │   api-ms-win-crt-environment-l1-1-0.dll
    │   │   api-ms-win-crt-filesystem-l1-1-0.dll
    │   │   api-ms-win-crt-heap-l1-1-0.dll
    │   │   api-ms-win-crt-locale-l1-1-0.dll
    │   │   api-ms-win-crt-math-l1-1-0.dll
    │   │   api-ms-win-crt-multibyte-l1-1-0.dll
    │   │   api-ms-win-crt-private-l1-1-0.dll
    │   │   api-ms-win-crt-process-l1-1-0.dll
    │   │   api-ms-win-crt-runtime-l1-1-0.dll
    │   │   api-ms-win-crt-stdio-l1-1-0.dll
    │   │   api-ms-win-crt-string-l1-1-0.dll
    │   │   api-ms-win-crt-time-l1-1-0.dll
    │   │   api-ms-win-crt-utility-l1-1-0.dll
    │   │   java.dll
    │   │   jimage.dll
    │   │   jli.dll
    │   │   msvcp140.dll
    │   │   net.dll
    │   │   nio.dll
    │   │   syslookup.dll
    │   │   ucrtbase.dll
    │   │   vcruntime140.dll
    │   │   vcruntime140_1.dll
    │   │   verify.dll
    │   │   zip.dll
    │   │
    │   ├───client
    │   │       jvm.dll
    │   │
    │   └───server
    │           jvm.dll
    │
    ├───conf
    │   │   logging.properties
    │   │   net.properties
    │   │
    │   └───security
    │       │   java.policy
    │       │   java.security
    │       │
    │       └───policy
    │           │   README.txt
    │           │
    │           ├───limited
    │           │       default_local.policy
    │           │       default_US_export.policy
    │           │       exempt_local.policy
    │           │
    │           └───unlimited
    │                   default_local.policy
    │                   default_US_export.policy
    │
    ├───legal
    │   ├───java.base
    │   │       ADDITIONAL_LICENSE_INFO
    │   │       aes.md
    │   │       asm.md
    │   │       ASSEMBLY_EXCEPTION
    │   │       c-libutl.md
    │   │       cldr.md
    │   │       icu.md
    │   │       LICENSE
    │   │       public_suffix.md
    │   │       unicode.md
    │   │       wepoll.md
    │   │       zlib.md
    │   │
    │   ├───java.logging
    │   │       ADDITIONAL_LICENSE_INFO
    │   │       ASSEMBLY_EXCEPTION
    │   │       LICENSE
    │   │
    │   ├───java.naming
    │   │       ADDITIONAL_LICENSE_INFO
    │   │       ASSEMBLY_EXCEPTION
    │   │       LICENSE
    │   │
    │   ├───java.security.sasl
    │   │       ADDITIONAL_LICENSE_INFO
    │   │       ASSEMBLY_EXCEPTION
    │   │       LICENSE
    │   │
    │   ├───java.sql
    │   │       ADDITIONAL_LICENSE_INFO
    │   │       ASSEMBLY_EXCEPTION
    │   │       LICENSE
    │   │
    │   ├───java.transaction.xa
    │   │       ADDITIONAL_LICENSE_INFO
    │   │       ASSEMBLY_EXCEPTION
    │   │       LICENSE
    │   │
    │   └───java.xml
    │           ADDITIONAL_LICENSE_INFO
    │           ASSEMBLY_EXCEPTION
    │           bcel.md
    │           dom.md
    │           jcup.md
    │           LICENSE
    │           xalan.md
    │           xerces.md
    │
    └───lib
        │   classlist
        │   jrt-fs.jar
        │   jvm.cfg
        │   jvm.lib
        │   modules
        │   tzdb.dat
        │   tzmappings
        │
        └───security
                blocked.certs
                cacerts
                default.policy
                public_suffix_list.dat
Slaw
  • 37,820
  • 8
  • 53
  • 80