Skip to content

CAMEL-24477: camel-cxf - add a muteException consumer option - #25742

Merged
Croway merged 2 commits into
apache:mainfrom
oscerd:fix/CAMEL-24477
Aug 28, 2026
Merged

CAMEL-24477: camel-cxf - add a muteException consumer option#25742
Croway merged 2 commits into
apache:mainfrom
oscerd:fix/CAMEL-24477

Conversation

@oscerd

@oscerd oscerd commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Fixes CAMEL-24477.

The CXF consumer describes route failures in the SOAP fault

CxfConsumer.checkFailure() takes whatever is on Exchange.getException() and builds a CXF Fault from
it, so the exception's message becomes the SOAP faultstring — and its class name does, when it has no
message:

private static void buildFaultFromThrowable(Throwable t) {
    Fault fault = new Fault(t);
    if (fault.getMessage() == null) {
        fault.setMessage(t.getClass().getSimpleName());
    }

Measured against the unfixed code, a client of a failing cxf: route receives:

jakarta.xml.ws.soap.SOAPFaultException: the-internal-detail-a-caller-must-not-see

CAMEL-23651 aligned the HTTP consumers on a
muteException option defaulting to true. camel-cxf has no equivalent.

The change, and why it is narrower than the other consumers

A muteException consumer option on CxfEndpoint, defaulting to true. Unlike knative/mina, it cannot
apply to every fault
— for a SOAP service, some faults are the contract. Muting those would break every
client written against the WSDL rather than protect anything.

So muting applies only to an undeclared failure: something on Exchange.getException() whose class
carries no @WebFault annotation. These are all still returned in full:

Deliberate fault Why it is preserved
a CXF Fault / SoapFault thrown by the route already a fault object the route built
an exception annotated @WebFault declared in the WSDL; clients are generated against it
a Throwable or <soap:Fault> CxfPayload set as the message body the documented "SOAP faults as message body" path, kept for backwards compatibility

checkFailure now separates the exception path from the body path to make that distinction, which also
let extractThrowable go — it existed only to merge the two.

The muted stand-in has its stack trace cleared, since CXF can be configured to put the stack trace in the
fault detail.

Behaviour change worth a reviewer's eye

Muting covers framework failures too, not just route exceptions. A continuationTimeout expiry
previously returned The OUT message was not received within: 5000 millis. and now returns the generic
fault. CxfConsumerContinuationTimeoutTest asserts that message, so it now sets muteException=false
the same migration a deployment relying on that diagnostic performs. The upgrade guide calls this out
specifically.

I kept it muted for consistency with the rest of the family, but this is the one place where the default
costs real diagnostic signal — happy to special-case timeouts if the project would rather.

Testing

CxfConsumerMuteExceptionTest covers all four behaviours:

  • an undeclared failure is not described to the caller — verified RED against unfixed code, with
    SOAPFaultException: the-internal-detail-a-caller-must-not-see as the actual value;
  • muteException=false describes it as before;
  • a declared @WebFault (PingMeFault) is still returned in full while muting is on — the carve-out
    that makes the default safe;
  • the option defaults to true.

Cross-checks that the carve-out is correctly scoped rather than inert: CxfCustomizedExceptionTest,
CxfConsumerFaultTest, CxfConsumerPayloadFaultTest and JaxWsWebFaultAnnotationToFaultTest all pass
unchanged — they throw SoapFault/@WebFault, which take the preserved branches.

camel-cxf-soap 166/166 (2 pre-existing skips) and a full mvn clean install -DskipTests across the reactor.
camel-cxf-soap did not declare assertj-core; added with test scope.

Scope

Third of four consumers split out of CAMEL-24428 (rescoped to camel-knative,
#25685). Siblings:
CAMEL-24476 (mina, built) and
CAMEL-24478 (grpc, remaining).

Claude Code on behalf of oscerd

@oscerd
oscerd requested review from davsclaus and gnodet August 26, 2026 05:05
@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against the project's rules/conventions and verified behaviorally (not just diff-read): built and ran the full affected test suite (CxfConsumerMuteExceptionTest, CxfConsumerContinuationTimeoutTest, CxfConsumerFaultTest, CxfConsumerPayloadFaultTest, CxfCustomizedExceptionTest, JaxWsWebFaultAnnotationToFaultTest) in an isolated worktree against the PR branch — all 13 pass. Also traced the checkFailure/isMuted control flow to confirm the continuationTimeout path genuinely routes through muting via ExchangeTimedOutException (not a @WebFault), matching the PR description.

Confirmed, no issues:

  • The checkFailure refactor (splitting the old extractThrowable into a direct getException() check + extractFromBody) is behavior-preserving for all three "always returned in full" paths: CXF Fault/SoapFault, @WebFault-annotated exceptions, and a Throwable/CxfPayload fault set as the body.
  • @UriParam(label = "consumer", defaultValue = "true") with the description on the setter's Javadoc matches this file's existing convention (skipFaultLogging, mtomEnabled) and mirrors the camel-http-common muteException precedent from CAMEL-23651. No security = "insecure:*" needed since this is secure-by-default, not an opt-in risk flag.
  • Test coverage is solid: covers muted/unmuted/declared-fault/default-value, and CxfConsumerContinuationTimeoutTest was correctly narrowed to only the affected route rather than papered over.
  • All generated artifacts (catalog JSON, CxfEndpointConfigurer, CxfEndpointUriFactory, CxfEndpointBuilderFactory) are regenerated and included.
  • Upgrade guide entry on main correctly flags the continuation-timeout diagnostic regression as a behavior change worth a reviewer's eye.
  • JIRA CAMEL-24477 is correctly typed (Improvement), assigned to the author, In Progress, fixVersion 4.23.0.
  • Scope is tight to camel-cxf-soap; the added assertj-core test dependency is justified.

No blocking issues. One non-blocking note left inline.

This review does not replace specialized tools like CodeRabbit/Sourcery or static analysis (SonarCloud) — it's a rules/conventions + behavioral-verification pass.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

*/
private boolean isMuted(Throwable t) {
return ((CxfEndpoint) getEndpoint()).isMuteException()
&& t.getClass().getAnnotation(WebFault.class) == null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, non-blocking: getAnnotation(WebFault.class) only sees the annotation when declared directly on the exception's own class — @WebFault isn't meta-annotated @Inherited in the JAX-WS spec. If a generated @WebFault exception were ever subclassed without repeating the annotation, muting would incorrectly apply to it. In practice CXF-generated fault classes are essentially never subclassed, so this is a theoretical edge case rather than a real risk — just flagging it for awareness, not requesting a change.

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • catalog/camel-catalog
  • components/camel-cxf/camel-cxf-soap
  • docs
  • dsl/camel-endpointdsl

🔬 Scalpel shadow comparison — Scalpel: 14 tested, 25 compile-only — current: 11 all tested

Maveniverse Scalpel detected 39 affected modules (current approach: 11).

⚠️ Modules only in Scalpel (28)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

Skip-tests mode would test 14 modules (4 direct + 10 downstream), skip tests for 25 (generated code, meta-modules)

Modules Scalpel would test (14)
  • camel-catalog
  • camel-cxf-soap
  • camel-cxf-spring-soap
  • camel-endpointdsl
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-launcher-container
  • camel-soap
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
  • docs
Modules with tests skipped (25)
  • apache-camel
  • camel-allcomponents
  • camel-catalog-console
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (39 modules)
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: CXF :: SOAP
  • Camel :: CXF :: SOAP :: Spring
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: SOAP
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

CxfConsumer built the SOAP fault from whatever reached Exchange.getException(),
so the exception's message - or its class name when it had none - became the
faultstring returned to the caller. The http consumers gained a muteException
option defaulting to true in CAMEL-23651; camel-cxf had none.

Muting is narrower here than in the other consumers because for a SOAP service
some faults are the contract. Only an undeclared failure is replaced: a CXF
Fault, an exception annotated @webfault, and a Throwable or <soap:Fault>
CxfPayload set as the message body are all still returned in full. checkFailure
now separates the exception path from the body path to make that distinction,
which retires extractThrowable.

The stand-in has its stack trace cleared, since CXF can be configured to put
the stack trace in the fault detail.

Note this also covers framework failures: a continuationTimeout expiry no
longer reports its timeout to the caller, so
CxfConsumerContinuationTimeoutTest sets muteException=false.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Croway
Croway merged commit fd4fd27 into apache:main Aug 28, 2026
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants