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
