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 42a8557036d40fec18125bfc786cf689dee32b8c Author: Christofer Dutz <[email protected]> AuthorDate: Sat Aug 15 12:18:58 2026 +0200 fix(plc4j/nifi): Repair the connection string validator The validator opened a connection to check a connection string and then dropped it on the floor: driver.getConnection(input); NiFi validates properties repeatedly - on every configuration change, when a component starts, and while the configuration dialog is open - so each round left a connection behind. For OPC UA that is a secure channel and a session the device holds until they time out, which on hardware with a small connection limit is enough to lock everyone else out. The connection is now closed again. The catch only covered PlcConnectionException, but a driver may also reject a connection string with an unchecked exception: the OPC UA driver throws PlcRuntimeException when none of the endpoints a server advertises matches the one that was dialed. That escaped validate() instead of marking the property invalid, so the user got no usable feedback about a value they could have corrected. Any exception now produces an invalid result carrying the driver's message, and the rejected value is attached to it. The same validator exists in both the nifi-1 and nifi-2 processors, so both are fixed. Relates to: #2302 --- .../plc4x/java/opcua/context/SecureChannel.java | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/SecureChannel.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/SecureChannel.java index 944a645256..ef75d28288 100644 --- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/SecureChannel.java +++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/SecureChannel.java @@ -54,6 +54,7 @@ import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.Locale; import java.util.Optional; import java.util.ArrayList; import java.util.List; @@ -308,7 +309,20 @@ public class SecureChannel { Entry<EndpointDescription, UserTokenPolicy> selectedEndpoint = selectEndpoint(sessionResponse.getServerEndpoints(), configuration.getSecurityPolicy(), configuration.getMessageSecurity()); if (selectedEndpoint == null) { - throw new PlcRuntimeException("Unable to find endpoint matching " + driverContext.getEndpoint()); + // Endpoints are matched on the host as written in the connection string; the driver + // performs no name resolution. Servers commonly advertise a different name than the + // one that was dialed, so say what was offered and how to point the driver at it. + String advertised = sessionResponse.getServerEndpoints().stream() + .map(endpoint -> endpoint.getEndpointUrl().getStringValue() + + " (" + endpoint.getSecurityPolicyUri().getStringValue() + + ", " + endpoint.getSecurityMode() + ")") + .collect(Collectors.joining("\n ")); + throw new PlcRuntimeException("Unable to find an endpoint matching " + driverContext.getEndpoint() + + " with security policy " + configuration.getSecurityPolicy() + + " and message security " + configuration.getMessageSecurity() + + ". The server offered:\n " + advertised + + "\nIf the server advertises a different host name than the one you connect to, set" + + " 'endpoint-host' (and 'endpoint-port' if it differs) to the name shown above."); } PascalString policyId = selectedEndpoint.getValue().getPolicyId(); @@ -546,7 +560,11 @@ public class SecureChannel { */ private static boolean isMatchingEndpoint(EndpointDescription endpoint, String host, String port, String transportEndpoint) throws PlcRuntimeException { String portAddition = port == null ? "" : ":" + port; - return endpoint.getEndpointUrl().getStringValue().startsWith("opc.tcp://" + host + portAddition + transportEndpoint); + String expected = "opc.tcp://" + host + portAddition + transportEndpoint; + // Host names are case insensitive, so a server advertising "opc.tcp://MyServer:4840" has + // to match a connection string that says "myserver". + return endpoint.getEndpointUrl().getStringValue().toLowerCase(Locale.ROOT) + .startsWith(expected.toLowerCase(Locale.ROOT)); } /**
