java-coap
java-coap copied to clipboard
General discussion / feedback from Leshan Team
Summary :
Leshan is a java implementation of LWM2M and until now it was strictly based on californium. Recently we did massive changes to abstract transport Layer. (See : https://github.com/eclipse/leshan/issues/1025)
Now to test this abstraction we are playing with java-coap (See : https://github.com/eclipse/leshan/issues/1373)
I will use this general issue to ask questions and provides feedback about it.
Some questions :
-
In californium, there is a endpoint concept. If I simplify the concept a lot this is the socket where message are received. In californium a coap server can have several endpoints. If I understand java-coap correctly, 1 server is only associated to 1 socket right ?
-
is there a way to check if a message (request or response) is CON or not ? something like :
if (!CON.equals(coapRequest.getType())) -
is there a way to set a message (request or response) as CON ? something like :
coapRequest.setConfirmable(true); -
is there a way to execute code after the message( mainly needed for response) is sent ? Maybe a callback once the message is sent ?
-
Patch, IPatch and Fetch seems not implemented ? correct ? is it planned to implement it ? (I think we mainly need Ipatch and fetch for lwm2m composite operation)
-
If you are curious you can have a look at how we are using java-coap, See : https://github.com/eclipse/leshan/compare/5b0b66fa675da2ad9224d15e828036d0c06d0f40 E.g : I created a kind of ResourceService to be able to add some Resource (see RegistrationResource), I don't know if this is a good idea or if this could give you some hint about changing
java-coapAPI ? :man_shrugging:
(more question is coming soon :grin: )
Some answers
- In java-coap, socket is abstracted with
CoapTransportinterface. If you need to use multiple sockets, then it can be done by creatingCoapTransportadapter that will proxy to multiple, underlying instances. Out of curiosity, what is the use case when you want to receive from multiple sockets? - At the moment there is no way to check it if incoming request or response was NON. If needed this info can be easily added to
TransportContext(lets create separate ticket). - By default all outgoing requests are CON, to change it to NON one need to set it in TransportContext, for example: https://github.com/open-coap/java-coap/blob/master/coap-core/src/test/java/com/mbed/coap/server/messaging/ExchangeFilterTest.java#L105
- Not sure if I get you point, but you could wrap
CoapTransportclass and overwritesendPackemethod, there you could add callback to CompletableFuture. - Yes, it is planned.
- Thanks for link, I will have a look.
thx :pray:
-
I don't know if I really need to have several socket by
coapserver:grimacing:. What I know for sure is that 1 LWM2M server should have several socket open with different protocol. n socket for coap, n socket for coaps, n socket for coap+tcp etc etc... This is just different way for a client to connect/communicate to a server. With Californium I create 1coap-serverwith several endpoints. And as I have Californium design/API in mind, I tried to do the same with java-coap... But maybe I should just create severalcoapserver? -
#29 I created the ticket but no urgency to work on it. Maybe better to first identify all problems :) (unless you're sure you want to add it to java-coap)
-
I will look at this.
-
I will try it.
-
Good to know.
-
:warning: this draft code not so clean. (do not hesitate to ask question if you see something not clear)
- I think the only way would be to create multiple CoapServers, or on runtime client can create the one that is needed.
Some more questions about observe. I think this will be one of the big part. Considering observe, a LWM2M server acts as a CoAP client (it sends request and receive notifications)
o1. I find API to send the observe request but didn't find how to get notification (I guess I should use ObservationConsumer but I'm not sure how)
o2. Leshan server based on Californium is able to persists "Observation", so if the server reboots no need to resent a new Observe request to new handle notification. Do you think this is doable with java-coap ? (if yes, could you share some hint)
I added a TokenGenerator to my experimentation, for now I go with a "not so java-coap" spirit way. (See https://github.com/eclipse/leshan/commit/914f0d48c2072518a76a454fd0adda7c1d533457). I guess it's OK for our POC.
But just by curiosity, reading https://github.com/open-coap/java-coap/issues/24#issuecomment-1349583886, I understand there is maybe a more elegant way to do with filter ?
I try to investigate a bit but I'm not sure to find how I should add a filter to my client which I created like this CoapClient coapClient = CoapClientBuilder.clientFor(destination.getIdentity().getPeerAddress(), coapServer);
I guess that I should customize server.clientService() ? so maybe I need to create CoapClient without using Builder ?
Or maybe there is a way to customize clientService when I create coapServer but looking at the builder I didn't find anything.
I see there is an MIDSupplier but not a Token one.
I also maybe find a typo in coap server should inboundService be called outboundService ?
o1. To receive notifications, you could something like this:
CoapClient client = CoapClientBuilder
.newBuilder(new InetSocketAddress("localhost", 5683))
.build();
Function<CoapResponse, Boolean> consumer = coapResponse -> {
System.out.println(coapResponse);
return true; // return false to terminate observation
};
// send request to observe resource, observations will be handled in consumer callback
CompletableFuture<CoapResponse> resp3 = client.observe("/sensors/temperature", consumer);
Note that this sets separate callback per observation relation. I'm thinking to simplify it and set one observation consumer in CoapClientBuilder, what do you think?
o2. Currently that is not possible. How important is that feature for integration with Leshan?
Related to TokenGenerator, yes, it would be more 'elegant' and testable to create a filter. However I noticed that there is a missing method in builder class to add addition filters. I will try to improve it.
I also maybe find a type in coap server should
inboundServicebe calledoutboundService?
You're correct, thanks for pointing this out.
o1. I will try to play with your example then I let you know if 1 observation consumer seems better to me.
o2. It seems that users like observe feature but me not that much :grin: ... I try to rather encourage to use LWM2M Send Opeation which is a most simple alternative.
Why I don't like it ? because we faced lot of issues with observe until now. If you're curious you could have a look at : https://github.com/eclipse/leshan/wiki/LWM2M-Observe
So don't worry, I will be very surprised if java-coap fully support our needs out of the box concerning observe.
Currently that is not possible.
Maybe, it makes sense that some implementation support to persist observe relation and some others don't ? :thinking:
So we could have a coap(s) support based on californium with persisted observation and coap(s)/coap(s)-tcp support based on java-coap without it ?
I need to check if I can modify Leshan in that way.
How important is that feature for integration with Leshan?
For some use case this could be very important. Without going too deep in the details. I guess when you create a LWM2M server based on Leshan and you manage lot of clients in production with lot of Observe Relation. When you reboot your server, this will be an issue to resend all observe requests. (For sure at Sierra, we need it but at least at short/mid term we will still use Californium and Scandium for coap/coaps in production)
I could not agree more with your finding about observe.
Related to storing observation relations, it comes down not only to store Token->UriPath but also IP address... which is very fragile device association because those will change (NAT). Anyway, at least for that reason we would need to have single a single observation consumer.
Thats seems to confirm that implementers don't like so much "Observe" where users like it :sweat_smile:
Related to storing observation relations, it comes down not only to store Token->UriPath but also IP address...
Ideally even more than a IP address, we find that to really make it work, it's better to store a kind of peer identify which are not the same depending of transport used or/and use cases. (coap => peer address / dtls => could be a connection Id or a psk identity or a public key .... / coap+tcp => a tcp connection ?)
About o2) I just realize that I talk only about persisting observations but actually this is more than that... Most of people who use Leshan in a production server want to be able to launch it in a cluster and so observation relation must be shared between all instance of the cluster. Currently in Californium this is achieve with a ObservationStore interface.
Let me know, if this is something you would "like" we explore together (first just by talking about it) or if you are too traumatized by "observe" feature :grin:
In com.mbed.coap.packet.Code, should it be ?
C201_CREATED(2, 01, 201),
- C202_DELETED(2, 02, 200),
+ C202_DELETED(2, 02, 202),
- C203_VALID(2, 03, 200),
+ C203_VALID(2, 03, 203),
- C204_CHANGED(2, 04, 200),
+ C204_CHANGED(2, 04, 204),
- C205_CONTENT(2, 05, 200),
+ C205_CONTENT(2, 05, 205),
No, third parameter is HTTP status code.
No, third parameter is HTTP status code.
:thinking: So I probably totally missed the purpose of this. Could you give me some hints ? :pray:
I was thinking this was just a way to get the code as int but not as it is encoded in CoAP (only 1 byte)
Yes, it is used to translate (proxy) with HTTP
I have a first working version of observe with java-coap at LWM2M server side. If you're curious, the first draft looks like this.
Without support of cluster, I don't need "single observation consumer" (more than that if you look at the code I think I could not implement if with 1 single observation consumer because too much information is missing in coapresponse)
Please create separate ticket about observation consumer and let's discuss more there. For start, what context information would you need?
Just to be sure this ticket should be about :
I'm thinking to simplify it and set one observation consumer in CoapClientBuilder
and ? or ?
persist observation / observation in a cluster
About storing observation relations.
Please create separate ticket about observation consumer and let's discuss more there. For start, what context information would you need?
This is done : https://github.com/open-coap/java-coap/issues/36
(After an huge refactoring in our integrations tests https://github.com/eclipse/leshan/pull/1425 :tired_face: which will allow to reuse most of our tests with java-coap, I'm back to work on this again)
I'm currently using coapRequest.options().getUriQueryMap().
It works well when all part of the query are key value (e.g. field1=value1&field2=value2&field3=value3)
But doesn't work if some key has no value (e.g. field1=value1&field2&field3)
This is needed in LWM2M for Q option of Register operation. (see : LWM2M-v1.1.1@transport§Table: 6.4.3.-1 Operation to Method and URI Mapping (Registration Interface))
Just in case I double check if this is valid in CoAP, I think it is the RFC says :
The query serves to further parameterize the resource. It consists of a sequence of arguments separated by an ampersand character (U+0026 AMPERSAND "&"). An argument is often in the form of a "key=value" pair.
(source : https://datatracker.ietf.org/doc/html/rfc7252#section-6.1)
- For each Uri-Query Option in the request, append a single character U+003F QUESTION MARK (?) (first option) or U+0026 AMPERSAND (&) (subsequent options) followed by the option's value to |resource name|, after converting any character that is not either in the "unreserved" set, in the "sub-delims" set (except U+0026 AMPERSAND (&)), a U+003A COLON (:), a U+0040 COMMERCIAL AT (@), a U+002F SOLIDUS (/), or a U+003F QUESTION MARK (?) character to its percent-encoded form.
(source : https://datatracker.ietf.org/doc/html/rfc7252#section-6.5)
Let me know, if you want to adapt java-coap code and if I should create a dedicated issue. :slightly_smiling_face:
Sounds like a missing feature so if you could create a separate ticket for this. That should not be too difficult to support. Thanks.
For some integrations tests, I need to configure CoAP Timeout. With californium the code looks like :
// configure retransmission, with this configuration a request without ACK should timeout in
// ~200*5ms
configuration.set(CoapConfig.ACK_TIMEOUT, 200, TimeUnit.MILLISECONDS) //
.set(CoapConfig.ACK_INIT_RANDOM, 1f) //
.set(CoapConfig.ACK_TIMEOUT_SCALE, 1f) //
.set(CoapConfig.MAX_RETRANSMIT, 4);
In java-coap, I guess this can be done like this :
CoapServer.builder().timeout(new CoapTimeout(ackTimeout, maxRetransmit));
But there is no way to change ACK_RANDOM_FACTOR which should be what replaces ACK_INIT_RANDOM and ACK_TIMEOUT_SCALE.
For now I fallback with CoapServer.builder().timeout(new SingleTimeout(1000)); in my tests.
If this is something you want to add, I can create a new issue.
Adding parameter for ACK_RANDOM_FACTOR should be trivial thing to do, so if it helps you, please create a separate ticket.
By the way, there was API change on how to set retransmission strategy (it was inspired by one of our discussion in this area). Now it is for example:
CoapServer.builder()
.retransmission(RetransmissionBackOff.ofExponential(Duration.ofMillis(100), 4))
Not really a feature request but just a user feedback about CoapRequest API.
CoapRequest can be modified using some "setter".
Some of this setters are grouped as // --- MODIFIERS ---
other are grouped under // --- OPTIONS MODIFIERS ---
Modifiers does not modify the instance but create (clone) a new CoapRequest from the original with modified field and returns it.
Option Modifiers modify directly the instance and return it.
(not sure I'm so clear :sweat_smile:)
Maybe there are good reason to do that but from a user point of view, I think this is a bit missleading.
My first try with the API looks like this and it took me some time to find that some 'setter' returns new instances.
// Create CoAP request from LWM2M Read Request
coapRequest = CoapRequest.get(getAddress(), getURI(request.getPath()));
coapRequest.token(tokenGenerator.createToken()); // this is the missleading part.
if (request.getContentFormat() != null)
coapRequest.options().setAccept(request.getContentFormat().getCode());
I didn't find the way to get token on CoapResponse, is there an API for this ?
I also see there is a way to get PeerAddress on CoapRequest but not on CoapResponse ? (This could be needed for observation but this more or less related to https://github.com/open-coap/java-coap/issues/36)
When we handle a message (mainly a request) is there a way to know local address (CoapTransport.getLocalSocketAddress()) which received it ?
Not really a feature request but just a user feedback about
CoapRequestAPI.
CoapRequestcan be modified using some "setter".Some of this setters are grouped as
// --- MODIFIERS ---other are grouped under// --- OPTIONS MODIFIERS ---Modifiers does not modify the instance but create (clone) a new
CoapRequestfrom the original with modified field and returns it. Option Modifiers modify directly the instance and return it. (not sure I'm so clear 😅)Maybe there are good reason to do that but from a user point of view, I think this is a bit missleading.
My first try with the API looks like this and it took me some time to find that some 'setter' returns new instances.
// Create CoAP request from LWM2M Read Request coapRequest = CoapRequest.get(getAddress(), getURI(request.getPath())); coapRequest.token(tokenGenerator.createToken()); // this is the missleading part. if (request.getContentFormat() != null) coapRequest.options().setAccept(request.getContentFormat().getCode());
I agree, it is misleading. The idea is to have immutable CoapRequest and CoapResponse, so that if it's modified down the pipeline, there is no surprises. Currently header options are not immutable so that is one problem.
Maybe one solution would be to have dedicated builder class for CoapRequest and CoapResponse.
I didn't find the way to get token on
CoapResponse, is there an API for this ?I also see there is a way to get PeerAddress on
CoapRequestbut not onCoapResponse? (This could be needed for observation but this more or less related to #36)
Yes, there is no API to get token from coap-response. Observation are wrapped around SeparateResponse class will provide those metadata.
When we handle a message (mainly a request) is there a way to know local address (
CoapTransport.getLocalSocketAddress()) which received it ?
No, there is no such an API. What's the use case behind it?