Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First experimentation about Write Attributes support at cliend side. #1514

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
920657a
First experimentation about Write Attributes support at cliend side.
sbernard31 Sep 19, 2023
137208a
First try with a more dynamic implemention (remove some hardcoded value)
sbernard31 Jan 19, 2024
dcf81bd
Remove unwanted usage of Californium LOGGER
sbernard31 Jan 22, 2024
7df5442
More work about write attribute support.
sbernard31 Jan 25, 2024
001eb0c
Fix issue in IntegerChecker
sbernard31 Jan 29, 2024
2ddfea7
Add some Tests
sbernard31 Jan 29, 2024
b00d265
Modify LwM2mObjectEnabler.getAttributesFor to get it by Server
sbernard31 Jan 31, 2024
440f65d
Add Notification attributes support to discover
sbernard31 Jan 31, 2024
b585b10
First support of write attribute for java-coap
sbernard31 Feb 2, 2024
73a35b8
Fix Float Checker
sbernard31 Feb 6, 2024
c73d181
Adapt code to test lt, gt, st with UNSIGNED_INTEGER and FLOAT.
sbernard31 Feb 6, 2024
9593c10
Enhance error message on assert.isSuccess()
sbernard31 Feb 7, 2024
1a87859
Add support of unsigned integer to gt, lt and st
sbernard31 Feb 7, 2024
be8e43e
Better rounding when attribute target LWM2M integer resource
sbernard31 Feb 8, 2024
9a63c86
Add Test about write attributes house keeping
sbernard31 Feb 14, 2024
08a4bf0
Implement write attributes HouseKeeping.
sbernard31 Feb 14, 2024
7dc140c
Fix javadoc on NotificationAttributeTree
sbernard31 Feb 15, 2024
dee15c3
Fix remove/clear method in NotificationDataStore
sbernard31 Feb 16, 2024
b9bfd4e
Add javadoc.
sbernard31 Feb 16, 2024
67b288c
Add more housekeeping tests
sbernard31 Feb 19, 2024
58167b3
Implement more housekeeping
sbernard31 Feb 19, 2024
031ff46
Make possible to provide its own Notification Strategy + javadoc
sbernard31 Feb 20, 2024
4cd42a8
Make executor configurable
sbernard31 Feb 20, 2024
311bb4a
Fix regression
sbernard31 Feb 22, 2024
8cc88a1
Add support of pmin=pmax
sbernard31 Feb 23, 2024
8a75bcb
Add more invalid attribute test case + more validation
sbernard31 Feb 27, 2024
c130ab9
Use ResourceObserver for californium housekeeping
sbernard31 Feb 27, 2024
00c0a52
Write attribute tests
mgdlkundera Feb 20, 2024
20dc17d
clean tests and add missing one
sbernard31 Feb 20, 2024
52df708
Fix numeric strategy
sbernard31 Feb 29, 2024
90d07eb
Enhance a bit more some tests.
sbernard31 Mar 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.eclipse.leshan.client.endpoint.ClientEndpointToolbox;
import org.eclipse.leshan.client.endpoint.LwM2mClientEndpoint;
import org.eclipse.leshan.client.endpoint.LwM2mClientEndpointsProvider;
import org.eclipse.leshan.client.notification.NotificationManager;
import org.eclipse.leshan.client.request.DownlinkRequestReceiver;
import org.eclipse.leshan.client.resource.LwM2mObjectTree;
import org.eclipse.leshan.client.servers.LwM2mServer;
Expand Down Expand Up @@ -146,7 +147,7 @@ public LwM2mServer extractIdentity(Exchange exchange, IpPeer foreignPeer) {

@Override
public void init(LwM2mObjectTree objectTree, DownlinkRequestReceiver requestReceiver,
ClientEndpointToolbox toolbox) {
NotificationManager notificationManager, ClientEndpointToolbox toolbox) {
this.objectTree = objectTree;

// create coap server
Expand All @@ -160,7 +161,7 @@ protected Resource createRoot() {

// create resources
List<Resource> resources = messagetranslator.createResources(coapServer, identityHandlerProvider,
identityExtrator, requestReceiver, toolbox, objectTree);
identityExtrator, requestReceiver, notificationManager, toolbox, objectTree);
coapServer.add(resources.toArray(new Resource[resources.size()]));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.eclipse.leshan.client.californium.request.CoapRequestBuilder;
import org.eclipse.leshan.client.californium.request.LwM2mResponseBuilder;
import org.eclipse.leshan.client.endpoint.ClientEndpointToolbox;
import org.eclipse.leshan.client.notification.NotificationManager;
import org.eclipse.leshan.client.request.DownlinkRequestReceiver;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.LwM2mObjectTree;
Expand Down Expand Up @@ -89,24 +90,24 @@ public void resourceChanged(LwM2mPath... paths) {

public List<Resource> createResources(CoapServer coapServer, IdentityHandlerProvider identityHandlerProvider,
ServerIdentityExtractor identityExtrator, DownlinkRequestReceiver requestReceiver,
ClientEndpointToolbox toolbox, LwM2mObjectTree objectTree) {
NotificationManager notificationManager, ClientEndpointToolbox toolbox, LwM2mObjectTree objectTree) {
ArrayList<Resource> resources = new ArrayList<>();

// create bootstrap resource
resources.add(new BootstrapResource(identityHandlerProvider, identityExtrator, requestReceiver));

// create object resources
for (LwM2mObjectEnabler enabler : objectTree.getObjectEnablers().values()) {
resources.add(
createObjectResource(enabler, identityHandlerProvider, identityExtrator, requestReceiver, toolbox));
resources.add(createObjectResource(enabler, identityHandlerProvider, identityExtrator, requestReceiver,
notificationManager, toolbox));
}

// link resource to object tree
objectTree.addListener(new ObjectsListenerAdapter() {
@Override
public void objectAdded(LwM2mObjectEnabler object) {
CoapResource clientObject = createObjectResource(object, identityHandlerProvider, identityExtrator,
requestReceiver, toolbox);
requestReceiver, notificationManager, toolbox);
coapServer.add(clientObject);
}

Expand All @@ -124,9 +125,10 @@ public void objectRemoved(LwM2mObjectEnabler object) {

public CoapResource createObjectResource(LwM2mObjectEnabler objectEnabler,
IdentityHandlerProvider identityHandlerProvider, ServerIdentityExtractor identityExtractor,
DownlinkRequestReceiver requestReceiver, ClientEndpointToolbox toolbox) {
DownlinkRequestReceiver requestReceiver, NotificationManager notificationManager,
ClientEndpointToolbox toolbox) {
ObjectResource objectResource = new ObjectResource(objectEnabler.getId(), identityHandlerProvider,
identityExtractor, requestReceiver, toolbox);
identityExtractor, requestReceiver, notificationManager, toolbox);
objectEnabler.addListener(objectResource);
return objectResource;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,23 @@
import org.eclipse.californium.core.coap.CoAP.ResponseCode;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.observe.ObserveRelation;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.core.server.resources.ResourceObserverAdapter;
import org.eclipse.leshan.client.californium.LwM2mClientCoapResource;
import org.eclipse.leshan.client.californium.endpoint.ServerIdentityExtractor;
import org.eclipse.leshan.client.endpoint.ClientEndpointToolbox;
import org.eclipse.leshan.client.notification.NotificationManager;
import org.eclipse.leshan.client.request.DownlinkRequestReceiver;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.NotificationSender;
import org.eclipse.leshan.client.resource.listener.ObjectListener;
import org.eclipse.leshan.client.servers.LwM2mServer;
import org.eclipse.leshan.core.californium.identity.IdentityHandlerProvider;
import org.eclipse.leshan.core.link.attributes.InvalidAttributeException;
import org.eclipse.leshan.core.link.lwm2m.attributes.InvalidAttributesException;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;
import org.eclipse.leshan.core.node.InvalidLwM2mPathException;
import org.eclipse.leshan.core.node.LwM2mNode;
Expand Down Expand Up @@ -75,22 +81,57 @@
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.response.WriteAttributesResponse;
import org.eclipse.leshan.core.response.WriteResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A CoAP {@link Resource} in charge of handling requests targeting a lwM2M Object.
*/
public class ObjectResource extends LwM2mClientCoapResource implements ObjectListener {

private static final Logger LOG = LoggerFactory.getLogger(ObjectResource.class);

protected DownlinkRequestReceiver requestReceiver;
protected ClientEndpointToolbox toolbox;
protected NotificationManager notificationManager;

public ObjectResource(int objectId, IdentityHandlerProvider identityHandlerProvider,
ServerIdentityExtractor serverIdentityExtractor, DownlinkRequestReceiver requestReceiver,
ClientEndpointToolbox toolbox) {
NotificationManager notificationManager, ClientEndpointToolbox toolbox) {
super(Integer.toString(objectId), identityHandlerProvider, serverIdentityExtractor);
this.requestReceiver = requestReceiver;
this.notificationManager = notificationManager;
this.toolbox = toolbox;
setObservable(true);

this.addObserver(new ResourceObserverAdapter() {

@Override
public void removedObserveRelation(ObserveRelation relation) {
// Get object URI
Request request = relation.getExchange().getRequest();
String URI = request.getOptions().getUriPathString();
// we don't manage observation on root path
if (URI == null)
return;

// Get Server identity
LwM2mServer extractIdentity = extractIdentity(relation.getExchange(), request);

// handle content format for Read and Observe Request
ContentFormat requestedContentFormat = null;
if (request.getOptions().hasAccept()) {
// If an request ask for a specific content format, use it (if we support it)
requestedContentFormat = ContentFormat.fromCode(request.getOptions().getAccept());
}

// Create Observe request
ObserveRequest observeRequest = new ObserveRequest(requestedContentFormat, URI, request);

// Remove notification data for this request
notificationManager.clear(extractIdentity, observeRequest);
}
});
}

@Override
Expand Down Expand Up @@ -143,16 +184,44 @@ public void handleGET(CoapExchange exchange) {
// Manage Observe Request
if (exchange.getRequestOptions().hasObserve()) {
ObserveRequest observeRequest = new ObserveRequest(requestedContentFormat, URI, coapRequest);
ObserveResponse response = requestReceiver.requestReceived(server, observeRequest).getResponse();
if (response.getCode() == org.eclipse.leshan.core.ResponseCode.CONTENT) {
LwM2mPath path = getPath(URI);
LwM2mNode content = response.getContent();
ContentFormat format = getContentFormat(observeRequest, requestedContentFormat);
exchange.respond(ResponseCode.CONTENT,
toolbox.getEncoder().encode(content, format, path, toolbox.getModel()), format.getCode());
return;

boolean isObserveRelationEstablishement = coapRequest.isObserve()
&& (exchange.advanced().getRelation() == null
|| !exchange.advanced().getRelation().isEstablished());
boolean isActiveObserveCancellation = coapRequest.isObserveCancel();
if (isObserveRelationEstablishement || isActiveObserveCancellation) {
// Handle observe request
ObserveResponse response = requestReceiver.requestReceived(server, observeRequest).getResponse();
if (response.getCode() == org.eclipse.leshan.core.ResponseCode.CONTENT) {
LwM2mPath path = getPath(URI);
LwM2mNode content = response.getContent();
ContentFormat format = getContentFormat(observeRequest, requestedContentFormat);

// change notification manager state
if (isObserveRelationEstablishement) {
try {
notificationManager.initRelation(server, observeRequest, content,
createNotificationSender(exchange, server, observeRequest,
requestedContentFormat));
} catch (InvalidAttributesException e) {
exchange.respond(
toCoapResponseCode(org.eclipse.leshan.core.ResponseCode.INTERNAL_SERVER_ERROR),
"Invalid Attributes state : " + e.getMessage());
}
}

// send response
exchange.respond(ResponseCode.CONTENT,
toolbox.getEncoder().encode(content, format, path, toolbox.getModel()),
format.getCode());
} else {
exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
return;
}
} else {
exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
// Handle notifications
notificationManager.notificationTriggered(server, observeRequest,
createNotificationSender(exchange, server, observeRequest, requestedContentFormat));
return;
}
} else {
Expand Down Expand Up @@ -194,6 +263,39 @@ public void handleGET(CoapExchange exchange) {
}
}

protected NotificationSender createNotificationSender(CoapExchange exchange, LwM2mServer server,
ObserveRequest observeRequest, ContentFormat requestedContentFormat) {
return new NotificationSender() {
@Override
public boolean sendNotification(ObserveResponse response) {
try {
if (exchange.advanced().getRelation() != null && !exchange.advanced().getRelation().isCanceled()) {
if (response.getCode() == org.eclipse.leshan.core.ResponseCode.CONTENT) {
LwM2mPath path = observeRequest.getPath();
LwM2mNode content = response.getContent();
ContentFormat format = getContentFormat(observeRequest, requestedContentFormat);
Response coapResponse = new Response(ResponseCode.CONTENT);
coapResponse
.setPayload(toolbox.getEncoder().encode(content, format, path, toolbox.getModel()));
coapResponse.getOptions().setContentFormat(format.getCode());
exchange.respond(coapResponse);
return true;
} else {
exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
return false;
}
}
return false;
} catch (Exception e) {
LOG.error("Exception while sending notification [{}] for [{}] to {}", response, observeRequest,
server, e);
exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR, "failure sending notification");
return false;
}
}
};
}

protected ContentFormat getContentFormat(DownlinkRequest<?> request, ContentFormat requestedContentFormat) {
if (requestedContentFormat != null) {
// we already check before this content format is supported.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
import org.eclipse.leshan.client.endpoint.LwM2mClientEndpointsProvider;
import org.eclipse.leshan.client.engine.RegistrationEngine;
import org.eclipse.leshan.client.engine.RegistrationEngineFactory;
import org.eclipse.leshan.client.notification.DefaultNotificationStrategy;
import org.eclipse.leshan.client.notification.NotificationDataStore;
import org.eclipse.leshan.client.notification.NotificationManager;
import org.eclipse.leshan.client.notification.NotificationStrategy;
import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.observer.LwM2mClientObserverAdapter;
import org.eclipse.leshan.client.observer.LwM2mClientObserverDispatcher;
Expand All @@ -56,6 +60,7 @@
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.codec.LwM2mDecoder;
import org.eclipse.leshan.core.node.codec.LwM2mEncoder;
import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.response.ErrorCallback;
import org.eclipse.leshan.core.response.ResponseCallback;
Expand All @@ -82,6 +87,7 @@ public class LeshanClient implements LwM2mClient {
private final RegistrationEngine engine;
private final LwM2mClientObserverDispatcher observers;
private final DataSenderManager dataSenderManager;
private final NotificationManager notificationManager;

public LeshanClient(String endpoint, List<? extends LwM2mObjectEnabler> objectEnablers,
List<DataSender> dataSenders, List<Certificate> trustStore, RegistrationEngineFactory engineFactory,
Expand Down Expand Up @@ -120,7 +126,29 @@ public LeshanClient(String endpoint, List<? extends LwM2mObjectEnabler> objectEn
engine);
createRegistrationUpdateHandler(engine, endpointsManager, bootstrapHandler, objectTree, linkFormatHelper);

endpointsProvider.init(objectTree, requestReceiver, toolbox);
notificationManager = createNotificationManager(objectTree, requestReceiver, sharedExecutor);
endpointsProvider.init(objectTree, requestReceiver, notificationManager, toolbox);
}

protected NotificationManager createNotificationManager(LwM2mObjectTree objectTree,
DownlinkRequestReceiver requestReceiver, ScheduledExecutorService sharedExecutor) {
final NotificationManager notificationManager = new NotificationManager(objectTree, requestReceiver,
createNotificationStore(), createNotificationStrategy(), sharedExecutor);
this.addObserver(new LwM2mClientObserverAdapter() {
@Override
public void onBootstrapStarted(LwM2mServer bsserver, BootstrapRequest request) {
notificationManager.clear();
}
});
return notificationManager;
}

protected NotificationDataStore createNotificationStore() {
return new NotificationDataStore();
}

protected NotificationStrategy createNotificationStrategy() {
return new DefaultNotificationStrategy();
}

protected LwM2mRootEnabler createRootEnabler(LwM2mObjectTree tree) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.Collections;
import java.util.List;

import org.eclipse.leshan.client.notification.NotificationManager;
import org.eclipse.leshan.client.request.DownlinkRequestReceiver;
import org.eclipse.leshan.client.resource.LwM2mObjectTree;
import org.eclipse.leshan.client.servers.LwM2mServer;
Expand All @@ -46,9 +47,9 @@ public DefaultCompositeClientEndpointsProvider(Collection<LwM2mClientEndpointsPr

@Override
public void init(LwM2mObjectTree objectTree, DownlinkRequestReceiver requestReceiver,
ClientEndpointToolbox toolbox) {
NotificationManager notificationManager, ClientEndpointToolbox toolbox) {
for (LwM2mClientEndpointsProvider provider : providers) {
provider.init(objectTree, requestReceiver, toolbox);
provider.init(objectTree, requestReceiver, notificationManager, toolbox);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
import java.util.Collection;
import java.util.List;

import org.eclipse.leshan.client.notification.NotificationManager;
import org.eclipse.leshan.client.request.DownlinkRequestReceiver;
import org.eclipse.leshan.client.resource.LwM2mObjectTree;
import org.eclipse.leshan.client.servers.LwM2mServer;
import org.eclipse.leshan.client.servers.ServerInfo;

public interface LwM2mClientEndpointsProvider {

void init(LwM2mObjectTree objectTree, DownlinkRequestReceiver requestReceiver, ClientEndpointToolbox toolbox);
void init(LwM2mObjectTree objectTree, DownlinkRequestReceiver requestReceiver,
NotificationManager notificationManager, ClientEndpointToolbox toolbox);

LwM2mServer createEndpoint(ServerInfo serverInfo, boolean clientInitiatedOnly, List<Certificate> trustStore,
ClientEndpointToolbox toolbox);
Expand Down
Loading