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/11/10 20:57:23 UTC

[camel-quarkus] branch 2.13.x updated (ac60c43b75 -> 1b7846dce5)

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

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


    from ac60c43b75 Disable GoogleBigqueryTest.sqlCrudOperations due to #4029
     new ae9ac0edd7 automatic configuration of FileLockClusterService #4262
     new 8220f1bbf7 Enhance test covarege of MTOM with PAYLOAD data format
     new aa75b61bc8 add file cluster service automatic configuration itest #4262
     new 9a1c961ba6 controlbus: Added language tests (fixes #4008)
     new 1b7846dce5 CxfSoapClientTest.wsdlUpToDate() and CxfSoapWssClientTest.wsdlUpToDate() fail on the platform #4254

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../pages/reference/extensions/controlbus.adoc     |  48 ++++-
 .../ROOT/pages/reference/extensions/file.adoc      |  82 ++++++++
 .../ROOT/pages/reference/extensions/master.adoc    |   1 +
 .../controlbus/runtime/src/main/doc/usage.adoc     |  46 ++++-
 extensions/file/deployment/pom.xml                 |   5 +
 .../FileLockClusterServiceProcessor.java           |  26 ++-
 ...ockClusterServiceConfigDefaultEnabledTest.java} |  36 +++-
 .../FileLockClusterServiceConfigDefaultTest.java}  |  14 +-
 ...ClusterServiceConfigNonDefaultEnabledTest.java} |  66 ++++---
 .../file/runtime/src/main/doc/configuration.adoc   |  24 +++
 .../file/cluster/FileLockClusterServiceConfig.java |  80 ++++++++
 .../cluster/FileLockClusterServiceRecorder.java    |  49 +++++
 .../master/runtime/src/main/doc/configuration.adoc |   1 +
 .../cxf-soap/cxf-soap-client/pom.xml               |   1 +
 .../cxf/soap/client/it/CxfSoapClientTest.java      |  19 +-
 .../cxf-soap/cxf-soap-mtom/README.adoc             |   2 +-
 .../cxf/soap/mtom/it/CxfSoapMtomResource.java      |  60 ++++--
 .../cxf/soap/mtom/it/CxfSoapMtomRoutes.java        | 218 +++++++++++++++++++--
 .../cxf/soap/mtom/it/CxfSoapMtomTest.java          |  20 +-
 .../cxf-soap/cxf-soap-ws-security-client/pom.xml   |   1 +
 .../soap/wss/client/it/CxfSoapWssClientTest.java   |  18 +-
 .../foundation/controlbus/pom.xml                  |  17 ++
 .../component/controlbus/it/ControlbusBean.java    |  19 +-
 .../controlbus/it/ControlbusLanguageResource.java  |  86 ++++++++
 .../controlbus/it/ControlbusLanguageRoute.java     |  27 +--
 ...ControlbusIT.java => ControlbusLanguageIT.java} |   2 +-
 .../controlbus/it/ControlbusLanguageTest.java      |  31 ++-
 integration-tests/cxf-soap-grouped/pom.xml         |   3 +
 integration-tests/{master => master-file}/pom.xml  |   7 +-
 .../component/master/it/MasterResource.java        |   0
 .../quarkus/component/master/it/MasterRoutes.java  |   2 +-
 .../src/main/resources/application.properties      |   6 +-
 .../quarkus/component/master/it/MasterFileIT.java} |   2 +-
 .../component/master/it/MasterFileTest.java}       |  27 ++-
 integration-tests/pom.xml                          |   1 +
 tooling/scripts/test-categories.yaml               |   1 +
 tooling/test-list/pom.xml                          |   1 +
 37 files changed, 886 insertions(+), 163 deletions(-)
 copy integration-tests-support/custom-log-component/deployment/src/main/java/org/apache/camel/quarkus/core/support/deployment/SupportBuildStep.java => extensions/file/deployment/src/main/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceProcessor.java (52%)
 copy extensions/{kubernetes/deployment/src/test/java/org/apache/camel/quarkus/component/kubernetes/cluster/deployment/KubernetesClusterServiceConfigEnabledWithRebalancingtTest.java => file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigDefaultEnabledTest.java} (62%)
 copy extensions/{kubernetes/deployment/src/test/java/org/apache/camel/quarkus/component/kubernetes/cluster/deployment/KubernetesClusterServiceConfigDefaultTest.java => file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigDefaultTest.java} (84%)
 copy extensions/{kubernetes/deployment/src/test/java/org/apache/camel/quarkus/component/kubernetes/cluster/deployment/KubernetesClusterServiceConfigEnabledWithoutRebalancingTest.java => file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigNonDefaultEnabledTest.java} (53%)
 create mode 100644 extensions/file/runtime/src/main/doc/configuration.adoc
 create mode 100644 extensions/file/runtime/src/main/java/org/apache/camel/quarkus/component/file/cluster/FileLockClusterServiceConfig.java
 create mode 100644 extensions/file/runtime/src/main/java/org/apache/camel/quarkus/component/file/cluster/FileLockClusterServiceRecorder.java
 copy integration-tests/xpath/src/main/java/org/apache/camel/quarkus/language/xpath/PriceBean.java => integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusBean.java (70%)
 create mode 100644 integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageResource.java
 copy integration-tests-jvm/controlbus-statistics/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusRoute.java => integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageRoute.java (57%)
 copy integration-test-groups/foundation/controlbus/src/test/java/org/apache/camel/quarkus/component/controlbus/it/{ControlbusIT.java => ControlbusLanguageIT.java} (94%)
 copy integration-tests-jvm/controlbus-statistics/src/test/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusTest.java => integration-test-groups/foundation/controlbus/src/test/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageTest.java (58%)
 copy integration-tests/{master => master-file}/pom.xml (98%)
 copy integration-tests/{master-openshift => master-file}/src/main/java/org/apache/camel/quarkus/component/master/it/MasterResource.java (100%)
 copy integration-tests/{master-openshift => master-file}/src/main/java/org/apache/camel/quarkus/component/master/it/MasterRoutes.java (97%)
 copy integration-tests/{master-openshift => master-file}/src/main/resources/application.properties (83%)
 copy integration-tests/{master/src/test/java/org/apache/camel/quarkus/component/master/it/MasterIT.java => master-file/src/test/java/org/apache/camel/quarkus/component/master/it/MasterFileIT.java} (95%)
 copy integration-tests/{master/src/test/java/org/apache/camel/quarkus/component/master/it/MasterTest.java => master-file/src/test/java/org/apache/camel/quarkus/component/master/it/MasterFileTest.java} (87%)


[camel-quarkus] 02/05: Enhance test covarege of MTOM with PAYLOAD data format

Posted by ja...@apache.org.
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 8220f1bbf7c4deac2284e80232b101cc99b020b1
Author: Lukas Lowinger <ll...@redhat.com>
AuthorDate: Wed Oct 26 20:25:29 2022 +0200

    Enhance test covarege of MTOM with PAYLOAD data format
---
 .../cxf-soap/cxf-soap-mtom/README.adoc             |   2 +-
 .../cxf/soap/mtom/it/CxfSoapMtomResource.java      |  60 ++++--
 .../cxf/soap/mtom/it/CxfSoapMtomRoutes.java        | 218 +++++++++++++++++++--
 .../cxf/soap/mtom/it/CxfSoapMtomTest.java          |  20 +-
 4 files changed, 258 insertions(+), 42 deletions(-)

diff --git a/integration-test-groups/cxf-soap/cxf-soap-mtom/README.adoc b/integration-test-groups/cxf-soap/cxf-soap-mtom/README.adoc
index dac25bdb6f..cbce978cf7 100644
--- a/integration-test-groups/cxf-soap/cxf-soap-mtom/README.adoc
+++ b/integration-test-groups/cxf-soap/cxf-soap-mtom/README.adoc
@@ -1,4 +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.
+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 and POJO/PAYLOAD dataformat.
 
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
index 405241bc9e..96e7529ac8 100644
--- 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
@@ -16,7 +16,10 @@
  */
 package org.apache.camel.quarkus.component.cxf.soap.mtom.it;
 
+import java.io.ByteArrayInputStream;
 import java.net.URI;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
@@ -28,9 +31,13 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePattern;
 import org.apache.camel.ProducerTemplate;
+import org.apache.camel.attachment.AttachmentMessage;
 
 import static org.apache.camel.component.cxf.common.message.CxfConstants.OPERATION_NAME;
+import static org.apache.camel.quarkus.component.cxf.soap.mtom.it.CxfSoapMtomRoutes.ROUTE_PAYLOAD_MODE_RESULT_HEADER_KEY_NAME;
 
 @Path("/cxf-soap/mtom")
 @ApplicationScoped
@@ -41,13 +48,25 @@ public class CxfSoapMtomResource {
 
     @Path("/upload")
     @POST
+    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
     @Produces(MediaType.TEXT_PLAIN)
     public Response upload(@QueryParam("imageName") String imageName, @QueryParam("mtomEnabled") boolean mtomEnabled,
-            byte[] image) throws Exception {
-        final String response = producerTemplate.requestBodyAndHeader(
-                "direct:" + mtomEndpoint(mtomEnabled),
-                new Object[] { new ImageFile(image), imageName },
-                OPERATION_NAME, "uploadImage", String.class);
+            @QueryParam("endpointDataFormat") String endpointDataFormat, byte[] image) throws Exception {
+        Map<String, Object> headers = new LinkedHashMap<>();
+        headers.put(OPERATION_NAME, "uploadImage");
+        headers.put("endpointDataFormat", endpointDataFormat);
+        headers.put("mtomEnabled", mtomEnabled);
+        Object body = new Object[] { new ImageFile(image), imageName };
+        Exchange result = producerTemplate.request("direct:invoker", exchange -> {
+            exchange.getIn().setBody(body);
+            exchange.getIn().setHeaders(headers);
+        });
+        Object response = null;
+        if ("PAYLOAD".equals(endpointDataFormat)) {
+            response = result.getMessage().getHeader(ROUTE_PAYLOAD_MODE_RESULT_HEADER_KEY_NAME);
+        } else {
+            response = result.getMessage().getBody(String.class);
+        }
         return Response
                 .created(new URI("https://camel.apache.org/"))
                 .entity(response)
@@ -56,22 +75,31 @@ public class CxfSoapMtomResource {
 
     @Path("/download")
     @POST
+    @Produces(MediaType.APPLICATION_OCTET_STREAM)
     @Consumes(MediaType.TEXT_PLAIN)
-    public Response download(@QueryParam("imageName") String imageName, @QueryParam("mtomEnabled") boolean mtomEnabled)
+    public Response download(@QueryParam("imageName") String imageName, @QueryParam("mtomEnabled") boolean mtomEnabled,
+            @QueryParam("endpointDataFormat") String endpointDataFormat)
             throws Exception {
-        final ImageFile response = (ImageFile) producerTemplate.requestBodyAndHeader(
-                "direct:" + mtomEndpoint(mtomEnabled),
-                imageName,
-                OPERATION_NAME,
-                "downloadImage", ImageFile.class);
+        Map<String, Object> headers = new LinkedHashMap<>();
+        headers.put(OPERATION_NAME, "downloadImage");
+        headers.put("endpointDataFormat", endpointDataFormat);
+        headers.put("mtomEnabled", mtomEnabled);
+        Exchange result = producerTemplate.request("direct:invoker", exchange -> {
+            exchange.setPattern(ExchangePattern.InOut);
+            exchange.getIn().setBody(imageName);
+            exchange.getIn().setHeaders(headers);
+        });
+        byte[] response = null;
+        if ("PAYLOAD".equals(endpointDataFormat)) {
+            response = ((ByteArrayInputStream) result.getMessage(AttachmentMessage.class).getAttachment(imageName).getContent())
+                    .readAllBytes();
+        } else {
+            response = result.getMessage().getBody(ImageFile.class).getContent();
+        }
         return Response
                 .created(new URI("https://camel.apache.org/"))
-                .entity(response.getContent())
+                .entity(response)
                 .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
index a7f454a035..011622415d 100644
--- 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
@@ -16,28 +16,88 @@
  */
 package org.apache.camel.quarkus.component.cxf.soap.mtom.it;
 
+import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
+import javax.activation.DataHandler;
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.inject.Produces;
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
 import javax.xml.ws.handler.Handler;
+import javax.xml.xpath.XPathConstants;
 
+import org.w3c.dom.Element;
+
+import com.sun.istack.ByteArrayDataSource;
 import io.quarkus.runtime.LaunchMode;
 import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
+import org.apache.camel.attachment.AttachmentMessage;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.cxf.common.CxfPayload;
 import org.apache.camel.component.cxf.jaxws.CxfEndpoint;
+import org.apache.camel.converter.jaxp.XmlConverter;
+import org.apache.cxf.binding.soap.SoapHeader;
 import org.apache.cxf.ext.logging.LoggingFeature;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.helpers.XPathUtils;
 import org.apache.cxf.message.MessageContentsList;
+import org.apache.cxf.staxutils.StaxUtils;
 import org.eclipse.microprofile.config.Config;
 import org.eclipse.microprofile.config.ConfigProvider;
 
+import static org.apache.camel.component.cxf.common.message.CxfConstants.OPERATION_NAME;
+
 @ApplicationScoped
 public class CxfSoapMtomRoutes extends RouteBuilder {
 
+    public static final String SERVICE_TYPES_NS = "http://it.mtom.soap.cxf.component.quarkus.camel.apache.org/";
+    public static final String XOP_NS = "http://www.w3.org/2004/08/xop/include";
+
+    public static final String RESP_UPLOAD_MSG = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
+            "<ns2:uploadImageResponse xmlns:ns2=\"http://it.mtom.soap.cxf.component.quarkus.camel.apache.org/\">" +
+            "<return>%s</return>" +
+            "</ns2:uploadImageResponse>";
+
+    public static final String RESP_DOWNLOAD_MSG_MTOM_ENABLED = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
+            "<ns2:downloadImageResponse xmlns:ns2=\"http://it.mtom.soap.cxf.component.quarkus.camel.apache.org/\">" +
+            "<arg0><content><xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\"" +
+            " href=\"cid:%s\"/></content></arg0>" +
+            "<arg1>%s</arg1>" +
+            "</ns2:downloadImageResponse>";
+    public static final String RESP_DOWNLOAD_MSG_MTOM_DISABLED = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
+            "<ns2:downloadImageResponse xmlns:ns2=\"http://it.mtom.soap.cxf.component.quarkus.camel.apache.org/\">" +
+            "<arg0><content>cid:%s</content></arg0>" +
+            "<arg1>%s</arg1>" +
+            "</ns2:downloadImageResponse>";
+
+    public static final String REQ_UPLOAD_MSG_MTOM_DISABLED = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
+            "<ns2:uploadImage xmlns:ns2=\"http://it.mtom.soap.cxf.component.quarkus.camel.apache.org/\">" +
+            "<arg0><content>cid:%s</content></arg0>" +
+            "<arg1>%s</arg1>" +
+            "</ns2:uploadImage>";
+    public static final String REQ_UPLOAD_MSG_MTOM_ENABLED = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
+            "<ns2:uploadImage xmlns:ns2=\"http://it.mtom.soap.cxf.component.quarkus.camel.apache.org/\">" +
+            "<arg0><content><xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\"" +
+            " href=\"cid:%s\"/></content></arg0>" +
+            "<arg1>%s</arg1>" +
+            "</ns2:uploadImage>";
+    public static final String REQ_DOWNLOAD_MSG = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
+            "<ns2:downloadImage xmlns:ns2=\"http://it.mtom.soap.cxf.component.quarkus.camel.apache.org/\">" +
+            "<arg0>%s</arg0>" +
+            "</ns2:downloadImage>";
+
+    /**
+     * For transfering String response instead of parsing CXFPayload back in CxfSoapMtomResource class.
+     */
+    public static final String ROUTE_PAYLOAD_MODE_RESULT_HEADER_KEY_NAME = "routeResultPayloadModeHeaderKeyName";
+
     @Inject
     @Named("loggingMtomFeatureClient")
     LoggingFeature loggingFeature;
@@ -45,27 +105,129 @@ public class CxfSoapMtomRoutes extends RouteBuilder {
     @Override
     public void configure() {
 
-        from("direct:mtomEnabledInvoker")
-                .to("cxf:bean:soapMtomEnabledClientEndpoint?dataFormat=POJO");
+        from("direct:invoker")
+                .process(exchange -> {
+                    Map<String, Object> headers = exchange.getIn().getHeaders();
+                    String endpointDataFormat = headers.get("endpointDataFormat").toString();
+                    boolean mtomEnabled = Boolean.parseBoolean(headers.get("mtomEnabled").toString());
+                    headers.put("address", getServerUrl() + "/soapservice/mtom-" +
+                            (mtomEnabled ? "enabled" : "disabled") + "-" + endpointDataFormat.toLowerCase() +
+                            "-mode-image-service");
+                    if ("PAYLOAD".equals(endpointDataFormat)) {
+                        if ("uploadImage".equals(headers.get(OPERATION_NAME))) {
+                            Object[] reqParams = exchange.getIn().getBody(Object[].class);
+                            ImageFile image = (ImageFile) reqParams[0];
+                            String imageName = (String) reqParams[1];
+                            List<Source> elements = new ArrayList<>();
+                            String reqMessage = mtomEnabled ? REQ_UPLOAD_MSG_MTOM_ENABLED : REQ_UPLOAD_MSG_MTOM_DISABLED;
+                            elements.add(new DOMSource(StaxUtils
+                                    .read(new StringReader(String.format(reqMessage, imageName, imageName)))
+                                    .getDocumentElement()));
+                            CxfPayload payload = new CxfPayload<>(
+                                    new ArrayList<SoapHeader>(), elements, null);
+                            exchange.getIn().setBody(payload);
+                            exchange.getIn(AttachmentMessage.class).addAttachment(imageName,
+                                    new DataHandler(new ByteArrayDataSource(image.getContent(), "application/octet-stream")));
+                        } else if ("downloadImage".equals(headers.get(OPERATION_NAME))) {
+                            Object[] reqParams = exchange.getIn().getBody(Object[].class);
+                            String imageName = (String) reqParams[0];
+                            List<Source> elements = new ArrayList<>();
+                            elements.add(
+                                    new DOMSource(StaxUtils.read(new StringReader(String.format(REQ_DOWNLOAD_MSG, imageName)))
+                                            .getDocumentElement()));
+                            CxfPayload payload = new CxfPayload<>(
+                                    new ArrayList<SoapHeader>(), elements, null);
+                            exchange.getIn().setBody(payload);
+                        }
+                    }
+                })
+                .choice().when(simple("${header.mtomEnabled} == 'true'"))
+                .toD("cxf:bean:soapClientMtomEnabledEndpoint?address=${header.address}&mtomEnabled=${header.mtomEnabled}&dataFormat=${header.endpointDataFormat}")
+                .otherwise()
+                .toD("cxf:bean:soapClientMtomDisabledEndpoint?address=${header.address}&mtomEnabled=${header.mtomEnabled}&dataFormat=${header.endpointDataFormat}");
+
+        from("cxf:bean:soapMtomEnabledServerPojoModeEndpoint?dataFormat=POJO")
+                .to("direct:pojoModeProcessor");
+
+        from("cxf:bean:soapMtomDisabledServerPojoModeEndpoint?dataFormat=POJO")
+                .to("direct:pojoModeProcessor");
+
+        from("direct:pojoModeProcessor")
+                .process("pojoModeProcessor")
+                .toD("bean:imageService?method=${header.operationName}");
+
+        from("cxf:bean:soapMtomEnabledServerPayloadModeEndpoint?dataFormat=PAYLOAD")
+                .process("payloadModeProcessor");
+
+        from("cxf:bean:soapMtomDisabledServerPayloadModeEndpoint?dataFormat=PAYLOAD")
+                .process("payloadModeProcessor");
+
+    }
+
+    @ApplicationScoped
+    @Named("payloadModeProcessor")
+    static class PayloadModeProcessor implements Processor {
 
-        from("direct:mtomDisabledInvoker")
-                .to("cxf:bean:soapMtomDisabledClientEndpoint?dataFormat=POJO");
+        @Inject
+        @Named("imageService")
+        ImageService imageService;
 
-        from("cxf:bean:soapMtomEnabledServerEndpoint?dataFormat=POJO")
-                .to("direct:processImage");
+        @Override
+        public void process(Exchange exchange) throws Exception {
+            CxfPayload<SoapHeader> in = exchange.getIn().getBody(CxfPayload.class);
+            String operation = in.getBody().get(0).getLocalName();
+            if ("uploadImage".equals(operation)) {
+                Map<String, String> ns = new HashMap<>();
+                ns.put("ns2", SERVICE_TYPES_NS);
+                ns.put("xop", XOP_NS);
 
-        from("cxf:bean:soapMtomDisabledServerEndpoint?dataFormat=POJO")
-                .to("direct:processImage");
+                XPathUtils xu = new XPathUtils(ns);
+                Element body = new XmlConverter().toDOMElement(in.getBody().get(0));
+                Element ele = (Element) xu.getValue("//ns2:uploadImage/arg1", body,
+                        XPathConstants.NODE);
+                String imageName = ele.getTextContent();
+                DataHandler dr = exchange.getIn(AttachmentMessage.class).getAttachment(imageName);
+                String uploadStatus = imageService.uploadImage(
+                        new ImageFile(IOUtils.readBytesFromStream(dr.getInputStream())), imageName);
+                List<Source> elements = new ArrayList<>();
+                elements.add(new DOMSource(StaxUtils.read(new StringReader(String.format(RESP_UPLOAD_MSG, uploadStatus)))
+                        .getDocumentElement()));
+                CxfPayload payload = new CxfPayload<>(
+                        new ArrayList<SoapHeader>(), elements, null);
+                exchange.getIn().setBody(payload);
+                // We have correctly uploaded the image, so we can put the upload status in the header, so we don't mess with CXFPayload in CxfSoapMtomResource
+                exchange.getIn().setHeader(ROUTE_PAYLOAD_MODE_RESULT_HEADER_KEY_NAME, uploadStatus);
+            } else if ("downloadImage".equals(operation)) {
+                Map<String, String> ns = new HashMap<>();
+                ns.put("ns2", SERVICE_TYPES_NS);
+                ns.put("xop", XOP_NS);
 
-        from("direct:processImage")
-                .process("imageServiceProcessor")
-                .recipientList((simple("bean:imageService?method=${header.operationName}")));
+                XPathUtils xu = new XPathUtils(ns);
+                Element body = new XmlConverter().toDOMElement(in.getBody().get(0));
+                Element ele = (Element) xu.getValue("//ns2:downloadImage/arg0", body,
+                        XPathConstants.NODE);
+                String imageName = ele.getTextContent();
+                List<Source> elements = new ArrayList<>();
+                boolean mtomEnabled = Boolean.parseBoolean(exchange.getIn().getHeaders().get("mtomEnabled").toString());
+                String respMessage = mtomEnabled ? RESP_DOWNLOAD_MSG_MTOM_ENABLED : RESP_DOWNLOAD_MSG_MTOM_DISABLED;
+                elements.add(
+                        new DOMSource(
+                                StaxUtils.read(new StringReader(String.format(respMessage, imageName, imageName)))
+                                        .getDocumentElement()));
+                ImageFile imageFile = imageService.downloadImage(imageName);
+                CxfPayload payload = new CxfPayload<>(
+                        new ArrayList<SoapHeader>(), elements, null);
+                exchange.getIn().setBody(payload);
+                exchange.getIn(AttachmentMessage.class).addAttachment(imageName, new DataHandler(
+                        new ByteArrayDataSource(imageFile.getContent(), "application/octet-stream")));
+            }
 
+        }
     }
 
     @ApplicationScoped
-    @Named("imageServiceProcessor")
-    static class ImageServiceProcessor implements Processor {
+    @Named("pojoModeProcessor")
+    static class PojoModeProcessor implements Processor {
         @Override
         public void process(Exchange exchange) throws Exception {
             String operationName = (String) exchange.getIn().getHeaders().get("operationName");
@@ -94,29 +256,43 @@ public class CxfSoapMtomRoutes extends RouteBuilder {
     @Produces
     @ApplicationScoped
     @Named
-    CxfEndpoint soapMtomEnabledClientEndpoint() {
-        return commonCxfEndpoint(true, getServerUrl() + "/soapservice/mtom-enabled-image-service");
+    CxfEndpoint soapClientMtomEnabledEndpoint() {
+        return commonCxfEndpoint(true, "");
+    }
+
+    @Produces
+    @ApplicationScoped
+    @Named
+    CxfEndpoint soapClientMtomDisabledEndpoint() {
+        return commonCxfEndpoint(false, "");
+    }
+
+    @Produces
+    @ApplicationScoped
+    @Named
+    CxfEndpoint soapMtomDisabledServerPayloadModeEndpoint() {
+        return commonCxfEndpoint(false, "/mtom-disabled-payload-mode-image-service");
     }
 
     @Produces
     @ApplicationScoped
     @Named
-    CxfEndpoint soapMtomDisabledClientEndpoint() {
-        return commonCxfEndpoint(false, getServerUrl() + "/soapservice/mtom-disabled-image-service");
+    CxfEndpoint soapMtomEnabledServerPayloadModeEndpoint() {
+        return commonCxfEndpoint(true, "/mtom-enabled-payload-mode-image-service");
     }
 
     @Produces
     @ApplicationScoped
     @Named
-    CxfEndpoint soapMtomEnabledServerEndpoint() {
-        return commonCxfEndpoint(true, "/mtom-enabled-image-service");
+    CxfEndpoint soapMtomEnabledServerPojoModeEndpoint() {
+        return commonCxfEndpoint(true, "/mtom-enabled-pojo-mode-image-service");
     }
 
     @Produces
     @ApplicationScoped
     @Named
-    CxfEndpoint soapMtomDisabledServerEndpoint() {
-        return commonCxfEndpoint(false, "/mtom-disabled-image-service");
+    CxfEndpoint soapMtomDisabledServerPojoModeEndpoint() {
+        return commonCxfEndpoint(false, "/mtom-disabled-pojo-mode-image-service");
     }
 
     CxfEndpoint commonCxfEndpoint(boolean mtomEnabled, String address) {
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
index 01c7249d48..be5b10aa7a 100644
--- 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
@@ -19,6 +19,7 @@ package org.apache.camel.quarkus.component.cxf.soap.mtom.it;
 import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.util.stream.Stream;
 
 import javax.imageio.ImageIO;
 
@@ -28,20 +29,30 @@ 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;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 
 @QuarkusTest
 class CxfSoapMtomTest {
 
+    private static Stream<Arguments> matrix() {
+        return Stream.of(
+                Arguments.of(true, "POJO"),
+                Arguments.of(false, "POJO"),
+                Arguments.of(true, "PAYLOAD"),
+                Arguments.of(false, "PAYLOAD"));
+    }
+
     @ParameterizedTest
-    @ValueSource(booleans = { true, false })
-    public void uploadDownloadMtom(boolean mtomEnabled) throws IOException {
+    @MethodSource("matrix")
+    public void uploadDownloadMtom(boolean mtomEnabled, String endpointDataFormat) throws IOException {
         byte[] imageBytes = CxfSoapMtomTest.class.getClassLoader().getResourceAsStream("linux-image.png").readAllBytes();
-        String imageName = "linux-image-name";
+        String imageName = String.format("linux-image-name-mtom-%s-%s-mode", mtomEnabled, endpointDataFormat);
         RestAssured.given()
                 .contentType(ContentType.BINARY)
                 .queryParam("imageName", imageName)
                 .queryParam("mtomEnabled", mtomEnabled)
+                .queryParam("endpointDataFormat", endpointDataFormat)
                 .body(imageBytes)
                 .post("/cxf-soap/mtom/upload")
                 .then()
@@ -51,6 +62,7 @@ class CxfSoapMtomTest {
                 .contentType(ContentType.TEXT)
                 .queryParam("imageName", imageName)
                 .queryParam("mtomEnabled", mtomEnabled)
+                .queryParam("endpointDataFormat", endpointDataFormat)
                 .post("/cxf-soap/mtom/download")
                 .then()
                 .statusCode(201)


[camel-quarkus] 03/05: add file cluster service automatic configuration itest #4262

Posted by ja...@apache.org.
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 aa75b61bc851931a6a6dd3890f9894eb917bd74d
Author: aldettinger <al...@gmail.com>
AuthorDate: Wed Nov 9 12:40:57 2022 +0100

    add file cluster service automatic configuration itest #4262
---
 .../ROOT/pages/reference/extensions/file.adoc      |   1 +
 .../file/runtime/src/main/doc/configuration.adoc   |   1 +
 integration-tests/master-file/pom.xml              | 231 +++++++++++++++++++++
 .../component/master/it/MasterResource.java        |  36 ++++
 .../quarkus/component/master/it/MasterRoutes.java  |  41 ++++
 .../src/main/resources/application.properties      |  23 ++
 .../quarkus/component/master/it/MasterFileIT.java  |  24 +++
 .../component/master/it/MasterFileTest.java        | 106 ++++++++++
 integration-tests/pom.xml                          |   1 +
 tooling/scripts/test-categories.yaml               |   1 +
 tooling/test-list/pom.xml                          |   1 +
 11 files changed, 466 insertions(+)

diff --git a/docs/modules/ROOT/pages/reference/extensions/file.adoc b/docs/modules/ROOT/pages/reference/extensions/file.adoc
index 90f69ee3e3..39f1d45907 100644
--- a/docs/modules/ROOT/pages/reference/extensions/file.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/file.adoc
@@ -65,6 +65,7 @@ It's possible to enable the file cluster service with a property like below:
 
 ```
 quarkus.camel.cluster.file.enabled = true
+quarkus.camel.cluster.file.root = target/cluster-folder-where-lock-file-will-be-held
 ```
 
 As a result, a single consumer will be active across the `ns` camel master namespace.
diff --git a/extensions/file/runtime/src/main/doc/configuration.adoc b/extensions/file/runtime/src/main/doc/configuration.adoc
index 194f451d48..fc379d6044 100644
--- a/extensions/file/runtime/src/main/doc/configuration.adoc
+++ b/extensions/file/runtime/src/main/doc/configuration.adoc
@@ -14,6 +14,7 @@ It's possible to enable the file cluster service with a property like below:
 
 ```
 quarkus.camel.cluster.file.enabled = true
+quarkus.camel.cluster.file.root = target/cluster-folder-where-lock-file-will-be-held
 ```
 
 As a result, a single consumer will be active across the `ns` camel master namespace.
diff --git a/integration-tests/master-file/pom.xml b/integration-tests/master-file/pom.xml
new file mode 100644
index 0000000000..bb18c22b54
--- /dev/null
+++ b/integration-tests/master-file/pom.xml
@@ -0,0 +1,231 @@
+<?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.2-SNAPSHOT</version>
+        <relativePath>../../poms/build-parent-it/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>camel-quarkus-integration-test-master-file</artifactId>
+    <name>Camel Quarkus :: Integration Tests :: Master With File</name>
+    <description>Integration tests for Camel Quarkus Master extension with File</description>
+
+    <properties>
+        <quarkus.runner>${project.build.directory}/quarkus-app/quarkus-run.jar</quarkus.runner>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-file</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-log</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-master</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-microprofile-health</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-timer</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-resteasy</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>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-integration-tests-process-executor-support</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+
+        <profile>
+            <id>full</id>
+            <activation>
+                <property>
+                    <name>!quickly</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <!-- Move surefire:test to integration-test phase to be able to run
+                             java -jar target/*runner.jar from a test -->
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>default-test</id>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <phase>integration-test</phase>
+                                <configuration>
+                                    <systemProperties>
+                                        <quarkus.runner>${quarkus.runner}</quarkus.runner>
+                                    </systemProperties>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <properties>
+                <quarkus.package.type>native</quarkus.package.type>
+                <quarkus.runner>${project.build.directory}/${project.artifactId}-${project.version}-runner</quarkus.runner>
+            </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>
+                        <configuration>
+                            <systemProperties>
+                                <quarkus.runner>${quarkus.runner}</quarkus.runner>
+                            </systemProperties>
+                        </configuration>
+                    </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-file-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-log-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-master-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-microprofile-health-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-timer-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-tests/master-file/src/main/java/org/apache/camel/quarkus/component/master/it/MasterResource.java b/integration-tests/master-file/src/main/java/org/apache/camel/quarkus/component/master/it/MasterResource.java
new file mode 100644
index 0000000000..c9eb6c9a84
--- /dev/null
+++ b/integration-tests/master-file/src/main/java/org/apache/camel/quarkus/component/master/it/MasterResource.java
@@ -0,0 +1,36 @@
+/*
+ * 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.master.it;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.apache.camel.CamelContext;
+
+@Path("/master")
+public class MasterResource {
+
+    @Inject
+    CamelContext camelContext;
+
+    @Path("/camel/stop/leader")
+    @GET
+    public void stopLeaderRoute() throws Exception {
+        camelContext.getRouteController().stopRoute("leader");
+    }
+}
diff --git a/integration-tests/master-file/src/main/java/org/apache/camel/quarkus/component/master/it/MasterRoutes.java b/integration-tests/master-file/src/main/java/org/apache/camel/quarkus/component/master/it/MasterRoutes.java
new file mode 100644
index 0000000000..8c4f744f62
--- /dev/null
+++ b/integration-tests/master-file/src/main/java/org/apache/camel/quarkus/component/master/it/MasterRoutes.java
@@ -0,0 +1,41 @@
+/*
+ * 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.master.it;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+@ApplicationScoped
+public class MasterRoutes extends RouteBuilder {
+
+    @ConfigProperty(name = "application.id")
+    String applicationId;
+
+    @Override
+    public void configure() {
+        // Output the id of the application into a file
+        from("master:ns:timer:test?period=100")
+                .id("leader")
+                .setBody(constant("leader"))
+                .setHeader(Exchange.FILE_NAME, constant(String.format("%s.txt", applicationId)))
+                .log(String.format("Application %s is writing into file", applicationId))
+                .to("file:target/cluster/");
+    }
+}
diff --git a/integration-tests/master-file/src/main/resources/application.properties b/integration-tests/master-file/src/main/resources/application.properties
new file mode 100644
index 0000000000..04061f086d
--- /dev/null
+++ b/integration-tests/master-file/src/main/resources/application.properties
@@ -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.
+## ---------------------------------------------------------------------------
+
+application.id = leader
+
+quarkus.camel.cluster.file.enabled = true
+quarkus.camel.cluster.file.root = target/cluster
+quarkus.camel.cluster.file.acquire-lock-delay = 1s
+quarkus.camel.cluster.file.acquire-lock-interval = 1000
diff --git a/integration-tests/master-file/src/test/java/org/apache/camel/quarkus/component/master/it/MasterFileIT.java b/integration-tests/master-file/src/test/java/org/apache/camel/quarkus/component/master/it/MasterFileIT.java
new file mode 100644
index 0000000000..c0d3dd44ea
--- /dev/null
+++ b/integration-tests/master-file/src/test/java/org/apache/camel/quarkus/component/master/it/MasterFileIT.java
@@ -0,0 +1,24 @@
+/*
+ * 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.master.it;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class MasterFileIT extends MasterFileTest {
+
+}
diff --git a/integration-tests/master-file/src/test/java/org/apache/camel/quarkus/component/master/it/MasterFileTest.java b/integration-tests/master-file/src/test/java/org/apache/camel/quarkus/component/master/it/MasterFileTest.java
new file mode 100644
index 0000000000..229c3731d7
--- /dev/null
+++ b/integration-tests/master-file/src/test/java/org/apache/camel/quarkus/component/master/it/MasterFileTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.master.it;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import org.apache.camel.quarkus.test.support.process.QuarkusProcessExecutor;
+import org.apache.commons.io.FileUtils;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.zeroturnaround.exec.StartedProcess;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.emptyString;
+
+@QuarkusTest
+class MasterFileTest {
+
+    @BeforeAll
+    public static void deleteClusterFiles() throws IOException {
+        FileUtils.deleteDirectory(Paths.get("target/cluster/").toFile());
+    }
+
+    @Test
+    public void testFailover() throws IOException {
+
+        List<String> jvmArgs = new ArrayList<>();
+        jvmArgs.add("-Dapplication.id=follower");
+
+        // Start secondary application process
+        QuarkusProcessExecutor quarkusProcessExecutor = new QuarkusProcessExecutor(jvmArgs.toArray(String[]::new));
+        StartedProcess process = quarkusProcessExecutor.start();
+
+        // Wait until the process is fully initialized
+        awaitStartup(quarkusProcessExecutor);
+
+        try {
+            // Verify that this process is the cluster leader
+            Awaitility.await().atMost(10, TimeUnit.SECONDS).with().until(() -> {
+                return readLeaderFile("leader").equals("leader");
+            });
+
+            // Verify the follower hasn't took leader role
+            assertThat(readLeaderFile("follower"), emptyString());
+
+            // Stop camel leader route to trigger fail-over
+            RestAssured.given().get("/master/camel/stop/leader").then().statusCode(204);
+
+            // Verify that the secondary application has been elected as the
+            // cluster leader
+            Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> {
+                return readLeaderFile("follower").equals("leader");
+            });
+        } finally {
+            if (process != null && process.getProcess().isAlive()) {
+                process.getProcess().destroy();
+            }
+        }
+    }
+
+    private void awaitStartup(QuarkusProcessExecutor quarkusProcessExecutor) {
+        Awaitility.await().atMost(10, TimeUnit.SECONDS).pollDelay(1, TimeUnit.SECONDS).until(() -> {
+            return isApplicationHealthy(quarkusProcessExecutor.getHttpPort());
+        });
+    }
+
+    private boolean isApplicationHealthy(int port) {
+        try {
+            int status = RestAssured.given().port(port).get("/q/health").then().extract().statusCode();
+            return status == 200;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    private String readLeaderFile(String fileName) throws IOException {
+        Path path = Paths.get(String.format("target/cluster/%s.txt", fileName));
+        if (path.toFile().exists()) {
+            return FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8);
+        }
+        return "";
+    }
+}
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index f192dc9c20..cdda0dcaff 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -144,6 +144,7 @@
         <module>lumberjack</module>
         <module>mail</module>
         <module>master</module>
+        <module>master-file</module>
         <module>master-openshift</module>
         <module>messaging</module>
         <module>micrometer</module>
diff --git a/tooling/scripts/test-categories.yaml b/tooling/scripts/test-categories.yaml
index 72fd18de4b..5ab72b46ca 100644
--- a/tooling/scripts/test-categories.yaml
+++ b/tooling/scripts/test-categories.yaml
@@ -128,6 +128,7 @@ group-08:
   - dozer
   - jq
   - master
+  - master-file
   - master-openshift
   - pdf
   - saxon
diff --git a/tooling/test-list/pom.xml b/tooling/test-list/pom.xml
index 844f3720bf..b6b78a1ee4 100644
--- a/tooling/test-list/pom.xml
+++ b/tooling/test-list/pom.xml
@@ -58,6 +58,7 @@
                                 <exclude>support/**/*</exclude>
                                 <exclude>master/pom.xml</exclude>
                                 <exclude>master-openshift/pom.xml</exclude>
+                                <exclude>master-file/pom.xml</exclude>
                                 <exclude>main-command-mode/pom.xml</exclude>
                                 <exclude>main-unknown-args-fail/pom.xml</exclude>
                                 <exclude>main-unknown-args-ignore/pom.xml</exclude>


[camel-quarkus] 01/05: automatic configuration of FileLockClusterService #4262

Posted by ja...@apache.org.
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 ae9ac0edd711df8b7cda100fc40e4bf4e1bfc7a2
Author: aldettinger <al...@gmail.com>
AuthorDate: Tue Nov 8 15:37:45 2022 +0100

    automatic configuration of FileLockClusterService #4262
---
 .../ROOT/pages/reference/extensions/file.adoc      |  81 +++++++++++++++++
 .../ROOT/pages/reference/extensions/master.adoc    |   1 +
 extensions/file/deployment/pom.xml                 |   5 +
 .../FileLockClusterServiceProcessor.java           |  41 +++++++++
 ...LockClusterServiceConfigDefaultEnabledTest.java |  92 +++++++++++++++++++
 .../FileLockClusterServiceConfigDefaultTest.java   |  74 +++++++++++++++
 ...kClusterServiceConfigNonDefaultEnabledTest.java | 101 +++++++++++++++++++++
 .../file/runtime/src/main/doc/configuration.adoc   |  23 +++++
 .../file/cluster/FileLockClusterServiceConfig.java |  80 ++++++++++++++++
 .../cluster/FileLockClusterServiceRecorder.java    |  49 ++++++++++
 .../master/runtime/src/main/doc/configuration.adoc |   1 +
 11 files changed, 548 insertions(+)

diff --git a/docs/modules/ROOT/pages/reference/extensions/file.adoc b/docs/modules/ROOT/pages/reference/extensions/file.adoc
index 5c4676e61d..90f69ee3e3 100644
--- a/docs/modules/ROOT/pages/reference/extensions/file.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/file.adoc
@@ -44,3 +44,84 @@ Or add the coordinates to your existing project:
 ifeval::[{doc-show-user-guide-link} == true]
 Check the xref:user-guide/index.adoc[User guide] for more information about writing Camel Quarkus applications.
 endif::[]
+
+[id="extensions-file-additional-camel-quarkus-configuration"]
+== Additional Camel Quarkus configuration
+
+
+[id="extensions-file-configuration-having-only-a-single-consumer-in-a-cluster-consuming-from-a-given-endpoint"]
+=== Having only a single consumer in a cluster consuming from a given endpoint
+
+When the same route is deployed on multiple JVMs, it could be interesting to use this extension in conjunction with the xref:reference/extensions/master.adoc[Master one].
+In such a setup, a single consumer will be active at a time across the whole camel master namespace.
+
+For instance, having the route below deployed on multiple JVMs:
+
+```
+from("master:ns:timer:test?period=100").log("Timer invoked on a single JVM at a time");
+```
+
+It's possible to enable the file cluster service with a property like below:
+
+```
+quarkus.camel.cluster.file.enabled = true
+```
+
+As a result, a single consumer will be active across the `ns` camel master namespace.
+It means that, at a given time, only a single timer will generate exchanges across all JVMs.
+In other words, messages will be logged every 100ms on a single JVM at a time.
+
+The file cluster service could further be tuned by tweaking `quarkus.camel.cluster.file.*` properties.
+
+
+[width="100%",cols="80,5,15",options="header"]
+|===
+| Configuration property | Type | Default
+
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.cluster.file.enabled]]`link:#quarkus.camel.cluster.file.enabled[quarkus.camel.cluster.file.enabled]`
+
+Whether a File Lock Cluster Service should be automatically configured according to 'quarkus.camel.cluster.file.++*++' configurations.
+| `boolean`
+| `false`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.cluster.file.id]]`link:#quarkus.camel.cluster.file.id[quarkus.camel.cluster.file.id]`
+
+The cluster service ID (defaults to null).
+| `string`
+| 
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.cluster.file.root]]`link:#quarkus.camel.cluster.file.root[quarkus.camel.cluster.file.root]`
+
+The root path (defaults to null).
+| `string`
+| 
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.cluster.file.order]]`link:#quarkus.camel.cluster.file.order[quarkus.camel.cluster.file.order]`
+
+The service lookup order/priority (defaults to 2147482647).
+| `java.lang.Integer`
+| 
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.cluster.file.acquire-lock-delay]]`link:#quarkus.camel.cluster.file.acquire-lock-delay[quarkus.camel.cluster.file.acquire-lock-delay]`
+
+The time to wait before starting to try to acquire lock (defaults to 1000ms).
+| `string`
+| 
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.cluster.file.acquire-lock-interval]]`link:#quarkus.camel.cluster.file.acquire-lock-interval[quarkus.camel.cluster.file.acquire-lock-interval]`
+
+The time to wait between attempts to try to acquire lock (defaults to 10000ms).
+| `string`
+| 
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.cluster.file.attributes]]`link:#quarkus.camel.cluster.file.attributes[quarkus.camel.cluster.file.attributes]`
+
+The custom attributes associated to the service (defaults to empty map).
+| ``Map<String,String>``
+| 
+|===
+
+[.configuration-legend]
+{doc-link-icon-lock}[title=Fixed at build time] Configuration property fixed at build time. All other configuration properties are overridable at runtime.
+
diff --git a/docs/modules/ROOT/pages/reference/extensions/master.adoc b/docs/modules/ROOT/pages/reference/extensions/master.adoc
index 09c2ce58bc..d5e7bf3c11 100644
--- a/docs/modules/ROOT/pages/reference/extensions/master.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/master.adoc
@@ -50,5 +50,6 @@ endif::[]
 
 This extension can be used in conjunction with extensions below:
 
+* xref:reference/extensions/file.adoc[Camel Quarkus File]
 * xref:reference/extensions/kubernetes.adoc[Camel Quarkus Kubernetes]
 
diff --git a/extensions/file/deployment/pom.xml b/extensions/file/deployment/pom.xml
index f0041bdfab..f0ef69e136 100644
--- a/extensions/file/deployment/pom.xml
+++ b/extensions/file/deployment/pom.xml
@@ -38,6 +38,11 @@
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-file</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5-internal</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/extensions/file/deployment/src/main/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceProcessor.java b/extensions/file/deployment/src/main/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceProcessor.java
new file mode 100644
index 0000000000..36793289ed
--- /dev/null
+++ b/extensions/file/deployment/src/main/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceProcessor.java
@@ -0,0 +1,41 @@
+/*
+ * 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.file.cluster.deployment;
+
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.Consume;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.runtime.RuntimeValue;
+import org.apache.camel.component.file.cluster.FileLockClusterService;
+import org.apache.camel.quarkus.component.file.cluster.FileLockClusterServiceConfig;
+import org.apache.camel.quarkus.component.file.cluster.FileLockClusterServiceRecorder;
+import org.apache.camel.quarkus.core.deployment.spi.CamelBeanBuildItem;
+import org.apache.camel.quarkus.core.deployment.spi.CamelContextBuildItem;
+
+class FileLockClusterServiceProcessor {
+
+    @Record(ExecutionTime.STATIC_INIT)
+    @BuildStep(onlyIf = FileLockClusterServiceConfig.Enabled.class)
+    @Consume(CamelContextBuildItem.class)
+    CamelBeanBuildItem setupFileLockClusterService(FileLockClusterServiceConfig config,
+            FileLockClusterServiceRecorder recorder) {
+
+        final RuntimeValue<FileLockClusterService> flcs = recorder.createFileLockClusterService(config);
+        return new CamelBeanBuildItem("fileLockClusterService", FileLockClusterService.class.getName(), flcs);
+    }
+}
diff --git a/extensions/file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigDefaultEnabledTest.java b/extensions/file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigDefaultEnabledTest.java
new file mode 100644
index 0000000000..1a6e13cb74
--- /dev/null
+++ b/extensions/file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigDefaultEnabledTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.file.cluster.deployment;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Ordered;
+import org.apache.camel.component.file.cluster.FileLockClusterService;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.Asset;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FileLockClusterServiceConfigDefaultEnabledTest {
+
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addAsResource(applicationProperties(),
+                    "application.properties"));
+
+    public static final Asset applicationProperties() {
+        Writer writer = new StringWriter();
+
+        Properties props = new Properties();
+        props.setProperty("quarkus.camel.cluster.file.enabled", "true");
+
+        try {
+            props.store(writer, "");
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return new StringAsset(writer.toString());
+    }
+
+    @Inject
+    CamelContext camelContext;
+
+    @Test
+    public void defaultEnabledConfigShouldAutoConfigureWithDefaults() {
+
+        DefaultCamelContext dcc = camelContext.adapt(DefaultCamelContext.class);
+        assertNotNull(dcc);
+
+        FileLockClusterService[] flcs = dcc.getServices().stream().filter(s -> s instanceof FileLockClusterService)
+                .toArray(FileLockClusterService[]::new);
+        assertEquals(1, flcs.length);
+
+        FileLockClusterService service = flcs[0];
+        assertNotNull(service);
+
+        assertNull(service.getId());
+        assertNull(service.getRoot());
+        assertEquals(Ordered.LOWEST, service.getOrder());
+        assertNotNull(service.getAttributes());
+        assertTrue(service.getAttributes().isEmpty());
+        assertEquals(1L, service.getAcquireLockDelay());
+        assertEquals(TimeUnit.SECONDS, service.getAcquireLockDelayUnit());
+        assertEquals(10L, service.getAcquireLockInterval());
+        assertEquals(TimeUnit.SECONDS, service.getAcquireLockIntervalUnit());
+    }
+}
diff --git a/extensions/file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigDefaultTest.java b/extensions/file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigDefaultTest.java
new file mode 100644
index 0000000000..52ad45157a
--- /dev/null
+++ b/extensions/file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigDefaultTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.file.cluster.deployment;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Properties;
+
+import javax.inject.Inject;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.file.cluster.FileLockClusterService;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.Asset;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class FileLockClusterServiceConfigDefaultTest {
+
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addAsResource(applicationProperties(),
+                    "application.properties"));
+
+    public static final Asset applicationProperties() {
+        Writer writer = new StringWriter();
+
+        Properties props = new Properties();
+
+        try {
+            props.store(writer, "");
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return new StringAsset(writer.toString());
+    }
+
+    @Inject
+    CamelContext camelContext;
+
+    @Test
+    public void defaultConfigShouldNotAutoConfigure() {
+
+        DefaultCamelContext dcc = camelContext.adapt(DefaultCamelContext.class);
+        assertNotNull(dcc);
+
+        FileLockClusterService[] flcs = dcc.getServices().stream().filter(s -> s instanceof FileLockClusterService)
+                .toArray(FileLockClusterService[]::new);
+        assertEquals(0, flcs.length);
+    }
+}
diff --git a/extensions/file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigNonDefaultEnabledTest.java b/extensions/file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigNonDefaultEnabledTest.java
new file mode 100644
index 0000000000..114443f1c2
--- /dev/null
+++ b/extensions/file/deployment/src/test/java/org/apache/camel/quarkus/component/file/cluster/deployment/FileLockClusterServiceConfigNonDefaultEnabledTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.file.cluster.deployment;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.file.cluster.FileLockClusterService;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.Asset;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FileLockClusterServiceConfigNonDefaultEnabledTest {
+
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addAsResource(applicationProperties(),
+                    "application.properties"));
+
+    public static final Asset applicationProperties() {
+        Writer writer = new StringWriter();
+
+        Properties props = new Properties();
+        props.setProperty("quarkus.camel.cluster.file.enabled", "true");
+        props.setProperty("quarkus.camel.cluster.file.id", "service-id");
+        props.setProperty("quarkus.camel.cluster.file.root", "root-path");
+        props.setProperty("quarkus.camel.cluster.file.order", "10");
+        props.setProperty("quarkus.camel.cluster.file.attributes.key1", "value1");
+        props.setProperty("quarkus.camel.cluster.file.attributes.key2", "value2");
+        props.setProperty("quarkus.camel.cluster.file.acquire-lock-delay", "5");
+        props.setProperty("quarkus.camel.cluster.file.acquire-lock-interval", "1h");
+
+        try {
+            props.store(writer, "");
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return new StringAsset(writer.toString());
+    }
+
+    @Inject
+    CamelContext camelContext;
+
+    @Test
+    public void nonDefaultEnabledConfigShouldAutoConfigureWithoutDefaults() {
+
+        DefaultCamelContext dcc = camelContext.adapt(DefaultCamelContext.class);
+        assertNotNull(dcc);
+
+        FileLockClusterService[] flcs = dcc.getServices().stream().filter(s -> s instanceof FileLockClusterService)
+                .toArray(FileLockClusterService[]::new);
+        assertEquals(1, flcs.length);
+
+        FileLockClusterService service = flcs[0];
+        assertNotNull(service);
+        assertEquals("service-id", service.getId());
+        assertEquals("root-path", service.getRoot());
+        assertEquals(10, service.getOrder());
+
+        assertNotNull(service.getAttributes());
+        assertTrue(service.getAttributes().containsKey("key1"));
+        assertEquals("value1", service.getAttributes().get("key1"));
+        assertTrue(service.getAttributes().containsKey("key2"));
+        assertEquals("value2", service.getAttributes().get("key2"));
+
+        assertEquals(5L, service.getAcquireLockDelay());
+        assertEquals(TimeUnit.MILLISECONDS, service.getAcquireLockDelayUnit());
+        assertEquals(3600000, service.getAcquireLockInterval());
+        assertEquals(TimeUnit.MILLISECONDS, service.getAcquireLockIntervalUnit());
+    }
+}
diff --git a/extensions/file/runtime/src/main/doc/configuration.adoc b/extensions/file/runtime/src/main/doc/configuration.adoc
new file mode 100644
index 0000000000..194f451d48
--- /dev/null
+++ b/extensions/file/runtime/src/main/doc/configuration.adoc
@@ -0,0 +1,23 @@
+
+=== Having only a single consumer in a cluster consuming from a given endpoint
+
+When the same route is deployed on multiple JVMs, it could be interesting to use this extension in conjunction with the xref:reference/extensions/master.adoc[Master one].
+In such a setup, a single consumer will be active at a time across the whole camel master namespace.
+
+For instance, having the route below deployed on multiple JVMs:
+
+```
+from("master:ns:timer:test?period=100").log("Timer invoked on a single JVM at a time");
+```
+
+It's possible to enable the file cluster service with a property like below:
+
+```
+quarkus.camel.cluster.file.enabled = true
+```
+
+As a result, a single consumer will be active across the `ns` camel master namespace.
+It means that, at a given time, only a single timer will generate exchanges across all JVMs.
+In other words, messages will be logged every 100ms on a single JVM at a time.
+
+The file cluster service could further be tuned by tweaking `quarkus.camel.cluster.file.*` properties.
diff --git a/extensions/file/runtime/src/main/java/org/apache/camel/quarkus/component/file/cluster/FileLockClusterServiceConfig.java b/extensions/file/runtime/src/main/java/org/apache/camel/quarkus/component/file/cluster/FileLockClusterServiceConfig.java
new file mode 100644
index 0000000000..e67c5e72bf
--- /dev/null
+++ b/extensions/file/runtime/src/main/java/org/apache/camel/quarkus/component/file/cluster/FileLockClusterServiceConfig.java
@@ -0,0 +1,80 @@
+/*
+ * 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.file.cluster;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BooleanSupplier;
+
+import io.quarkus.runtime.annotations.ConfigItem;
+import io.quarkus.runtime.annotations.ConfigRoot;
+import org.eclipse.microprofile.config.ConfigProvider;
+
+@ConfigRoot(name = "camel.cluster.file")
+public class FileLockClusterServiceConfig {
+
+    /**
+     * Whether a File Lock Cluster Service should be automatically configured
+     * according to 'quarkus.camel.cluster.file.*' configurations.
+     */
+    @ConfigItem(defaultValue = "false")
+    public boolean enabled;
+
+    /**
+     * The cluster service ID (defaults to null).
+     */
+    @ConfigItem
+    public Optional<String> id;
+
+    /**
+     * The root path (defaults to null).
+     */
+    @ConfigItem
+    public Optional<String> root;
+
+    /**
+     * The service lookup order/priority (defaults to 2147482647).
+     */
+    @ConfigItem
+    public Optional<Integer> order;
+
+    /**
+     * The custom attributes associated to the service (defaults to empty map).
+     */
+    @ConfigItem
+    public Map<String, String> attributes;
+
+    /**
+     * The time to wait before starting to try to acquire lock (defaults to 1000ms).
+     */
+    @ConfigItem
+    public Optional<String> acquireLockDelay;
+
+    /**
+     * The time to wait between attempts to try to acquire lock (defaults to 10000ms).
+     */
+    @ConfigItem
+    public Optional<String> acquireLockInterval;
+
+    public static final class Enabled implements BooleanSupplier {
+        @Override
+        public boolean getAsBoolean() {
+            return ConfigProvider.getConfig().getOptionalValue("quarkus.camel.cluster.file.enabled", Boolean.class)
+                    .orElse(Boolean.FALSE);
+        }
+    }
+}
diff --git a/extensions/file/runtime/src/main/java/org/apache/camel/quarkus/component/file/cluster/FileLockClusterServiceRecorder.java b/extensions/file/runtime/src/main/java/org/apache/camel/quarkus/component/file/cluster/FileLockClusterServiceRecorder.java
new file mode 100644
index 0000000000..5744961c5d
--- /dev/null
+++ b/extensions/file/runtime/src/main/java/org/apache/camel/quarkus/component/file/cluster/FileLockClusterServiceRecorder.java
@@ -0,0 +1,49 @@
+/*
+ * 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.file.cluster;
+
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.runtime.RuntimeValue;
+import io.quarkus.runtime.annotations.Recorder;
+import org.apache.camel.catalog.impl.TimePatternConverter;
+import org.apache.camel.component.file.cluster.FileLockClusterService;
+
+@Recorder
+public class FileLockClusterServiceRecorder {
+
+    public RuntimeValue<FileLockClusterService> createFileLockClusterService(FileLockClusterServiceConfig config) {
+        FileLockClusterService flcs = new FileLockClusterService();
+
+        config.id.ifPresent(id -> flcs.setId(id));
+        config.root.ifPresent(root -> flcs.setRoot(root));
+        config.order.ifPresent(order -> flcs.setOrder(order));
+        config.acquireLockDelay.ifPresent(delay -> {
+            flcs.setAcquireLockDelay(TimePatternConverter.toMilliSeconds(delay), TimeUnit.MILLISECONDS);
+        });
+        config.acquireLockInterval.ifPresent(interval -> {
+            flcs.setAcquireLockInterval(TimePatternConverter.toMilliSeconds(interval), TimeUnit.MILLISECONDS);
+        });
+
+        config.attributes.forEach((key, value) -> {
+            flcs.setAttribute(key, value);
+        });
+
+        return new RuntimeValue<FileLockClusterService>(flcs);
+    }
+
+}
diff --git a/extensions/master/runtime/src/main/doc/configuration.adoc b/extensions/master/runtime/src/main/doc/configuration.adoc
index 8fa5657956..ad818766dd 100644
--- a/extensions/master/runtime/src/main/doc/configuration.adoc
+++ b/extensions/master/runtime/src/main/doc/configuration.adoc
@@ -1,3 +1,4 @@
 This extension can be used in conjunction with extensions below:
 
+* xref:reference/extensions/file.adoc[Camel Quarkus File]
 * xref:reference/extensions/kubernetes.adoc[Camel Quarkus Kubernetes]
\ No newline at end of file


[camel-quarkus] 05/05: CxfSoapClientTest.wsdlUpToDate() and CxfSoapWssClientTest.wsdlUpToDate() fail on the platform #4254

Posted by ja...@apache.org.
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 1b7846dce5fe0a5390df772373e3a4caed733bf8
Author: Peter Palaga <pp...@redhat.com>
AuthorDate: Wed Nov 9 15:29:53 2022 +0100

    CxfSoapClientTest.wsdlUpToDate() and CxfSoapWssClientTest.wsdlUpToDate() fail on the platform #4254
---
 .../cxf-soap/cxf-soap-client/pom.xml                  |  1 +
 .../cxf/soap/client/it/CxfSoapClientTest.java         | 19 ++++++++++++++++---
 .../cxf-soap/cxf-soap-ws-security-client/pom.xml      |  1 +
 .../cxf/soap/wss/client/it/CxfSoapWssClientTest.java  | 18 +++++++++++++++---
 integration-tests/cxf-soap-grouped/pom.xml            |  3 +++
 5 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/integration-test-groups/cxf-soap/cxf-soap-client/pom.xml b/integration-test-groups/cxf-soap/cxf-soap-client/pom.xml
index 6e293ce9c4..0d2cc76841 100644
--- a/integration-test-groups/cxf-soap/cxf-soap-client/pom.xml
+++ b/integration-test-groups/cxf-soap/cxf-soap-client/pom.xml
@@ -44,6 +44,7 @@
                             <wsdlOptions>
                                 <wsdlOption>
                                     <wsdl>${basedir}/src/main/resources/wsdl/CalculatorService.wsdl</wsdl>
+                                    <wsdlLocation>classpath:wsdl/CalculatorService.wsdl</wsdlLocation>
                                 </wsdlOption>
                             </wsdlOptions>
                         </configuration>
diff --git a/integration-test-groups/cxf-soap/cxf-soap-client/src/test/java/org/apache/camel/quarkus/component/cxf/soap/client/it/CxfSoapClientTest.java b/integration-test-groups/cxf-soap/cxf-soap-client/src/test/java/org/apache/camel/quarkus/component/cxf/soap/client/it/CxfSoapClientTest.java
index 5f96277b2f..88f7684ee4 100644
--- a/integration-test-groups/cxf-soap/cxf-soap-client/src/test/java/org/apache/camel/quarkus/component/cxf/soap/client/it/CxfSoapClientTest.java
+++ b/integration-test-groups/cxf-soap/cxf-soap-client/src/test/java/org/apache/camel/quarkus/component/cxf/soap/client/it/CxfSoapClientTest.java
@@ -17,8 +17,10 @@
 package org.apache.camel.quarkus.component.cxf.soap.client.it;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 
 import io.quarkus.test.common.QuarkusTestResource;
@@ -68,11 +70,22 @@ class CxfSoapClientTest {
         final String wsdlUrl = ConfigProvider.getConfig()
                 .getValue("camel-quarkus.it.calculator.baseUri", String.class);
 
-        final String staticCopyPath = "target/classes/wsdl/CalculatorService.wsdl";
+        final String wsdlRelPath = "wsdl/CalculatorService.wsdl";
+        final Path staticCopyPath = Paths.get("target/classes/" + wsdlRelPath);
+        if (!Files.isRegularFile(staticCopyPath)) {
+            /* The test is run inside Quarkus Platform
+             * and the resource is not available in the filesystem
+             * So let's copy it */
+            Files.createDirectories(staticCopyPath.getParent());
+            try (InputStream in = getClass().getClassLoader().getResourceAsStream(wsdlRelPath)) {
+                Files.copy(in, staticCopyPath);
+            }
+        }
+
         /* The changing Docker IP address in the WSDL should not matter */
         final String sanitizerRegex = "<soap:address location=\"http://[^/]*/calculator-ws/CalculatorService\"></soap:address>";
         final String staticCopyContent = Files
-                .readString(Paths.get(staticCopyPath), StandardCharsets.UTF_8)
+                .readString(staticCopyPath, StandardCharsets.UTF_8)
                 .replaceAll(sanitizerRegex, "")
                 //remove a comment with license
                 .replaceAll("<!--[.\\s\\S]*?-->", "\n")
@@ -86,7 +99,7 @@ class CxfSoapClientTest {
                 .extract().body().asString();
 
         if (!expected.replaceAll(sanitizerRegex, "").replaceAll("\\s", "").equals(staticCopyContent)) {
-            Files.writeString(Paths.get(staticCopyPath), expected, StandardCharsets.UTF_8);
+            Files.writeString(staticCopyPath, expected, StandardCharsets.UTF_8);
             Assertions.fail("The static WSDL copy in " + staticCopyPath
                     + " went out of sync with the WSDL served by the container. The content was updated by the test, you just need to review and commit the changes.");
         }
diff --git a/integration-test-groups/cxf-soap/cxf-soap-ws-security-client/pom.xml b/integration-test-groups/cxf-soap/cxf-soap-ws-security-client/pom.xml
index 29fa54e7c3..913fbdfe16 100644
--- a/integration-test-groups/cxf-soap/cxf-soap-ws-security-client/pom.xml
+++ b/integration-test-groups/cxf-soap/cxf-soap-ws-security-client/pom.xml
@@ -44,6 +44,7 @@
                             <wsdlOptions>
                                 <wsdlOption>
                                     <wsdl>${basedir}/src/main/resources/wsdl/WssCalculatorService.wsdl</wsdl>
+                                    <wsdlLocation>classpath:wsdl/wsdl/WssCalculatorService.wsdl</wsdlLocation>
                                 </wsdlOption>
                             </wsdlOptions>
                         </configuration>
diff --git a/integration-test-groups/cxf-soap/cxf-soap-ws-security-client/src/test/java/org/apache/camel/quarkus/component/cxf/soap/wss/client/it/CxfSoapWssClientTest.java b/integration-test-groups/cxf-soap/cxf-soap-ws-security-client/src/test/java/org/apache/camel/quarkus/component/cxf/soap/wss/client/it/CxfSoapWssClientTest.java
index 45f1b47938..ca35e586f8 100644
--- a/integration-test-groups/cxf-soap/cxf-soap-ws-security-client/src/test/java/org/apache/camel/quarkus/component/cxf/soap/wss/client/it/CxfSoapWssClientTest.java
+++ b/integration-test-groups/cxf-soap/cxf-soap-ws-security-client/src/test/java/org/apache/camel/quarkus/component/cxf/soap/wss/client/it/CxfSoapWssClientTest.java
@@ -17,8 +17,10 @@
 package org.apache.camel.quarkus.component.cxf.soap.wss.client.it;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 
 import io.quarkus.test.common.QuarkusTestResource;
@@ -56,11 +58,21 @@ class CxfSoapWssClientTest {
         final String wsdlUrl = ConfigProvider.getConfig()
                 .getValue("camel-quarkus.it.wss.client.baseUri", String.class);
 
-        final String staticCopyPath = "target/classes/wsdl/WssCalculatorService.wsdl";
+        final String wsdlRelPath = "wsdl/WssCalculatorService.wsdl";
+        final Path staticCopyPath = Paths.get("target/classes/" + wsdlRelPath);
+        if (!Files.isRegularFile(staticCopyPath)) {
+            /* The test is run inside Quarkus Platform
+             * and the resource is not available in the filesystem
+             * So let's copy it */
+            Files.createDirectories(staticCopyPath.getParent());
+            try (InputStream in = getClass().getClassLoader().getResourceAsStream(wsdlRelPath)) {
+                Files.copy(in, staticCopyPath);
+            }
+        }
         /* The changing Docker IP address in the WSDL should not matter */
         final String sanitizerRegex = "<soap:address location=\"http://[^/]*/calculator-ws/WssCalculatorService\"></soap:address>";
         final String staticCopyContent = Files
-                .readString(Paths.get(staticCopyPath), StandardCharsets.UTF_8)
+                .readString(staticCopyPath, StandardCharsets.UTF_8)
                 .replaceAll(sanitizerRegex, "")
                 //remove a comment with license
                 .replaceAll("<!--[.\\s\\S]*?-->", "\n")
@@ -76,7 +88,7 @@ class CxfSoapWssClientTest {
         final String expectedContent = expected.replaceAll(sanitizerRegex, "");
 
         if (!expected.replaceAll(sanitizerRegex, "").replaceAll("\\s", "").equals(staticCopyContent)) {
-            Files.writeString(Paths.get(staticCopyPath), expectedContent, StandardCharsets.UTF_8);
+            Files.writeString(staticCopyPath, expectedContent, StandardCharsets.UTF_8);
             Assertions.fail("The static WSDL copy in " + staticCopyPath
                     + " went out of sync with the WSDL served by the container. The content was updated by the test, you just need to review and commit the changes.");
         }
diff --git a/integration-tests/cxf-soap-grouped/pom.xml b/integration-tests/cxf-soap-grouped/pom.xml
index 791ad5b1ff..842e3fa584 100644
--- a/integration-tests/cxf-soap-grouped/pom.xml
+++ b/integration-tests/cxf-soap-grouped/pom.xml
@@ -169,12 +169,15 @@
                             <wsdlOptions>
                                 <wsdlOption>
                                     <wsdl>${basedir}/target/classes/wsdl/CalculatorService.wsdl</wsdl>
+                                    <wsdlLocation>classpath:wsdl/CalculatorService.wsdl</wsdlLocation>
                                 </wsdlOption>
                                 <wsdlOption>
                                     <wsdl>${basedir}/target/classes/wsdl/HelloService.wsdl</wsdl>
+                                    <wsdlLocation>classpath:wsdl/HelloService.wsdl</wsdlLocation>
                                 </wsdlOption>
                                 <wsdlOption>
                                     <wsdl>${basedir}/target/classes/wsdl/WssCalculatorService.wsdl</wsdl>
+                                    <wsdlLocation>classpath:wsdl/WssCalculatorService.wsdl</wsdlLocation>
                                 </wsdlOption>
                             </wsdlOptions>
                         </configuration>


[camel-quarkus] 04/05: controlbus: Added language tests (fixes #4008)

Posted by ja...@apache.org.
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 9a1c961ba607cb96a0bfd62372cb8ac9e67467d3
Author: Darren Coleman <dc...@redhat.com>
AuthorDate: Tue Nov 8 14:21:00 2022 +0000

    controlbus: Added language tests (fixes #4008)
---
 .../pages/reference/extensions/controlbus.adoc     | 48 +++++++++++-
 .../controlbus/runtime/src/main/doc/usage.adoc     | 46 +++++++++++-
 .../foundation/controlbus/pom.xml                  | 17 +++++
 .../component/controlbus/it/ControlbusBean.java    | 40 ++++++++++
 .../controlbus/it/ControlbusLanguageResource.java  | 86 ++++++++++++++++++++++
 .../controlbus/it/ControlbusLanguageRoute.java     | 45 +++++++++++
 .../controlbus/it/ControlbusLanguageIT.java        | 24 ++++++
 .../controlbus/it/ControlbusLanguageTest.java      | 54 ++++++++++++++
 8 files changed, 357 insertions(+), 3 deletions(-)

diff --git a/docs/modules/ROOT/pages/reference/extensions/controlbus.adoc b/docs/modules/ROOT/pages/reference/extensions/controlbus.adoc
index 2791345494..191182d933 100644
--- a/docs/modules/ROOT/pages/reference/extensions/controlbus.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/controlbus.adoc
@@ -47,8 +47,11 @@ endif::[]
 
 [id="extensions-controlbus-usage"]
 == Usage
+[id="extensions-controlbus-usage-actions"]
+=== Actions
+
 [id="extensions-controlbus-usage-statistics"]
-=== Statistics
+==== Statistics
 
 When using the `stats` command endpoint, the `camel-quarkus-management` extension must be added as a project dependency to enable JMX. Maven users will have to add the following to their `pom.xml`:
 
@@ -61,6 +64,49 @@ When using the `stats` command endpoint, the `camel-quarkus-management` extensio
 ----
 
 
+[id="extensions-controlbus-usage-languages"]
+=== Languages
+
+[id="extensions-controlbus-usage-bean"]
+==== Bean
+
+The Bean language can be used to invoke a method on a Bean to control the state of routes. The `org.apache.camel.quarkus:camel-quarkus-bean` extension must be added to the classpath. Maven users must add the following dependency to the POM:
+
+[source,xml]
+----
+<dependency>
+    <groupId>org.apache.camel.quarkus</groupId>
+    <artifactId>camel-quarkus-bean</artifactId>
+</dependency>
+----
+
+In native mode, the Bean class must be annotated with `@RegisterForReflection`.
+
+[id="extensions-controlbus-usage-simple"]
+==== Simple
+
+The Simple language can be used to control the state of routes. The following example uses a `ProducerTemplate` to stop a route with the id `foo`:
+
+[source,java]
+----
+template.sendBody(
+    "controlbus:language:simple", 
+    "${camelContext.getRouteController().stopRoute('foo')}"
+);
+----
+
+To use the OGNL notation, the `org.apache.camel.quarkus:camel-quarkus-bean` extension must be added as a dependency.
+
+In native mode, the classes used in the OGNL notation must be registered for reflection. In the above code snippet, the `org.apache.camel.spi.RouteController` class returned from `camelContext.getRouteController()` must be registered. As this is a third-party class, it cannot be annotated with `@RegisterForReflection` directly - instead you can annotate a different class and specifying the target classes to register. For example, the class defining the Camel routes could be annotated wit [...]
+
+Alternatively, add the following line to your `src/main/resources/application.properties`:
+
+[source,properties]
+----
+quarkus.camel.native.reflection.include-patterns = org.apache.camel.spi.RouteController
+----
+
+
 [id="extensions-controlbus-camel-quarkus-limitations"]
 == Camel Quarkus limitations
 
diff --git a/extensions/controlbus/runtime/src/main/doc/usage.adoc b/extensions/controlbus/runtime/src/main/doc/usage.adoc
index cbe7cdd4c9..39d6a7df72 100644
--- a/extensions/controlbus/runtime/src/main/doc/usage.adoc
+++ b/extensions/controlbus/runtime/src/main/doc/usage.adoc
@@ -1,4 +1,6 @@
-=== Statistics
+=== Actions
+
+==== Statistics
 
 When using the `stats` command endpoint, the `camel-quarkus-management` extension must be added as a project dependency to enable JMX. Maven users will have to add the following to their `pom.xml`:
 
@@ -8,4 +10,44 @@ When using the `stats` command endpoint, the `camel-quarkus-management` extensio
     <groupId>org.apache.camel.quarkus</groupId>
     <artifactId>camel-quarkus-management</artifactId>
 </dependency>
-----
\ No newline at end of file
+----
+
+
+=== Languages
+
+==== Bean
+
+The Bean language can be used to invoke a method on a Bean to control the state of routes. The `org.apache.camel.quarkus:camel-quarkus-bean` extension must be added to the classpath. Maven users must add the following dependency to the POM:
+
+[source,xml]
+----
+<dependency>
+    <groupId>org.apache.camel.quarkus</groupId>
+    <artifactId>camel-quarkus-bean</artifactId>
+</dependency>
+----
+
+In native mode, the Bean class must be annotated with `@RegisterForReflection`.
+
+==== Simple
+
+The Simple language can be used to control the state of routes. The following example uses a `ProducerTemplate` to stop a route with the id `foo`:
+
+[source,java]
+----
+template.sendBody(
+    "controlbus:language:simple", 
+    "${camelContext.getRouteController().stopRoute('foo')}"
+);
+----
+
+To use the OGNL notation, the `org.apache.camel.quarkus:camel-quarkus-bean` extension must be added as a dependency.
+
+In native mode, the classes used in the OGNL notation must be registered for reflection. In the above code snippet, the `org.apache.camel.spi.RouteController` class returned from `camelContext.getRouteController()` must be registered. As this is a third-party class, it cannot be annotated with `@RegisterForReflection` directly - instead you can annotate a different class and specifying the target classes to register. For example, the class defining the Camel routes could be annotated wit [...]
+
+Alternatively, add the following line to your `src/main/resources/application.properties`:
+
+[source,properties]
+----
+quarkus.camel.native.reflection.include-patterns = org.apache.camel.spi.RouteController
+----
diff --git a/integration-test-groups/foundation/controlbus/pom.xml b/integration-test-groups/foundation/controlbus/pom.xml
index 95751ee60d..55ed23b4f3 100644
--- a/integration-test-groups/foundation/controlbus/pom.xml
+++ b/integration-test-groups/foundation/controlbus/pom.xml
@@ -31,6 +31,10 @@
     <description>Integration tests for Camel Quarkus Control Bus extension</description>
 
     <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-bean</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-controlbus</artifactId>
@@ -102,6 +106,19 @@
             </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-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-controlbus-deployment</artifactId>
diff --git a/integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusBean.java b/integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusBean.java
new file mode 100644
index 0000000000..4d7db4000e
--- /dev/null
+++ b/integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusBean.java
@@ -0,0 +1,40 @@
+/*
+ * 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.controlbus.it;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Named;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Header;
+
+@ApplicationScoped
+@Named("controlbus-bean")
+@RegisterForReflection(fields = false)
+public class ControlbusBean {
+
+    private CamelContext context;
+
+    ControlbusBean(CamelContext context) {
+        this.context = context;
+    }
+
+    public void stopRoute(@Header("routeId") String routeId) throws Exception {
+        context.getRouteController().stopRoute(routeId);
+    }
+}
diff --git a/integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageResource.java b/integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageResource.java
new file mode 100644
index 0000000000..1f57cee344
--- /dev/null
+++ b/integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageResource.java
@@ -0,0 +1,86 @@
+/*
+ * 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.controlbus.it;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.camel.ProducerTemplate;
+
+@Path("/controlbus/language")
+@ApplicationScoped
+public class ControlbusLanguageResource {
+
+    private static final String CONTROL_ROUTE_ID = "lang-control";
+
+    @Inject
+    ProducerTemplate producerTemplate;
+
+    @Path("/status")
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String status() throws Exception {
+        return producerTemplate.requestBody(
+                String.format("controlbus:route?routeId=%s&action=status", CONTROL_ROUTE_ID),
+                null, String.class);
+    }
+
+    @Path("/start")
+    @POST
+    public void start() throws Exception {
+        producerTemplate.sendBody(String.format("controlbus:route?routeId=%s&action=start", CONTROL_ROUTE_ID), null);
+    }
+
+    @Path("/simple")
+    @POST
+    public void simple() throws Exception {
+        producerTemplate.sendBody(
+                "controlbus:language:simple",
+                String.format("${camelContext.getRouteController().stopRoute('%s')}", CONTROL_ROUTE_ID));
+    }
+
+    @Path("/bean")
+    @POST
+    public void bean() throws Exception {
+        producerTemplate.sendBodyAndHeader(
+                "controlbus:language:bean",
+                "controlbus-bean?method=stopRoute",
+                "routeId",
+                CONTROL_ROUTE_ID);
+    }
+
+    @Path("/header")
+    @POST
+    public void header() throws Exception {
+        producerTemplate.sendBody(
+                "direct:header",
+                "action");
+    }
+
+    @Path("/exchangeProperty")
+    @POST
+    public void exchangeProperty() throws Exception {
+        producerTemplate.sendBody(
+                "direct:exchangeProperty",
+                "action");
+    }
+}
diff --git a/integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageRoute.java b/integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageRoute.java
new file mode 100644
index 0000000000..b566b1c3bc
--- /dev/null
+++ b/integration-test-groups/foundation/controlbus/src/main/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageRoute.java
@@ -0,0 +1,45 @@
+/*
+ * 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.controlbus.it;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.RouteController;
+
+@ApplicationScoped
+@RegisterForReflection(targets = { RouteController.class })
+public class ControlbusLanguageRoute extends RouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        from("direct:lang-control")
+                .routeId("lang-control")
+                .log("control: ${body}");
+
+        from("direct:header")
+                .setHeader("action", constant("stop"))
+                .to("controlbus:language:header")
+                .toD("controlbus:route?routeId=lang-control&action=${body}");
+
+        from("direct:exchangeProperty")
+                .setProperty("action", constant("stop"))
+                .to("controlbus:language:exchangeProperty")
+                .toD("controlbus:route?routeId=lang-control&action=${body}");
+    }
+}
diff --git a/integration-test-groups/foundation/controlbus/src/test/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageIT.java b/integration-test-groups/foundation/controlbus/src/test/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageIT.java
new file mode 100644
index 0000000000..50a18816d4
--- /dev/null
+++ b/integration-test-groups/foundation/controlbus/src/test/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageIT.java
@@ -0,0 +1,24 @@
+/*
+ * 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.controlbus.it;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class ControlbusLanguageIT extends ControlbusLanguageTest {
+
+}
diff --git a/integration-test-groups/foundation/controlbus/src/test/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageTest.java b/integration-test-groups/foundation/controlbus/src/test/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageTest.java
new file mode 100644
index 0000000000..ac3962bc7e
--- /dev/null
+++ b/integration-test-groups/foundation/controlbus/src/test/java/org/apache/camel/quarkus/component/controlbus/it/ControlbusLanguageTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.controlbus.it;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+
+@QuarkusTest
+class ControlbusLanguageTest {
+
+    @BeforeEach
+    public void startRoute() {
+        String status = RestAssured.get("/controlbus/language/status").asString();
+        if ("Stopped".equals(status)) {
+            RestAssured.post("/controlbus/language/start");
+        }
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "simple", "bean", "header", "exchangeProperty" })
+    public void testLanguage(String language) {
+        RestAssured.given()
+                .contentType(ContentType.TEXT).get("/controlbus/language/status")
+                .then().body(equalTo("Started"));
+
+        RestAssured.given()
+                .contentType(ContentType.TEXT).post("/controlbus/language/" + language)
+                .then().statusCode(204);
+
+        RestAssured.given()
+                .contentType(ContentType.TEXT).get("/controlbus/language/status")
+                .then().body(equalTo("Stopped"));
+    }
+}