You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ja...@apache.org on 2022/10/31 19:00:38 UTC

[camel-quarkus] 07/19: [closes #4203] Add tests for CXF SOAP MTOM

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

jamesnetherton pushed a commit to branch 2.13.x
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git

commit d7ce39e28867f1f100348fd436a9f2d098eae04f
Author: Lukas Lowinger <ll...@redhat.com>
AuthorDate: Thu Oct 20 13:31:42 2022 +0200

    [closes #4203] Add tests for CXF SOAP MTOM
---
 .../cxf/soap/deployment/CxfSoapProcessor.java      |  20 ++-
 .../cxf-soap/cxf-soap-mtom/README.adoc             |   4 +
 .../cxf-soap/cxf-soap-mtom/pom.xml                 | 150 +++++++++++++++++++++
 .../cxf/soap/mtom/it/CxfSoapMtomResource.java      |  88 ++++++++++++
 .../cxf/soap/mtom/it/CxfSoapMtomRoutes.java        | 141 +++++++++++++++++++
 .../component/cxf/soap/mtom/it/IImageService.java  |  33 +++++
 .../component/cxf/soap/mtom/it/ImageService.java   |  63 +++++++++
 .../cxf/soap/mtom/it/MtomAttachmentChecker.java    |  96 +++++++++++++
 .../src/main/resources/application.properties      |  19 +++
 .../component/cxf/soap/mtom/it/CxfSoapMtomIT.java  |  23 ++++
 .../cxf/soap/mtom/it/CxfSoapMtomTest.java          |  83 ++++++++++++
 .../src/test/resources/linux-image.png             | Bin 0 -> 38982 bytes
 integration-test-groups/cxf-soap/pom.xml           |   1 +
 integration-tests/cxf-soap-grouped/pom.xml         |  16 ++-
 14 files changed, 732 insertions(+), 5 deletions(-)

diff --git a/extensions/cxf-soap/deployment/src/main/java/org/apache/camel/quarkus/component/cxf/soap/deployment/CxfSoapProcessor.java b/extensions/cxf-soap/deployment/src/main/java/org/apache/camel/quarkus/component/cxf/soap/deployment/CxfSoapProcessor.java
index 17f76a9365..0e9985491f 100644
--- a/extensions/cxf-soap/deployment/src/main/java/org/apache/camel/quarkus/component/cxf/soap/deployment/CxfSoapProcessor.java
+++ b/extensions/cxf-soap/deployment/src/main/java/org/apache/camel/quarkus/component/cxf/soap/deployment/CxfSoapProcessor.java
@@ -25,6 +25,8 @@ import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
 import io.quarkus.deployment.builditem.SystemPropertyBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
 import org.jboss.jandex.DotName;
 import org.jboss.jandex.IndexView;
 
@@ -43,7 +45,10 @@ class CxfSoapProcessor {
     }
 
     @BuildStep
-    void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClass, CombinedIndexBuildItem combinedIndex) {
+    void registerForReflection(
+            CombinedIndexBuildItem combinedIndex,
+            BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
+            BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods) {
 
         IndexView index = combinedIndex.getIndex();
 
@@ -55,6 +60,19 @@ class CxfSoapProcessor {
                 .map(className -> new ReflectiveClassBuildItem(false, false, className))
                 .forEach(reflectiveClass::produce);
 
+        reflectiveMethods.produce(new ReflectiveMethodBuildItem("org.apache.cxf.frontend.AbstractWSDLBasedEndpointFactory",
+                "getServiceFactory", new String[0]));
+
+    }
+
+    @BuildStep
+    void registerForReflection(
+            BuildProducer<RuntimeInitializedClassBuildItem> runtimeInitializedClasses) {
+
+        // TODO: Move to quarkus-cxf
+        runtimeInitializedClasses
+                .produce(new RuntimeInitializedClassBuildItem("com.sun.xml.messaging.saaj.soap.StringDataContentHandler"));
+
     }
 
     @BuildStep
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/README.adoc b/integration-test-groups/cxf-soap/cxf-soap-mtom/README.adoc
new file mode 100644
index 0000000000..dac25bdb6f
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/README.adoc
@@ -0,0 +1,4 @@
+= Camel Quarkus CXF SOAP MTOM tests
+
+Tests are not using any external SOAP services (eg. via testcontainers). They purely test if Camel Quarkus is able to send and consume messages with attachments with both MTOM enabled/disabled.
+
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/pom.xml b/integration-test-groups/cxf-soap/cxf-soap-mtom/pom.xml
new file mode 100644
index 0000000000..ef3aea6031
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.camel.quarkus</groupId>
+        <artifactId>camel-quarkus-build-parent-it</artifactId>
+        <version>2.13.1-SNAPSHOT</version>
+        <relativePath>../../../poms/build-parent-it/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>camel-quarkus-integration-test-cxf-soap-mtom</artifactId>
+    <name>Camel Quarkus :: Integration Tests :: Cxf Soap Mtom</name>
+    <description>Integration tests for Camel Quarkus CXF extension Mtom</description>
+
+    <dependencies>
+        <dependency><!-- for java.awt.Image -->
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-awt</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-resteasy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-bean</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-direct</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-cxf-soap</artifactId>
+        </dependency>
+
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <properties>
+                <quarkus.package.type>native</quarkus.package.type>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>virtualDependencies</id>
+            <activation>
+                <property>
+                    <name>!noVirtualDependencies</name>
+                </property>
+            </activation>
+            <dependencies>
+                <!-- The following dependencies guarantee that this module is built after them. You can update them by running `mvn process-resources -Pformat -N` from the source tree root directory -->
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-bean</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-cxf-soap-deployment</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-direct-deployment</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomResource.java b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomResource.java
new file mode 100644
index 0000000000..4a4b08ed08
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomResource.java
@@ -0,0 +1,88 @@
+/*
+ * 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.camel.quarkus.component.cxf.soap.mtom.it;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.net.URI;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.imageio.ImageIO;
+import javax.inject.Inject;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.camel.ProducerTemplate;
+
+import static org.apache.camel.component.cxf.common.message.CxfConstants.OPERATION_NAME;
+
+@Path("/cxf-soap/mtom")
+@ApplicationScoped
+public class CxfSoapMtomResource {
+
+    @Inject
+    ProducerTemplate producerTemplate;
+
+    @Path("/upload")
+    @POST
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response upload(@QueryParam("imageName") String imageName, @QueryParam("mtomEnabled") boolean mtomEnabled,
+            byte[] image) throws Exception {
+        try (ByteArrayInputStream bais = new ByteArrayInputStream(image)) {
+            final String response = producerTemplate.requestBodyAndHeader(
+                    "direct:" + mtomEndpoint(mtomEnabled),
+                    new Object[] { ImageIO.read(bais), imageName },
+                    OPERATION_NAME, "uploadImage", String.class);
+            return Response
+                    .created(new URI("https://camel.apache.org/"))
+                    .entity(response)
+                    .build();
+        }
+    }
+
+    @Path("/download")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    public Response download(@QueryParam("imageName") String imageName, @QueryParam("mtomEnabled") boolean mtomEnabled)
+            throws Exception {
+        final BufferedImage response = (BufferedImage) producerTemplate.requestBodyAndHeader(
+                "direct:" + mtomEndpoint(mtomEnabled),
+                imageName,
+                OPERATION_NAME,
+                "downloadImage", Image.class);
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            ImageIO.write(response, "png", baos);
+            byte[] bytes = baos.toByteArray();
+            return Response
+                    .created(new URI("https://camel.apache.org/"))
+                    .entity(bytes)
+                    .build();
+        }
+    }
+
+    private String mtomEndpoint(boolean mtomEnabled) {
+        return mtomEnabled ? "mtomEnabledInvoker" : "mtomDisabledInvoker";
+    }
+
+}
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomRoutes.java b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomRoutes.java
new file mode 100644
index 0000000000..a7f454a035
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomRoutes.java
@@ -0,0 +1,141 @@
+/*
+ * 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.camel.quarkus.component.cxf.soap.mtom.it;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Produces;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.xml.ws.handler.Handler;
+
+import io.quarkus.runtime.LaunchMode;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.cxf.jaxws.CxfEndpoint;
+import org.apache.cxf.ext.logging.LoggingFeature;
+import org.apache.cxf.message.MessageContentsList;
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.ConfigProvider;
+
+@ApplicationScoped
+public class CxfSoapMtomRoutes extends RouteBuilder {
+
+    @Inject
+    @Named("loggingMtomFeatureClient")
+    LoggingFeature loggingFeature;
+
+    @Override
+    public void configure() {
+
+        from("direct:mtomEnabledInvoker")
+                .to("cxf:bean:soapMtomEnabledClientEndpoint?dataFormat=POJO");
+
+        from("direct:mtomDisabledInvoker")
+                .to("cxf:bean:soapMtomDisabledClientEndpoint?dataFormat=POJO");
+
+        from("cxf:bean:soapMtomEnabledServerEndpoint?dataFormat=POJO")
+                .to("direct:processImage");
+
+        from("cxf:bean:soapMtomDisabledServerEndpoint?dataFormat=POJO")
+                .to("direct:processImage");
+
+        from("direct:processImage")
+                .process("imageServiceProcessor")
+                .recipientList((simple("bean:imageService?method=${header.operationName}")));
+
+    }
+
+    @ApplicationScoped
+    @Named("imageServiceProcessor")
+    static class ImageServiceProcessor implements Processor {
+        @Override
+        public void process(Exchange exchange) throws Exception {
+            String operationName = (String) exchange.getIn().getHeaders().get("operationName");
+            MessageContentsList list = (MessageContentsList) exchange.getIn().getBody();
+            if ("uploadImage".equals(operationName)) {
+                exchange.getIn().getHeaders().put("image", list.get(0));
+                exchange.getIn().getHeaders().put("imageName", list.get(1));
+                exchange.getIn().getHeaders()
+                        .put("operationName", "uploadImage(${header.image},${header.imageName})");
+            } else if ("downloadImage".equals(operationName)) {
+                exchange.getIn().setBody(list.get(0));
+            }
+        }
+
+    }
+
+    @Produces
+    @ApplicationScoped
+    @Named("loggingMtomFeatureClient")
+    public LoggingFeature loggingFeature() {
+        final LoggingFeature result = new LoggingFeature();
+        result.setPrettyLogging(true);
+        return result;
+    }
+
+    @Produces
+    @ApplicationScoped
+    @Named
+    CxfEndpoint soapMtomEnabledClientEndpoint() {
+        return commonCxfEndpoint(true, getServerUrl() + "/soapservice/mtom-enabled-image-service");
+    }
+
+    @Produces
+    @ApplicationScoped
+    @Named
+    CxfEndpoint soapMtomDisabledClientEndpoint() {
+        return commonCxfEndpoint(false, getServerUrl() + "/soapservice/mtom-disabled-image-service");
+    }
+
+    @Produces
+    @ApplicationScoped
+    @Named
+    CxfEndpoint soapMtomEnabledServerEndpoint() {
+        return commonCxfEndpoint(true, "/mtom-enabled-image-service");
+    }
+
+    @Produces
+    @ApplicationScoped
+    @Named
+    CxfEndpoint soapMtomDisabledServerEndpoint() {
+        return commonCxfEndpoint(false, "/mtom-disabled-image-service");
+    }
+
+    CxfEndpoint commonCxfEndpoint(boolean mtomEnabled, String address) {
+        final CxfEndpoint result = new CxfEndpoint();
+        result.getFeatures().add(loggingFeature);
+        result.setServiceClass(IImageService.class);
+        result.setMtomEnabled(mtomEnabled);
+        result.setAddress(address);
+        List<Handler> handlers = new ArrayList<>();
+        handlers.add(new MtomAttachmentChecker(mtomEnabled));
+        result.setHandlers(handlers);
+        return result;
+    }
+
+    private static String getServerUrl() {
+        Config config = ConfigProvider.getConfig();
+        final int port = LaunchMode.current().equals(LaunchMode.TEST) ? config.getValue("quarkus.http.test-port", Integer.class)
+                : config.getValue("quarkus.http.port", Integer.class);
+        return String.format("http://localhost:%d", port);
+    }
+
+}
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/IImageService.java b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/IImageService.java
new file mode 100644
index 0000000000..3340c2cfbf
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/IImageService.java
@@ -0,0 +1,33 @@
+/*
+ * 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.camel.quarkus.component.cxf.soap.mtom.it;
+
+import java.awt.*;
+
+import javax.jws.WebMethod;
+import javax.jws.WebService;
+
+@WebService
+public interface IImageService {
+
+    @WebMethod
+    Image downloadImage(String name);
+
+    @WebMethod
+    String uploadImage(Image image, String name);
+
+}
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/ImageService.java b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/ImageService.java
new file mode 100644
index 0000000000..93ec42986d
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/ImageService.java
@@ -0,0 +1,63 @@
+/*
+ * 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.camel.quarkus.component.cxf.soap.mtom.it;
+
+import java.awt.*;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Named;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ApplicationScoped
+@Named("imageService")
+public class ImageService implements IImageService {
+
+    public static final String MSG_SUCCESS = "Upload Successful";
+    private static final Logger log = LoggerFactory.getLogger(ImageService.class);
+
+    private final Map<String, Image> imageRepository;
+
+    public ImageService() {
+        imageRepository = new ConcurrentHashMap<>();
+    }
+
+    @Override
+    public Image downloadImage(String name) {
+        final Image image = imageRepository.get(name);
+        if (image == null) {
+            throw new IllegalStateException("Image with name " + name + " does not exist.");
+        }
+        return image;
+    }
+
+    @Override
+    public String uploadImage(Image image, String name) {
+
+        log.info("Upload image: " + image + " with name: " + name);
+
+        if (image != null && name != null && !"".equals(name)) {
+            imageRepository.put(name, image);
+            return MSG_SUCCESS;
+        }
+        throw new IllegalStateException("Illegal Data Format.");
+    }
+
+}
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/MtomAttachmentChecker.java b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/MtomAttachmentChecker.java
new file mode 100644
index 0000000000..d8eefeafce
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/MtomAttachmentChecker.java
@@ -0,0 +1,96 @@
+/*
+ * 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.camel.quarkus.component.cxf.soap.mtom.it;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+import javax.xml.soap.SOAPBody;
+import javax.xml.soap.SOAPEnvelope;
+import javax.xml.soap.SOAPException;
+import javax.xml.ws.WebServiceException;
+import javax.xml.ws.handler.MessageContext;
+import javax.xml.ws.handler.soap.SOAPHandler;
+import javax.xml.ws.handler.soap.SOAPMessageContext;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Asserts whether the attachment is present where expected (inline or multipart body) throws an IllegalStateException
+ * to signal that something is wrong.
+ */
+public class MtomAttachmentChecker implements SOAPHandler<SOAPMessageContext> {
+    boolean mtomEnabled;
+
+    public MtomAttachmentChecker(boolean mtomEnabled) {
+        this.mtomEnabled = mtomEnabled;
+    }
+
+    static boolean walk(String localName, NodeList nodes) {
+        boolean found = false;
+        for (int i = 0; i < nodes.getLength(); i++) {
+            Node n = nodes.item(i);
+            if (localName.equals(n.getLocalName())) {
+                found = true;
+                break;
+            } else if (!found) {
+                found = walk(localName, n.getChildNodes());
+            }
+        }
+
+        return found;
+    }
+
+    @Override
+    public boolean handleMessage(SOAPMessageContext msgContext) {
+        try {
+            SOAPEnvelope envelope = msgContext.getMessage().getSOAPPart().getEnvelope();
+            SOAPBody body = envelope.getBody();
+            boolean found = walk("Include", body.getChildNodes());
+            if (mtomEnabled) {
+                // skip those messages which don't have attachments
+                boolean skip = walk("uploadImageResponse", body.getChildNodes()) || walk("downloadImage", body.getChildNodes());
+                if (!skip && !found) {
+                    throw new IllegalStateException("The SOAP message should contain an <xop:Include> element");
+                }
+            } else if (found) {
+                throw new IllegalStateException("The SOAP message shouldn't contain an <xop:Include> element");
+            }
+
+        } catch (SOAPException ex) {
+            throw new WebServiceException(ex);
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean handleFault(SOAPMessageContext context) {
+        return true;
+    }
+
+    @Override
+    public void close(MessageContext context) {
+    }
+
+    @Override
+    public Set<QName> getHeaders() {
+        return Collections.emptySet();
+    }
+}
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/resources/application.properties b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/resources/application.properties
new file mode 100644
index 0000000000..574727024a
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/main/resources/application.properties
@@ -0,0 +1,19 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+quarkus.cxf.path=/soapservice
+quarkus.native.resources.includes = wsdl/*.wsdl
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/src/test/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomIT.java b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/test/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomIT.java
new file mode 100644
index 0000000000..6da4ad90da
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/test/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomIT.java
@@ -0,0 +1,23 @@
+/*
+ * 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.camel.quarkus.component.cxf.soap.mtom.it;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class CxfSoapMtomIT extends CxfSoapMtomTest {
+}
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/src/test/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomTest.java b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/test/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomTest.java
new file mode 100644
index 0000000000..9fdc27c1f8
--- /dev/null
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/test/java/org/apache/camel/quarkus/component/cxf/soap/mtom/it/CxfSoapMtomTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.camel.quarkus.component.cxf.soap.mtom.it;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import io.quarkus.test.junit.DisabledOnIntegrationTest;
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
+import org.hamcrest.CoreMatchers;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+@QuarkusTest
+class CxfSoapMtomTest {
+
+    @DisabledOnIntegrationTest("https://github.com/apache/camel-quarkus/issues/4208")
+    @ParameterizedTest
+    @ValueSource(booleans = { true, false })
+    public void uploadDownloadMtom(boolean mtomEnabled) throws IOException {
+        byte[] imageBytes = CxfSoapMtomTest.class.getClassLoader().getResourceAsStream("linux-image.png").readAllBytes();
+        String imageName = "linux-image-name";
+        RestAssured.given()
+                .contentType(ContentType.BINARY)
+                .queryParam("imageName", imageName)
+                .queryParam("mtomEnabled", mtomEnabled)
+                .body(imageBytes)
+                .post("/cxf-soap/mtom/upload")
+                .then()
+                .statusCode(201)
+                .body(CoreMatchers.equalTo(ImageService.MSG_SUCCESS));
+        byte[] downloadedImageBytes = RestAssured.given()
+                .contentType(ContentType.TEXT)
+                .queryParam("imageName", imageName)
+                .queryParam("mtomEnabled", mtomEnabled)
+                .post("/cxf-soap/mtom/download")
+                .then()
+                .statusCode(201)
+                .extract().asByteArray();
+
+        try (ByteArrayInputStream imageBais = new ByteArrayInputStream(
+                imageBytes); ByteArrayInputStream downloadedImageBais = new ByteArrayInputStream(downloadedImageBytes)) {
+            Assertions.assertTrue(bufferedImagesEqual(ImageIO.read(imageBais),
+                    ImageIO.read(downloadedImageBais)), "Uploaded image should match downloaded");
+        }
+    }
+
+    // copied from https://stackoverflow.com/a/15305092
+    boolean bufferedImagesEqual(BufferedImage img1, BufferedImage img2) {
+        if (img1.getWidth() == img2.getWidth() && img1.getHeight() == img2.getHeight()) {
+            for (int x = 0; x < img1.getWidth(); x++) {
+                for (int y = 0; y < img1.getHeight(); y++) {
+                    if (img1.getRGB(x, y) != img2.getRGB(x, y))
+                        return false;
+                }
+            }
+        } else {
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/src/test/resources/linux-image.png b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/test/resources/linux-image.png
new file mode 100644
index 0000000000..17d7bfef0d
Binary files /dev/null and b/integration-test-groups/cxf-soap/cxf-soap-mtom/src/test/resources/linux-image.png differ
diff --git a/integration-test-groups/cxf-soap/pom.xml b/integration-test-groups/cxf-soap/pom.xml
index d5f1c22d43..bfb960c740 100644
--- a/integration-test-groups/cxf-soap/pom.xml
+++ b/integration-test-groups/cxf-soap/pom.xml
@@ -38,6 +38,7 @@
     <modules>
         <!-- extensions a..z; do not remove this comment, it is important when sorting via  mvn process-resources -Pformat -->
         <module>cxf-soap-client</module>
+        <module>cxf-soap-mtom</module>
         <module>cxf-soap-server</module>
         <module>cxf-soap-ws-security-client</module>
     </modules>
diff --git a/integration-tests/cxf-soap-grouped/pom.xml b/integration-tests/cxf-soap-grouped/pom.xml
index ca70eea4f2..6b809b1b6e 100644
--- a/integration-tests/cxf-soap-grouped/pom.xml
+++ b/integration-tests/cxf-soap-grouped/pom.xml
@@ -32,6 +32,14 @@
 
     <!-- Regenerate the dependencies via `mvn process-resources -Pformat -N` from the source tree root directory -->
     <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-bean</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-direct</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-cxf-soap</artifactId>
@@ -48,13 +56,13 @@
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
         </dependency>
-        <dependency>
+        <dependency><!-- for java.awt.Image -->
             <groupId>io.quarkus</groupId>
-            <artifactId>quarkus-resteasy</artifactId>
+            <artifactId>quarkus-awt</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.apache.camel.quarkus</groupId>
-            <artifactId>camel-quarkus-direct</artifactId>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-resteasy</artifactId>
         </dependency>
 
         <!-- test dependencies -->