Californium CoAP path parameters
Asked Answered
B

3

6

I'm working on a CoAP application using Eclipse Californium and I need to pass parameters using the URL as we do in restful web services. Is it possible to do it in californium coap implementation, and if so please let me know how to do it. ex:

coap://localhost:5683/foo/{fooID}
Bonitabonito answered 13/7, 2015 at 15:46 Comment(0)
R
4

The short answer is yes you can do it.

As stated in JavaDocs

  • When a request arrives at the server, the {@link ServerMessageDeliverer} searches in the resource tree for the destination resource. It travels down the resource tree by looking for one element of the destination URI after another and by calling the method {@link #getChild(String)} on each element. It is allowed to override this method and to return an arbitrary resource. This allows for instance to serve URIs with wildcards or delegate requests to any sub-URI to the same resource.

So basically you have to override deliverRequest and maybe findResource methods in org.eclipse.californium.core.server.ServerMessageDeliverer in order to return appropriate resource which will handle the request. Also it will be required to analyse Exchange Request UriPath as a part of resource handleGET/PUT/POST/etc to fetch path variable (this can be done by using CoapExchange.advanced().getRequest().getOptions().getUriPath())

Based on the source code of Californium it should be pretty easy to override the default behaviour of a request deliverer.

Good luck with that!

Remaremain answered 2/11, 2015 at 21:56 Comment(1)
I’ve learned that it’s easy to use placeholder paths to route incoming requests, but important to update the URI path with what the device actually sends. So, if it sends a device ID in one segment, that needs to replace the placeholder value before going out or it may not be processed by an IoT device. System clients might be a different story, but maybe not.Dania
P
2

From what I have seen so far, creating a custom ServerMessageDeliverer seems to be the more complicated solution. Actually it looks like the correct solution is to override CoapResource#getChild(String) so it returns the resource you want to be associated with that name. The ServerMessageDeliverer looks to me more like the way to implement some sort of controller that delivers or distribute requests in a more complicated environment.

For the question where the last part of the URI is the parameter, the solution could look like this:

public class UriParameterResource extends CoapResource {

    public UriParameterResource() {
        super("foo");
    }

    @Override
    public void handleGET(CoapExchange exchange) {
        List<String> uriPath = exchange.getRequestOptions().getUriPath();
        // check if there is a sub-resource given, and if so use it for processing
        if (uriPath.size() > 1) {
            exchange.respond("Process " + uriPath.get(1));
        } else {
            exchange.respond(ResponseCode.NOT_IMPLEMENTED);
        }
    }

    @Override
    public Resource getChild(String name) {
        // even sub-resources will land in the GET of this resource
        return this;
    }

}

Regarding the the answer from @Copernic, I personally think it does not match the idea of REST. Each part of the URI path should return its own resource related to its parent, which makes it a tree structure per definition and not a flat list that simply inspects parts of the path as some sort of parameter.

IMHO even the sensors example could be resolved by using CoapResource implementations where the variable child resource could be dynamically resolved. The snippet below is just an example, of course this would need to be dependent on the real situation where a house and its rooms would need to register somehow.

public class HousesResource extends CoapResource {

    public HousesResource() {
        super("houses");
    }

    @Override
    public void handleGET(CoapExchange exchange) {
        // could return a list of available houses
        exchange.respond(ResponseCode.NOT_IMPLEMENTED);
    }

    @Override
    public Resource getChild(String name) {
        Resource child = super.getChild(name);
        if (child == null) {
            child = new HouseResource(name);
            add(child);
        }
        return child;
    }


    class HouseResource extends CoapResource {

        public HouseResource(String name) {
            super(name);
            add(new RoomsResource());
        }

        @Override
        public void handleGET(CoapExchange exchange) {
            exchange.respond(ResponseCode.NOT_IMPLEMENTED);
        }

    }

    class RoomsResource extends CoapResource {

        public RoomsResource() {
            super("rooms");
        }

        @Override
        public void handleGET(CoapExchange exchange) {
            // could return a list of available rooms
            exchange.respond(ResponseCode.NOT_IMPLEMENTED);
        }

        @Override
        public Resource getChild(String name) {
            Resource child = super.getChild(name);
            if (child == null) {
                child = new RoomResource(name);
                add(child);
            }
            return child;
        }

    }

    class RoomResource extends CoapResource {

        public RoomResource(String roomName) {
            super(roomName);
            add(new SensorsResource());
        }

        @Override
        public void handleGET(CoapExchange exchange) {
            // could return a summary board about the room
            exchange.respond(ResponseCode.NOT_IMPLEMENTED);
        }

    }

    class SensorsResource extends CoapResource {

        public SensorsResource() {
            super("sensors");
            add(new TemperatureResource());
        }

        @Override
        public void handleGET(CoapExchange exchange) {
            // this could return a list of registered sensors
            exchange.respond(ResponseCode.NOT_IMPLEMENTED);
        }

    }

    class TemperatureResource extends CoapResource {

        public TemperatureResource() {
            super("temperature");
        }

        @Override
        public void handleGET(CoapExchange exchange) {
            // as the structure is fixed we know that two levels up 
            // there is the room, and four levels up there is the house
            String room = getParent().getParent().getName();
            String house = getParent().getParent().getParent().getParent().getName();

            exchange.respond("The temperature of the " + house + " in the " + room + " is : 25 degree");
        }

    }
}

In that example the resources are dynamically created if they did not exist before. This could be also exchanged with some lookup or register mechanism (e.g. a house is registered via PUT or PUSH).

Don't misunderstand me here. The solution by @Copernic seems to work and is probably a suitable solution for some scenarios (e.g. each house has its own server and the requests need to be redirected), but for a rather simple scenario it looks to me that it is not the correct way to go.

Petty answered 17/3, 2017 at 12:21 Comment(0)
V
1

You can override deliverRequest as stated by Alex, However my approach is that I don't pre-register the resources tree, I register resource by resource without maintaining a hierarchy.

public DynamicMessageDeliverer (List<ProxyRes> resources) {
    this.resources  = resources;
}

public void deliverRequest (final Exchange exchange) {
    Request request         = exchange.getRequest ();
    List<String> path       = request.getOptions ().getUriPath ();

    final Resource resource = registerResources (path);     
    if (resource != null) {
        executeResource (exchange, resource);           
    } else {
        exchange.sendResponse (new Response (ResponseCode.NOT_FOUND));
        throw new RuntimeException ("Did not find resource " + path.toString() + " requested by " + request.getSource()+":"+request.getSourcePort());
    }
}

private void executeResource (final Exchange exchange, final Resource resource) {
    // Get the executor and let it process the request
    Executor executor = resource.getExecutor ();
    if (executor != null) {
        exchange.setCustomExecutor ();
        executor.execute (new Runnable () {

            public void run () {
                resource.handleRequest (exchange);
            } 
        });
    } else {
        resource.handleRequest (exchange);
    }
}

private Resource registerResources (List<String> list) {
    LinkedList<String> path         = new LinkedList<String> (list);
    String flatRequestedEndpoint    = Arrays.toString (path.toArray ());
    LinkedList<String> wildcards    = new LinkedList <String> ();
    ProxyRes retainedResource       = null;

    for (ProxyRes proxyRes : resources) {
        String[] res = proxyRes.getPath ().replaceFirst ("/", "").split ("/");

        int length = res.length;
        if (length != path.size ()) {
            continue;
        }

        String flatResEndpoint = Arrays.toString (res);
        if (flatResEndpoint.equals (flatRequestedEndpoint)) {
            retainedResource = proxyRes;
            break;
        }

        boolean match = true;

        for (int i = 0; i < length; i ++) {
            String str = res[i];
            if (str.equals ("*")) {
                wildcards.add (path.get (i));
                continue;
            }

            if (!str.equals (path.get (i))) {
                match = false;
                break;
            }
        }

        if (!match) {
            wildcards.clear ();
            continue;
        }

        retainedResource = proxyRes;
        break;
    }

    if (retainedResource == null) {
        return null;
    }

    ((AbstractResource)retainedResource.getCoapRes ()).setWildcard (wildcards);
    return retainedResource.getCoapRes ();
}

Full answer code with steps is here : Eclipse Californium CoAP wildcard as url path

Vulcan answered 20/5, 2016 at 0:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.