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-extras.git
The following commit(s) were added to refs/heads/develop by this push:
new 2092725b fix(plc4j/nifi): Repair the connection string validator
2092725b is described below
commit 2092725b71698262498665e78088a67890059cca
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
---
.../org/apache/plc4x/nifi/BasePlc4xProcessor.java | 20 +++++++++++++++-----
.../org/apache/plc4x/nifi/BasePlc4xProcessor.java | 20 +++++++++++++++-----
2 files changed, 30 insertions(+), 10 deletions(-)
diff --git
a/plc4j/integrations/apache-nifi/nifi-1/nifi-plc4x-processors/src/main/java/org/apache/plc4x/nifi/BasePlc4xProcessor.java
b/plc4j/integrations/apache-nifi/nifi-1/nifi-plc4x-processors/src/main/java/org/apache/plc4x/nifi/BasePlc4xProcessor.java
index 52cc63f4..a56c6835 100644
---
a/plc4j/integrations/apache-nifi/nifi-1/nifi-plc4x-processors/src/main/java/org/apache/plc4x/nifi/BasePlc4xProcessor.java
+++
b/plc4j/integrations/apache-nifi/nifi-1/nifi-plc4x-processors/src/main/java/org/apache/plc4x/nifi/BasePlc4xProcessor.java
@@ -48,7 +48,6 @@ import org.apache.nifi.serialization.record.RecordSchema;
import org.apache.plc4x.java.DefaultPlcDriverManager;
import org.apache.plc4x.java.api.PlcConnection;
import org.apache.plc4x.java.api.PlcDriver;
-import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
import org.apache.plc4x.java.api.messages.PlcReadResponse;
import org.apache.plc4x.java.api.messages.PlcWriteRequest;
@@ -341,12 +340,23 @@ public abstract class BasePlc4xProcessor extends
AbstractProcessor {
if (context.isExpressionLanguageSupported(subject) &&
context.isExpressionLanguagePresent(input)) {
return new
ValidationResult.Builder().subject(subject).input(input).explanation("Expression
Language Present").valid(true).build();
}
+ // Validation opens a real connection, so it has to close it
again: NiFi validates
+ // properties repeatedly and every leaked connection leaves a
session behind on the
+ // device until it times out.
try {
- PlcDriver driver =
AddressesAccessUtils.getManager().getDriverForUrl(input);
- driver.getConnection(input);
- } catch (PlcConnectionException e) {
+ PlcDriver driver =
AddressesAccessUtils.getManager().getDriverForUrl(input);
+ try (PlcConnection connection = driver.getConnection(input)) {
+ // Opening and closing is the check; nothing to do with
the connection itself.
+ }
+ } catch (Exception e) {
+ // Not just PlcConnectionException: a driver may reject a
connection string with an
+ // unchecked exception (the OPC UA driver throws
PlcRuntimeException when no
+ // endpoint matches, for instance). Letting that escape turns
a property the user
+ // can fix into a failing validator.
+ String explanation = e.getMessage() == null ?
e.getClass().getName() : e.getMessage();
return new ValidationResult.Builder().subject(subject)
- .explanation(e.getMessage())
+ .input(input)
+ .explanation(explanation)
.valid(false)
.build();
}
diff --git
a/plc4j/integrations/apache-nifi/nifi-2/nifi-2-plc4x-processors/src/main/java/org/apache/plc4x/nifi/BasePlc4xProcessor.java
b/plc4j/integrations/apache-nifi/nifi-2/nifi-2-plc4x-processors/src/main/java/org/apache/plc4x/nifi/BasePlc4xProcessor.java
index 4f2a3991..70f3675d 100644
---
a/plc4j/integrations/apache-nifi/nifi-2/nifi-2-plc4x-processors/src/main/java/org/apache/plc4x/nifi/BasePlc4xProcessor.java
+++
b/plc4j/integrations/apache-nifi/nifi-2/nifi-2-plc4x-processors/src/main/java/org/apache/plc4x/nifi/BasePlc4xProcessor.java
@@ -48,7 +48,6 @@ import org.apache.nifi.serialization.record.RecordSchema;
import org.apache.plc4x.java.DefaultPlcDriverManager;
import org.apache.plc4x.java.api.PlcConnection;
import org.apache.plc4x.java.api.PlcDriver;
-import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
import org.apache.plc4x.java.api.messages.PlcReadResponse;
import org.apache.plc4x.java.api.messages.PlcWriteRequest;
@@ -341,12 +340,23 @@ public abstract class BasePlc4xProcessor extends
AbstractProcessor {
if (context.isExpressionLanguageSupported(subject) &&
context.isExpressionLanguagePresent(input)) {
return new
ValidationResult.Builder().subject(subject).input(input).explanation("Expression
Language Present").valid(true).build();
}
+ // Validation opens a real connection, so it has to close it
again: NiFi validates
+ // properties repeatedly and every leaked connection leaves a
session behind on the
+ // device until it times out.
try {
- PlcDriver driver =
AddressesAccessUtils.getManager().getDriverForUrl(input);
- driver.getConnection(input);
- } catch (PlcConnectionException e) {
+ PlcDriver driver =
AddressesAccessUtils.getManager().getDriverForUrl(input);
+ try (PlcConnection connection = driver.getConnection(input)) {
+ // Opening and closing is the check; nothing to do with
the connection itself.
+ }
+ } catch (Exception e) {
+ // Not just PlcConnectionException: a driver may reject a
connection string with an
+ // unchecked exception (the OPC UA driver throws
PlcRuntimeException when no
+ // endpoint matches, for instance). Letting that escape turns
a property the user
+ // can fix into a failing validator.
+ String explanation = e.getMessage() == null ?
e.getClass().getName() : e.getMessage();
return new ValidationResult.Builder().subject(subject)
- .explanation(e.getMessage())
+ .input(input)
+ .explanation(explanation)
.valid(false)
.build();
}