This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch fix/CAMEL-23909
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 4cc7369f62e3fcf13f70ceb5442923ef60c878b4
Author: Guillaume Nodet <[email protected]>
AuthorDate: Sat Jul 4 17:47:46 2026 +0000

    CAMEL-23909: Fix flaky CxfConsumerPayLoadFaultMessageTest
    
    Enhance CxfConsumer.extractFromBody() to detect CxfPayload bodies
    containing raw SOAP Fault XML elements and convert them to proper
    SoapFault exceptions. Previously, only bodies that were direct
    Throwable instances were recognized as faults. When a route set a
    CxfPayload containing a <soap:Fault> element directly on the message
    body (as in CxfConsumerPayLoadFaultMessageTest), the fault was not
    detected and went through CXF's normal response serialization path
    instead of the fault handling path, leading to intermittent failures.
    
    The fix adds extractFaultFromPayload() which checks for a single
    Element body with localName "Fault" in SOAP 1.1 or 1.2 namespace,
    extracts faultcode/faultstring/detail, and constructs a SoapFault
    that CXF can process through its standard fault handling pipeline.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../camel/component/cxf/jaxws/CxfConsumer.java     | 82 ++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git 
a/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/CxfConsumer.java
 
b/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/CxfConsumer.java
index 73c3d199da52..923a5553ce73 100644
--- 
a/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/CxfConsumer.java
+++ 
b/components/camel-cxf/camel-cxf-soap/src/main/java/org/apache/camel/component/cxf/jaxws/CxfConsumer.java
@@ -18,11 +18,16 @@ package org.apache.camel.component.cxf.jaxws;
 
 import java.lang.reflect.Method;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import jakarta.xml.ws.WebFault;
 
+import javax.xml.namespace.QName;
+
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 import org.apache.camel.AsyncCallback;
 import org.apache.camel.ExchangePattern;
@@ -30,11 +35,13 @@ import org.apache.camel.ExchangeTimedOutException;
 import org.apache.camel.Processor;
 import org.apache.camel.Suspendable;
 import org.apache.camel.component.cxf.common.CxfBinding;
+import org.apache.camel.component.cxf.common.CxfPayload;
 import org.apache.camel.component.cxf.common.DataFormat;
 import org.apache.camel.component.cxf.common.UnitOfWorkCloserInterceptor;
 import org.apache.camel.component.cxf.common.message.CxfConstants;
 import org.apache.camel.support.DefaultConsumer;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.cxf.binding.soap.SoapFault;
 import org.apache.cxf.continuations.Continuation;
 import org.apache.cxf.continuations.ContinuationProvider;
 import org.apache.cxf.endpoint.Server;
@@ -402,9 +409,84 @@ public class CxfConsumer extends DefaultConsumer 
implements Suspendable {
             Object body = camelExchange.getMessage().getBody();
             if (body instanceof Throwable throwable) {
                 t = throwable;
+            } else if (body instanceof CxfPayload<?> payload) {
+                // Check if the CxfPayload contains a SOAP Fault element set 
directly as body
+                t = extractFaultFromPayload(payload);
             }
             return t;
         }
 
+        /**
+         * Detects a SOAP Fault element inside a CxfPayload body and converts 
it to a proper SoapFault. This handles the
+         * case where a route sets a CxfPayload containing a raw {@code 
<soap:Fault>} XML element directly on the
+         * message body, ensuring it is routed through CXF's standard fault 
handling path rather than the normal
+         * response path.
+         */
+        private static SoapFault extractFaultFromPayload(CxfPayload<?> 
payload) {
+            List<Element> elements = payload.getBody();
+            if (elements == null || elements.size() != 1) {
+                return null;
+            }
+            Element element = elements.get(0);
+            if (!"Fault".equals(element.getLocalName())) {
+                return null;
+            }
+            String nsUri = element.getNamespaceURI();
+            if (!"http://schemas.xmlsoap.org/soap/envelope/".equals(nsUri)
+                    && 
!"http://www.w3.org/2003/05/soap-envelope".equals(nsUri)) {
+                return null;
+            }
+
+            // Extract faultcode, faultstring, and detail from the Fault 
element
+            String faultString = null;
+            QName faultCode = null;
+            Element detail = null;
+
+            NodeList children = element.getChildNodes();
+            for (int i = 0; i < children.getLength(); i++) {
+                Node child = children.item(i);
+                if (child.getNodeType() != Node.ELEMENT_NODE) {
+                    continue;
+                }
+                Element childElem = (Element) child;
+                String localName = childElem.getLocalName();
+                if ("faultcode".equals(localName) || "Code".equals(localName)) 
{
+                    faultCode = parseFaultCode(element, childElem);
+                } else if ("faultstring".equals(localName) || 
"Reason".equals(localName)) {
+                    faultString = childElem.getTextContent();
+                } else if ("detail".equals(localName) || 
"Detail".equals(localName)) {
+                    detail = childElem;
+                }
+            }
+
+            if (faultCode == null) {
+                faultCode = new QName(nsUri, "Server");
+            }
+            SoapFault fault = new SoapFault(faultString != null ? faultString 
: "", faultCode);
+            if (detail != null) {
+                fault.setDetail(detail);
+            }
+            return fault;
+        }
+
+        private static QName parseFaultCode(Element faultElement, Element 
codeElement) {
+            String codeText = codeElement.getTextContent();
+            if (codeText == null || codeText.isBlank()) {
+                return null;
+            }
+            codeText = codeText.trim();
+            int colonIndex = codeText.indexOf(':');
+            if (colonIndex > 0) {
+                String prefix = codeText.substring(0, colonIndex);
+                String localPart = codeText.substring(colonIndex + 1);
+                String namespaceURI = faultElement.lookupNamespaceURI(prefix);
+                if (namespaceURI != null) {
+                    return new QName(namespaceURI, localPart, prefix);
+                }
+                return new QName("", localPart, prefix);
+            }
+            return new QName("", codeText);
+        }
+
     }
 }

Reply via email to