This is an automated email from the ASF dual-hosted git repository.

chrisdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 405c9aafb882681e9f8af7bf4e364bff03df3066
Author: Christofer Dutz <[email protected]>
AuthorDate: Wed Jun 24 16:00:15 2026 +0200

    fix: Made the client clean up after failing to connect.
---
 .../apache/plc4x/java/plc4x/Plc4xConnection.java   | 69 ++++++++++++++--------
 1 file changed, 46 insertions(+), 23 deletions(-)

diff --git 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xConnection.java
 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xConnection.java
index 597cb0b51d..dc9767993a 100644
--- 
a/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xConnection.java
+++ 
b/plc4j/drivers/plc4x/src/main/java/org/apache/plc4x/java/plc4x/Plc4xConnection.java
@@ -123,36 +123,59 @@ public class Plc4xConnection extends 
ConnectionBase<Plc4xConfiguration> {
             }
         });
 
-        // Authenticate first. The proxy mandates username/password auth; no 
operation is
-        // permitted until this exchange succeeds. We never log the 
credentials.
-        authenticate();
-
-        // Open the underlying proxied connection.
-        int requestId = txIdGenerator.getAndIncrement();
-        CompletableFuture<Plc4xMessage> future = registerPending(requestId);
         try {
-            messageCodec.send(new Plc4xConnectRequest(requestId, 
configuration.getRemoteConnectionString()));
-        } catch (MessageCodecException e) {
-            pendingResponses.remove(requestId);
-            throw new PlcConnectionException("Failed to send proxy connect 
request", e);
-        }
+            // Authenticate first. The proxy mandates username/password auth; 
no operation is
+            // permitted until this exchange succeeds. We never log the 
credentials.
+            authenticate();
 
-        try {
-            Plc4xMessage response = future
-                .orTimeout(configuration.getRequestTimeout(), 
TimeUnit.MILLISECONDS)
-                .get();
-            if (!(response instanceof Plc4xConnectResponse connectResponse)) {
-                throw new PlcConnectionException("Unexpected response to proxy 
connect: " + response);
+            // Open the underlying proxied connection.
+            int requestId = txIdGenerator.getAndIncrement();
+            CompletableFuture<Plc4xMessage> future = 
registerPending(requestId);
+            try {
+                messageCodec.send(new Plc4xConnectRequest(requestId, 
configuration.getRemoteConnectionString()));
+            } catch (MessageCodecException e) {
+                pendingResponses.remove(requestId);
+                throw new PlcConnectionException("Failed to send proxy connect 
request", e);
             }
-            connectionId = connectResponse.getConnectionId();
-            handshakeComplete = true;
-        } catch (PlcConnectionException e) {
+
+            try {
+                Plc4xMessage response = future
+                    .orTimeout(configuration.getRequestTimeout(), 
TimeUnit.MILLISECONDS)
+                    .get();
+                if (!(response instanceof Plc4xConnectResponse 
connectResponse)) {
+                    throw new PlcConnectionException("Unexpected response to 
proxy connect: " + response);
+                }
+                connectionId = connectResponse.getConnectionId();
+                handshakeComplete = true;
+            } catch (PlcConnectionException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new PlcConnectionException("Error establishing proxy 
connection", e);
+            }
+        } catch (PlcConnectionException | RuntimeException e) {
+            // A failed connect (e.g. rejected credentials) must not leak the 
receive loop or the
+            // underlying transport - otherwise the non-daemon transport 
reader thread keeps the
+            // JVM alive even though no usable connection was returned.
+            cleanupAfterFailedConnect();
             throw e;
-        } catch (Exception e) {
-            throw new PlcConnectionException("Error establishing proxy 
connection", e);
         }
     }
 
+    private void cleanupAfterFailedConnect() {
+        handshakeComplete = false;
+        stopReceiving();
+        if (messageCodec != null) {
+            try {
+                messageCodec.close();
+            } catch (Exception closeError) {
+                LOGGER.debug("Error closing codec after failed connect", 
closeError);
+            }
+        }
+        pendingResponses.values().forEach(f ->
+            f.completeExceptionally(new PlcRuntimeException("Connection setup 
failed")));
+        pendingResponses.clear();
+    }
+
     /**
      * Performs the mandatory username/password handshake with the proxy. 
Throws if the
      * server rejects the credentials or the exchange does not complete in 
time. Credentials

Reply via email to