Skip to content

Commit

Permalink
First experimentation about Write Attributes support at cliend side.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Sep 19, 2023
1 parent 6e62e54 commit 285cdcd
Show file tree
Hide file tree
Showing 8 changed files with 440 additions and 20 deletions.
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 @@ -33,8 +33,10 @@
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;
Expand Down Expand Up @@ -83,12 +85,14 @@ public class ObjectResource extends LwM2mClientCoapResource implements ObjectLis

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);
}
Expand Down Expand Up @@ -143,16 +147,29 @@ 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;

// TODO handle active cancel observe, we must call : notificationManager.clear(server, observeRequest);
if (exchange.advanced().getRelation() == null || !exchange.advanced().getRelation().isEstablished()) {
// 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);
exchange.respond(ResponseCode.CONTENT,
toolbox.getEncoder().encode(content, format, path, toolbox.getModel()),
format.getCode());

notificationManager.initRelation(server, observeRequest, content,
createNotificationSender(exchange, server, observeRequest, requestedContentFormat));
} 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 +211,37 @@ 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);
exchange.respond(ResponseCode.CONTENT,
toolbox.getEncoder().encode(content, format, path, toolbox.getModel()),
format.getCode());
return true;
} else {
exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
return false;
}
}
return false;
} catch (Exception e) {
LOGGER.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 @@ -32,6 +32,7 @@
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.NotificationManager;
import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.observer.LwM2mClientObserverAdapter;
import org.eclipse.leshan.client.observer.LwM2mClientObserverDispatcher;
Expand Down Expand Up @@ -80,6 +81,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 @@ -118,7 +120,13 @@ public LeshanClient(String endpoint, List<? extends LwM2mObjectEnabler> objectEn
engine);
createRegistrationUpdateHandler(engine, endpointsManager, bootstrapHandler, objectTree, linkFormatHelper);

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

protected NotificationManager createNotificationManager(LwM2mObjectTree objectTree,
DownlinkRequestReceiver requestReceiver) {
return new NotificationManager(objectTree, requestReceiver);
}

protected LwM2mRootEnabler createRootEnabler(LwM2mObjectTree tree) {
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*******************************************************************************
* Copyright (c) 2023 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.client.notification;

import java.util.concurrent.ScheduledFuture;

import org.eclipse.leshan.client.servers.LwM2mServer;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;
import org.eclipse.leshan.core.node.LwM2mNode;
import org.eclipse.leshan.core.request.ObserveRequest;

/**
* This class store information needed to handle write attributes behavior.
* <p>
* It is used by {@link NotificationManager}
*/
public class NotificationDataStore {

// TODO create a real data structure
// for testing purpose we only store 1 NotificationData
private NotificationData data;

public NotificationData getNotificationData(LwM2mServer server, ObserveRequest request) {
return data;
}

public NotificationData addNotificationData(LwM2mServer server, ObserveRequest request, NotificationData data) {
// cancel task of previous data
NotificationData previousData = this.data;
if (previousData != null) {
if (previousData.getPminFuture() != null) {
previousData.getPminFuture().cancel(false);
}
if (previousData.getPmaxFuture() != null) {
previousData.getPmaxFuture().cancel(false);
}
}
// update data
this.data = data;

return previousData;
}

public static class NotificationData {
private final LwM2mAttributeSet attributes;
private final Long lastSendingTime; // time of last sent notification
private final LwM2mNode lastSentValue; // last value sent
private final ScheduledFuture<Void> pminTask; // task which will send delayed notification for pmin.
private final ScheduledFuture<Void> pmaxTask; // task which will send delayed notification for pmax.

public NotificationData(LwM2mAttributeSet attributes, Long lastSendingTime, LwM2mNode lastSentValue,
ScheduledFuture<Void> nextNotification) {
this.attributes = attributes;
this.lastSendingTime = lastSendingTime;
this.lastSentValue = lastSentValue;
this.pminTask = null;
this.pmaxTask = nextNotification;
}

public NotificationData(NotificationData previous, ScheduledFuture<Void> pminTask) {
this.attributes = previous.getAttributes();
this.lastSendingTime = previous.getLastSendingTime();
this.lastSentValue = previous.getLastSentValue();
this.pminTask = pminTask;
this.pmaxTask = previous.getPmaxFuture();
}

public LwM2mAttributeSet getAttributes() {
return attributes;
}

public Long getLastSendingTime() {
return lastSendingTime;
}

public LwM2mNode getLastSentValue() {
return lastSentValue;
}

public ScheduledFuture<Void> getPminFuture() {
return pminTask;
}

public ScheduledFuture<Void> getPmaxFuture() {
return pmaxTask;
}

public boolean usePmin() {
return lastSendingTime != null;
}

public boolean pminTaskScheduled() {
return pminTask != null;
}
}
}
Loading

0 comments on commit 285cdcd

Please sign in to comment.