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]
