Skip to content

Commit

Permalink
Experiments some LwM2mEndpoint abstraction at Server side.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Aug 22, 2022
1 parent f0d7263 commit 2021e1c
Show file tree
Hide file tree
Showing 79 changed files with 4,457 additions and 2,892 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.observe.Observation;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.observation.CompositeObservation;
import org.eclipse.leshan.core.observation.SingleObservation;
Expand All @@ -42,37 +43,63 @@ public class ObserveUtil {
public static final String CTX_ENDPOINT = "leshan-endpoint";
public static final String CTX_REGID = "leshan-regId";
public static final String CTX_LWM2M_PATH = "leshan-path";
public static final String CTX_CF_OBERSATION = "leshan-cf-obs";

public static org.eclipse.leshan.core.observation.Observation createLwM2mObservation(Observation observation,
String serializedObservation) {
if (observation == null)
return null;

if (observation.getRequest().getCode() == CoAP.Code.GET) {
return ObserveUtil.createLwM2mObservation(observation.getRequest(), serializedObservation);
} else if (observation.getRequest().getCode() == CoAP.Code.FETCH) {
return ObserveUtil.createLwM2mCompositeObservation(observation.getRequest(), serializedObservation);
} else {
throw new IllegalStateException("Observation request can be GET or FETCH only");
}
}

public static SingleObservation createLwM2mObservation(Request request) {
return createLwM2mObservation(request, null);
}

/**
* Create a LWM2M observation from a CoAP request.
*/
public static SingleObservation createLwM2mObservation(Request request) {
ObserveCommon observeCommon = new ObserveCommon(request);
public static SingleObservation createLwM2mObservation(Request request, String serializedObservation) {
ObserveCommon observeCommon = new ObserveCommon(request, serializedObservation);

if (observeCommon.lwm2mPaths.size() != 1) {
throw new IllegalStateException(
"1 path is expected in observe request context but was " + observeCommon.lwm2mPaths);
}

return new SingleObservation(request.getToken().getBytes(), observeCommon.regId,
observeCommon.lwm2mPaths.get(0), observeCommon.responseContentFormat, observeCommon.context);
observeCommon.lwm2mPaths.get(0), observeCommon.responseContentFormat, observeCommon.context,
observeCommon.protocolData);
}

public static CompositeObservation createLwM2mCompositeObservation(Request request) {
ObserveCommon observeCommon = new ObserveCommon(request);
return createLwM2mCompositeObservation(request, null);
}

public static CompositeObservation createLwM2mCompositeObservation(Request request, String serializedObservation) {
ObserveCommon observeCommon = new ObserveCommon(request, serializedObservation);

return new CompositeObservation(request.getToken().getBytes(), observeCommon.regId, observeCommon.lwm2mPaths,
observeCommon.requestContentFormat, observeCommon.responseContentFormat, observeCommon.context);
observeCommon.requestContentFormat, observeCommon.responseContentFormat, observeCommon.context,
observeCommon.protocolData);
}

private static class ObserveCommon {
String regId;
Map<String, String> context;
Map<String, String> protocolData;
List<LwM2mPath> lwm2mPaths;
ContentFormat requestContentFormat;
ContentFormat responseContentFormat;

public ObserveCommon(Request request) {
public ObserveCommon(Request request, String serializedObservation) {
if (request.getUserContext() == null) {
throw new IllegalStateException("missing request context");
}
Expand Down Expand Up @@ -105,6 +132,11 @@ public ObserveCommon(Request request) {
if (request.getOptions().hasAccept()) {
responseContentFormat = ContentFormat.fromCode(request.getOptions().getAccept());
}

if (serializedObservation != null) {
protocolData = new HashMap<>();
protocolData.put(CTX_CF_OBERSATION, serializedObservation);
}
}
}

Expand Down Expand Up @@ -204,6 +236,10 @@ public static String extractEndpoint(org.eclipse.californium.core.observe.Observ
return observation.getRequest().getUserContext().get(CTX_ENDPOINT);
}

public static String extractSerializedObservation(org.eclipse.leshan.core.observation.Observation observation) {
return observation.getProtocolData().get(CTX_CF_OBERSATION);
}

/**
* Validate the Californium observation. It is valid if it contains all necessary context for Leshan.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ public class CompositeObservation extends Observation {
* @param context additional information relative to this observation.
*/
public CompositeObservation(byte[] id, String registrationId, List<LwM2mPath> paths,
ContentFormat requestContentFormat, ContentFormat responseContentFormat, Map<String, String> context) {
super(id, registrationId, context);
ContentFormat requestContentFormat, ContentFormat responseContentFormat, Map<String, String> context,
Map<String, String> protocolData) {
super(id, registrationId, context, protocolData);
this.requestContentFormat = requestContentFormat;
this.responseContentFormat = responseContentFormat;
this.paths = paths;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public abstract class Observation {
protected final byte[] id;
protected final String registrationId;
protected final Map<String, String> context;
protected final Map<String, String> protocolData;

/**
* An abstract constructor for {@link Observation}.
Expand All @@ -38,13 +39,18 @@ public abstract class Observation {
* @param registrationId client's unique registration identifier.
* @param context additional information relative to this observation.
*/
public Observation(byte[] id, String registrationId, Map<String, String> context) {
public Observation(byte[] id, String registrationId, Map<String, String> context,
Map<String, String> protocolData) {
this.id = id;
this.registrationId = registrationId;
if (context != null)
this.context = Collections.unmodifiableMap(new HashMap<>(context));
else
this.context = Collections.emptyMap();
if (protocolData != null)
this.protocolData = Collections.unmodifiableMap(new HashMap<>(protocolData));
else
this.protocolData = Collections.emptyMap();
}

/**
Expand All @@ -71,6 +77,13 @@ public Map<String, String> getContext() {
return context;
}

/**
* @return internal data specific to LwM2mEndpointsProvider
*/
public Map<String, String> getProtocolData() {
return protocolData;
}

@Override
public int hashCode() {
final int prime = 31;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (c) 2022 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.core.observation;

import java.util.Arrays;

import org.eclipse.leshan.core.util.Hex;

/**
* An Observation Identifier.
*/
public class ObservationIdentifier {

private final byte[] bytes;

public ObservationIdentifier(byte[] bytes) {
this.bytes = Arrays.copyOf(bytes, bytes.length);
}

public final byte[] getBytes() {
return Arrays.copyOf(this.bytes, length());
}

public String getAsHexString() {
return Hex.encodeHexString(bytes);
}

public boolean isEmpty() {
return bytes.length == 0;
}

public int length() {
return bytes.length;
}

@Override
public String toString() {
return String.format("Bytes(Ox%s)", getAsHexString());
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(bytes);
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ObservationIdentifier other = (ObservationIdentifier) obj;
if (!Arrays.equals(bytes, other.bytes))
return false;
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public class SingleObservation extends Observation {
* @param context additional information relative to this observation.
*/
public SingleObservation(byte[] id, String registrationId, LwM2mPath path, ContentFormat contentFormat,
Map<String, String> context) {
super(id, registrationId, context);
Map<String, String> context, Map<String, String> protocolData) {
super(id, registrationId, context, protocolData);
this.path = path;
this.contentFormat = contentFormat;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.eclipse.leshan.integration.tests.util.BootstrapRequestChecker;
import org.eclipse.leshan.integration.tests.util.TestObjectsInitializer;
import org.eclipse.leshan.server.bootstrap.BootstrapFailureCause;
import org.eclipse.leshan.server.endpoint.Protocol;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.junit.After;
Expand Down Expand Up @@ -293,7 +294,8 @@ public void bootstrapWithDiscoverOnRootThenRebootstrap() throws InvalidRequestEx
"</>;lwm2m=1.0,</0>;ver=1.1,</0/0>;uri=\"coap://%s:%d\",</0/1>;ssid=2222;uri=\"coap://%s:%d\",</1>;ver=1.1,</1/0>;ssid=2222,</3>;ver=1.1,</3/0>,</21>;ver=2.0",
helper.bootstrapServer.getUnsecuredAddress().getHostString(),
helper.bootstrapServer.getUnsecuredAddress().getPort(),
helper.server.getUnsecuredAddress().getHostString(), helper.server.getUnsecuredAddress().getPort()),
helper.server.getEndpoint(Protocol.COAP).getInetSocketAddress().getHostString(),
helper.server.getEndpoint(Protocol.COAP).getInetSocketAddress().getPort()),
linkSerializer.serializeCoreLinkFormat(lastDiscoverAnswer.getObjectLinks()));

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

public class DeleteTest {
Expand Down Expand Up @@ -74,7 +75,9 @@ public void delete_created_object_instance() throws InterruptedException {
assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));
}

// TODO TL add Coap API again ?
@Test
@Ignore
public void cannot_delete_resource() throws InterruptedException {
// create ACL instance
helper.server.send(helper.getCurrentRegistration(),
Expand All @@ -83,10 +86,10 @@ public void cannot_delete_resource() throws InterruptedException {
// try to delete this resource using coap API as lwm2m API does not allow it.
Request delete = Request.newDelete();
delete.getOptions().addUriPath("2").addUriPath("0").addUriPath("0");
Response response = helper.server.coap().send(helper.getCurrentRegistration(), delete);

// verify result
assertEquals(org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST, response.getCode());
// Response response = helper.server.coap().send(helper.getCurrentRegistration(), delete);
//
// // verify result
// assertEquals(org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST, response.getCode());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
package org.eclipse.leshan.integration.tests;

import org.eclipse.leshan.integration.tests.util.RedisIntegrationTestHelper;
import org.junit.Ignore;

//TODO TL: redis not yet implemented
@Ignore
public class RedisRegistrationTest extends RegistrationTest {

public RedisRegistrationTest() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.junit.Ignore;
import org.junit.Test;

//TODO TL: redis not yet implemented
@Ignore
public class RedisSecurityTest extends SecurityTest {
public RedisSecurityTest() {
helper = new RedisSecureIntegrationTestHelper();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.integration.tests.util.Callback;
import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
import org.eclipse.leshan.server.endpoint.Protocol;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.junit.After;
Expand Down Expand Up @@ -252,7 +253,7 @@ public void register_observe_deregister_observe() throws NonUniqueSecurityInfoEx
} catch (SendFailedException e) {
return;
}
fail("Observe request should be sent");
fail("Observe request should NOT be sent");
}

@Test
Expand Down Expand Up @@ -287,7 +288,8 @@ public void register_with_invalid_request() throws InterruptedException, IOExcep

// create a register request without the list of supported object
Request coapRequest = new Request(Code.POST);
coapRequest.setDestinationContext(new AddressEndpointContext(helper.server.getUnsecuredAddress()));
coapRequest.setDestinationContext(
new AddressEndpointContext(helper.server.getEndpoint(Protocol.COAP).getInetSocketAddress()));
coapRequest.getOptions().setContentFormat(ContentFormat.LINK.getCode());
coapRequest.getOptions().addUriPath("rd");
coapRequest.getOptions().addUriQuery("ep=" + helper.currentEndpointIdentifier);
Expand Down
Loading

0 comments on commit 2021e1c

Please sign in to comment.