From 0201c7c277de09b4dde4fbf8fa7da2816fa95a64 Mon Sep 17 00:00:00 2001 From: Rudi Date: Thu, 24 Sep 2026 17:34:43 +0000 Subject: [PATCH] Fix Read Modified Query from Moshix bug report --- .../haus/nightmare/lib3270j/Telnet3270Client.java | 15 ++++++++++++--- .../lib3270j/testutil/Mock3270Server.java | 9 ++++++++- .../testutil/Mock3270ServerIntegrationTest.java | 9 ++++++--- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/Telnet3270Client.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/Telnet3270Client.java index 4b6e4fd..2e9a124 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/Telnet3270Client.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/Telnet3270Client.java @@ -47,6 +47,7 @@ public class Telnet3270Client { private final java.util.concurrent.atomic.AtomicBoolean reconnecting = new java.util.concurrent.atomic.AtomicBoolean(false); private volatile Thread reconnectThread; + private volatile boolean intentionallyDisconnected = false; public Telnet3270Client(ConnectionConfig config) { this.config = config; @@ -131,6 +132,7 @@ public class Telnet3270Client { * then returns while telnet/TN3270E negotiation continues asynchronously. */ public void connect() throws IOException { + intentionallyDisconnected = false; log.info("Connecting to " + config.getHost() + ":" + config.getPort() + " model=" + config.getModel()); @@ -194,6 +196,7 @@ public class Telnet3270Client { * Disconnect from the host. */ public void disconnect() { + intentionallyDisconnected = true; cancelAutoReconnect(); if (connection != null) { try { @@ -211,14 +214,20 @@ public class Telnet3270Client { public void cancelAutoReconnect() { reconnecting.set(false); - if (reconnectThread != null) { - reconnectThread.interrupt(); + Thread t = reconnectThread; + if (t != null) { + t.interrupt(); + try { + t.join(200); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } reconnectThread = null; } } public void initiateAutoReconnect() { - if (config == null || !config.isAutoReconnect()) { + if (intentionallyDisconnected || config == null || !config.isAutoReconnect()) { return; } if (!reconnecting.compareAndSet(false, true)) { diff --git a/lib3270j/src/test/java/haus/nightmare/lib3270j/testutil/Mock3270Server.java b/lib3270j/src/test/java/haus/nightmare/lib3270j/testutil/Mock3270Server.java index 4bd1ad5..59a3078 100644 --- a/lib3270j/src/test/java/haus/nightmare/lib3270j/testutil/Mock3270Server.java +++ b/lib3270j/src/test/java/haus/nightmare/lib3270j/testutil/Mock3270Server.java @@ -67,7 +67,9 @@ public class Mock3270Server implements AutoCloseable { } public Mock3270Server(int requestedPort) throws IOException { - this.serverSocket = new ServerSocket(requestedPort, 10, InetAddress.getByName("127.0.0.1")); + this.serverSocket = new ServerSocket(); + this.serverSocket.setReuseAddress(true); + this.serverSocket.bind(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), requestedPort), 10); this.port = serverSocket.getLocalPort(); this.translator = new EbcdicTranslator(); executor.submit(this::acceptLoop); @@ -571,6 +573,11 @@ public class Mock3270Server implements AutoCloseable { } catch (Exception ignored) {} disconnectClient(); executor.shutdownNow(); + try { + executor.awaitTermination(200, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } log.info("Mock3270Server stopped"); } } diff --git a/lib3270j/src/test/java/haus/nightmare/lib3270j/testutil/Mock3270ServerIntegrationTest.java b/lib3270j/src/test/java/haus/nightmare/lib3270j/testutil/Mock3270ServerIntegrationTest.java index f03dd57..b1ce9b5 100644 --- a/lib3270j/src/test/java/haus/nightmare/lib3270j/testutil/Mock3270ServerIntegrationTest.java +++ b/lib3270j/src/test/java/haus/nightmare/lib3270j/testutil/Mock3270ServerIntegrationTest.java @@ -76,8 +76,8 @@ public class Mock3270ServerIntegrationTest { public void testSendScreenAndVerifyPresentationSpace() throws Exception { ConnectionConfig config = new ConnectionConfig("127.0.0.1", server.getPort(), TerminalModel.IBM_3279_2); client = new Telnet3270Client(config); - assertTrue(client.connect(8000)); - assertTrue(server.waitForHandshake(5, TimeUnit.SECONDS)); + assertTrue(client.connect(8000), "Client should connect to mock server"); + assertTrue(server.waitForHandshake(5, TimeUnit.SECONDS), "Server handshake should complete within 5s"); waitForState(client, ConnectionState.CONNECTED_TN3270E, 5000); // Server sends formatted greeting screen at row 2, col 10 @@ -243,7 +243,10 @@ public class Mock3270ServerIntegrationTest { // Disconnect active client connection abruptly server.disconnectClient(); - // Wait for client to detect disconnect and reconnect automatically + // Wait for client to detect disconnect and enter RECONNECTING + waitForState(client, ConnectionState.RECONNECTING, 5000); + + // Wait for client to reconnect automatically waitForState(client, ConnectionState.CONNECTED_TN3270E, 8000); assertEquals(ConnectionState.CONNECTED_TN3270E, client.getConnectionState()); }