Croway commented on PR #29450:
URL: https://github.com/apache/camel/pull/29450#issuecomment-6765673404

   Thanks for hardening this. Before writing this comment I ran reproducers 
against the PR head (f928913) and the merge-base (52680d1).
   
   **1. The CAMEL-26696 hang does not come from `CxfClientCallback`. It comes 
from CXF 4.2.4, so this PR does not fix it.**
   
   I stress-tested the async wrong-server path: a fresh endpoint per call, the 
same `wrongServerConfigurer`, 16 threads, and the POJO, PAYLOAD and RAW data 
formats. With CXF 4.2.4 a few calls never complete, on the PR head and on the 
merge-base alike. I added instrumentation, and every stuck call looks the same:
   
   ```
   HANG, done never called; outMessage Exception 
content=java.util.concurrent.CompletionException: java.net.ConnectException
    | inMessage=null | ClientCallback still on cxf exchange (never 
dispatched)=true
   ```
   
   CXF never calls `handleResponse` or `handleException` for those exchanges. 
The `ClientCallback` is still on the CXF exchange. A counting `CxfBinding` saw 
one `populateExchangeFromCxfResponse` call per completed exchange, none for the 
stuck ones, and no throws at all. No change in `CxfClientCallback` can complete 
them.
   
   I used the same harness and the same PR head, changing only `-Dcxf-version`:
   
   | CXF | async wrong-server calls (16 threads) | stuck (> 8 s) |
   |---|---|---|
   | 4.2.3 | 4800 | 0 |
   | 4.2.4 | 4800 | 6 (POJO 3, PAYLOAD 1, RAW 2) |
   
   Sequential runs (3300 calls) never got stuck locally, so it needs some load, 
as on CI.
   
   The likely cause is CXF-9250, which shipped in 4.2.4 (Camel bumped to it on 
2026-10-09). Its `future.exceptionally(...)` in `HttpClientHTTPConduit` now 
calls `outMessage.setContent(Exception.class, ex)`. When that callback runs 
before the caller thread's `close()`, 
`WrappedOutputStream.handleResponseOnWorkqueue` sees `exceptionSet == true` and 
schedules nothing, and nothing calls the fault observer. The raw 
`CompletionException` on the stuck exchanges matches that path: completed calls 
get a `Fault` caused by `ConnectException`.
   
   What this means for this PR:
   - `testInvokingAWrongServerAsync` brings back the CAMEL-26600 flake, now in 
three classes, because `CxfProducerOperationTest` and `CxfProducerContextTest` 
inherit it. It passes with or without the fix.
   - The PR removes the comment above `getWrongEndpointUri()` that explains 
`synchronous=true` but keeps the workaround. Please keep the comment.
   - I don't think CAMEL-26696 should be resolved by this PR. A plain-CXF 
reproducer (a JAX-WS client calling `client.invoke(ClientCallback, ...)`, no 
Camel) gives the same result: 22 stuck calls out of 4800 on 4.2.4, 0 on 4.2.3, 
and 42 on today's 4.2.5-SNAPSHOT. So CXF main still has it. I reported it to 
CXF as [CXF-9272](https://issues.apache.org/jira/browse/CXF-9272).
   
   **2. A `RuntimeException` from the cookie store now completes the exchange 
twice, both times as a success.**
   
   This is point 1 of the description. I used an async endpoint against a live 
`HelloService`, with a `cookieHandler` whose `storeCookies` throws 
`IllegalStateException`:
   
   ```
   merge-base: completedWithin10s=false doneCalls=0        (hangs)
   PR head:    doneCalls=2
               done#1(sync=false) exception=null body=[hello]
               done#2(sync=false) exception=null body=[hello]
               final exception=null
   ```
   
   In a `direct -> cxf -> mock` route, the caller gets `exception=null` and 
`body=[hello]`: the request comes back as the reply, and the cookie failure is 
neither logged nor set on the exchange. This happens in two steps:
   - In `handleResponse`, `storeCookies` throws before 
`populateExchangeFromCxfResponse` runs. The `finally` calls `done(false)` with 
no exception on the exchange, and then the exception escapes.
   - CXF `ClientImpl.onMessage` catches it and calls `handleException(resCtx, 
ex)` on the same callback. Since `ClientImpl` always sets a `ConduitSelector`, 
`ex` is overwritten with `outMessage.getContent(Exception.class)` and then with 
the in-message lookup, and both are null. Nothing gets set, the cookie store 
throws again, and `done(false)` runs a second time.
   
   Suggestions:
   - Catch `RuntimeException` around `storeCookies` the same way as 
`IOException`, or set it on the exchange.
   - In `handleException`, keep the original `ex` when both content lookups 
return null.
   - Make sure `done` runs only once, for example with an `AtomicBoolean`.
   
   **3. The new unit test does not guard the behaviour it claims to.**
   
   - I removed both `camelExchange.setException(ex)` lines from 
`handleException`, and all 7 tests in `CxfClientCallbackTest` still pass. The 
"preserves root cause" tests stub `camelExchange.getException()` to return the 
transport exception. A real `DefaultExchange`, or 
`verify(camelExchange).setException(transportEx)`, would catch this.
   - `cxfExchange.get(ConduitSelector.class)` is never stubbed, so the tests 
only cover the `else` branch. With `ClientImpl` the selector is always set.
   - The cookie path from point 1 is not tested, because `getCookieHandler()` 
always returns null.
   
   **4. Minor**
   
   - `cur.addSuppressed(t)` throws `IllegalArgumentException: Self-suppression 
not permitted` when a custom `CxfBinding` rethrows the exception that is 
already on the exchange. I reproduced this: `done` is called once, then the IAE 
escapes instead of the transport exception. Adding `if (cur != t)` avoids it. 
`DefaultCxfBinding` never does this, so it only affects custom bindings.
   - `org.mockito.Mockito.doAnswer(...)` is fully qualified three times; the 
project rules ask for imports.
   - The module's tests use JUnit assertions (52 files, versus this one using 
AssertJ), and the project rules ask new tests to match the module's style.
   
   I can share the reproducer test class if it helps.
   
   _Claude Code on behalf of Croway_
   


-- 
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