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

gnodet pushed a commit to branch cxf-7455-tolerate-missing-parts
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit f89ac5374294144065eff8587263bf359c1a8c09
Author: Guillaume Nodet <[email protected]>
AuthorDate: Thu Mar 12 13:38:45 2026 +0100

    [CXF-7455] Tolerate missing message parts in SOAP responses
    
    HolderInInterceptor threw IndexOutOfBoundsException when a SOAP
    response was missing output parts defined in the WSDL. This can
    happen with non-compliant servers (e.g. Windows WinRM). The fix
    adds a bounds check before accessing the MessageContentsList,
    leaving the holder value unchanged when the response part is absent.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../jaxws/interceptors/HolderInInterceptor.java    |   8 +-
 .../interceptors/HolderInInterceptorTest.java      | 113 +++++++++++++++++++++
 2 files changed, 119 insertions(+), 2 deletions(-)

diff --git 
a/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/interceptors/HolderInInterceptor.java
 
b/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/interceptors/HolderInInterceptor.java
index 348f4f5d35..3365818c83 100644
--- 
a/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/interceptors/HolderInInterceptor.java
+++ 
b/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/interceptors/HolderInInterceptor.java
@@ -63,9 +63,13 @@ public class HolderInInterceptor extends 
AbstractPhaseInterceptor<Message> {
             for (MessagePartInfo part : parts) {
                 if (part.getIndex() != 0 && part.getTypeClass() != null) {
                     @SuppressWarnings("unchecked")
-                    Holder<Object> holder = 
(Holder<Object>)outHolders.get(part.getIndex() - 1);
+                    Holder<Object> holder = (Holder<Object>)outHolders
+                        .get(part.getIndex() - 1);
                     if (holder != null) {
-                        holder.value = inObjects.get(part);
+                        if (inObjects != null
+                            && part.getIndex() < inObjects.size()) {
+                            holder.value = inObjects.get(part);
+                        }
                         inObjects.put(part, holder);
                     }
                 }
diff --git 
a/rt/frontend/jaxws/src/test/java/org/apache/cxf/jaxws/interceptors/HolderInInterceptorTest.java
 
b/rt/frontend/jaxws/src/test/java/org/apache/cxf/jaxws/interceptors/HolderInInterceptorTest.java
new file mode 100644
index 0000000000..9b4d3946b1
--- /dev/null
+++ 
b/rt/frontend/jaxws/src/test/java/org/apache/cxf/jaxws/interceptors/HolderInInterceptorTest.java
@@ -0,0 +1,113 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.jaxws.interceptors;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import jakarta.xml.ws.Holder;
+
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.ExchangeImpl;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageContentsList;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.service.model.BindingOperationInfo;
+import org.apache.cxf.service.model.InterfaceInfo;
+import org.apache.cxf.service.model.MessageInfo;
+import org.apache.cxf.service.model.MessagePartInfo;
+import org.apache.cxf.service.model.OperationInfo;
+import org.apache.cxf.service.model.ServiceInfo;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * CXF-7455: HolderInInterceptor should tolerate missing
+ * message parts in SOAP responses from non-compliant servers.
+ */
+public class HolderInInterceptorTest {
+
+    private static final String NS = "http://test";;
+
+    @Test
+    public void testMissingOutputPartDoesNotThrow() {
+        ServiceInfo si = new ServiceInfo();
+        InterfaceInfo ii = new InterfaceInfo(si,
+            new QName(NS, "testInterface"));
+        OperationInfo op = ii.addOperation(
+            new QName(NS, "testOp"));
+
+        MessageInfo inMsg = op.createMessage(
+            new QName(NS, "inputMsg"), MessageInfo.Type.INPUT);
+        op.setInput("input", inMsg);
+        MessageInfo outMsg = op.createMessage(
+            new QName(NS, "outputMsg"), MessageInfo.Type.OUTPUT);
+        op.setOutput("output", outMsg);
+
+        MessagePartInfo retPart =
+            outMsg.addMessagePart("return");
+        retPart.setTypeClass(String.class);
+
+        MessagePartInfo holderPart =
+            outMsg.addMessagePart("holderParam");
+        holderPart.setTypeClass(String.class);
+
+        BindingOperationInfo bop =
+            new BindingOperationInfo(null, op);
+
+        // Response with only the return value (index 0).
+        // The holder part at index 1 is missing.
+        MessageContentsList inObjects =
+            new MessageContentsList("returnValue");
+
+        Message inMessage = new MessageImpl();
+        inMessage.setContent(List.class, inObjects);
+        inMessage.put(Message.REQUESTOR_ROLE, Boolean.TRUE);
+
+        List<Holder<?>> holders = new ArrayList<>();
+        holders.add(new Holder<String>("original"));
+
+        Message outMessage = new MessageImpl();
+        outMessage.put(
+            HolderInInterceptor.CLIENT_HOLDERS, holders);
+
+        Exchange exchange = new ExchangeImpl();
+        exchange.setOutMessage(outMessage);
+        exchange.put(BindingOperationInfo.class, bop);
+        inMessage.setExchange(exchange);
+
+        // Before fix: IndexOutOfBoundsException (CXF-7455)
+        HolderInInterceptor interceptor =
+            new HolderInInterceptor();
+        interceptor.handleMessage(inMessage);
+
+        // Holder retains its original value since the
+        // response part was missing
+        @SuppressWarnings("unchecked")
+        Holder<String> holder =
+            (Holder<String>) holders.get(0);
+        assertNotNull(holder);
+        assertEquals("original", holder.value);
+    }
+}

Reply via email to