Repository: camel Updated Branches: refs/heads/master 2ed525a0d -> 741924393
Implement the rest API for the coap consumer, start getting the query params and other coap info into the exchange Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/74192439 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/74192439 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/74192439 Branch: refs/heads/master Commit: 741924393379923efbdb0a98d1be84bece75f659 Parents: 2ed525a Author: Daniel Kulp <dk...@apache.org> Authored: Wed Jul 22 16:15:15 2015 -0400 Committer: Daniel Kulp <dk...@apache.org> Committed: Wed Jul 22 16:15:54 2015 -0400 ---------------------------------------------------------------------- .../apache/camel/coap/CamelCoapResource.java | 134 +++++++++++++++++++ .../org/apache/camel/coap/CoAPComponent.java | 51 ++++++- .../org/apache/camel/coap/CoAPConsumer.java | 75 ++++++----- .../org/apache/camel/coap/CoAPEndpoint.java | 14 ++ .../camel/coap/CoAPRestComponentTest.java | 82 ++++++++++++ 5 files changed, 319 insertions(+), 37 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/74192439/components/camel-coap/src/main/java/org/apache/camel/coap/CamelCoapResource.java ---------------------------------------------------------------------- diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CamelCoapResource.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CamelCoapResource.java new file mode 100644 index 0000000..ecd8e10 --- /dev/null +++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CamelCoapResource.java @@ -0,0 +1,134 @@ +/** + * 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 java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.camel.Message; +import org.eclipse.californium.core.CoapResource; +import org.eclipse.californium.core.coap.CoAP.ResponseCode; +import org.eclipse.californium.core.network.Exchange; +import org.eclipse.californium.core.server.resources.CoapExchange; +import org.eclipse.californium.core.server.resources.Resource; + +final class CamelCoapResource extends CoapResource { + private final Map<String, CoAPConsumer> consumers = new ConcurrentHashMap<>(); + private final List<CamelCoapResource> possibles; + + CamelCoapResource(String name, CoAPConsumer consumer) { + super(name); + consumers.put(consumer.getCoapEndpoint().getCoapMethod(), consumer); + possibles = null; + } + + private CamelCoapResource(String name, List<CamelCoapResource> possibles) { + super(name); + this.possibles = possibles; + } + + void addConsumer(CoAPConsumer consumer) { + consumers.put(consumer.getCoapEndpoint().getCoapMethod(), consumer); + } + + @Override + public Resource getChild(String name) { + if (possibles != null) { + //FIXME - find which might work... + } + Resource child = super.getChild(name); + if (child == null) { + final List<CamelCoapResource> possibles = new LinkedList<>(); + for (Resource r : getChildren()) { + if (r.getName().startsWith("{") && r.getName().endsWith("}")) { + possibles.add((CamelCoapResource)r); + } + } + if (possibles.size() == 1) { + return possibles.get(0); + } + if (!possibles.isEmpty()) { + return new CamelCoapResource(name, possibles); + } + } + return child; + } + @Override + public void handleRequest(Exchange exchange) { + org.apache.camel.Exchange camelExchange = null; + CoAPConsumer consumer = null; + if (possibles != null) { + consumers.putAll(possibles.get(0).consumers); + } + CoapExchange cexchange = new CoapExchange(exchange, this); + try { + consumer = consumers.get(exchange.getRequest().getCode().name()); + if (consumer == null) { + consumer = consumers.get("*"); + } + + camelExchange = consumer.getEndpoint().createExchange(); + consumer.createUoW(camelExchange); + + for (String s : exchange.getRequest().getOptions().getUriQuery()) { + int i = s.indexOf('='); + if (i == -1) { + camelExchange.getIn().setHeader(s, ""); + } else { + camelExchange.getIn().setHeader(s.substring(0, i), s.substring(i + 1)); + } + } + + List<String> path = exchange.getRequest().getOptions().getUriPath(); + LinkedList<Resource> resources = new LinkedList<>(); + Resource r = this; + while (r != null) { + resources.push(r); + r = r.getParent(); + } + if (resources.getFirst().getName().isEmpty()) { + resources.removeFirst(); + } + int res = 0; + while (!resources.isEmpty() && res < path.size()) { + r = resources.removeFirst(); + if (r.getName().charAt(0) == '{' && r.getName().charAt(r.getName().length() - 1) == '}') { + String n = r.getName().substring(1, r.getName().length() - 1); + camelExchange.getIn().setHeader(n, path.get(res)); + } + res++; + } + + byte bytes[] = exchange.getCurrentRequest().getPayload(); + camelExchange.getIn().setBody(bytes); + + consumer.getProcessor().process(camelExchange); + Message target = camelExchange.hasOut() ? camelExchange.getOut() : camelExchange.getIn(); + + cexchange.respond(ResponseCode.CONTENT, target.getBody(byte[].class)); + + } catch (Exception e) { + cexchange.respond(ResponseCode.INTERNAL_SERVER_ERROR, e.getMessage()); + } finally { + if (camelExchange != null) { + consumer.doneUoW(camelExchange); + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/74192439/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java ---------------------------------------------------------------------- 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 15ac123..8c96224 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 @@ -16,6 +16,8 @@ */ package org.apache.camel.coap; +import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -24,7 +26,9 @@ import org.apache.camel.Consumer; import org.apache.camel.Endpoint; import org.apache.camel.Processor; import org.apache.camel.impl.UriEndpointComponent; +import org.apache.camel.spi.RestConfiguration; import org.apache.camel.spi.RestConsumerFactory; +import org.apache.camel.util.URISupport; import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.network.config.NetworkConfig; @@ -33,6 +37,7 @@ import org.eclipse.californium.core.network.config.NetworkConfig; */ public class CoAPComponent extends UriEndpointComponent implements RestConsumerFactory { final Map<Integer, CoapServer> servers = new ConcurrentHashMap<>(); + CoapServer defaultServer; public CoAPComponent() { super(CoAPEndpoint.class); @@ -44,6 +49,12 @@ public class CoAPComponent extends UriEndpointComponent implements RestConsumerF public synchronized CoapServer getServer(int port) { CoapServer server = servers.get(port); + if (server == null && port == -1) { + server = defaultServer; + } + if (server == null && port == -1) { + server = servers.get(5684); + } if (server == null) { NetworkConfig config = new NetworkConfig(); //FIXME- configure the network stuff @@ -71,13 +82,51 @@ public class CoAPComponent extends UriEndpointComponent implements RestConsumerF String consumes, String produces, Map<String, Object> parameters) throws Exception { - return null; + RestConfiguration config = getCamelContext().getRestConfiguration(); + Map<String, Object> map = new HashMap<String, Object>(); + // build query string, and append any endpoint configuration properties + if (config != null && (config.getComponent() == null || config.getComponent().equals("restlet"))) { + // setup endpoint options + if (config.getEndpointProperties() != null && !config.getEndpointProperties().isEmpty()) { + map.putAll(config.getEndpointProperties()); + } + } + + String query = URISupport.createQueryString(map); + + + String url = config.getScheme() + "://" + config.getHost(); + if (config.getPort() != -1) { + url += ":" + config.getPort(); + } + String restrict = verb.toUpperCase(Locale.US); + if (uriTemplate == null) { + uriTemplate = ""; + } + url += basePath + uriTemplate + "?coapMethod=" + restrict; + if (!query.isEmpty()) { + url += "&" + query; + } + + CoAPEndpoint endpoint = camelContext.getEndpoint(url, CoAPEndpoint.class); + setProperties(endpoint, parameters); + return endpoint.createConsumer(processor); } @Override protected void doStart() throws Exception { super.doStart(); + + RestConfiguration config = getCamelContext().getRestConfiguration(); + if (config != null && (config.getComponent() == null || config.getComponent().equals("coap"))) { + // configure additional options on spark configuration + if (config.getComponentProperties() != null && !config.getComponentProperties().isEmpty()) { + setProperties(this, config.getComponentProperties()); + } + defaultServer = getServer(config.getPort()); + } + for (CoapServer s : servers.values()) { s.start(); } http://git-wip-us.apache.org/repos/asf/camel/blob/74192439/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConsumer.java ---------------------------------------------------------------------- diff --git a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConsumer.java b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConsumer.java index fc7c0a3..ac4ab6c 100644 --- a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConsumer.java +++ b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPConsumer.java @@ -17,64 +17,67 @@ package org.apache.camel.coap; -import org.apache.camel.Message; +import java.util.LinkedList; +import java.util.List; + import org.apache.camel.Processor; import org.apache.camel.impl.DefaultConsumer; import org.eclipse.californium.core.CoapResource; -import org.eclipse.californium.core.coap.CoAP.ResponseCode; -import org.eclipse.californium.core.network.Exchange; -import org.eclipse.californium.core.server.resources.CoapExchange; +import org.eclipse.californium.core.server.resources.Resource; /** * The CoAP consumer. */ public class CoAPConsumer extends DefaultConsumer { private final CoAPEndpoint endpoint; - private CoapResource resource; + private List<CoapResource> resources = new LinkedList<>(); public CoAPConsumer(final CoAPEndpoint endpoint, final Processor processor) { super(endpoint, processor); - this.endpoint = endpoint; + this.endpoint = endpoint; + } + + public CoAPEndpoint getCoapEndpoint() { + return endpoint; + } + + @Override + protected void doStart() throws Exception { + super.doStart(); String path = endpoint.getUri().getPath(); if (path.startsWith("/")) { path = path.substring(1); } - - this.resource = new CoapResource(path) { - - @Override - public void handleRequest(Exchange exchange) { - CoapExchange cexchange = new CoapExchange(exchange, this); - org.apache.camel.Exchange camelExchange = endpoint.createExchange(); - byte bytes[] = exchange.getCurrentRequest().getPayload(); - camelExchange.getIn().setBody(bytes); - try { - processor.process(camelExchange); - - - Message target = camelExchange.hasOut() ? camelExchange.getOut() : camelExchange.getIn(); - - cexchange.respond(ResponseCode.CONTENT, target.getBody(byte[].class)); - - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + Resource cr = endpoint.getCoapServer().getRoot(); + while (!path.isEmpty()) { + int idx = path.indexOf('/'); + String part1 = path; + if (idx != -1) { + part1 = path.substring(0, idx); + path = path.substring(idx + 1); + } else { + path = ""; } - - }; - } - - @Override - protected void doStart() throws Exception { - super.doStart(); - endpoint.getCoapServer().add(resource); + Resource child = cr.getChild(part1); + if (child == null) { + child = new CamelCoapResource(part1, this); + cr.add(child); + cr = child; + } else if (path.isEmpty()) { + ((CamelCoapResource)child).addConsumer(this); + } else { + cr = child; + } + } } @Override protected void doStop() throws Exception { - endpoint.getCoapServer().remove(resource); + for (CoapResource r : resources) { + r.getParent().remove(r); + } + resources.clear(); super.doStop(); } } http://git-wip-us.apache.org/repos/asf/camel/blob/74192439/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPEndpoint.java ---------------------------------------------------------------------- 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 fb985f1..758a329 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 @@ -23,6 +23,7 @@ import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.impl.DefaultEndpoint; import org.apache.camel.spi.UriEndpoint; +import org.apache.camel.spi.UriParam; import org.apache.camel.spi.UriPath; import org.eclipse.californium.core.CoapServer; @@ -33,6 +34,8 @@ import org.eclipse.californium.core.CoapServer; public class CoAPEndpoint extends DefaultEndpoint { @UriPath private URI uri; + @UriParam(defaultValue = "*") + private String coapMethod = "*"; private CoAPComponent component; @@ -46,6 +49,17 @@ public class CoAPEndpoint extends DefaultEndpoint { this.component = component; } + public void setCoapMethod(String m) { + coapMethod = m; + } + /** + * The CoAP method this endpoint binds to. Default is to bind to all ("*") but can + * be restricted to GET, POST, PUT, DELETE + * @return + */ + public String getCoapMethod() { + return coapMethod; + } public Producer createProducer() throws Exception { return new CoAPProducer(this); } http://git-wip-us.apache.org/repos/asf/camel/blob/74192439/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPRestComponentTest.java ---------------------------------------------------------------------- diff --git a/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPRestComponentTest.java b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPRestComponentTest.java new file mode 100644 index 0000000..f2b56ec --- /dev/null +++ b/components/camel-coap/src/test/java/org/apache/camel/coap/CoAPRestComponentTest.java @@ -0,0 +1,82 @@ +/** + * 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.MediaTypeRegistry; +import org.eclipse.californium.core.network.config.NetworkConfig; +import org.junit.Test; + +public class CoAPRestComponentTest extends CamelTestSupport { + int port = AvailablePortFinder.getNextAvailable(); + + @Test + public void testCoAP() throws Exception { + NetworkConfig.createStandardWithoutFile(); + CoapClient client; + CoapResponse rsp; + + client = new CoapClient("coap://localhost:" + port + "/TestResource/Ducky"); + client.setTimeout(1000000); + rsp = client.get(); + assertEquals("Hello Ducky", rsp.getResponseText()); + rsp = client.post("data", MediaTypeRegistry.TEXT_PLAIN); + assertEquals("Hello Ducky: data", rsp.getResponseText()); + + client = new CoapClient("coap://localhost:" + port + "/TestParms?id=Ducky"); + client.setTimeout(1000000); + rsp = client.get(); + assertEquals("Hello Ducky", rsp.getResponseText()); + rsp = client.post("data", MediaTypeRegistry.TEXT_PLAIN); + assertEquals("Hello Ducky: data", rsp.getResponseText()); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + restConfiguration().component("coap").host("localhost").scheme("coap").port(port); + rest("/TestParms") + .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); + exchange.getOut().setBody("Hello " + id + ": " + exchange.getIn().getBody(String.class)); + } + }); + } + }; + } +}