This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 696f8c88aed7 CAMEL-26156: camel-pubnub - FIRE and SETSTATE with an
empty body fail without calling PubNub, and PNMessageResult converts to String
(#28928)
696f8c88aed7 is described below
commit 696f8c88aed7868870fa3bdea3d3c5756fd232fb
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 2 21:47:19 2026 +0100
CAMEL-26156: camel-pubnub - FIRE and SETSTATE with an empty body fail
without calling PubNub, and PNMessageResult converts to String (#28928)
* CAMEL-26156: camel-pubnub - FIRE and SETSTATE with an empty body fail…
… without calling PubNub, and PNMessageResult converts to String
FIRE and SETSTATE set an exception and called the callback when the body
was empty, but
did not return: the PubNub call still ran and called the callback a second
time, and
process returned false. They now throw the CamelException as PUBLISH does,
so process
completes the exchange once, synchronously, without calling PubNub.
Add a PubNubConverter so the PNMessageResult body of the consumer converts
to a String:
a string message as its text, any other message as JSON.
* CAMEL-26156: camel-pubnub - SETSTATE with an empty body fails with "C…
…an not set state with an empty body"
The SETSTATE failure said "Can not publish empty message", which is the
wording of a
publish. It now says "Can not set state with an empty body".
* CAMEL-26156: camel-pubnub - escape attribute references in the docs
Asciidoctor read ${...} in inline code as a document attribute reference,
which failed the
PR doc validation. Use the +...+ passthrough so the text shows as written.
* Merge remote-tracking branch 'origin/main' into CAMEL-26156-pubnub-fi…
…re-setstate
# Conflicts:
# docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../apache/camel/catalog/components/pubnub.json | 2 +-
.../camel/catalog/docs/pubnub-component.adoc | 3 +
.../component/pubnub/PubNubConverterLoader.java | 60 ++++++++++++++
.../org/apache/camel/component/pubnub/pubnub.json | 2 +-
.../services/org/apache/camel/TypeConverterLoader | 2 +
.../src/main/docs/pubnub-component.adoc | 3 +
.../camel/component/pubnub/PubNubConverter.java | 45 +++++++++++
.../camel/component/pubnub/PubNubEndpoint.java | 3 +-
.../camel/component/pubnub/PubNubProducer.java | 10 +--
.../component/pubnub/PubNubConverterTest.java | 63 +++++++++++++++
.../component/pubnub/PubNubEmptyBodyTest.java | 92 ++++++++++++++++++++++
.../component/pubnub/PubNubSubscriberTest.java | 3 +
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 10 +++
13 files changed, 289 insertions(+), 9 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pubnub.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pubnub.json
index e4d1181ad220..8f1b477e5a07 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pubnub.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pubnub.json
@@ -45,7 +45,7 @@
"CamelPubNubUUID": { "index": 4, "kind": "header", "displayName": "",
"group": "producer", "label": "producer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "UUID to be used as a device identifier.",
"constantName": "org.apache.camel.component.pubnub.PubNubConstants#UUID" }
},
"bodyTypes": {
- "consumerOut": { "carriers": [ "pojo" ], "javaType":
"com.pubnub.api.models.consumer.pubsub.PNMessageResult", "byOption": [ {
"option": "withPresence", "value": "true", "carriers": [ "pojo" ] } ],
"description": "The PubNub PNMessageResult; its getMessage() is the payload as
a Gson JsonElement, and it does not convert to the payload text. With
withPresence, a presence event comes as a PNPresenceEventResult. The channel
and timetoken are in the CamelPubNubChannel and CamelPubNubTimeTo [...]
+ "consumerOut": { "carriers": [ "pojo" ], "javaType":
"com.pubnub.api.models.consumer.pubsub.PNMessageResult", "byOption": [ {
"option": "withPresence", "value": "true", "carriers": [ "pojo" ] } ],
"description": "The PubNub PNMessageResult; its getMessage() is the payload as
a Gson JsonElement. It converts to a String: a string message as its text, any
other message as JSON. With withPresence, a presence event comes as a
PNPresenceEventResult. The channel and timetoken are in the Cam [...]
"producerIn": { "carriers": [ "text", "value", "map", "list", "pojo" ],
"byOption": [ { "option": "operation", "value": "SETSTATE", "carriers": [
"map", "pojo" ] }, { "option": "operation", "value": "GETHISTORY", "carriers":
[ "any" ] }, { "option": "operation", "value": "GETSTATE", "carriers": [ "any"
] }, { "option": "operation", "value": "HERENOW", "carriers": [ "any" ] } ],
"description": "The message to publish or fire, serialized to JSON by the
PubNub client: text is sent as a [...]
"producerOut": { "carriers": [ "unchanged" ], "byOption": [ { "option":
"operation", "value": "GETHISTORY", "carriers": [ "list", "unchanged" ] }, {
"option": "operation", "value": "GETSTATE", "carriers": [ "pojo" ], "javaType":
"com.pubnub.api.models.consumer.presence.PNGetStateResult" }, { "option":
"operation", "value": "HERENOW", "carriers": [ "pojo" ], "javaType":
"com.pubnub.api.models.consumer.presence.PNHereNowResult" }, { "option":
"operation", "value": "SETSTATE", "carriers [...]
},
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pubnub-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pubnub-component.adoc
index c99ce1395990..ac299c662bd6 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pubnub-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pubnub-component.adoc
@@ -64,6 +64,9 @@ Pass the full object as the message payload.
PubNub will take care of object serialization.
When receiving the message body uses objects provided by the PubNub API.
+A message is a `PNMessageResult`, and `getMessage()` is the payload as a Gson
`JsonElement`.
+The `PNMessageResult` converts to a `String`, so `+${body}+` or
`convertBodyTo(String.class)` gives the payload:
+a string message as its text, and any other message (an object, an array, a
number) as JSON.
== Examples
diff --git
a/components/camel-pubnub/src/generated/java/org/apache/camel/component/pubnub/PubNubConverterLoader.java
b/components/camel-pubnub/src/generated/java/org/apache/camel/component/pubnub/PubNubConverterLoader.java
new file mode 100644
index 000000000000..32df8f30563d
--- /dev/null
+++
b/components/camel-pubnub/src/generated/java/org/apache/camel/component/pubnub/PubNubConverterLoader.java
@@ -0,0 +1,60 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.pubnub;
+
+import javax.annotation.processing.Generated;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.DeferredContextBinding;
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConversionException;
+import org.apache.camel.TypeConverterLoaderException;
+import org.apache.camel.spi.TypeConverterLoader;
+import org.apache.camel.spi.TypeConverterRegistry;
+import org.apache.camel.support.SimpleTypeConverter;
+import org.apache.camel.support.TypeConverterSupport;
+import org.apache.camel.util.DoubleMap;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.TypeConverterLoaderGeneratorMojo")
+@SuppressWarnings("unchecked")
+@DeferredContextBinding
+public final class PubNubConverterLoader implements TypeConverterLoader,
CamelContextAware {
+
+ private CamelContext camelContext;
+
+ public PubNubConverterLoader() {
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+
+ @Override
+ public void load(TypeConverterRegistry registry) throws
TypeConverterLoaderException {
+ registerConverters(registry);
+ }
+
+ private void registerConverters(TypeConverterRegistry registry) {
+ addTypeConverter(registry, java.lang.String.class,
com.pubnub.api.models.consumer.pubsub.PNMessageResult.class, false,
+ (type, exchange, value) -> {
+ Object answer =
org.apache.camel.component.pubnub.PubNubConverter.toString((com.pubnub.api.models.consumer.pubsub.PNMessageResult)
value);
+ if (false && answer == null) {
+ answer = Void.class;
+ }
+ return answer;
+ });
+ }
+
+ private static void addTypeConverter(TypeConverterRegistry registry,
Class<?> toType, Class<?> fromType, boolean allowNull,
SimpleTypeConverter.ConversionMethod method) {
+ registry.addTypeConverter(toType, fromType, new
SimpleTypeConverter(allowNull, method));
+ }
+}
diff --git
a/components/camel-pubnub/src/generated/resources/META-INF/org/apache/camel/component/pubnub/pubnub.json
b/components/camel-pubnub/src/generated/resources/META-INF/org/apache/camel/component/pubnub/pubnub.json
index e4d1181ad220..8f1b477e5a07 100644
---
a/components/camel-pubnub/src/generated/resources/META-INF/org/apache/camel/component/pubnub/pubnub.json
+++
b/components/camel-pubnub/src/generated/resources/META-INF/org/apache/camel/component/pubnub/pubnub.json
@@ -45,7 +45,7 @@
"CamelPubNubUUID": { "index": 4, "kind": "header", "displayName": "",
"group": "producer", "label": "producer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "UUID to be used as a device identifier.",
"constantName": "org.apache.camel.component.pubnub.PubNubConstants#UUID" }
},
"bodyTypes": {
- "consumerOut": { "carriers": [ "pojo" ], "javaType":
"com.pubnub.api.models.consumer.pubsub.PNMessageResult", "byOption": [ {
"option": "withPresence", "value": "true", "carriers": [ "pojo" ] } ],
"description": "The PubNub PNMessageResult; its getMessage() is the payload as
a Gson JsonElement, and it does not convert to the payload text. With
withPresence, a presence event comes as a PNPresenceEventResult. The channel
and timetoken are in the CamelPubNubChannel and CamelPubNubTimeTo [...]
+ "consumerOut": { "carriers": [ "pojo" ], "javaType":
"com.pubnub.api.models.consumer.pubsub.PNMessageResult", "byOption": [ {
"option": "withPresence", "value": "true", "carriers": [ "pojo" ] } ],
"description": "The PubNub PNMessageResult; its getMessage() is the payload as
a Gson JsonElement. It converts to a String: a string message as its text, any
other message as JSON. With withPresence, a presence event comes as a
PNPresenceEventResult. The channel and timetoken are in the Cam [...]
"producerIn": { "carriers": [ "text", "value", "map", "list", "pojo" ],
"byOption": [ { "option": "operation", "value": "SETSTATE", "carriers": [
"map", "pojo" ] }, { "option": "operation", "value": "GETHISTORY", "carriers":
[ "any" ] }, { "option": "operation", "value": "GETSTATE", "carriers": [ "any"
] }, { "option": "operation", "value": "HERENOW", "carriers": [ "any" ] } ],
"description": "The message to publish or fire, serialized to JSON by the
PubNub client: text is sent as a [...]
"producerOut": { "carriers": [ "unchanged" ], "byOption": [ { "option":
"operation", "value": "GETHISTORY", "carriers": [ "list", "unchanged" ] }, {
"option": "operation", "value": "GETSTATE", "carriers": [ "pojo" ], "javaType":
"com.pubnub.api.models.consumer.presence.PNGetStateResult" }, { "option":
"operation", "value": "HERENOW", "carriers": [ "pojo" ], "javaType":
"com.pubnub.api.models.consumer.presence.PNHereNowResult" }, { "option":
"operation", "value": "SETSTATE", "carriers [...]
},
diff --git
a/components/camel-pubnub/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader
b/components/camel-pubnub/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader
new file mode 100644
index 000000000000..851225edb141
--- /dev/null
+++
b/components/camel-pubnub/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+org.apache.camel.component.pubnub.PubNubConverterLoader
diff --git a/components/camel-pubnub/src/main/docs/pubnub-component.adoc
b/components/camel-pubnub/src/main/docs/pubnub-component.adoc
index c99ce1395990..ac299c662bd6 100644
--- a/components/camel-pubnub/src/main/docs/pubnub-component.adoc
+++ b/components/camel-pubnub/src/main/docs/pubnub-component.adoc
@@ -64,6 +64,9 @@ Pass the full object as the message payload.
PubNub will take care of object serialization.
When receiving the message body uses objects provided by the PubNub API.
+A message is a `PNMessageResult`, and `getMessage()` is the payload as a Gson
`JsonElement`.
+The `PNMessageResult` converts to a `String`, so `+${body}+` or
`convertBodyTo(String.class)` gives the payload:
+a string message as its text, and any other message (an object, an array, a
number) as JSON.
== Examples
diff --git
a/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubConverter.java
b/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubConverter.java
new file mode 100644
index 000000000000..bdd908389c5f
--- /dev/null
+++
b/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubConverter.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.component.pubnub;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.pubnub.api.models.consumer.pubsub.PNMessageResult;
+import org.apache.camel.Converter;
+
+/**
+ * Converts the PubNub objects the consumer sets as the message body.
+ */
+@Converter(generateLoader = true)
+public final class PubNubConverter {
+
+ private PubNubConverter() {
+ }
+
+ /**
+ * The payload of a received PubNub message as text: a string message as
its text, any other message (a JSON object,
+ * array, number or boolean) as JSON.
+ */
+ @Converter
+ public static String toString(PNMessageResult message) {
+ JsonElement payload = message.getMessage();
+ if (payload instanceof JsonPrimitive primitive &&
primitive.isString()) {
+ return primitive.getAsString();
+ }
+ return payload.toString();
+ }
+}
diff --git
a/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubEndpoint.java
b/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubEndpoint.java
index 24e56b3438ad..d8ab8417d30f 100644
---
a/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubEndpoint.java
+++
b/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubEndpoint.java
@@ -42,7 +42,8 @@ import org.apache.camel.support.DefaultEndpoint;
byOption = @BodyType.When(option =
"withPresence", value = "true",
carriers =
Carrier.POJO),
description = "The PubNub PNMessageResult;
its getMessage() is the payload as a Gson"
- + " JsonElement, and it does
not convert to the payload text. With"
+ + " JsonElement. It converts
to a String: a string message as its text,"
+ + " any other message as
JSON. With"
+ " withPresence, a presence
event comes as a PNPresenceEventResult. The"
+ " channel and timetoken are
in the CamelPubNubChannel and"
+ " CamelPubNubTimeToken
headers"),
diff --git
a/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubProducer.java
b/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubProducer.java
index 54c0cb402169..0a65abda1d00 100644
---
a/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubProducer.java
+++
b/components/camel-pubnub/src/main/java/org/apache/camel/component/pubnub/PubNubProducer.java
@@ -123,11 +123,10 @@ public class PubNubProducer extends DefaultAsyncProducer {
});
}
- private void doFire(Exchange exchange, AsyncCallback callback) {
+ private void doFire(Exchange exchange, AsyncCallback callback) throws
CamelException {
Object body = exchange.getIn().getBody();
if (ObjectHelper.isEmpty(body)) {
- exchange.setException(new CamelException("Can not fire empty
message"));
- callback.done(true);
+ throw new CamelException("Can not fire empty message");
}
LOG.debug("Sending message [{}] to channel [{}]", body,
getChannel(exchange));
endpoint.getPubnub()
@@ -171,11 +170,10 @@ public class PubNubProducer extends DefaultAsyncProducer {
});
}
- private void doSetState(Exchange exchange, AsyncCallback callback) {
+ private void doSetState(Exchange exchange, AsyncCallback callback) throws
CamelException {
Object body = exchange.getIn().getBody();
if (ObjectHelper.isEmpty(body)) {
- exchange.setException(new CamelException("Can not publish empty
message"));
- callback.done(true);
+ throw new CamelException("Can not set state with an empty body");
}
LOG.debug("Sending setState [{}] to channel [{}]", body,
getChannel(exchange));
endpoint.getPubnub()
diff --git
a/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubConverterTest.java
b/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubConverterTest.java
new file mode 100644
index 000000000000..1f898f5e2bc0
--- /dev/null
+++
b/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubConverterTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.pubnub;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.pubnub.api.models.consumer.pubsub.BasePubSubResult;
+import com.pubnub.api.models.consumer.pubsub.PNMessageResult;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class PubNubConverterTest extends CamelTestSupport {
+
+ @Test
+ public void testStringMessageToString() {
+ assertEquals("Hello World", convert(new JsonPrimitive("Hello World")));
+ }
+
+ @Test
+ public void testObjectMessageToString() {
+ JsonObject json = new JsonObject();
+ json.addProperty("text", "Message");
+ json.addProperty("count", 3);
+ assertEquals("{\"text\":\"Message\",\"count\":3}", convert(json));
+ }
+
+ @Test
+ public void testArrayMessageToString() {
+ JsonArray json = new JsonArray();
+ json.add("a");
+ json.add(1);
+ assertEquals("[\"a\",1]", convert(json));
+ }
+
+ @Test
+ public void testNumberMessageToString() {
+ assertEquals("42", convert(new JsonPrimitive(42)));
+ }
+
+ private String convert(JsonElement payload) {
+ PNMessageResult message = new PNMessageResult(
+ new BasePubSubResult("mychannel", null, 14607577960932487L,
null, "publisher"), payload, null, null);
+ return context.getTypeConverter().convertTo(String.class, message);
+ }
+}
diff --git
a/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubEmptyBodyTest.java
b/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubEmptyBodyTest.java
new file mode 100644
index 000000000000..1085a09aa288
--- /dev/null
+++
b/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubEmptyBodyTest.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.component.pubnub;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.AsyncProducer;
+import org.apache.camel.CamelException;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.support.DefaultExchange;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
+import static com.github.tomakehurst.wiremock.client.WireMock.verify;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * FIRE and SETSTATE with an empty body fail without calling PubNub, like
PUBLISH.
+ */
+public class PubNubEmptyBodyTest extends PubNubTestBase {
+
+ @Test
+ public void testFireNullBody() {
+ Exchange exchange = template.request("direct:fire", e ->
e.getIn().setBody(null));
+
+ CamelException cause = assertInstanceOf(CamelException.class,
exchange.getException());
+ assertEquals("Can not fire empty message", cause.getMessage());
+ verify(0, anyRequestedFor(anyUrl()));
+ }
+
+ @Test
+ public void testSetStateEmptyBody() {
+ Exchange exchange = template.request("direct:setstate", e ->
e.getIn().setBody(Map.of()));
+
+ CamelException cause = assertInstanceOf(CamelException.class,
exchange.getException());
+ assertEquals("Can not set state with an empty body",
cause.getMessage());
+ verify(0, anyRequestedFor(anyUrl()));
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "FIRE", "SETSTATE" })
+ public void testEmptyBodyCompletesSynchronouslyOnce(String operation)
throws Exception {
+ AsyncProducer producer =
context.getEndpoint("pubnub:someChannel?uuid=myuuid&pubnub=#pubnub").createAsyncProducer();
+ producer.start();
+ try {
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setHeader(PubNubConstants.OPERATION, operation);
+ AtomicInteger doneSync = new AtomicInteger();
+ AtomicInteger doneAsync = new AtomicInteger();
+
+ boolean sync = producer.process(exchange, ds -> (ds ? doneSync :
doneAsync).incrementAndGet());
+
+ assertTrue(sync, "process must return true as the callback was
called synchronously");
+ assertEquals(1, doneSync.get());
+ assertEquals(0, doneAsync.get());
+ assertInstanceOf(CamelException.class, exchange.getException());
+ } finally {
+ producer.stop();
+ }
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+
from("direct:fire").to("pubnub:someChannel?operation=fire&pubnub=#pubnub");
+
from("direct:setstate").to("pubnub:someChannel?operation=setstate&uuid=myuuid&pubnub=#pubnub");
+ }
+ };
+ }
+}
diff --git
a/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubSubscriberTest.java
b/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubSubscriberTest.java
index 5caf4c9cdda8..382d9b82b572 100644
---
a/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubSubscriberTest.java
+++
b/components/camel-pubnub/src/test/java/org/apache/camel/component/pubnub/PubNubSubscriberTest.java
@@ -25,6 +25,7 @@ import static
com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
public class PubNubSubscriberTest extends PubNubTestBase {
@@ -45,6 +46,8 @@ public class PubNubSubscriberTest extends PubNubTestBase {
mockResult.expectedMessageCount(1);
mockResult.expectedHeaderReceived(PubNubConstants.CHANNEL,
"mychannel");
MockEndpoint.assertIsSatisfied(context);
+ // the PNMessageResult body converts to the JSON of the message
+ assertEquals("{\"text\":\"Message\"}",
mockResult.getReceivedExchanges().get(0).getIn().getBody(String.class));
}
@Override
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index c6c2feb52962..051f557f6290 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -1049,6 +1049,16 @@ changed fields of an update are now in the new
`CamelDebeziumUpdateDescription`
The `Struct` to `Map` type converter now also converts a `Struct` inside a
list or a map.
+=== camel-pubnub - FIRE and SETSTATE with an empty body fail without calling
PubNub
+
+The `FIRE` and `SETSTATE` operations with an empty body now fail with a
`CamelException` (`Can not fire empty
+message` or `Can not set state with an empty body`) and do not call PubNub, as
`PUBLISH` does. Before, the call to
+PubNub still ran, so `FIRE` with a null body could end with the
`PubNubException` `Message Missing` instead. The
+message of the `SETSTATE` failure was `Can not publish empty message`.
+
+The `PNMessageResult` body of the consumer now converts to a `String`: a
string message as its text, any other message
+as JSON. Before, the conversion gave the `toString()` of the `PNMessageResult`
object.
+
=== camel-jbang - camel validate normalize writes one file per input file to
an output directory
`camel validate normalize <files> --output=<dir>` now writes one normalized
file per input file into the