'Docker Compose Java Application & MySql Connection Problem with JDBC

When implementing a Java application in a docker container and a MySQL database configured in its own docker container together with Docker Compose, the following error always occurs in the Java application when creating a connection to the database with jdbc:

currency_server_v0_1  | com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
    currency_server_v0_1  |
    currency_server_v0_1  | The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
    currency_server_v0_1  |         at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174)
    currency_server_v0_1  |         at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
    currency_server_v0_1  |         at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:836)
    currency_server_v0_1  |         at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:456)
    currency_server_v0_1  |         at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:246)
    currency_server_v0_1  |         at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:197)
    currency_server_v0_1  |         at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:677)
    currency_server_v0_1  |         at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:228)
    currency_server_v0_1  |         at Test.main(Test.java:22)
    currency_server_v0_1  | Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure
    currency_server_v0_1  |
    currency_server_v0_1  | The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
    currency_server_v0_1  |         at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    currency_server_v0_1  |         at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:78)
    currency_server_v0_1  |         at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    currency_server_v0_1  |         at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    currency_server_v0_1  |         at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
    currency_server_v0_1  |         at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
    currency_server_v0_1  |         at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)
    currency_server_v0_1  |         at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:151)
    currency_server_v0_1  |         at com.mysql.cj.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:167)
    currency_server_v0_1  |         at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:91)
    currency_server_v0_1  |         at com.mysql.cj.NativeSession.connect(NativeSession.java:144)
    currency_server_v0_1  |         at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:956)
    currency_server_v0_1  |         at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:826)
    currency_server_v0_1  |         ... 6 more
    currency_server_v0_1  | Caused by: java.net.ConnectException: Connection refused
    currency_server_v0_1  |         at java.base/sun.nio.ch.Net.connect0(Native Method)
    currency_server_v0_1  |         at java.base/sun.nio.ch.Net.connect(Net.java:576)
    currency_server_v0_1  |         at java.base/sun.nio.ch.Net.connect(Net.java:565)
    currency_server_v0_1  |         at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:588)
    currency_server_v0_1  |         at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:333)
    currency_server_v0_1  |         at java.base/java.net.Socket.connect(Socket.java:645)
    currency_server_v0_1  |         at com.mysql.cj.protocol.StandardSocketFactory.connect(StandardSocketFactory.java:155)
    currency_server_v0_1  |         at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:65)
    currency_server_v0_1  |         ... 9 more
    currency_server_v0_currency_server_v0_1 exited with code 0

The corresponding Docker files and Java code look like this: docker-compose.yml: version: "3.7"

services:
  currency_server_v0:
    build: .
    ports:
      - 8080:8080
    depends_on:
      - mysqldb
    networks:
      currency-mysql:

  mysqldb:
    image: mysql:5.7
    container_name: mysqldb
    ports:
      - 3306:3306
    networks:
      currency-mysql:
    volumes:
      - db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=currency_server_db
      - MYSQL_USER= admin
      - MYSQL_PASSWORD= admin
volumes:
  db:
networks:
  currency-mysql:

JDBC Connection in Java:

try (Connection conn = DriverManager.getConnection(
        "jdbc:mysql://mysqldb:3306/currency_server_db", "root", "root");
     Statement stmt = conn.createStatement();
) {
    String sql = "CREATE TABLE TEST " +
            "(id INTEGER not NULL, " +
            " PRIMARY KEY ( id ))";

    stmt.executeUpdate(sql);
    System.out.println("Created table in given database...");
} catch (SQLException e) {
    e.printStackTrace();
}

Dockerfile:

FROM openjdk:16-alpine3.13
COPY target/currency_server_v0-1.0-SNAPSHOT-jar-with-dependencies.jar app.jar
ENTRYPOINT ["java","-jar", "app.jar"]

Does anyone have or know this problem? Thanks for your answers.



Solution 1:[1]

Your service starts faster than MySql despite depends_on which does not know how to check if a container is ready to accept connections. You could implement a wait behaviour in your service Dockerfile or you could do something like this in Java:

public static void main(final String[] args) {
    waitForServer("mysqldb", 3306, 10_000);
}

public static void waitForServer(String host, int port, int timeout) {
    long start = System.currentTimeMillis();
    while (portUnavailable(host, port)) {
        System.out.println("Waiting for port to open: " + port);
        if (System.currentTimeMillis() - start > timeout) {
            throw new Error("Timeout waiting for port to open: " + port);
        }
    }
}

private static boolean portUnavailable(String host, int port) {
    try (Socket s = new Socket(host, port)) {
        return false;
    } catch (Exception e) {
        return true;
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Delta George