Fix Read Modified Query from Moshix bug report
Build and Test j3270 / Build JAR & Run Tests (Java 17) (push) Successful in 50s
Build and Test j3270 / Build JAR & Run Tests (Java 21) (push) Successful in 1m24s
Build and Test j3270 / Build JAR & Run Tests (Java 11) (push) Successful in 1m38s
Release j3270 / Build & Publish Release (push) Successful in 1m38s

This commit is contained in:
2026-09-24 17:34:43 +00:00
parent b61702a57c
commit 0201c7c277
3 changed files with 26 additions and 7 deletions
@@ -47,6 +47,7 @@ public class Telnet3270Client {
private final java.util.concurrent.atomic.AtomicBoolean reconnecting = new java.util.concurrent.atomic.AtomicBoolean(false); private final java.util.concurrent.atomic.AtomicBoolean reconnecting = new java.util.concurrent.atomic.AtomicBoolean(false);
private volatile Thread reconnectThread; private volatile Thread reconnectThread;
private volatile boolean intentionallyDisconnected = false;
public Telnet3270Client(ConnectionConfig config) { public Telnet3270Client(ConnectionConfig config) {
this.config = config; this.config = config;
@@ -131,6 +132,7 @@ public class Telnet3270Client {
* then returns while telnet/TN3270E negotiation continues asynchronously. * then returns while telnet/TN3270E negotiation continues asynchronously.
*/ */
public void connect() throws IOException { public void connect() throws IOException {
intentionallyDisconnected = false;
log.info("Connecting to " + config.getHost() + ":" + config.getPort() + log.info("Connecting to " + config.getHost() + ":" + config.getPort() +
" model=" + config.getModel()); " model=" + config.getModel());
@@ -194,6 +196,7 @@ public class Telnet3270Client {
* Disconnect from the host. * Disconnect from the host.
*/ */
public void disconnect() { public void disconnect() {
intentionallyDisconnected = true;
cancelAutoReconnect(); cancelAutoReconnect();
if (connection != null) { if (connection != null) {
try { try {
@@ -211,14 +214,20 @@ public class Telnet3270Client {
public void cancelAutoReconnect() { public void cancelAutoReconnect() {
reconnecting.set(false); reconnecting.set(false);
if (reconnectThread != null) { Thread t = reconnectThread;
reconnectThread.interrupt(); if (t != null) {
t.interrupt();
try {
t.join(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
reconnectThread = null; reconnectThread = null;
} }
} }
public void initiateAutoReconnect() { public void initiateAutoReconnect() {
if (config == null || !config.isAutoReconnect()) { if (intentionallyDisconnected || config == null || !config.isAutoReconnect()) {
return; return;
} }
if (!reconnecting.compareAndSet(false, true)) { if (!reconnecting.compareAndSet(false, true)) {
@@ -67,7 +67,9 @@ public class Mock3270Server implements AutoCloseable {
} }
public Mock3270Server(int requestedPort) throws IOException { 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.port = serverSocket.getLocalPort();
this.translator = new EbcdicTranslator(); this.translator = new EbcdicTranslator();
executor.submit(this::acceptLoop); executor.submit(this::acceptLoop);
@@ -571,6 +573,11 @@ public class Mock3270Server implements AutoCloseable {
} catch (Exception ignored) {} } catch (Exception ignored) {}
disconnectClient(); disconnectClient();
executor.shutdownNow(); executor.shutdownNow();
try {
executor.awaitTermination(200, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log.info("Mock3270Server stopped"); log.info("Mock3270Server stopped");
} }
} }
@@ -76,8 +76,8 @@ public class Mock3270ServerIntegrationTest {
public void testSendScreenAndVerifyPresentationSpace() throws Exception { public void testSendScreenAndVerifyPresentationSpace() throws Exception {
ConnectionConfig config = new ConnectionConfig("127.0.0.1", server.getPort(), TerminalModel.IBM_3279_2); ConnectionConfig config = new ConnectionConfig("127.0.0.1", server.getPort(), TerminalModel.IBM_3279_2);
client = new Telnet3270Client(config); client = new Telnet3270Client(config);
assertTrue(client.connect(8000)); assertTrue(client.connect(8000), "Client should connect to mock server");
assertTrue(server.waitForHandshake(5, TimeUnit.SECONDS)); assertTrue(server.waitForHandshake(5, TimeUnit.SECONDS), "Server handshake should complete within 5s");
waitForState(client, ConnectionState.CONNECTED_TN3270E, 5000); waitForState(client, ConnectionState.CONNECTED_TN3270E, 5000);
// Server sends formatted greeting screen at row 2, col 10 // Server sends formatted greeting screen at row 2, col 10
@@ -243,7 +243,10 @@ public class Mock3270ServerIntegrationTest {
// Disconnect active client connection abruptly // Disconnect active client connection abruptly
server.disconnectClient(); 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); waitForState(client, ConnectionState.CONNECTED_TN3270E, 8000);
assertEquals(ConnectionState.CONNECTED_TN3270E, client.getConnectionState()); assertEquals(ConnectionState.CONNECTED_TN3270E, client.getConnectionState());
} }