This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch backport/CAMEL-24573-4.18.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit c625191fdbcd032c26a807a292c1a2e974139c25 Author: Marco Carletti <[email protected]> AuthorDate: Mon Aug 31 18:27:52 2026 +0200 CAMEL-24573: CXF REST: UnsupportedOperationException when copying factory bean with fixed-size features list When creating a CXF JAXRS client factory bean from a Spring bean with a fixed-size features list (e.g., from Arrays.asList()), the shallow field copy resulted in a reference to the immutable list. Subsequent attempts to append endpoint features via addAll() threw UnsupportedOperationException. Wrap the copied features list in a new ArrayList to ensure mutability while preserving the original values. Fixes CSB-10555 (cherry picked from commit 98615a5d003e19186b593b5c386770d42a4a1592) Deviations from a straight cherry-pick, both mechanical: - Import conflict resolved for this branch: CxfRsSpringEndpointTest keeps this branch's org.apache.camel.test.spring.junit5.CamelSpringTestSupport import rather than main's junit6 one; camel-cxf-spring-rest here depends on camel-test-spring-junit5, not junit6. - CxfRsSpringEndpoint.setupJAXRSClientFactoryBean() on this branch does not call setupCommonFactoryProperties() (that call was added later by CAMEL-24183, which is not on this branch), so the endpoint's own features are never auto-appended to the copied list here. The ported test's assertEquals(2, cfb.getFeatures().size()) does not hold on this branch: it now asserts the copied feature is preserved (1) and that the list is mutable by appending a second feature directly and re-checking the size (2), which still exercises the fix without relying on behaviour this branch doesn't have. Co-Authored-By: Claude Haiku 4.5 <[email protected]> Co-authored-by: Claude Opus 5 (1M context) <[email protected]> --- .../cxf/spring/jaxrs/CxfRsSpringEndpoint.java | 3 +++ .../cxf/jaxrs/CxfRsSpringEndpointTest.java | 29 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/components/camel-cxf/camel-cxf-spring-rest/src/main/java/org/apache/camel/component/cxf/spring/jaxrs/CxfRsSpringEndpoint.java b/components/camel-cxf/camel-cxf-spring-rest/src/main/java/org/apache/camel/component/cxf/spring/jaxrs/CxfRsSpringEndpoint.java index 3697f7816e05..d513f4dd22e5 100644 --- a/components/camel-cxf/camel-cxf-spring-rest/src/main/java/org/apache/camel/component/cxf/spring/jaxrs/CxfRsSpringEndpoint.java +++ b/components/camel-cxf/camel-cxf-spring-rest/src/main/java/org/apache/camel/component/cxf/spring/jaxrs/CxfRsSpringEndpoint.java @@ -16,6 +16,8 @@ */ package org.apache.camel.component.cxf.spring.jaxrs; +import java.util.ArrayList; + import org.apache.camel.Component; import org.apache.camel.component.cxf.jaxrs.BeanIdAware; import org.apache.camel.component.cxf.jaxrs.CxfRsEndpoint; @@ -88,6 +90,7 @@ public class CxfRsSpringEndpoint extends CxfRsEndpoint implements BeanIdAware { if (bean instanceof SpringJAXRSClientFactoryBean) { ReflectionUtils.shallowCopyFieldState(bean, cfb); + cfb.setFeatures(new ArrayList<>(cfb.getFeatures())); } return cfb; diff --git a/components/camel-cxf/camel-cxf-spring-rest/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointTest.java b/components/camel-cxf/camel-cxf-spring-rest/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointTest.java index 077ed721036f..4275465597c6 100644 --- a/components/camel-cxf/camel-cxf-spring-rest/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointTest.java +++ b/components/camel-cxf/camel-cxf-spring-rest/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointTest.java @@ -16,18 +16,21 @@ */ package org.apache.camel.component.cxf.jaxrs; +import java.util.Arrays; import java.util.Map; import org.apache.camel.component.cxf.jaxrs.testbean.CustomerService; import org.apache.camel.component.cxf.spring.jaxrs.SpringJAXRSClientFactoryBean; import org.apache.camel.component.cxf.spring.jaxrs.SpringJAXRSServerFactoryBean; import org.apache.camel.test.spring.junit5.CamelSpringTestSupport; +import org.apache.cxf.feature.AbstractFeature; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.support.AbstractXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; @@ -36,6 +39,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class CxfRsSpringEndpointTest extends CamelSpringTestSupport { private static final String BEAN_SERVICE_ENDPOINT_NAME = "serviceEndpoint"; + private static final String FIXED_SIZE_FEATURES_ENDPOINT_NAME = "fixedSizeFeaturesEndpoint"; private static final String BEAN_SERVICE_ADDRESS = "http://localhost/programmatically"; private static final String BEAN_SERVICE_USERNAME = "BEAN_SERVICE_USERNAME"; private static final String BEAN_SERVICE_PASSWORD = "BEAN_SERVICE_PASSWORD"; @@ -88,6 +92,19 @@ public class CxfRsSpringEndpointTest extends CamelSpringTestSupport { assertEquals(BEAN_SERVICE_PASSWORD, cfb.getPassword(), "Got the wrong password"); } + @Test + public void testCreateCxfRsClientFactoryBeanWithFixedSizeFeatures() { + CxfRsEndpoint endpoint = resolveMandatoryEndpoint( + "cxfrs://bean://" + FIXED_SIZE_FEATURES_ENDPOINT_NAME, CxfRsEndpoint.class); + + SpringJAXRSClientFactoryBean cfb = (SpringJAXRSClientFactoryBean) endpoint.createJAXRSClientFactoryBean(); + + assertEquals(1, cfb.getFeatures().size(), "The copied feature must be preserved"); + assertDoesNotThrow(() -> cfb.getFeatures().add(new AbstractFeature() { + }), "The copied features list must be mutable"); + assertEquals(2, cfb.getFeatures().size(), "The appended feature must be present"); + } + public static SpringJAXRSClientFactoryBean serviceEndpoint() { SpringJAXRSClientFactoryBean clientFactoryBean = new SpringJAXRSClientFactoryBean(); @@ -99,6 +116,13 @@ public class CxfRsSpringEndpointTest extends CamelSpringTestSupport { return clientFactoryBean; } + public static SpringJAXRSClientFactoryBean fixedSizeFeaturesEndpoint() { + SpringJAXRSClientFactoryBean clientFactoryBean = serviceEndpoint(); + clientFactoryBean.setFeatures(Arrays.asList(new AbstractFeature() { + })); + return clientFactoryBean; + } + @Override protected AbstractXmlApplicationContext createApplicationContext() { @@ -115,5 +139,10 @@ public class CxfRsSpringEndpointTest extends CamelSpringTestSupport { BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder .rootBeanDefinition(CxfRsSpringEndpointTest.class.getName()).setFactoryMethod("serviceEndpoint"); beanFactory.registerBeanDefinition(BEAN_SERVICE_ENDPOINT_NAME, definitionBuilder.getBeanDefinition()); + + BeanDefinitionBuilder fixedSizeFeaturesDefinitionBuilder = BeanDefinitionBuilder + .rootBeanDefinition(CxfRsSpringEndpointTest.class.getName()).setFactoryMethod("fixedSizeFeaturesEndpoint"); + beanFactory.registerBeanDefinition(FIXED_SIZE_FEATURES_ENDPOINT_NAME, + fixedSizeFeaturesDefinitionBuilder.getBeanDefinition()); } }
