MySQL connector does not work with GraalVM and Docker

Viewed 210

I'm trying to do a basic SQL query using JDBC and mysql-connector-java on an image with Graalvm (The cause of the problem).

Apparently everyone who tries to use GraalVM with JDBC/MySQL and Docker will have the same problem every time.

I'm having the following Exception

Exception in thread "main" java.lang.ExceptionInInitializerError
   at com.mysql.cj.Constants.<clinit>(Constants.java:49)
   at com.mysql.cj.util.Util.<clinit>(Util.java:67)
   at com.mysql.cj.conf.ConnectionUrl$Type.getImplementingInstance(ConnectionUrl.java:241)
   at com.mysql.cj.conf.ConnectionUrl$Type.getConnectionUrlInstance(ConnectionUrl.java:211)
   at com.mysql.cj.conf.ConnectionUrl.getConnectionUrlInstance(ConnectionUrl.java:280)
   at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:195)
   at java.sql.DriverManager.getConnection(DriverManager.java:681)
   at java.sql.DriverManager.getConnection(DriverManager.java:229)
   at com.me.test.Main.testJDBC(Main.java:73)
   at com.me.test.Main.main(Main.java:94)
Caused by: java.lang.RuntimeException: Can't load resource bundle due to underlying     exception java.util.MissingResourceException: Can't find bundle for base name com.mysql.cj.LocalizedErrorMessages, locale en_US
   at com.mysql.cj.Messages.<clinit>(Messages.java:60)
... 10 more
Caused by: java.util.MissingResourceException: Can't find bundle for base name com.mysql.cj.LocalizedErrorMessages, locale en_US
   at java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:2045)
   at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1683)
   at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1586)
   at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1549)
   at java.util.ResourceBundle.getBundle(ResourceBundle.java:858)
   at com.mysql.cj.Messages.<clinit>(Messages.java:58)
... 10 more

My Dockerfile

FROM ghcr.io/graalvm/graalvm-ce:latest

RUN gu install native-image
WORKDIR /tmp/dist
ADD ./app.jar ./app.jar

COPY ./reflect-config.json ./reflect-config.json

RUN native-image -jar ./app.jar --verbose --no-fallback
RUN  microdnf install zip

ADD bootstrap bootstrap

RUN chmod +x bootstrap
RUN chmod +x ./app

RUN zip -j function.zip bootstrap app


ENTRYPOINT ["./app"]

Dependencies in my pom file

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.29</version>
</dependency>
<dependency>
  <groupId>com.oracle.database.jdbc</groupId>
  <artifactId>ojdbc8</artifactId>
  <version>21.5.0.0</version>
</dependency>

And finally my java code:

public void testJDBC() throws SQLException, IOException {
    String url = "jdbc:mysql://foo.blabla.aws-region.rds.amazonaws.com:3306/database";
    String user = "root";
    String password = "password";
    try(java.sql.Connection conn = java.sql.DriverManager.getConnection(url, user, password)){
        doSomething(conn);
    }
}

EDIT 1:

I followed Mark Rotteveel's recommendation, and added a "resource-config.json" file with the following:

{
  "resources": {
    "includes": [
      {"pattern": "com.mysql.cj.LocalizedErrorMessages.properties"}
    ]
  }
}

And in the "native-image.properties" file the following

Args = -H:EnableURLProtocols=http,https -H:ReflectionConfigurationFiles=/tmp/dist/reflect-config.json -H:ResourceConfigurationFiles=/tmp/dist/resource-config.json

Fixed the current error, but as expected we have a new one:

Exception in thread "main" java.lang.ClassCastException: com.mysql.cj.exceptions.CJException cannot be cast to com.mysql.cj.exceptions.WrongArgumentException
   at com.mysql.cj.util.Util.getInstance(Util.java:169)
   at com.mysql.cj.util.Util.getInstance(Util.java:174)
   at com.mysql.cj.conf.ConnectionUrl$Type.getImplementingInstance(ConnectionUrl.java:241)
   at com.mysql.cj.conf.ConnectionUrl$Type.getConnectionUrlInstance(ConnectionUrl.java:211)
   at com.mysql.cj.conf.ConnectionUrl.getConnectionUrlInstance(ConnectionUrl.java:280)
   at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:195)
   at java.sql.DriverManager.getConnection(DriverManager.java:681)
   at java.sql.DriverManager.getConnection(DriverManager.java:229)
   at com.me.test.Main.testJDBC(Main.java:77)
   at com.me.test.Main.main(Main.java:98)

EDIT 2:

I configured the following in the file "reflect-config.json"

[

  {
    "name": "com.mysql.cj.conf.url.SingleConnectionUrl",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "com.mysql.cj.conf.url.FailoverDnsSrvConnectionUrl",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "com.mysql.cj.conf.url.LoadBalanceDnsSrvConnectionUrl",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "com.mysql.cj.conf.url.ReplicationDnsSrvConnectionUrl",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "com.mysql.cj.conf.url.XDevApiDnsSrvConnectionUrl",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "com.mysql.cj.conf.url.FailoverConnectionUrl",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "com.mysql.cj.conf.url.LoadBalanceConnectionUrl",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "com.mysql.cj.conf.url.ReplicationConnectionUrl",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },  {
  "name": "com.mysql.cj.conf.url.XDevApiConnectionUrl",
  "allDeclaredFields": true,
  "allDeclaredMethods": true,
  "allDeclaredConstructors": true
},
  {
    "name": "com.mysql.cj.log.Jdk14Logger",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "com.mysql.cj.log.NullLogger",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },
  {
        "name": "com.mysql.cj.log.Slf4JLogger",
        "allDeclaredFields": true,
        "allDeclaredMethods": true,
        "allDeclaredConstructors": true
    },
  {
  "name": "com.mysql.cj.log.StandardLogger",
  "allDeclaredFields": true,
  "allDeclaredMethods": true,
  "allDeclaredConstructors": true
},
  {
    "name": "com.mysql.jdbc.Driver",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  },

  {
    "name":"oracle.jdbc.OracleDriver",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name":"oracle.jdbc.driver.T4CDriverExtension",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name" : "oracle.jdbc.driver.T2CDriverExtension",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name":"oracle.net.ano.Ano",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name":"oracle.net.ano.AuthenticationService",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name":"oracle.net.ano.DataIntegrityService",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name":"oracle.net.ano.EncryptionService",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name":"oracle.net.ano.SupervisorService",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  }
]

But for some reason there is a connection error (Tested With AWS RDS, and host.docker.internal).

Exception in thread "main" java.sql.SQLNonTransientConnectionException: Cannot connect to MySQL server on foo.blabla.aws-region.rds.amazonaws.com:3,306.

Make sure that there is a MySQL server running on the machine/port you are trying to connect to and that the machine this software is running on is able to connect to this host/port (i.e. not firewalled). Also make sure that the server has not been started with the --skip-networking flag.


    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:110)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:73)
    at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:462)
    at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241)
    at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198)
    at java.sql.DriverManager.getConnection(DriverManager.java:681)
    at java.sql.DriverManager.getConnection(DriverManager.java:229)
    at com.me.test.Main.testJDBC(Main.java:104)
    at com.me.test.Main.main(Main.java:125)
Caused by: java.lang.ClassCastException: com.mysql.cj.exceptions.CJException cannot be cast to com.mysql.cj.exceptions.UnableToConnectException
    at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:825)
    at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:448)
    ... 6 more

This error only occurs using "ghcr.io/graalvm/graalvm-ce:latest", if I use another VM like "amazoncorretto:18-alpine" everything works perfect.

Full Source Code: https://github.com/ederbaum/MySQLGraalVM

Any possible solution?

0 Answers
Related