This is an automated email from the ASF dual-hosted git repository. coheigea 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 420e9cd CAMEL-13471 - Adding support for coap+tcp 420e9cd is described below commit 420e9cd186c0aa773225dfd91e302baebc776f1c Author: Colm O hEigeartaigh <cohei...@apache.org> AuthorDate: Tue Apr 30 14:55:26 2019 +0100 CAMEL-13471 - Adding support for coap+tcp --- components/camel-coap/pom.xml | 5 + .../java/org/apache/camel/coap/CoAPComponent.java | 10 +- .../java/org/apache/camel/coap/CoAPEndpoint.java | 6 +- .../java/org/apache/camel/coap/CoAPProducer.java | 14 ++- .../services/org/apache/camel/component/coaps | 18 --- .../org/apache/camel/coap/CoAPComponentTest.java | 28 ++++- .../camel/coap/CoAPRestComponentTCPTest.java | 131 +++++++++++++++++++++ .../springboot/CoAPComponentAutoConfiguration.java | 2 +- 8 files changed, 190 insertions(+), 24 deletions(-) diff --git a/components/camel-coap/pom.xml b/components/camel-coap/pom.xml index badedf1..4f064de 100644 --- a/components/camel-coap/pom.xml +++ b/components/camel-coap/pom.xml @@ -51,6 +51,11 @@ <artifactId>scandium</artifactId> <version>${californium-version}</version> </dependency> + <dependency> + <groupId>org.eclipse.californium</groupId> + <artifactId>element-connector-tcp</artifactId> + <version>${californium-version}</version> + </dependency> <!-- logging --> <dependency> diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java index 6c213ef..c36317f 100644 --- a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java +++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java @@ -37,6 +37,7 @@ import org.apache.camel.util.URISupport; import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.network.CoapEndpoint; import org.eclipse.californium.core.network.config.NetworkConfig; +import org.eclipse.californium.elements.tcp.TcpServerConnector; import org.eclipse.californium.scandium.DTLSConnector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,7 +45,7 @@ import org.slf4j.LoggerFactory; /** * Represents the component that manages {@link CoAPEndpoint}. */ -@Component("coap") +@Component("coap,coaps,coap+tcp,coaps+tcp") public class CoAPComponent extends DefaultComponent implements RestConsumerFactory { static final int DEFAULT_PORT = 5684; private static final Logger LOG = LoggerFactory.getLogger(CoAPComponent.class); @@ -65,10 +66,15 @@ public class CoAPComponent extends DefaultComponent implements RestConsumerFacto InetSocketAddress address = new InetSocketAddress(port); coapBuilder.setNetworkConfig(config); - // Configure TLS + // Configure TLS and / or TCP if (CoAPEndpoint.enableTLS(endpoint.getUri())) { DTLSConnector connector = endpoint.createDTLSConnector(address, false); coapBuilder.setConnector(connector); + } else if (CoAPEndpoint.enableTCP(endpoint.getUri())) { + int tcpThreads = config.getInt(NetworkConfig.Keys.TCP_WORKER_THREADS); + int tcpIdleTimeout = config.getInt(NetworkConfig.Keys.TCP_CONNECTION_IDLE_TIMEOUT); + TcpServerConnector tcpConnector = new TcpServerConnector(address, tcpThreads, tcpIdleTimeout); + coapBuilder.setConnector(tcpConnector); } else { coapBuilder.setInetSocketAddress(address); } diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPEndpoint.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPEndpoint.java index e169d8c..36954ca 100644 --- a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPEndpoint.java +++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPEndpoint.java @@ -49,7 +49,7 @@ import org.eclipse.californium.scandium.dtls.rpkstore.TrustedRpkStore; /** * The coap component is used for sending and receiving messages from COAP capable devices. */ -@UriEndpoint(firstVersion = "2.16.0", scheme = "coap", title = "CoAP", syntax = "coap:uri", label = "iot") +@UriEndpoint(firstVersion = "2.16.0", scheme = "coap,coaps,coap+tcp,coaps+tcp", title = "CoAP", syntax = "coap:uri", label = "iot") public class CoAPEndpoint extends DefaultEndpoint { @UriPath private URI uri; @@ -356,6 +356,10 @@ public class CoAPEndpoint extends DefaultEndpoint { return "coaps".equals(uri.getScheme()); } + public static boolean enableTCP(URI uri) { + return uri.getScheme().endsWith("+tcp"); + } + public DTLSConnector createDTLSConnector(InetSocketAddress address, boolean client) { DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(); 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 d82852a..a764857 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 @@ -25,6 +25,8 @@ import org.eclipse.californium.core.CoapClient; import org.eclipse.californium.core.CoapResponse; import org.eclipse.californium.core.coap.MediaTypeRegistry; import org.eclipse.californium.core.network.CoapEndpoint; +import org.eclipse.californium.core.network.config.NetworkConfig; +import org.eclipse.californium.elements.tcp.TcpClientConnector; import org.eclipse.californium.scandium.DTLSConnector; /** @@ -94,13 +96,23 @@ public class CoAPProducer extends DefaultProducer { } client = new CoapClient(uri); - // Configure TLS + // Configure TLS and / or TCP if (CoAPEndpoint.enableTLS(uri)) { DTLSConnector connector = endpoint.createDTLSConnector(null, true); CoapEndpoint.Builder coapBuilder = new CoapEndpoint.Builder(); coapBuilder.setConnector(connector); client.setEndpoint(coapBuilder.build()); + } else if (CoAPEndpoint.enableTCP(endpoint.getUri())) { + NetworkConfig config = NetworkConfig.createStandardWithoutFile(); + int tcpThreads = config.getInt(NetworkConfig.Keys.TCP_WORKER_THREADS); + int tcpConnectTimeout = config.getInt(NetworkConfig.Keys.TCP_CONNECT_TIMEOUT); + int tcpIdleTimeout = config.getInt(NetworkConfig.Keys.TCP_CONNECTION_IDLE_TIMEOUT); + TcpClientConnector tcpConnector = new TcpClientConnector(tcpThreads, tcpConnectTimeout, tcpIdleTimeout); + CoapEndpoint.Builder tcpBuilder = new CoapEndpoint.Builder(); + tcpBuilder.setConnector(tcpConnector); + + client.setEndpoint(tcpBuilder.build()); } } diff --git a/components/camel-coap/src/main/resources/META-INF/services/org/apache/camel/component/coaps b/components/camel-coap/src/main/resources/META-INF/services/org/apache/camel/component/coaps deleted file mode 100644 index e0129bc..0000000 --- a/components/camel-coap/src/main/resources/META-INF/services/org/apache/camel/component/coaps +++ /dev/null @@ -1,18 +0,0 @@ -# -# 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. -# - -class=org.apache.camel.coap.CoAPComponent diff --git a/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPComponentTest.java b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPComponentTest.java index d4c8f49..aaad659 100644 --- a/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPComponentTest.java +++ b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPComponentTest.java @@ -21,6 +21,7 @@ import org.apache.camel.Produce; import org.apache.camel.ProducerTemplate; 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.eclipse.californium.core.CoapResponse; import org.eclipse.californium.core.coap.CoAP; @@ -29,9 +30,14 @@ import org.junit.Test; public class CoAPComponentTest extends CoAPTestSupport { + protected static final int TCP_PORT = AvailablePortFinder.getNextAvailable(); + @Produce("direct:start") protected ProducerTemplate sender; - + + @Produce("direct:starttcp") + protected ProducerTemplate tcpSender; + @Test public void testCoAPComponent() throws Exception { CoapClient client = createClient("/TestResource"); @@ -47,6 +53,17 @@ public class CoAPComponentTest extends CoAPTestSupport { assertMockEndpointsSatisfied(); } + @Test + public void testCoAPComponentTLS() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(1); + mock.expectedBodiesReceived("Hello Camel CoAP"); + mock.expectedHeaderReceived(Exchange.CONTENT_TYPE, MediaTypeRegistry.toString(MediaTypeRegistry.APPLICATION_OCTET_STREAM)); + mock.expectedHeaderReceived(CoAPConstants.COAP_RESPONSE_CODE, CoAP.ResponseCode.CONTENT.toString()); + tcpSender.sendBody("Camel CoAP"); + assertMockEndpointsSatisfied(); + } + @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { @@ -56,10 +73,19 @@ public class CoAPComponentTest extends CoAPTestSupport { .convertBodyTo(String.class) .transform(body().prepend("Hello ")); + fromF("coap+tcp://localhost:%d/TestResource", TCP_PORT) + .convertBodyTo(String.class) + .transform(body().prepend("Hello ")); + from("direct:start") .toF("coap://localhost:%d/TestResource", PORT) .to("mock:result"); + + from("direct:starttcp") + .toF("coap+tcp://localhost:%d/TestResource", TCP_PORT) + .to("mock:result"); } }; } + } diff --git a/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPRestComponentTCPTest.java b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPRestComponentTCPTest.java new file mode 100644 index 0000000..bd85eec --- /dev/null +++ b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPRestComponentTCPTest.java @@ -0,0 +1,131 @@ +/* + * 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.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.AvailablePortFinder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP.ResponseCode; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.eclipse.californium.core.network.CoapEndpoint; +import org.eclipse.californium.core.network.config.NetworkConfig; +import org.eclipse.californium.elements.tcp.TcpClientConnector; +import org.junit.Test; + +public class CoAPRestComponentTCPTest extends CamelTestSupport { + static int coapport = AvailablePortFinder.getNextAvailable(); + + @Test + public void testCoAP() throws Exception { + NetworkConfig.createStandardWithoutFile(); + CoapClient client; + CoapResponse rsp; + + client = new CoapClient("coap+tcp://localhost:" + coapport + "/TestResource/Ducky"); + decorateWithTCP(client); + rsp = client.get(); + assertEquals(ResponseCode.CONTENT, rsp.getCode()); + assertEquals("Hello Ducky", rsp.getResponseText()); + rsp = client.post("data", MediaTypeRegistry.TEXT_PLAIN); + assertEquals(ResponseCode.CONTENT, rsp.getCode()); + assertEquals("Hello Ducky: data", rsp.getResponseText()); + + client = new CoapClient("coap+tcp://localhost:" + coapport + "/TestParams?id=Ducky"); + decorateWithTCP(client); + client.setTimeout(1000000L); + rsp = client.get(); + assertEquals(ResponseCode.CONTENT, rsp.getCode()); + assertEquals("Hello Ducky", rsp.getResponseText()); + rsp = client.post("data", MediaTypeRegistry.TEXT_PLAIN); + assertEquals(ResponseCode.CONTENT, rsp.getCode()); + assertEquals("Hello Ducky: data", rsp.getResponseText()); + assertEquals(MediaTypeRegistry.TEXT_PLAIN, rsp.getOptions().getContentFormat()); + } + + @Test + public void testCoAPMethodNotAllowedResponse() throws Exception { + NetworkConfig.createStandardWithoutFile(); + CoapClient client = new CoapClient("coap+tcp://localhost:" + coapport + "/TestResource/Ducky"); + decorateWithTCP(client); + client.setTimeout(1000000L); + CoapResponse rsp = client.delete(); + assertEquals(ResponseCode.METHOD_NOT_ALLOWED, rsp.getCode()); + } + + @Test + public void testCoAPNotFoundResponse() throws Exception { + NetworkConfig.createStandardWithoutFile(); + CoapClient client = new CoapClient("coap+tcp://localhost:" + coapport + "/foo/bar/cheese"); + decorateWithTCP(client); + client.setTimeout(1000000L); + CoapResponse rsp = client.get(); + assertEquals(ResponseCode.NOT_FOUND, rsp.getCode()); + } + + private void decorateWithTCP(CoapClient client) { + NetworkConfig config = NetworkConfig.createStandardWithoutFile(); + int tcpThreads = config.getInt(NetworkConfig.Keys.TCP_WORKER_THREADS); + int tcpConnectTimeout = config.getInt(NetworkConfig.Keys.TCP_CONNECT_TIMEOUT); + int tcpIdleTimeout = config.getInt(NetworkConfig.Keys.TCP_CONNECTION_IDLE_TIMEOUT); + TcpClientConnector tcpConnector = new TcpClientConnector(tcpThreads, tcpConnectTimeout, tcpIdleTimeout); + CoapEndpoint.Builder tcpBuilder = new CoapEndpoint.Builder(); + tcpBuilder.setConnector(tcpConnector); + + client.setEndpoint(tcpBuilder.build()); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + restConfiguration("coap").scheme("coap+tcp").host("localhost").port(coapport); + + rest("/TestParams") + .get().to("direct:get1") + .post().to("direct:post1"); + + rest("/TestResource") + .get("/{id}").to("direct:get1") + .post("/{id}").to("direct:post1"); + + from("direct:get1").process(new Processor() { + public void process(Exchange exchange) throws Exception { + String id = exchange.getIn().getHeader("id", String.class); + exchange.getOut().setBody("Hello " + id); + } + }); + + from("direct:post1").process(new Processor() { + public void process(Exchange exchange) throws Exception { + String id = exchange.getIn().getHeader("id", String.class); + String ct = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class); + if (!"text/plain".equals(ct)) { + throw new Exception("No content type"); + } + exchange.getOut().setBody("Hello " + id + ": " + exchange.getIn().getBody(String.class)); + exchange.getOut().setHeader(Exchange.CONTENT_TYPE, ct); + } + }); + } + }; + } +} diff --git a/platforms/spring-boot/components-starter/camel-coap-starter/src/main/java/org/apache/camel/coap/springboot/CoAPComponentAutoConfiguration.java b/platforms/spring-boot/components-starter/camel-coap-starter/src/main/java/org/apache/camel/coap/springboot/CoAPComponentAutoConfiguration.java index 028d594..dcfba25 100644 --- a/platforms/spring-boot/components-starter/camel-coap-starter/src/main/java/org/apache/camel/coap/springboot/CoAPComponentAutoConfiguration.java +++ b/platforms/spring-boot/components-starter/camel-coap-starter/src/main/java/org/apache/camel/coap/springboot/CoAPComponentAutoConfiguration.java @@ -75,7 +75,7 @@ public class CoAPComponentAutoConfiguration { } @Lazy - @Bean(name = "coap-component") + @Bean({"coap-component", "coap+tcp-component", "coaps-component", "coaps+tcp-component"}) @ConditionalOnMissingBean(CoAPComponent.class) public CoAPComponent configureCoAPComponent() throws Exception { CoAPComponent component = new CoAPComponent();