etzel-mes opened a new pull request, #28045:
URL: https://github.com/apache/camel/pull/28045

   # Description
   
   [CAMEL-25690](https://issues.apache.org/jira/browse/CAMEL-25690).
   
   The `milo-client` producer (write, method call, read) and the `milo-browse` 
producer resolve the namespace URI (`nsu=`) of a node ID through the namespace 
table of the server. When the URI was not in the table, the lookup failed with 
a `NullPointerException`, and the producer took it for a lost connection: the 
client reconnected, which also recreated the subscriptions of the consumers 
that share the connection, and a read lost the values of its other nodes.
   
   Why this matters in automation: a namespace URI is configuration, and it 
goes stale when a server is reconfigured or replaced. A route with a stale URI 
then reconnected the client on every request. I checked this against a server 
that is not built on Milo (Microsoft opc-plc 2.15.4, on the OPC Foundation .NET 
stack). Without this change, one awaited write to such a node replaced the 
`OpcUaClient` of the connection, and the requests sent right after it failed 
with `No connected`: a write and two method calls that the server would have 
answered itself. One such browse reconnected the browse connection.
   
   The change:
   - The consumer and the producers now share one lookup, as suggested in the 
review of CAMEL-25671 (#27906). `lookupNamespace` resolves the namespace URI or 
index, completes with `null` for a URI that the server does not know, and 
replaces the consumer's `lookupMonitoredNode`. The consumer reports such a node 
with `Bad_NodeIdUnknown` and resolves it under the lock, both as before; its 
lookup still blocks while the monitored items are created, as before.
   - A producer answers such a node the way a server answers a node that it 
does not have (OPC 10000-4 v1.05, Table 179, and Table 61 for the Call 
service), without sending it to the server: a write returns 
`Bad_NodeIdUnknown`; a read returns `Bad_NodeIdUnknown` for that node in its 
position, and a browse under that node ID, both with the results of the other 
nodes; a method call returns `Bad_NodeIdUnknown` for an unknown object and 
`Bad_MethodInvalid` for an unknown method. A request without such a node takes 
the same path as before. A read or browse of only such nodes sends no request, 
because a server answers an empty one with `Bad_NothingToDo`, which the 
producer takes for a lost connection. A DEBUG line names the node, because a 
write that does not wait for its result reports nothing else.
   - New `ProducerUnknownNamespaceTest`: a write, and reads and browses of such 
a node next to a known node and alone, get these results, and a method call 
with an unknown method or object completes without an error. Each connection 
keeps its `OpcUaClient`, and after the write, the reads and the calls a 
consumer on the same connection receives the next value. Without the fix all 
four tests fail, and the client reconnects in each.
   - Component docs (Node ID section, with its catalog copy) and a 4.24 
upgrade-guide note.
   
   Against opc-plc with the change, each answer matched the answer of the 
server for a node it does not have (`ns=99;s=…`, or an unknown identifier in a 
known namespace): one read of six nodes returned `Good, Good, 
Bad_NodeIdUnknown, Good, Bad_NodeIdUnknown, Bad_NodeIdUnknown` in the requested 
order, and a browse returned the references of `Objects` next to 
`Bad_NodeIdUnknown`. Both connections kept their `OpcUaClient`, and a consumer 
on the client connection kept receiving its counter. On top of #27956, the two 
method calls also returned what the server returns for an unknown method of an 
object (`Bad_MethodInvalid`) and for an unknown object (`Bad_NodeIdUnknown`).
   
   This PR does not change the four request guards in `SubscriptionManager` 
that #27956 (CAMEL-25694, @gnodet) changes, and the two merge without conflicts 
in either order. The call guard on main drops every method call result, so for 
now a method call with such a node only keeps the connection, and its answer 
reaches the exchange with #27956. The docs and the test therefore leave the 
call answer out; I can add it to both once #27956 is merged. On top of #27956, 
such call assertions and the tests of #27956 already pass.
   
   The unchecked lookup dates back to the first client implementation and came 
back with the component in 84b7e90419c; this change does not revert any later 
work.
   
   Checked with `-Psourcecheck clean verify` on the module (79 tests), four 
more runs of the new and the affected test classes with 
`-Dsurefire.rerunFailingTestsCount=0`, one of them with 
`-XX:ActiveProcessorCount=1`, and the new test together with the tests of 
#27956 on top of it.
   
   @davsclaus @oscerd could you take a look?
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [x] If this is a large change, bug fix, or code improvement, I checked 
there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL) filed for 
the change (usually before you start working on it).
   
   # Apache Camel coding standards and style
   
   - [x] I checked that each commit in the pull request has a meaningful 
subject line and body.
   
   - [ ] I have run `mvn clean install -DskipTests` locally from root folder 
and I have committed all auto-generated changes.
   
   # AI-assisted contributions
   
   - [x] If this PR includes AI-generated code, commits have proper 
co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR 
description identifies the AI tool used.
   
   _Claude Code on behalf of etzel-mes_
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to