diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/object/ObjectResource.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/object/ObjectResource.java index c64377c99f..b062343278 100644 --- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/object/ObjectResource.java +++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/object/ObjectResource.java @@ -289,15 +289,20 @@ public void handlePOST(CoapExchange exchange) { // Manage Execute Request if (path.isResource()) { - byte[] payload = exchange.getRequestPayload(); - ExecuteResponse response = nodeEnabler.execute(identity, - new ExecuteRequest(URI, payload != null ? new String(payload) : null, coapRequest)); - if (response.getCode().isError()) { - exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage()); - } else { - exchange.respond(toCoapResponseCode(response.getCode())); + // execute request has no content format at all or a TEXT concent format for parameters. + if (!exchange.getRequestOptions().hasContentFormat() + || ContentFormat.fromCode(exchange.getRequestOptions().getContentFormat()) == ContentFormat.TEXT) { + byte[] payload = exchange.getRequestPayload(); + + ExecuteResponse response = nodeEnabler.execute(identity, + new ExecuteRequest(URI, payload != null ? new String(payload) : null, coapRequest)); + if (response.getCode().isError()) { + exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage()); + } else { + exchange.respond(toCoapResponseCode(response.getCode())); + } + return; } - return; } // handle content format for Write (Update) and Create request @@ -313,6 +318,22 @@ public void handlePOST(CoapExchange exchange) { } LwM2mModel model = new StaticModel(nodeEnabler.getObjectModel()); + // manage partial update of multi-instance resource + if (path.isResource()) { + try { + LwM2mNode lwM2mNode = decoder.decode(exchange.getRequestPayload(), contentFormat, path, model); + WriteResponse response = nodeEnabler.write(identity, + new WriteRequest(Mode.UPDATE, contentFormat, URI, lwM2mNode, coapRequest)); + if (response.getCode().isError()) { + exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage()); + } else { + exchange.respond(toCoapResponseCode(response.getCode())); + } + } catch (CodecException e) { + handleInvalidRequest(exchange.advanced(), "Unable to decode payload on WRITE", e); + } + return; + } // Manage Update Instance if (path.isObjectInstance()) { try { diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/ObjectEnabler.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/ObjectEnabler.java index 2de0be1306..ecdfe3d026 100644 --- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/ObjectEnabler.java +++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/resource/ObjectEnabler.java @@ -278,7 +278,8 @@ protected WriteResponse doWrite(ServerIdentity identity, WriteRequest request) { // Manage Resource case if (path.getResourceInstanceId() == null) { - return instance.write(identity, true, path.getResourceId(), (LwM2mResource) request.getNode()); + return instance.write(identity, request.isReplaceRequest(), path.getResourceId(), + (LwM2mResource) request.getNode()); } // Manage Resource Instance case diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/request/WriteRequest.java b/leshan-core/src/main/java/org/eclipse/leshan/core/request/WriteRequest.java index d49cde3f2d..0663a4b28f 100644 --- a/leshan-core/src/main/java/org/eclipse/leshan/core/request/WriteRequest.java +++ b/leshan-core/src/main/java/org/eclipse/leshan/core/request/WriteRequest.java @@ -299,23 +299,25 @@ public WriteRequest(int objectId, int objectInstanceId, int resourceId, MapMulti-Instance Resource using the TLV content format. * * @param mode the mode of the request : replace or update. * @param contentFormat Format of the payload (TLV or JSON). - * @param objectId the object ID of the resource - * @param objectInstanceId the object instance ID - * @param resourceId the (individual) resource's ID - * @param resourceInstanceId the resource instance's ID - * @param value the resource instance (id->value) to write. + * @param objectId the id of the object to write. + * @param objectInstanceId the id of the object instance to write. + * @param resourceId the id of the resource to write. + * @param values the list of resource instance (id->value) to write. * @param type the data type of the resource. + * @exception InvalidRequestException if parameters are invalid. */ public WriteRequest(Mode mode, ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId, - int resourceInstanceId, Object value, Type type) { - this(mode, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId, resourceInstanceId), - LwM2mResourceInstance.newInstance(resourceInstanceId, value, type), null); + Map values, Type type) throws InvalidRequestException { + this(mode, ContentFormat.TLV, new LwM2mPath(objectId, objectInstanceId, resourceId), + LwM2mMultipleResource.newResource(resourceId, values, type), null); } + // ***************** write resource instance****************** // + /** * Request to write a specific resource instance from a client, MODE = REPLACE. * @@ -387,7 +389,8 @@ public WriteRequest(Mode mode, ContentFormat format, LwM2mPath target, LwM2mNode throw new InvalidRequestException("new node value is mandatory for %s", target); // Validate Mode - if (getPath().isResource() && mode == Mode.UPDATE) + if (getPath().isResource() && mode == Mode.UPDATE + && (node instanceof LwM2mSingleResource || node instanceof LwM2mResourceInstance)) throw new InvalidRequestException("Invalid mode for '%s': update is not allowed on resource", target); // Validate node and path coherence diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteMultiValueTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteMultiValueTest.java index 204e9c9c05..73a7640172 100644 --- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteMultiValueTest.java +++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteMultiValueTest.java @@ -214,13 +214,15 @@ public void can_write_multi_instance_objlnk_resource_in_tlv() throws Interrupted @Test public void can_write_object_resource_instance() throws InterruptedException { + // -------------------------------- + // write (replace) multi instance + // -------------------------------- Map values = new HashMap<>(); values.put(10, "value10"); values.put(20, "value20"); - // write multi instance WriteResponse response = helper.server.send(helper.getCurrentRegistration(), - new WriteRequest(contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0, + new WriteRequest(Mode.REPLACE, contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, values, Type.STRING)); // Verify Write result @@ -239,6 +241,59 @@ public void can_write_object_resource_instance() throws InterruptedException { LwM2mMultipleResource resourceInstance = (LwM2mMultipleResource) readResponse.getContent(); assertEquals("value10", resourceInstance.getInstance(10).getValue()); assertEquals("value20", resourceInstance.getInstance(20).getValue()); - } + // -------------------------------- + // write (update) multi instance + // -------------------------------- + Map newValues = new HashMap<>(); + newValues.put(20, "value200"); + newValues.put(30, "value30"); + response = helper.server.send(helper.getCurrentRegistration(), + new WriteRequest(Mode.UPDATE, contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0, + IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, newValues, Type.STRING)); + + // Verify Write result + assertEquals(ResponseCode.CHANGED, response.getCode()); + assertNotNull(response.getCoapResponse()); + assertThat(response.getCoapResponse(), is(instanceOf(Response.class))); + + // read multi instance + readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(contentFormat, + IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID)); + + // verify result + assertEquals(CONTENT, readResponse.getCode()); + assertContentFormat(contentFormat, readResponse); + + resourceInstance = (LwM2mMultipleResource) readResponse.getContent(); + assertEquals("value200", resourceInstance.getInstance(20).getValue()); + assertEquals("value30", resourceInstance.getInstance(30).getValue()); + assertEquals("value10", resourceInstance.getInstance(10).getValue()); + + // -------------------------------- + // write (replace) multi instance + // -------------------------------- + newValues = new HashMap<>(); + newValues.put(1, "value1"); + response = helper.server.send(helper.getCurrentRegistration(), + new WriteRequest(Mode.REPLACE, contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0, + IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, newValues, Type.STRING)); + + // Verify Write result + assertEquals(ResponseCode.CHANGED, response.getCode()); + assertNotNull(response.getCoapResponse()); + assertThat(response.getCoapResponse(), is(instanceOf(Response.class))); + + // read multi instance + readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(contentFormat, + IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID)); + + // verify result + assertEquals(CONTENT, readResponse.getCode()); + assertContentFormat(contentFormat, readResponse); + + resourceInstance = (LwM2mMultipleResource) readResponse.getContent(); + assertEquals(1, resourceInstance.getInstances().size()); + assertEquals("value1", resourceInstance.getInstance(1).getValue()); + } }