Your comment was added to the bug successfully.

Bug #97824 NullPointerException in NativeProtocol

Submitted: 28 Nov 2019 7:42 Modified: 31 Aug 2020 22:29
Reporter: Roman Leventov Email Updates:
Status: No Feedback Impact on me:
None 
Category:Connector / J Severity:S3 (Non-critical)
Version:8.0.18 OS:MacOS
Assigned to: CPU Architecture:Any

[28 Nov 2019 7:42] Roman Leventov
Description:
There may be a NullPointerException in NativeProtocol.java in line 661:

        } finally {
            if (timeoutMillis != 0) {
                try {
                    this.socketConnection.getMysqlSocket().setSoTimeout(oldTimeout); // <- this line
                } catch (IOException e) {
                    throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
                            this.getPacketReceivedTimeHolder(), e, getExceptionInterceptor());
                }
            }
        }

this.socketConnection.getMysqlSocket() may return null.

How to repeat:
    private static Network network;
    private static MySQLContainer mysql;
    private static int mysqlPort;
    private static ToxiproxyContainer toxiproxy;
    private static ToxiproxyContainer.ContainerProxy proxy;
    private static String jdbcUrl;

    @BeforeClass
    public static void setupContainers() {
        network = Network.newNetwork();
        mysql = (MySQLContainer) new MySQLContainer("mysql:5.7").withNetwork(network);
        mysql.start();
        mysqlPort = mysql.getMappedPort(MySQLContainer.MYSQL_PORT);
        toxiproxy = new ToxiproxyContainer().withNetwork(network);
        toxiproxy.start();
        proxy = toxiproxy.getProxy(mysql, MySQLContainer.MYSQL_PORT);
        jdbcUrl = "jdbc:mysql://" + proxy.getContainerIpAddress() + ":" + proxy.getProxyPort() +
                "/" + mysql.getDatabaseName();
    }

    @AfterClass
    public static void shutdownContainers() {
        //noinspection EmptyTryBlock: let try-with-resources do the work
        try (final Network ignore1 = network;
             final MySQLContainer ignore2 = mysql;
             final ToxiproxyContainer ignore3 = toxiproxy) {
        }
    }

    @Test
    public void testHikariDbDisconnected() throws SQLException {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(jdbcUrl);
        config.setUsername(mysql.getUsername());
        config.setPassword(mysql.getPassword());
        try (HikariDataSource ds = new HikariDataSource(config)) {

            proxy.setConnectionCut(true); // Emulate db disconnection

            System.out.println(ds.getConnection().prepareCall("SELECT 1;").execute());
        }
    }
[31 Jul 2020 22:29] Filipe Silva
Hi Roman,

Thank you for this bug report and your interest in Connector/J.

I'm sorry for the late reply, but can you provide a full stack trace with the exception, please?

Thanks,
[1 Sep 2020 1:00] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
[3 Sep 2020 5:37] Marcle leo
Hi Filipe,
I had the same problem recently, and I have a reproducible test case here.

How to repeat:

import java.lang.reflect.Method;
import java.sql.*;

public class Demo {

    public static void main(String[] args) {
        Connection conn = null;
        Statement statement = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            // Set your connection parameters
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false" +
                    "&allowPublicKeyRetrieval=true&serverTimezone=UTC","root","12345678");
            // Set the breakpoint here or sleep the thread
            // Next, close the MySQL service and continue.
            Thread.sleep(30 * 1000);
            Method pingInternal = conn.getClass().getMethod("pingInternal", boolean.class, int.class);
            pingInternal.invoke(conn, true, 1000);
        } catch (Exception e) {
            //
            e.printStackTrace();
        } finally {
            try{
                if(statement!=null) statement.close();
            }catch(SQLException se2){
            }
            try{
                if(conn!=null) conn.close();
            }catch(SQLException se){
                se.printStackTrace();
            }
        }
    }

}

Description:

My guess is that when the MySQL service was first shut down, the server-side TCP connection was in the FIN_WAIT_2/FIN_WAIT_2 state and could accept requests but not return data, so the following code is executing normally.
(The following code is taken from NativeProtocol#sendCommand.)

try {
	clearInputStream();
	this.packetSequence = -1;
	send(queryPacket, queryPacket.getPosition());

} catch (CJException ex) {
	// don't wrap CJExceptions
	throw ex;
} catch (Exception ex) {
	throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
			this.getPacketReceivedTimeHolder(), ex, getExceptionInterceptor());
}

But this part of the code has an internal exception and sets the current this.socketConnection.getMysqlSocket() to null.
(The following code is taken from NativeProtocol#sendCommand.)

if (!skipCheck) {
	if ((COMMAND == NativeConstants.COM_STMT_EXECUTE) || (COMMAND == NativeConstants.COM_STMT_RESET)) {
		this.packetReader.resetMessageSequence();
	}

	returnPacket = checkErrorMessage(command);

	if (this.queryInterceptors ! = null) {
		returnPacket = (NativePacketPayload) invokeQueryInterceptorsPost(queryPacket, returnPacket, false);
	}
}
			
When executing to the finally statement block, when satisfying timeoutMillis ! = 0 condition, the following code throws the NPE.
(The following code is taken from NativeProtocol#sendCommand.)

this.socketConnection.getMysqlSocket().setSoTimeout(oldTimeout);

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.052 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.