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 7e96ef2f4c81 CAMEL-25251: camel-coap - fail the exchange when the CoAP
server does not respond (#27248)
7e96ef2f4c81 is described below
commit 7e96ef2f4c8197d3854f612385814d987645f04e
Author: allthingssecurity <[email protected]>
AuthorDate: Fri Oct 2 13:23:08 2026 +0530
CAMEL-25251: camel-coap - fail the exchange when the CoAP server does not
respond (#27248)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../java/org/apache/camel/coap/CoAPProducer.java | 16 ++++-
.../java/org/apache/camel/coap/CoAPMethodTest.java | 21 ++++++
.../camel/coap/CoAPProducerNoResponseTest.java | 75 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 7 ++
4 files changed, 116 insertions(+), 3 deletions(-)
diff --git
a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPProducer.java
b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPProducer.java
index 21cea49926a3..c563fceb47d3 100644
---
a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPProducer.java
+++
b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPProducer.java
@@ -16,9 +16,13 @@
*/
package org.apache.camel.coap;
+import java.util.Locale;
+
+import org.apache.camel.CamelExchangeException;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.support.DefaultProducer;
+import org.apache.camel.util.URISupport;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
@@ -53,7 +57,8 @@ public class CoAPProducer extends DefaultProducer {
// ?default?
ct = "application/octet-stream";
}
- String method = CoAPHelper.getDefaultMethod(exchange, client);
+ // the method header may be given in lower case
+ String method = CoAPHelper.getDefaultMethod(exchange,
client).toUpperCase(Locale.ROOT);
int mediaType = MediaTypeRegistry.parse(ct);
CoapResponse response = null;
boolean pingResponse = false;
@@ -76,14 +81,19 @@ public class CoAPProducer extends DefaultProducer {
pingResponse = client.ping();
break;
default:
- break;
+ throw new IllegalArgumentException("Unsupported CoAP method: "
+ method);
}
if (response != null) {
CoAPHelper.convertCoapResponseToMessage(response,
exchange.getOut());
+ } else if (!method.equals(CoAPConstants.METHOD_PING)) {
+ // the client returns null when no response was received (timeout,
rejected or cancelled request)
+ throw new CamelExchangeException(
+ "No response received from CoAP server for " + method + ":
" + URISupport.sanitizeUri(client.getURI()),
+ exchange);
}
- if (method.equalsIgnoreCase(CoAPConstants.METHOD_PING)) {
+ if (method.equals(CoAPConstants.METHOD_PING)) {
Message resp = exchange.getOut();
resp.setBody(pingResponse);
}
diff --git
a/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPMethodTest.java
b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPMethodTest.java
index 3673f4ebbed1..c216d14f850f 100644
---
a/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPMethodTest.java
+++
b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPMethodTest.java
@@ -16,11 +16,14 @@
*/
package org.apache.camel.coap;
+import org.apache.camel.CamelExecutionException;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
public class CoAPMethodTest extends CoAPTestSupport {
@@ -46,6 +49,24 @@ public class CoAPMethodTest extends CoAPTestSupport {
assertEquals("DELETE: /test/c", result);
}
+ @Test
+ void testCoAPMethodHeaderLowerCase() {
+ // the method header is accepted in lower case
+ String result = template.requestBodyAndHeader("coap://localhost:" +
PORT.getPort() + "/test/c", null,
+ CoAPConstants.COAP_METHOD,
+ "delete", String.class);
+ assertEquals("DELETE: /test/c", result);
+ }
+
+ @Test
+ void testCoAPMethodHeaderUnknown() {
+ String uri = "coap://localhost:" + PORT.getPort() + "/test/c";
+ CamelExecutionException e = assertThrows(CamelExecutionException.class,
+ () -> template.requestBodyAndHeader(uri, null,
CoAPConstants.COAP_METHOD, "FETCH", String.class));
+ IllegalArgumentException cause =
assertInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertEquals("Unsupported CoAP method: FETCH", cause.getMessage());
+ }
+
@Override
protected RoutesBuilder createRouteBuilder() {
return new RouteBuilder() {
diff --git
a/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPProducerNoResponseTest.java
b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPProducerNoResponseTest.java
new file mode 100644
index 000000000000..408e1058645f
--- /dev/null
+++
b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPProducerNoResponseTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.coap;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelExchangeException;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.AvailablePortFinder;
+import org.eclipse.californium.core.CoapClient;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The producer must fail the exchange when the CoAP server does not answer,
instead of completing it with the request
+ * body.
+ */
+public class CoAPProducerNoResponseTest extends CoAPTestSupport {
+
+ // nothing listens on this port
+ @RegisterExtension
+ static AvailablePortFinder.Port unusedPort = AvailablePortFinder.find();
+
+ @BindToRegistry("noAnswerClient")
+ private final CoapClient noAnswerClient = new CoapClient(
+ String.format("coap://localhost:%d/TestResource",
unusedPort.getPort())).setTimeout(500L);
+
+ @AfterEach
+ void shutdownClient() {
+ noAnswerClient.shutdown();
+ }
+
+ @Test
+ void testNoResponseFailsTheExchange() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(0);
+
+ Exchange out = template.request("direct:start", e ->
e.getIn().setBody("Hello"));
+
+ CamelExchangeException cause =
assertInstanceOf(CamelExchangeException.class, out.getException());
+ assertTrue(cause.getMessage().startsWith("No response received from
CoAP server"), cause.getMessage());
+ MockEndpoint.assertIsSatisfied(context);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+
.toF("coap://localhost:%d/TestResource?client=#noAnswerClient",
unusedPort.getPort())
+ .to("mock:result");
+ }
+ };
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 2e4dec9ba18a..66ea24ac74a5 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -4079,6 +4079,13 @@ coap://0.0.0.0:5683/my/resource?muteException=false
For the Rest DSL, set it with
`restConfiguration().endpointProperty("muteException", "false")`.
+==== Producer
+
+When the CoAP server does not answer a `GET`, `POST`, `PUT` or `DELETE`
request (the request timed out or was
+rejected), the producer now fails the exchange with a
`CamelExchangeException`. Before, the exchange continued as if
+the request had succeeded, with the request body as its body. The
`CamelCoapMethod` header is now also accepted in
+lower case, and an unknown method fails the exchange with an
`IllegalArgumentException` instead of sending nothing.
+
=== camel-quickfix - InOut replies are sent on the session the request arrived
on
With `exchangePattern=InOut`, the QuickFIX/J consumer now sends the reply on
the session the request was received on.