Skip to content

Commit

Permalink
eclipse-leshanGH-1421: Add leshanServerBuilder.setUpdateRegistrationO…
Browse files Browse the repository at this point in the history
…nSend
  • Loading branch information
sbernard31 authored and mgdlkundera committed Sep 25, 2023
1 parent 09989af commit 01248bb
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import org.eclipse.leshan.integration.tests.util.LeshanTestServer;
import org.eclipse.leshan.integration.tests.util.LeshanTestServerBuilder;
import org.eclipse.leshan.integration.tests.util.ReverseProxy;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.InMemorySecurityStore;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;
Expand Down Expand Up @@ -176,7 +177,43 @@ public void can_send_if_client_ip_changes_using_psk(Protocol givenProtocol, Stri
client.waitForRegistrationTo(server);

// Send Data should works
Registration registrationBeforeSend = server.getRegistrationFor(client);
assertSuccessfulSendAfterAddressChanged();

// check that client registration is not updated.
Registration registrationAfterObserve = server.getRegistrationFor(client);
assertThat(registrationAfterObserve).isEqualTo(registrationBeforeSend);
}

@TestTlsTransport
public void update_registration_on_send_using_psk(Protocol givenProtocol, String givenClientEndpointProvider,
String givenServerEndpointProvider)
throws InterruptedException, TimeoutException, NonUniqueSecurityInfoException {

// Start Client and Server
server = givenServerUsing(givenProtocol).with(givenServerEndpointProvider).withUpdateOnSendOperation().build();
server.start();

proxy = givenReverseProxyFor(server, givenProtocol);
proxy.start();

client = givenClientUsing(givenProtocol).with(givenClientEndpointProvider).connectingTo(server).behind(proxy)
.usingPsk(GOOD_PSK_ID, GOOD_PSK_KEY).build();

server.getSecurityStore()
.add(SecurityInfo.newPreSharedKeyInfo(client.getEndpointName(), GOOD_PSK_ID, GOOD_PSK_KEY));

client.start();
server.waitForNewRegistrationOf(client);
client.waitForRegistrationTo(server);

// Send Data should works
Registration registrationBeforeSend = server.getRegistrationFor(client);
assertSuccessfulSendAfterAddressChanged();

// check that client registration is updated.
Registration registrationAfterSend = server.getRegistrationFor(client);
assertThat(registrationAfterSend.getSocketAddress()).isNotEqualTo(registrationBeforeSend.getSocketAddress());
}

@TestTlsTransport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ public LeshanTestServer(LwM2mServerEndpointsProvider endpointsProvider, Registra
SecurityStore securityStore, Authorizer authorizer, LwM2mModelProvider modelProvider, LwM2mEncoder encoder,
LwM2mDecoder decoder, boolean noQueueMode, ClientAwakeTimeProvider awakeTimeProvider,
RegistrationIdProvider registrationIdProvider, RegistrationDataExtractor registrationDataExtractor,
LwM2mLinkParser linkParser, ServerSecurityInfo serverSecurityInfo,
boolean updateRegistrationOnNotification) {
LwM2mLinkParser linkParser, ServerSecurityInfo serverSecurityInfo, boolean updateRegistrationOnNotification,
boolean updateRegistrationOnSend) {
super(endpointsProvider, registrationStore, securityStore, authorizer, modelProvider, encoder, decoder,
noQueueMode, awakeTimeProvider, registrationIdProvider, registrationDataExtractor,
updateRegistrationOnNotification, linkParser, serverSecurityInfo);
updateRegistrationOnNotification, updateRegistrationOnSend, linkParser, serverSecurityInfo);

if (securityStore != null && !(securityStore instanceof EditableSecurityStore)) {
throw new IllegalStateException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ protected LeshanTestServer createServer(LwM2mServerEndpointsProvider endpointsPr
LwM2mModelProvider modelProvider, LwM2mEncoder encoder, LwM2mDecoder decoder, boolean noQueueMode,
ClientAwakeTimeProvider awakeTimeProvider, RegistrationIdProvider registrationIdProvider,
RegistrationDataExtractor registrationDataExtractor, LwM2mLinkParser linkParser,
ServerSecurityInfo serverSecurityInfo, boolean updateRegistrationOnNotification) {
ServerSecurityInfo serverSecurityInfo, boolean updateRegistrationOnNotification,
boolean updateRegistrationOnSend) {

// create endpoint provider.
if (endpointsProvider == null) {
Expand All @@ -105,7 +106,7 @@ protected LeshanTestServer createServer(LwM2mServerEndpointsProvider endpointsPr
}
return new LeshanTestServer(endpointsProvider, registrationStore, securityStore, authorizer, modelProvider,
encoder, decoder, noQueueMode, awakeTimeProvider, registrationIdProvider, registrationDataExtractor,
linkParser, serverSecurityInfo, updateRegistrationOnNotification);
linkParser, serverSecurityInfo, updateRegistrationOnNotification, updateRegistrationOnSend);
}

public static LeshanTestServerBuilder givenServerUsing(Protocol protocolToUse) {
Expand Down Expand Up @@ -169,6 +170,11 @@ public LeshanTestServerBuilder withUpdateOnNotification() {
return this;
}

public LeshanTestServerBuilder withUpdateOnSendOperation() {
setUpdateRegistrationOnSend(true);
return this;
}

protected ServerProtocolProvider getCaliforniumProtocolProvider(Protocol protocol) {
if (protocolToUse.equals(Protocol.COAP)) {
return new CoapServerProtocolProvider();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public class LeshanServer {
* Register operation.
* @param registrationDataExtractor to extract registration data from object links
* @param updateRegistrationOnNotification will activate registration update on observe notification.
* @param updateRegistrationOnSend will activate registration update on Send Operation.
* @param linkParser a parser {@link LwM2mLinkParser} used to parse a CoRE Link.
* @param serverSecurityInfo credentials of the Server
* @since 1.1
Expand All @@ -129,7 +130,7 @@ public LeshanServer(LwM2mServerEndpointsProvider endpointsProvider, Registration
SecurityStore securityStore, Authorizer authorizer, LwM2mModelProvider modelProvider, LwM2mEncoder encoder,
LwM2mDecoder decoder, boolean noQueueMode, ClientAwakeTimeProvider awakeTimeProvider,
RegistrationIdProvider registrationIdProvider, RegistrationDataExtractor registrationDataExtractor,
boolean updateRegistrationOnNotification, LwM2mLinkParser linkParser,
boolean updateRegistrationOnNotification, boolean updateRegistrationOnSend, LwM2mLinkParser linkParser,
ServerSecurityInfo serverSecurityInfo) {

Validate.notNull(endpointsProvider, "endpointsProvider cannot be null");
Expand All @@ -154,7 +155,7 @@ public LeshanServer(LwM2mServerEndpointsProvider endpointsProvider, Registration
presenceService = createPresenceService(registrationService, awakeTimeProvider,
updateRegistrationOnNotification);
}
this.sendService = createSendHandler();
this.sendService = createSendHandler(registrationStore, updateRegistrationOnSend);

// create endpoints
ServerEndpointToolbox toolbox = new ServerEndpointToolbox(decoder, encoder, linkParser,
Expand Down Expand Up @@ -194,8 +195,8 @@ protected PresenceServiceImpl createPresenceService(RegistrationService registra
return presenceService;
}

protected SendHandler createSendHandler() {
return new SendHandler();
protected SendHandler createSendHandler(RegistrationStore registrationStore, boolean updateRegistrationOnSend) {
return new SendHandler(registrationStore, updateRegistrationOnSend);
}

protected DownlinkRequestSender createRequestSender(LwM2mServerEndpointsProvider endpointsProvider,
Expand All @@ -215,8 +216,8 @@ protected DownlinkRequestSender createRequestSender(LwM2mServerEndpointsProvider

@Override
public void updated(RegistrationUpdate update, Registration updatedRegistration, Registration previousReg) {
if ((previousReg.getAddress() != null && !previousReg.getAddress().equals(update.getAddress())) || //
(previousReg.getPort() != null && !previousReg.getPort().equals(update.getPort()))) {
if ((previousReg.getAddress() != null && !previousReg.getAddress().equals(update.getAddress()))
|| (previousReg.getPort() != null && !previousReg.getPort().equals(update.getPort()))) {
requestSender.cancelOngoingRequests(previousReg);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ public class LeshanServerBuilder {
private Certificate[] trustedCertificates;

private boolean noQueueMode = false;
private boolean updateRegistrationOnNotification;
private boolean updateRegistrationOnNotification = false;
private boolean updateRegistrationOnSend = false;

private LwM2mServerEndpointsProvider endpointProvider;

Expand Down Expand Up @@ -269,13 +270,32 @@ public void setRegistrationDataExtractor(RegistrationDataExtractor registrationD
*
* @see <a href=
* "https://github.com/eclipse/leshan/wiki/LWM2M-Devices-with-Dynamic-IP#is-the-update-request-mandatory--should-i-update-registration-on-notification-">Dynamic
* IP environnement documentaiton</a>
* IP environment documentation</a>
*/
public LeshanServerBuilder setUpdateRegistrationOnNotification(boolean updateRegistrationOnNotification) {
this.updateRegistrationOnNotification = updateRegistrationOnNotification;
return this;
}

/**
* Update Registration on Send Operation.
* <p>
* There is some use cases where device can have a dynamic IP (E.g. NAT environment), the specification says to use
* an UPDATE request to notify server about IP address/ port changes. But it seems there is some rare use case where
* this update REQUEST can not be done.
* <p>
* With this option you can allow Leshan to update Registration on Send Operation. This is clearly OUT OF
* SPECIFICATION and so this is not recommended and should be used only if there is no other way.
*
* @see <a href=
* "https://github.com/eclipse/leshan/wiki/LWM2M-Devices-with-Dynamic-IP#is-the-update-request-mandatory--should-i-update-registration-on-notification-">Dynamic
* IP environment documentation</a>
*/
public LeshanServerBuilder setUpdateRegistrationOnSend(boolean updateRegistrationOnSend) {
this.updateRegistrationOnSend = updateRegistrationOnSend;
return this;
}

public LeshanServerBuilder setEndpointsProvider(LwM2mServerEndpointsProvider endpointProvider) {
this.endpointProvider = endpointProvider;
return this;
Expand Down Expand Up @@ -317,7 +337,7 @@ public LeshanServer build() {

return createServer(endpointProvider, registrationStore, securityStore, authorizer, modelProvider, encoder,
decoder, noQueueMode, awakeTimeProvider, registrationIdProvider, registrationDataExtractor, linkParser,
serverSecurityInfo, updateRegistrationOnNotification);
serverSecurityInfo, updateRegistrationOnNotification, updateRegistrationOnSend);
}

/**
Expand All @@ -328,16 +348,17 @@ public LeshanServer build() {
*
* @see LeshanServer#LeshanServer(LwM2mServerEndpointsProvider, RegistrationStore, SecurityStore, Authorizer,
* LwM2mModelProvider, LwM2mEncoder, LwM2mDecoder, boolean, ClientAwakeTimeProvider, RegistrationIdProvider,
* RegistrationDataExtractor, boolean, LwM2mLinkParser, ServerSecurityInfo)
* RegistrationDataExtractor, boolean, boolean, LwM2mLinkParser, ServerSecurityInfo)
*/
protected LeshanServer createServer(LwM2mServerEndpointsProvider endpointsProvider,
RegistrationStore registrationStore, SecurityStore securityStore, Authorizer authorizer,
LwM2mModelProvider modelProvider, LwM2mEncoder encoder, LwM2mDecoder decoder, boolean noQueueMode,
ClientAwakeTimeProvider awakeTimeProvider, RegistrationIdProvider registrationIdProvider,
RegistrationDataExtractor registrationDataExtractor, LwM2mLinkParser linkParser,
ServerSecurityInfo serverSecurityInfo, boolean updateRegistrationOnNotification) {
ServerSecurityInfo serverSecurityInfo, boolean updateRegistrationOnNotification,
boolean updateRegistrationOnSend) {
return new LeshanServer(endpointsProvider, registrationStore, securityStore, authorizer, modelProvider, encoder,
decoder, noQueueMode, awakeTimeProvider, registrationIdProvider, registrationDataExtractor,
updateRegistrationOnNotification, linkParser, serverSecurityInfo);
updateRegistrationOnNotification, updateRegistrationOnSend, linkParser, serverSecurityInfo);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void visit(BootstrapRequest request) {

@Override
public void visit(SendRequest request) {
response = sendHandler.handleSend(senderProfile.getRegistration(), request);
response = sendHandler.handleSend(sender, senderProfile.getRegistration(), request);
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@
import java.util.concurrent.CopyOnWriteArrayList;

import org.eclipse.leshan.core.node.TimestampedLwM2mNodes;
import org.eclipse.leshan.core.peer.LwM2mPeer;
import org.eclipse.leshan.core.request.SendRequest;
import org.eclipse.leshan.core.response.SendResponse;
import org.eclipse.leshan.core.response.SendableResponse;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.registration.RegistrationStore;
import org.eclipse.leshan.server.registration.RegistrationUpdate;
import org.eclipse.leshan.server.registration.UpdatedRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Class responsible to handle "Send" request from LWM2M client.
Expand All @@ -31,8 +37,18 @@
*/
public class SendHandler implements SendService {

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

private final RegistrationStore registrationStore;
private final boolean updateRegistrationOnSend;

private final List<SendListener> listeners = new CopyOnWriteArrayList<>();;

public SendHandler(RegistrationStore registrationStore, boolean updateRegistrationOnSend) {
this.registrationStore = registrationStore;
this.updateRegistrationOnSend = updateRegistrationOnSend;
}

@Override
public void addListener(SendListener listener) {
listeners.add(listener);
Expand All @@ -43,14 +59,50 @@ public void removeListener(SendListener listener) {
listeners.remove(listener);
}

public SendableResponse<SendResponse> handleSend(final Registration registration, final SendRequest request) {
public SendableResponse<SendResponse> handleSend(LwM2mPeer sender, Registration registration,
final SendRequest request) {

// try to update registration if needed
final Registration updatedRegistration;
try {
updatedRegistration = updateRegistration(sender, registration);
} catch (Exception e) {
SendableResponse<SendResponse> response = new SendableResponse<>(
SendResponse.internalServerError("unable to update registration"), new Runnable() {
@Override
public void run() {
onError(registration, e);
}
});
return response;
}

// Send Response to send request on success
SendableResponse<SendResponse> response = new SendableResponse<>(SendResponse.success(), new Runnable() {
@Override
public void run() {
fireDataReceived(registration, request.getTimestampedNodes(), request);
fireDataReceived(updatedRegistration, request.getTimestampedNodes(), request);
}
});
return response;

}

private Registration updateRegistration(LwM2mPeer sender, final Registration registration) {
if (updateRegistrationOnSend) {
RegistrationUpdate regUpdate = new RegistrationUpdate(registration.getId(), sender, null, null, null, null,
null, null, null, null, null, null);
UpdatedRegistration updatedRegistration = registrationStore.updateRegistration(regUpdate);
if (updatedRegistration == null || updatedRegistration.getUpdatedRegistration() == null) {
String errorMsg = String.format(
"Unexpected error when receiving Send Request: There is no registration with id %s",
registration.getId());
LOG.error(errorMsg);
throw new IllegalStateException(errorMsg);
}
return updatedRegistration.getUpdatedRegistration();
}
return registration;
}

protected void fireDataReceived(Registration registration, TimestampedLwM2mNodes data, SendRequest request) {
Expand Down

0 comments on commit 01248bb

Please sign in to comment.