gnodet opened a new pull request, #27956:
URL: https://github.com/apache/camel/pull/27956
## Summary
Fix OPC-UA write/call/readValues/browse service failures being silently
swallowed and reported as success when using the awaited
(`CamelMiloAwait=true`) mode.
## Root Cause
`SubscriptionManager.write()`, `call()`, `readValues()`, and `browse()` all
chained the underlying Milo client future with `handleAsync()`:
```java
return this.connected.write(nodeId, value).handleAsync((status, e) -> {
if (e != null) {
handleConnectionFailure(e);
}
return status; // returns null when e != null
}, this.executor);
```
When the OPC-UA operation fails (e.g. `Bad_Timeout` service fault),
`handleAsync` receives a non-null exception. It calls
`handleConnectionFailure(e)` for the reconnection side-effect, then **returns
the value (null)**, converting the exceptional completion into a normal null
completion.
`MiloClientProducer` awaits the returned future and only propagates errors
when the throwable is non-null:
```java
future.whenComplete((result, throwable) -> {
if (throwable != null) exchange.setException(throwable);
else msg.setBody(result); // sets null body → reports success
});
```
Since `handleAsync` had already discarded the exception, the producer saw a
null-completed future, set the body to `null`, and reported success. No error
was raised to the Camel route.
## Fix
Replace `handleAsync()` with `whenCompleteAsync()` in all four methods.
`whenCompleteAsync` runs the side-effect (reconnection) but propagates the
original completion unchanged — exceptions stay exceptional, successes stay
successful:
```java
return this.connected.write(nodeId, value).whenCompleteAsync((status, e) -> {
// handle outside the lock, running using
// whenCompleteAsync (preserves the original exceptional completion)
if (e != null) {
handleConnectionFailure(e);
}
}, this.executor);
```
## Tests
Two new test classes:
- **`MiloClientProducerWriteFailureTest`**: tests that `MiloClientProducer`
correctly propagates awaited write/call failures as exchange exceptions, and
that non-awaited (fire-and-forget) writes do not set exceptions. Uses a
`MiloClientConnection` stub returning pre-arranged futures.
- **`SubscriptionManagerExceptionPropagationTest`**: tests the
`whenCompleteAsync`-vs-`handleAsync` future completion contract directly (no
server needed), and verifies that disconnected managers return exceptional
futures from `write()`, `call()`, and `readValues()`.
Fixes https://issues.apache.org/jira/browse/CAMEL-25694
_Hermes Agent (Claude Sonnet 4.6) on behalf of Guillaume Nodet_
--
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]