Repository navigation
CXF-9257: Complete client futures if the async callback throws - #3565
Conversation
JaxrsClientCallback and JaxwsClientCallback invoke the user's callback before completing their future. If the callback throws, the exception escapes and the future is never completed, so threads blocked in Future.get() hang forever. In the JAX-WS case ClientImpl additionally catches the exception and invokes the same callback a second time. Guard every callback invocation: - If the callback throws while handling a successful response, the future completes exceptionally with that exception, like the JAX-WS reference implementation does. - If the callback throws while handling a failure, cancellation or interruption, the original exception is kept and the callback's exception is attached as a suppressed exception. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0149ipFQyLFL9z5WdmhCRg3r
|
Thanks for the change @markusheiden , the way CXF manages callback: There are some gaps in |
CXF callers invoke ClientCallback.handleException() if handleResponse() throws, so the callbacks don't need to guard the success path. Revert those guards and fix the call sites that don't follow the contract: ClientImpl's fault observer wrapper and WebClient's ClientAsyncResponseInterceptor. The failure paths stay guarded in the callbacks, because callers can't complete the future if handleException() throws. Don't add a callback exception as suppressed to itself, which would throw IllegalArgumentException if the callback rethrows the exception it was given. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ka6VjhpAcDrSERoiLP2Cxx
|
Thanks for the fast review @reta. I've reverted the guards in I added a table comparing the old and the new approach to https://issues.apache.org/jira/browse/CXF-9257 . |
* Complete client futures if the async callback throws JaxrsClientCallback and JaxwsClientCallback invoke the user's callback before completing their future. If the callback throws, the exception escapes and the future is never completed, so threads blocked in Future.get() hang forever. In the JAX-WS case ClientImpl additionally catches the exception and invokes the same callback a second time. Guard every callback invocation: - If the callback throws while handling a successful response, the future completes exceptionally with that exception, like the JAX-WS reference implementation does. - If the callback throws while handling a failure, cancellation or interruption, the original exception is kept and the callback's exception is attached as a suppressed exception. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0149ipFQyLFL9z5WdmhCRg3r * Follow the callback contract instead of guarding handleResponse CXF callers invoke ClientCallback.handleException() if handleResponse() throws, so the callbacks don't need to guard the success path. Revert those guards and fix the call sites that don't follow the contract: ClientImpl's fault observer wrapper and WebClient's ClientAsyncResponseInterceptor. The failure paths stay guarded in the callbacks, because callers can't complete the future if handleException() throws. Don't add a callback exception as suppressed to itself, which would throw IllegalArgumentException if the callback rethrows the exception it was given. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ka6VjhpAcDrSERoiLP2Cxx --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit cca9715)
If the callback of an asynchronous client invocation throws, the future returned by the invocation may never complete, so threads blocked in
Future.get()hang forever.CXF callers are expected to invoke
ClientCallback.handleException()ifhandleResponse()throws. This PR:WebClient'sClientAsyncResponseInterceptorand the fault observer wrapper inClientImpl;JaxrsClientCallbackandJaxwsClientCallback(handleException(),cancel(), the interruption path ofJaxrsResponseFuture.get()). Callers can't complete the future ifhandleException()itself throws, because the future is owned by the callback. If the user's callback throws there, the original exception is kept and the callback's exception is attached as suppressed (unless it is the same instance).Unit tests for both callbacks and a
WebClientsystem test inJAXRSAsyncClientTestare included. Details in https://issues.apache.org/jira/browse/CXF-9257.🤖 Generated with Claude Code
https://claude.ai/code/session_01Ka6VjhpAcDrSERoiLP2Cxx