diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/request/DefaultDownlinkReceiver.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/request/DefaultDownlinkReceiver.java index b0db198301..7f65828b02 100644 --- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/request/DefaultDownlinkReceiver.java +++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/request/DefaultDownlinkReceiver.java @@ -316,7 +316,7 @@ public ErrorResponseCreator(ResponseCode code, String errorMessage) { @Override public void visit(ReadRequest request) { - response = new ReadResponse(code, null, errorMessage); + response = new ReadResponse(code, null, null, errorMessage); } @Override diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/response/ObserveResponse.java b/leshan-core/src/main/java/org/eclipse/leshan/core/response/ObserveResponse.java index 60c989c3ab..66ab20df2e 100644 --- a/leshan-core/src/main/java/org/eclipse/leshan/core/response/ObserveResponse.java +++ b/leshan-core/src/main/java/org/eclipse/leshan/core/response/ObserveResponse.java @@ -40,8 +40,9 @@ public ObserveResponse(ResponseCode code, LwM2mNode content, List timestampedValues, SingleObservation observation, String errorMessage, Object coapResponse) { - super(code, timestampedValues != null && !timestampedValues.isEmpty() ? timestampedValues.get(0).getNode() - : content, errorMessage, coapResponse); + super(code, content, + timestampedValues != null && !timestampedValues.isEmpty() ? timestampedValues.get(0) : null, + errorMessage, coapResponse); // CHANGED is out of spec but is supported for backward compatibility. (previous draft version) if (ResponseCode.CHANGED.equals(code)) { @@ -53,7 +54,7 @@ public ObserveResponse(ResponseCode code, LwM2mNode content, List getTimestampedLwM2mNode() { + public List getTimestampedLwM2mNodes() { return timestampedValues; } diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/response/ReadResponse.java b/leshan-core/src/main/java/org/eclipse/leshan/core/response/ReadResponse.java index 3b6cad5271..770599560b 100644 --- a/leshan-core/src/main/java/org/eclipse/leshan/core/response/ReadResponse.java +++ b/leshan-core/src/main/java/org/eclipse/leshan/core/response/ReadResponse.java @@ -25,6 +25,7 @@ import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.ObjectLink; +import org.eclipse.leshan.core.node.TimestampedLwM2mNode; import org.eclipse.leshan.core.request.exception.InvalidResponseException; import org.eclipse.leshan.core.util.datatype.ULong; @@ -32,13 +33,19 @@ public class ReadResponse extends AbstractLwM2mResponse { protected final LwM2mChildNode content; - public ReadResponse(ResponseCode code, LwM2mNode content, String errorMessage) { - this(code, content, errorMessage, null); + protected final TimestampedLwM2mNode timestampedValue; + + public ReadResponse(ResponseCode code, LwM2mNode content, TimestampedLwM2mNode timestampedValue, + String errorMessage) { + this(code, content, timestampedValue, errorMessage, null); } - public ReadResponse(ResponseCode code, LwM2mNode content, String errorMessage, Object coapResponse) { + public ReadResponse(ResponseCode code, LwM2mNode content, TimestampedLwM2mNode timestampedValue, + String errorMessage, Object coapResponse) { super(code, errorMessage, coapResponse); + content = timestampedValue != null ? timestampedValue.getNode() : content; + if (ResponseCode.CONTENT.equals(code)) { if (content == null) throw new InvalidResponseException("Content is mandatory for successful response"); @@ -48,6 +55,11 @@ public ReadResponse(ResponseCode code, LwM2mNode content, String errorMessage, O content.getClass().getSimpleName()); } this.content = (LwM2mChildNode) content; + this.timestampedValue = timestampedValue; + } + + public TimestampedLwM2mNode getTimestampedLwM2mNode() { + return timestampedValue; } @Override @@ -94,65 +106,76 @@ public static ReadResponse success(LwM2mNode content) { return new ReadResponse(ResponseCode.CONTENT, content, null, null); } + public static ReadResponse success(TimestampedLwM2mNode timestampedValue) { + return new ReadResponse(ResponseCode.CONTENT, null, timestampedValue, null, null); + } + public static ReadResponse success(int resourceId, String value) { - return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newStringResource(resourceId, value), null); + return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newStringResource(resourceId, value), null, + null); } public static ReadResponse success(int resourceId, Date value) { - return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newDateResource(resourceId, value), null); + return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newDateResource(resourceId, value), null, + null); } public static ReadResponse success(int resourceId, long value) { - return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newIntegerResource(resourceId, value), null); + return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newIntegerResource(resourceId, value), null, + null); } public static ReadResponse success(int resourceId, ULong value) { return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newUnsignedIntegerResource(resourceId, value), - null); + null, null); } public static ReadResponse success(int resourceId, ObjectLink value) { return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newObjectLinkResource(resourceId, value), - null); + null, null); } public static ReadResponse success(int resourceId, double value) { - return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newFloatResource(resourceId, value), null); + return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newFloatResource(resourceId, value), null, + null); } public static ReadResponse success(int resourceId, boolean value) { - return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newBooleanResource(resourceId, value), null); + return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newBooleanResource(resourceId, value), null, + null); } public static ReadResponse success(int resourceId, byte[] value) { - return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newBinaryResource(resourceId, value), null); + return new ReadResponse(ResponseCode.CONTENT, LwM2mSingleResource.newBinaryResource(resourceId, value), null, + null); } public static ReadResponse success(int resourceId, Map value, Type type) { - return new ReadResponse(ResponseCode.CONTENT, LwM2mMultipleResource.newResource(resourceId, value, type), null); + return new ReadResponse(ResponseCode.CONTENT, LwM2mMultipleResource.newResource(resourceId, value, type), null, + null); } public static ReadResponse notFound() { - return new ReadResponse(ResponseCode.NOT_FOUND, null, null); + return new ReadResponse(ResponseCode.NOT_FOUND, null, null, null); } public static ReadResponse unauthorized() { - return new ReadResponse(ResponseCode.UNAUTHORIZED, null, null); + return new ReadResponse(ResponseCode.UNAUTHORIZED, null, null, null); } public static ReadResponse methodNotAllowed() { - return new ReadResponse(ResponseCode.METHOD_NOT_ALLOWED, null, null); + return new ReadResponse(ResponseCode.METHOD_NOT_ALLOWED, null, null, null); } public static ReadResponse notAcceptable() { - return new ReadResponse(ResponseCode.NOT_ACCEPTABLE, null, null); + return new ReadResponse(ResponseCode.NOT_ACCEPTABLE, null, null, null); } public static ReadResponse badRequest(String errorMessage) { - return new ReadResponse(ResponseCode.BAD_REQUEST, null, errorMessage); + return new ReadResponse(ResponseCode.BAD_REQUEST, null, null, errorMessage); } public static ReadResponse internalServerError(String errorMessage) { - return new ReadResponse(ResponseCode.INTERNAL_SERVER_ERROR, null, errorMessage); + return new ReadResponse(ResponseCode.INTERNAL_SERVER_ERROR, null, null, errorMessage); } } diff --git a/leshan-core/src/test/java/org/eclipse/leshan/core/response/ObserveResponseTest.java b/leshan-core/src/test/java/org/eclipse/leshan/core/response/ObserveResponseTest.java index 5bc120d13e..96f92ec63d 100644 --- a/leshan-core/src/test/java/org/eclipse/leshan/core/response/ObserveResponseTest.java +++ b/leshan-core/src/test/java/org/eclipse/leshan/core/response/ObserveResponseTest.java @@ -74,7 +74,7 @@ public void should_not_throw_exception_if_has_content(ResponseCode responseCode) // then assertEquals(exampleResource, response.getContent()); - assertNull(response.getTimestampedLwM2mNode()); + assertNull(response.getTimestampedLwM2mNodes()); } @TestAllResponseCode @@ -91,6 +91,6 @@ public void should_get_content_from_first_of_timestamped_values(ResponseCode res // then assertEquals(timestampedValues.get(0).getNode(), response.getContent()); - assertEquals(timestampedValues, response.getTimestampedLwM2mNode()); + assertEquals(timestampedValues, response.getTimestampedLwM2mNodes()); } } diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepLwM2mClient.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepLwM2mClient.java index 92c5f8a375..031deee3d2 100644 --- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepLwM2mClient.java +++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepLwM2mClient.java @@ -109,4 +109,8 @@ public Token sendCoapRequest(Request coapReq) { public String getEndpointName() { return endpointName; } + + public LwM2mModel getLwM2mModel() { + return model; + } } diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepTest.java index f7237ea104..d9c1dc54bf 100644 --- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepTest.java +++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/lockstep/LockStepTest.java @@ -35,6 +35,7 @@ import java.net.InetSocketAddress; import java.net.URI; import java.time.Duration; +import java.time.Instant; import java.util.EnumSet; import java.util.Set; import java.util.concurrent.Callable; @@ -54,6 +55,11 @@ import org.eclipse.leshan.core.endpoint.Protocol; import org.eclipse.leshan.core.link.LinkParser; import org.eclipse.leshan.core.link.lwm2m.DefaultLwM2mLinkParser; +import org.eclipse.leshan.core.node.LwM2mPath; +import org.eclipse.leshan.core.node.LwM2mSingleResource; +import org.eclipse.leshan.core.node.TimestampedLwM2mNodes; +import org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder; +import org.eclipse.leshan.core.node.codec.LwM2mEncoder; import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.request.BindingMode; import org.eclipse.leshan.core.request.ContentFormat; @@ -315,7 +321,7 @@ public ReadResponse call() throws Exception { // Acknowledge the response client.expectRequest().storeMID("R").go(); - client.sendEmpty(Type.ACK).loadMID("R").go(); + client.sendEmpty(Type.ACK, ContentFormat.TEXT).loadMID("R").go(); // Request should timedout in ~3s as we send the ACK Thread.sleep(1500); @@ -370,7 +376,7 @@ public void async_send_with_acknowleged_request_without_response(String givenSer // Acknowledge the response client.expectRequest().storeMID("R").go(); - client.sendEmpty(Type.ACK).loadMID("R").go(); + client.sendEmpty(Type.ACK, ContentFormat.TEXT).loadMID("R").go(); // Request should timedout in ~3s as we send a ack Thread.sleep(1500); @@ -418,8 +424,8 @@ public void register_deregister_observe(String givenServerEndpointProvider) thro // with java-coap it failed transparently at response reception. // TODO I don't know if this is the right behavior. client.expectRequest().storeMID("R").storeToken("T").go(); - client.sendResponse(Type.ACK, ResponseCode.CONTENT).payload("aaa").observe(2).loadMID("R").loadToken("T") - .go(); + client.sendResponse(Type.ACK, ResponseCode.CONTENT, ContentFormat.TEXT).payload("aaa").observe(2) + .loadMID("R").loadToken("T").go(); } // ensure we don't get answer and there is no observation in store. @@ -428,4 +434,47 @@ public void register_deregister_observe(String givenServerEndpointProvider) thro Set observations = server.getObservationService().getObservations(registration); assertThat(observations).isEmpty(); } + + @TestAllTransportLayer + public void read_timestamped(String givenServerEndpointProvider) throws Exception { + + // -------------------------------------------REGISTER + // CLIENT---------------------------------------------------- + LockStepLwM2mClient client = new LockStepLwM2mClient(server.getEndpoint(Protocol.COAP).getURI()); + Token token = client + .sendLwM2mRequest(new RegisterRequest(client.getEndpointName(), 60l, "1.1", EnumSet.of(BindingMode.U), + null, null, linkParser.parseCoreLinkFormat(",,".getBytes()), null)); + client.expectResponse().token(token).go(); + server.waitForNewRegistrationOf(client.getEndpointName()); + Registration registration = server.getRegistrationService().getByEndpoint(client.getEndpointName()); + // -------------------------------------------------------------------------------------------------------------- + + // ----------------------------------------------TIMESTAMP------------------------------------------------------- + LwM2mEncoder encoder = new DefaultLwM2mEncoder(); + TimestampedLwM2mNodes.Builder builder = new TimestampedLwM2mNodes.Builder(); + Instant t1 = Instant.now(); + builder.put(t1, new LwM2mPath("/1/0/1"), LwM2mSingleResource.newIntegerResource(1, 3600)); + TimestampedLwM2mNodes timestampedNodes = builder.build(); + byte[] payload = encoder.encodeTimestampedNodes(timestampedNodes, ContentFormat.SENML_JSON, + client.getLwM2mModel()); + // -------------------------------------------------------------------------------------------------------------- + + // Send read REQUEST + Future future = Executors.newSingleThreadExecutor().submit(new Callable() { + @Override + public ReadResponse call() throws Exception { + // send a request with 3 seconds timeout + return server.send(registration, new ReadRequest(ContentFormat.SENML_JSON, 1), 2000); + } + }); + + client.expectRequest().storeToken("TKN").storeMID("MID").go(); + + client.sendResponse(Type.ACK, ResponseCode.CONTENT, ContentFormat.SENML_JSON).loadMID("MID").loadToken("TKN") + .payload(payload).go(); + + ReadResponse response = future.get(2000, TimeUnit.MILLISECONDS); + assertThat(response.getTimestampedLwM2mNode()).isNotNull(); + } + } diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/observe/ObserveTimeStampTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/observe/ObserveTimeStampTest.java index 6eb3569ad7..4bb533cfd9 100644 --- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/observe/ObserveTimeStampTest.java +++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/observe/ObserveTimeStampTest.java @@ -153,7 +153,7 @@ public void can_observe_timestamped_resource(ContentFormat contentFormat, String ObserveResponse response = server.waitForNotificationOf(observation); assertThat(response).hasContentFormat(contentFormat, givenServerEndpointProvider); assertThat(response.getContent()).isEqualTo(mostRecentNode.getNode()); - assertThat(response.getTimestampedLwM2mNode()).isEqualTo(timestampedNodes); + assertThat(response.getTimestampedLwM2mNodes()).isEqualTo(timestampedNodes); } @TestAllCases @@ -194,7 +194,7 @@ public void can_observe_timestamped_instance(ContentFormat contentFormat, String ObserveResponse response = server.waitForNotificationOf(observation); assertThat(response).hasContentFormat(contentFormat, givenServerEndpointProvider); assertThat(response.getContent()).isEqualTo(mostRecentNode.getNode()); - assertThat(response.getTimestampedLwM2mNode()).isEqualTo(timestampedNodes); + assertThat(response.getTimestampedLwM2mNodes()).isEqualTo(timestampedNodes); } @TestAllCases @@ -235,6 +235,6 @@ public void can_observe_timestamped_object(ContentFormat contentFormat, String g ObserveResponse response = server.waitForNotificationOf(observation); assertThat(response).hasContentFormat(contentFormat, givenServerEndpointProvider); assertThat(response.getContent()).isEqualTo(mostRecentNode.getNode()); - assertThat(response.getTimestampedLwM2mNode()).isEqualTo(timestampedNodes); + assertThat(response.getTimestampedLwM2mNodes()).isEqualTo(timestampedNodes); } } diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/cf/LockstepEndpoint.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/cf/LockstepEndpoint.java index 86e82f9698..e1c74dadf4 100644 --- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/cf/LockstepEndpoint.java +++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/cf/LockstepEndpoint.java @@ -66,6 +66,7 @@ import org.eclipse.californium.elements.RawDataChannel; import org.eclipse.californium.elements.UDPConnector; import org.eclipse.californium.elements.config.Configuration; +import org.eclipse.leshan.core.request.ContentFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -255,7 +256,7 @@ public EmptyMessageExpectation expectEmpty(Type type, int mid) { return new EmptyMessageExpectation(type, mid); } - public RequestProperty sendRequest(Type type, Code code, Token token, int mid) { + public RequestProperty sendRequest(Type type, Code code, Token token, ContentFormat contentFormat, int mid) { if (type == null) { throw new NullPointerException(); } @@ -265,31 +266,40 @@ public RequestProperty sendRequest(Type type, Code code, Token token, int mid) { if (token == null) { throw new NullPointerException(); } + if (contentFormat == null) { + throw new NullPointerException(); + } if (mid < 0 || mid > Message.MAX_MID) { throw new RuntimeException(); } - return new RequestProperty(type, code, token, mid); + return new RequestProperty(type, code, token, contentFormat, mid); } - public ResponseProperty sendResponse(Type type, ResponseCode code) { + public ResponseProperty sendResponse(Type type, ResponseCode code, ContentFormat contentFormat) { if (type == null) { throw new NullPointerException(); } if (code == null) { throw new NullPointerException(); } - return new ResponseProperty(type, code); + if (contentFormat == null) { + throw new NullPointerException(); + } + return new ResponseProperty(type, code, contentFormat); } - public EmptyMessageProperty sendEmpty(Type type) { + public EmptyMessageProperty sendEmpty(Type type, ContentFormat contentFormat) { if (type == null) { throw new NullPointerException(); } - return sendEmpty(type, Message.NONE); + if (contentFormat == null) { + throw new NullPointerException(); + } + return sendEmpty(type, Message.NONE, contentFormat); } - public EmptyMessageProperty sendEmpty(Type type, int mid) { - return new EmptyMessageProperty(type, mid); + public EmptyMessageProperty sendEmpty(Type type, int mid, ContentFormat contentFormat) { + return new EmptyMessageProperty(type, mid, contentFormat); } public void send(RawData raw) { @@ -1428,16 +1438,19 @@ public abstract class MessageProperty implements Action { private final List> properties = new LinkedList>(); private final Type type; + private final ContentFormat contentFormat; private Token token; private int mid; - public MessageProperty(Type type) { + public MessageProperty(Type type, ContentFormat contentFormat) { this.type = type; + this.contentFormat = contentFormat; } - public MessageProperty(Type type, Token token, int mid) { + public MessageProperty(Type type, Token token, ContentFormat contentFormat, int mid) { this.type = type; this.token = token; + this.contentFormat = contentFormat; this.mid = mid; } @@ -1564,12 +1577,12 @@ public void set(Message message) { public class EmptyMessageProperty extends MessageProperty { - public EmptyMessageProperty(Type type, int mid) { - super(type, Token.EMPTY, mid); + public EmptyMessageProperty(Type type, int mid, ContentFormat contentFormat) { + super(type, Token.EMPTY, contentFormat, mid); } - public EmptyMessageProperty(Type type, String midVar) { - super(type); + public EmptyMessageProperty(Type type, String midVar, ContentFormat contentFormat) { + super(type, contentFormat); super.loadMID(midVar); } @@ -1591,8 +1604,8 @@ public class RequestProperty extends MessageProperty { private final Code code; - public RequestProperty(Type type, Code code, Token token, int mid) { - super(type, token, mid); + public RequestProperty(Type type, Code code, Token token, ContentFormat contentFormat, int mid) { + super(type, token, contentFormat, mid); this.code = code; } @@ -1698,8 +1711,8 @@ public class ResponseProperty extends MessageProperty { private final ResponseCode code; - public ResponseProperty(Type type, ResponseCode code) { - super(type); + public ResponseProperty(Type type, ResponseCode code, ContentFormat contentFormat) { + super(type, contentFormat); this.code = code; } @@ -1780,6 +1793,17 @@ public ResponseProperty payload(final String payload, final int from, final int return this; } + public ResponseProperty payload(final byte[] payload) { + properties.add(new Property() { + + @Override + public void set(Response response) { + response.setPayload(payload); + } + }); + return this; + } + public ResponseProperty path(final String path) { properties.add(new Property() { diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/endpoint/ServerCoapMessageTranslator.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/endpoint/ServerCoapMessageTranslator.java index 097271b9a5..b643bafa7e 100644 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/endpoint/ServerCoapMessageTranslator.java +++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/endpoint/ServerCoapMessageTranslator.java @@ -26,6 +26,7 @@ import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.californium.identity.IdentityHandler; import org.eclipse.leshan.core.californium.identity.IdentityHandlerProvider; +import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.TimestampedLwM2mNode; import org.eclipse.leshan.core.node.TimestampedLwM2mNodes; import org.eclipse.leshan.core.node.codec.CodecException; @@ -34,11 +35,13 @@ import org.eclipse.leshan.core.observation.SingleObservation; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.DownlinkRequest; +import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.request.exception.InvalidResponseException; import org.eclipse.leshan.core.response.AbstractLwM2mResponse; import org.eclipse.leshan.core.response.LwM2mResponse; import org.eclipse.leshan.core.response.ObserveCompositeResponse; import org.eclipse.leshan.core.response.ObserveResponse; +import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.californium.registration.RegisterResource; import org.eclipse.leshan.server.californium.request.CoapRequestBuilder; @@ -67,10 +70,39 @@ public Request createCoapRequest(ClientProfile clientProfile, DownlinkRequest T createLwM2mResponse(ClientProfile clientProfile, DownlinkRequest lwm2mRequest, Request coapRequest, Response coapResponse, ServerEndpointToolbox toolbox) { + ContentFormat contentFormat = coapResponse.getOptions().hasContentFormat() + ? ContentFormat.fromCode(coapResponse.getOptions().getContentFormat()) + : null; + if (contentFormat == null && lwm2mRequest instanceof ReadRequest) { + contentFormat = ((ReadRequest) lwm2mRequest).getContentFormat(); + } + + TimestampedLwM2mNode timestampedNode = null; + try { + List timestampedNodes = toolbox.getDecoder().decodeTimestampedData( + coapResponse.getPayload(), contentFormat, new LwM2mPath(1, 0, 1), clientProfile.getModel()); + timestampedNode = timestampedNodes != null ? timestampedNodes.get(0) : null; + } catch (CodecException e) { + LOG.debug(String.format("Unable to decode response payload of request [%s]", coapRequest)); + } + LwM2mResponseBuilder builder = new LwM2mResponseBuilder(coapRequest, coapResponse, clientProfile.getEndpoint(), clientProfile.getModel(), toolbox.getDecoder(), toolbox.getLinkParser()); lwm2mRequest.accept(builder); - return builder.getResponse(); + + if (builder.getResponse() instanceof ReadResponse) { + if (timestampedNode != null && timestampedNode.isTimestamped()) { + ReadResponse readResponse = new ReadResponse(ResponseCode.CONTENT, timestampedNode.getNode(), + timestampedNode, null, coapResponse); + return (T) readResponse; + + } else { + return builder.getResponse(); + } + } else { + return builder.getResponse(); + } + } public List createResources(UplinkRequestReceiver receiver, ServerEndpointToolbox toolbox, diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java index 96cd5fec48..7659c6f9c8 100644 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java +++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/request/LwM2mResponseBuilder.java @@ -35,6 +35,7 @@ import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.LwM2mPath; +import org.eclipse.leshan.core.node.TimestampedLwM2mNode; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.node.codec.LwM2mDecoder; import org.eclipse.leshan.core.observation.CompositeObservation; @@ -117,12 +118,30 @@ public LwM2mResponseBuilder(Request coapRequest, Response coapResponse, String c public void visit(ReadRequest request) { if (coapResponse.isError()) { // handle error response: - lwM2mresponse = new ReadResponse(toLwM2mResponseCode(coapResponse.getCode()), null, + lwM2mresponse = new ReadResponse(toLwM2mResponseCode(coapResponse.getCode()), null, null, coapResponse.getPayloadString(), coapResponse); + } else if (isResponseCodeContent()) { - // handle success response: - LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request, clientEndpoint); - lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, content, null, coapResponse); + ContentFormat contentFormat = null; + if (request.getContentFormat() != null) + contentFormat = request.getContentFormat(); + TimestampedLwM2mNode timestampedNode = null; + try { + List timestampedNodes = decoder.decodeTimestampedData(coapResponse.getPayload(), + contentFormat, request.getPath(), model); + timestampedNode = timestampedNodes != null ? timestampedNodes.get(0) : null; + } catch (CodecException e) { + LOG.debug(String.format("Unable to decode response payload of request [%s]", request)); + } + + if (timestampedNode != null && timestampedNode.isTimestamped()) { + lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, timestampedNode.getNode(), timestampedNode, null, + coapResponse); + } else { + // handle success response: + LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request, clientEndpoint); + lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, content, null, null, coapResponse); + } } else { // handle unexpected response: handleUnexpectedResponseCode(clientEndpoint, request, coapResponse); diff --git a/leshan-tl-javacoap-server/src/main/java/org/eclipse/leshan/transport/javacoap/server/request/LwM2mResponseBuilder.java b/leshan-tl-javacoap-server/src/main/java/org/eclipse/leshan/transport/javacoap/server/request/LwM2mResponseBuilder.java index 44f2186705..380790e613 100644 --- a/leshan-tl-javacoap-server/src/main/java/org/eclipse/leshan/transport/javacoap/server/request/LwM2mResponseBuilder.java +++ b/leshan-tl-javacoap-server/src/main/java/org/eclipse/leshan/transport/javacoap/server/request/LwM2mResponseBuilder.java @@ -25,6 +25,7 @@ import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.LwM2mPath; +import org.eclipse.leshan.core.node.TimestampedLwM2mNode; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.node.codec.LwM2mDecoder; import org.eclipse.leshan.core.observation.CompositeObservation; @@ -117,14 +118,38 @@ public LwM2mResponseBuilder(CoapResponse coapResponse, CoapRequest coapRequest, @Override public void visit(ReadRequest request) { + ContentFormat contentFormat = coapResponse.options().getContentFormat() != null + ? ContentFormat.fromCode(coapResponse.options().getContentFormat()) + : request.getContentFormat(); + + TimestampedLwM2mNode timestampedNode = null; + try { + List timestampedNodes = decoder.decodeTimestampedData( + coapResponse.getPayload().getBytes(), contentFormat, request.getPath(), model); + timestampedNode = timestampedNodes != null ? timestampedNodes.get(0) : null; + } catch (CodecException e) { + LOG.debug(String.format("Unable to decode response payload of request [%s]", request)); + } + if (coapResponse.getCode().getHttpCode() >= 400) { // handle error response: - lwM2mresponse = new ReadResponse(toLwM2mResponseCode(coapResponse.getCode()), null, - coapResponse.getPayloadString(), coapResponse); + if (timestampedNode != null && timestampedNode.isTimestamped()) { + lwM2mresponse = new ReadResponse(toLwM2mResponseCode(coapResponse.getCode()), null, timestampedNode, + coapResponse.getPayloadString(), coapResponse); + } else { + lwM2mresponse = new ReadResponse(toLwM2mResponseCode(coapResponse.getCode()), null, null, + coapResponse.getPayloadString(), coapResponse); + } } else if (isResponseCodeContent()) { // handle success response: - LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request, clientEndpoint); - lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, content, null, coapResponse); + + if (timestampedNode != null && timestampedNode.isTimestamped()) { + lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, timestampedNode.getNode(), timestampedNode, null, + coapResponse); + } else { + LwM2mNode content = decodeCoapResponse(request.getPath(), coapResponse, request, clientEndpoint); + lwM2mresponse = new ReadResponse(ResponseCode.CONTENT, content, null, null, coapResponse); + } } else { // handle unexpected response: handleUnexpectedResponseCode(clientEndpoint, request, coapResponse);