You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2021/03/22 05:42:20 UTC

[camel] branch master updated: CAMEL-16359 Camel-spring-rabbitmq - Allow null body (#5249)

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

davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new e9c3efb  CAMEL-16359 Camel-spring-rabbitmq - Allow null body (#5249)
e9c3efb is described below

commit e9c3efb5654ec9e05e5461bf96e9eb1d8a6dc02f
Author: Anthony Defraine <ro...@kinae.eu>
AuthorDate: Mon Mar 22 06:41:49 2021 +0100

    CAMEL-16359 Camel-spring-rabbitmq - Allow null body (#5249)
---
 .../catalog/docs/spring-rabbitmq-component.adoc    |  3 +-
 .../SpringRabbitMQComponentConfigurer.java         |  6 ++
 .../component/springrabbit/spring-rabbitmq.json    |  1 +
 .../src/main/docs/spring-rabbitmq-component.adoc   |  3 +-
 .../springrabbit/DefaultMessageConverter.java      | 16 +++++-
 .../springrabbit/SpringRabbitMQComponent.java      | 13 ++++-
 .../RabbitMQProducerNullBodyIntTest.java           | 67 ++++++++++++++++++++++
 .../dsl/SpringRabbitmqComponentBuilderFactory.java | 19 ++++++
 .../ROOT/pages/spring-rabbitmq-component.adoc      |  3 +-
 9 files changed, 125 insertions(+), 6 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-rabbitmq-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-rabbitmq-component.adoc
index f5743fd..a95736c 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-rabbitmq-component.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-rabbitmq-component.adoc
@@ -43,7 +43,7 @@ determines the exchange the queue will be bound to.
 == Options
 
 // component options: START
-The Spring RabbitMQ component supports 28 options, which are listed below.
+The Spring RabbitMQ component supports 29 options, which are listed below.
 
 
 
@@ -71,6 +71,7 @@ The Spring RabbitMQ component supports 28 options, which are listed below.
 | *prefetchCount* (consumer) | Tell the broker how many messages to send to each consumer in a single request. Often this can be set quite high to improve throughput. | 250 | int
 | *retry* (consumer) | Custom retry configuration to use. If this is configured then the other settings such as maximumRetryAttempts for retry are not in use. |  | RetryOperationsInterceptor
 | *shutdownTimeout* (consumer) | The time to wait for workers in milliseconds after the container is stopped. If any workers are active when the shutdown signal comes they will be allowed to finish processing as long as they can finish within this timeout. | 5000 | long
+| *allowNullBody* (producer) | Whether to allow sending messages with no body. If this option is false and the message body is null, then an MessageConversionException is thrown. | false | boolean
 | *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and [...]
 | *replyTimeout* (producer) | Specify the timeout in milliseconds to be used when waiting for a reply message when doing request/reply messaging. The default value is 5 seconds. A negative value indicates an indefinite timeout. | 5000 | long
 | *autowiredEnabled* (advanced) | Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc. | true | boolean
diff --git a/components/camel-spring-rabbitmq/src/generated/java/org/apache/camel/component/springrabbit/SpringRabbitMQComponentConfigurer.java b/components/camel-spring-rabbitmq/src/generated/java/org/apache/camel/component/springrabbit/SpringRabbitMQComponentConfigurer.java
index bfac196..9a9ffb6 100644
--- a/components/camel-spring-rabbitmq/src/generated/java/org/apache/camel/component/springrabbit/SpringRabbitMQComponentConfigurer.java
+++ b/components/camel-spring-rabbitmq/src/generated/java/org/apache/camel/component/springrabbit/SpringRabbitMQComponentConfigurer.java
@@ -21,6 +21,8 @@ public class SpringRabbitMQComponentConfigurer extends PropertyConfigurerSupport
     public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
         SpringRabbitMQComponent target = (SpringRabbitMQComponent) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "allownullbody":
+        case "allowNullBody": target.setAllowNullBody(property(camelContext, boolean.class, value)); return true;
         case "amqpadmin":
         case "amqpAdmin": target.setAmqpAdmin(property(camelContext, org.springframework.amqp.core.AmqpAdmin.class, value)); return true;
         case "autodeclare":
@@ -88,6 +90,8 @@ public class SpringRabbitMQComponentConfigurer extends PropertyConfigurerSupport
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "allownullbody":
+        case "allowNullBody": return boolean.class;
         case "amqpadmin":
         case "amqpAdmin": return org.springframework.amqp.core.AmqpAdmin.class;
         case "autodeclare":
@@ -151,6 +155,8 @@ public class SpringRabbitMQComponentConfigurer extends PropertyConfigurerSupport
     public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
         SpringRabbitMQComponent target = (SpringRabbitMQComponent) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "allownullbody":
+        case "allowNullBody": return target.isAllowNullBody();
         case "amqpadmin":
         case "amqpAdmin": return target.getAmqpAdmin();
         case "autodeclare":
diff --git a/components/camel-spring-rabbitmq/src/generated/resources/org/apache/camel/component/springrabbit/spring-rabbitmq.json b/components/camel-spring-rabbitmq/src/generated/resources/org/apache/camel/component/springrabbit/spring-rabbitmq.json
index 0106741..799bf57 100644
--- a/components/camel-spring-rabbitmq/src/generated/resources/org/apache/camel/component/springrabbit/spring-rabbitmq.json
+++ b/components/camel-spring-rabbitmq/src/generated/resources/org/apache/camel/component/springrabbit/spring-rabbitmq.json
@@ -43,6 +43,7 @@
     "prefetchCount": { "kind": "property", "displayName": "Prefetch Count", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 250, "description": "Tell the broker how many messages to send to each consumer in a single request. Often this can be set quite high to improve throughput." },
     "retry": { "kind": "property", "displayName": "Retry", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.springframework.retry.interceptor.RetryOperationsInterceptor", "deprecated": false, "autowired": false, "secret": false, "description": "Custom retry configuration to use. If this is configured then the other settings such as maximumRetryAttempts for retry are not in use." },
     "shutdownTimeout": { "kind": "property", "displayName": "Shutdown Timeout", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "5000", "description": "The time to wait for workers in milliseconds after the container is stopped. If any workers are active when the shutdown signal comes they will be allowed to finish processing as long as they c [...]
+    "allowNullBody": { "kind": "property", "displayName": "Allow Null Body", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow sending messages with no body. If this option is false and the message body is null, then an MessageConversionException is thrown." },
     "lazyStartProducer": { "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during star [...]
     "replyTimeout": { "kind": "property", "displayName": "Reply Timeout", "group": "producer", "label": "producer", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "5000", "description": "Specify the timeout in milliseconds to be used when waiting for a reply message when doing request\/reply messaging. The default value is 5 seconds. A negative value indicates an indefinite timeout." },
     "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
diff --git a/components/camel-spring-rabbitmq/src/main/docs/spring-rabbitmq-component.adoc b/components/camel-spring-rabbitmq/src/main/docs/spring-rabbitmq-component.adoc
index f5743fd..a95736c 100644
--- a/components/camel-spring-rabbitmq/src/main/docs/spring-rabbitmq-component.adoc
+++ b/components/camel-spring-rabbitmq/src/main/docs/spring-rabbitmq-component.adoc
@@ -43,7 +43,7 @@ determines the exchange the queue will be bound to.
 == Options
 
 // component options: START
-The Spring RabbitMQ component supports 28 options, which are listed below.
+The Spring RabbitMQ component supports 29 options, which are listed below.
 
 
 
@@ -71,6 +71,7 @@ The Spring RabbitMQ component supports 28 options, which are listed below.
 | *prefetchCount* (consumer) | Tell the broker how many messages to send to each consumer in a single request. Often this can be set quite high to improve throughput. | 250 | int
 | *retry* (consumer) | Custom retry configuration to use. If this is configured then the other settings such as maximumRetryAttempts for retry are not in use. |  | RetryOperationsInterceptor
 | *shutdownTimeout* (consumer) | The time to wait for workers in milliseconds after the container is stopped. If any workers are active when the shutdown signal comes they will be allowed to finish processing as long as they can finish within this timeout. | 5000 | long
+| *allowNullBody* (producer) | Whether to allow sending messages with no body. If this option is false and the message body is null, then an MessageConversionException is thrown. | false | boolean
 | *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and [...]
 | *replyTimeout* (producer) | Specify the timeout in milliseconds to be used when waiting for a reply message when doing request/reply messaging. The default value is 5 seconds. A negative value indicates an indefinite timeout. | 5000 | long
 | *autowiredEnabled* (advanced) | Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc. | true | boolean
diff --git a/components/camel-spring-rabbitmq/src/main/java/org/apache/camel/component/springrabbit/DefaultMessageConverter.java b/components/camel-spring-rabbitmq/src/main/java/org/apache/camel/component/springrabbit/DefaultMessageConverter.java
index e87087b..f9f1958 100644
--- a/components/camel-spring-rabbitmq/src/main/java/org/apache/camel/component/springrabbit/DefaultMessageConverter.java
+++ b/components/camel-spring-rabbitmq/src/main/java/org/apache/camel/component/springrabbit/DefaultMessageConverter.java
@@ -25,18 +25,30 @@ import org.springframework.amqp.core.Message;
 import org.springframework.amqp.core.MessageProperties;
 import org.springframework.amqp.support.converter.AbstractMessageConverter;
 import org.springframework.amqp.support.converter.MessageConversionException;
+import org.springframework.amqp.support.converter.MessageConverter;
 
-public class DefaultMessageConverter extends AbstractMessageConverter {
+public class DefaultMessageConverter extends AbstractMessageConverter implements MessageConverter {
 
     private final String defaultCharset = Charset.defaultCharset().name();
     private final CamelContext camelContext;
+    private final boolean allowNullBody;
 
-    public DefaultMessageConverter(CamelContext camelContext) {
+    public DefaultMessageConverter(CamelContext camelContext, boolean allowNullBody) {
         this.camelContext = camelContext;
+        this.allowNullBody = allowNullBody;
     }
 
     @Override
     public Message createMessage(Object body, MessageProperties messageProperties) throws MessageConversionException {
+        if (body == null) {
+            if (!allowNullBody) {
+                throw new MessageConversionException(
+                        "Cannot send message as message body is null, and option allowNullBody is false.");
+            } else {
+                return new Message(null, messageProperties);
+            }
+        }
+
         boolean text = body instanceof String;
         byte[] data;
         try {
diff --git a/components/camel-spring-rabbitmq/src/main/java/org/apache/camel/component/springrabbit/SpringRabbitMQComponent.java b/components/camel-spring-rabbitmq/src/main/java/org/apache/camel/component/springrabbit/SpringRabbitMQComponent.java
index f79a7de..29c3bdd 100644
--- a/components/camel-spring-rabbitmq/src/main/java/org/apache/camel/component/springrabbit/SpringRabbitMQComponent.java
+++ b/components/camel-spring-rabbitmq/src/main/java/org/apache/camel/component/springrabbit/SpringRabbitMQComponent.java
@@ -109,6 +109,9 @@ public class SpringRabbitMQComponent extends HeaderFilterStrategyComponent {
     @Metadata(label = "consumer", defaultValue = "true",
               description = "Whether a Rabbitmq consumer should reject the message without requeuing. This enables failed messages to be sent to a Dead Letter Exchange/Queue, if the broker is so configured.")
     private boolean rejectAndDontRequeue = true;
+    @Metadata(label = "producer", defaultValue = "false",
+              description = "Whether to allow sending messages with no body. If this option is false and the message body is null, then an MessageConversionException is thrown.")
+    private boolean allowNullBody;
 
     @Override
     protected void doInit() throws Exception {
@@ -118,7 +121,7 @@ public class SpringRabbitMQComponent extends HeaderFilterStrategyComponent {
             setHeaderFilterStrategy(new SpringRabbitMQHeaderFilterStrategy());
         }
         if (messageConverter == null) {
-            messageConverter = new DefaultMessageConverter(getCamelContext());
+            messageConverter = new DefaultMessageConverter(getCamelContext(), allowNullBody);
         }
         if (messagePropertiesConverter == null) {
             messagePropertiesConverter = new DefaultMessagePropertiesConverter(getCamelContext(), getHeaderFilterStrategy());
@@ -345,4 +348,12 @@ public class SpringRabbitMQComponent extends HeaderFilterStrategyComponent {
     public void setRejectAndDontRequeue(boolean rejectAndDontRequeue) {
         this.rejectAndDontRequeue = rejectAndDontRequeue;
     }
+
+    public boolean isAllowNullBody() {
+        return allowNullBody;
+    }
+
+    public void setAllowNullBody(boolean allowNullBody) {
+        this.allowNullBody = allowNullBody;
+    }
 }
diff --git a/components/camel-spring-rabbitmq/src/test/java/org/apache/camel/component/springrabbit/integration/RabbitMQProducerNullBodyIntTest.java b/components/camel-spring-rabbitmq/src/test/java/org/apache/camel/component/springrabbit/integration/RabbitMQProducerNullBodyIntTest.java
new file mode 100644
index 0000000..e26018d
--- /dev/null
+++ b/components/camel-spring-rabbitmq/src/test/java/org/apache/camel/component/springrabbit/integration/RabbitMQProducerNullBodyIntTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.component.springrabbit.integration;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.springrabbit.SpringRabbitMQComponent;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.amqp.core.AmqpAdmin;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.core.TopicExchange;
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.rabbit.core.RabbitAdmin;
+
+public class RabbitMQProducerNullBodyIntTest extends AbstractRabbitMQIntTest {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext camelContext = super.createCamelContext();
+        SpringRabbitMQComponent rmq = camelContext.getComponent("spring-rabbitmq", SpringRabbitMQComponent.class);
+        rmq.setAllowNullBody(true);
+        return camelContext;
+    }
+
+    @Test
+    public void testProducer() {
+        ConnectionFactory cf = context.getRegistry().lookupByNameAndType("myCF", ConnectionFactory.class);
+
+        Queue q = new Queue("myqueue");
+        TopicExchange t = new TopicExchange("foo");
+
+        AmqpAdmin admin = new RabbitAdmin(cf);
+        admin.declareQueue(q);
+        admin.declareExchange(t);
+        admin.declareBinding(BindingBuilder.bind(q).to(t).with("foo.bar.#"));
+
+        Assertions.assertDoesNotThrow(() -> template.sendBody("direct:start", null));
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                        .to("spring-rabbitmq:foo?routingKey=foo.bar");
+            }
+        };
+    }
+}
diff --git a/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/SpringRabbitmqComponentBuilderFactory.java b/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/SpringRabbitmqComponentBuilderFactory.java
index 016b2b2..232be02 100644
--- a/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/SpringRabbitmqComponentBuilderFactory.java
+++ b/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/SpringRabbitmqComponentBuilderFactory.java
@@ -407,6 +407,24 @@ public interface SpringRabbitmqComponentBuilderFactory {
             return this;
         }
         /**
+         * Whether to allow sending messages with no body. If this option is
+         * false and the message body is null, then an
+         * MessageConversionException is thrown.
+         * 
+         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
+         * 
+         * Default: false
+         * Group: producer
+         * 
+         * @param allowNullBody the value to set
+         * @return the dsl builder
+         */
+        default SpringRabbitmqComponentBuilder allowNullBody(
+                boolean allowNullBody) {
+            doSetProperty("allowNullBody", allowNullBody);
+            return this;
+        }
+        /**
          * Whether the producer should be started lazy (on the first message).
          * By starting lazy you can use this to allow CamelContext and routes to
          * startup in situations where a producer may otherwise fail during
@@ -575,6 +593,7 @@ public interface SpringRabbitmqComponentBuilderFactory {
             case "prefetchCount": ((SpringRabbitMQComponent) component).setPrefetchCount((int) value); return true;
             case "retry": ((SpringRabbitMQComponent) component).setRetry((org.springframework.retry.interceptor.RetryOperationsInterceptor) value); return true;
             case "shutdownTimeout": ((SpringRabbitMQComponent) component).setShutdownTimeout((long) value); return true;
+            case "allowNullBody": ((SpringRabbitMQComponent) component).setAllowNullBody((boolean) value); return true;
             case "lazyStartProducer": ((SpringRabbitMQComponent) component).setLazyStartProducer((boolean) value); return true;
             case "replyTimeout": ((SpringRabbitMQComponent) component).setReplyTimeout((long) value); return true;
             case "autowiredEnabled": ((SpringRabbitMQComponent) component).setAutowiredEnabled((boolean) value); return true;
diff --git a/docs/components/modules/ROOT/pages/spring-rabbitmq-component.adoc b/docs/components/modules/ROOT/pages/spring-rabbitmq-component.adoc
index 944203c..d683107 100644
--- a/docs/components/modules/ROOT/pages/spring-rabbitmq-component.adoc
+++ b/docs/components/modules/ROOT/pages/spring-rabbitmq-component.adoc
@@ -45,7 +45,7 @@ determines the exchange the queue will be bound to.
 == Options
 
 // component options: START
-The Spring RabbitMQ component supports 28 options, which are listed below.
+The Spring RabbitMQ component supports 29 options, which are listed below.
 
 
 
@@ -73,6 +73,7 @@ The Spring RabbitMQ component supports 28 options, which are listed below.
 | *prefetchCount* (consumer) | Tell the broker how many messages to send to each consumer in a single request. Often this can be set quite high to improve throughput. | 250 | int
 | *retry* (consumer) | Custom retry configuration to use. If this is configured then the other settings such as maximumRetryAttempts for retry are not in use. |  | RetryOperationsInterceptor
 | *shutdownTimeout* (consumer) | The time to wait for workers in milliseconds after the container is stopped. If any workers are active when the shutdown signal comes they will be allowed to finish processing as long as they can finish within this timeout. | 5000 | long
+| *allowNullBody* (producer) | Whether to allow sending messages with no body. If this option is false and the message body is null, then an MessageConversionException is thrown. | false | boolean
 | *lazyStartProducer* (producer) | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and [...]
 | *replyTimeout* (producer) | Specify the timeout in milliseconds to be used when waiting for a reply message when doing request/reply messaging. The default value is 5 seconds. A negative value indicates an indefinite timeout. | 5000 | long
 | *autowiredEnabled* (advanced) | Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc. | true | boolean