diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 225ae056e9..e2b70a4490 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -261,6 +261,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest client = new LwM2MTestClient(this.executor, this.endpoint); int clientPort = SocketUtils.findAvailableTcpPort(); client.init(security, coapConfig, clientPort); + client.start(); } private void clientDestroy() { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 3fc83625ba..f4ef1463d5 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -250,8 +250,6 @@ public class LwM2MTestClient { } }; this.client.addObserver(observer); - - client.start(); } public void destroy() { @@ -278,4 +276,10 @@ public class LwM2MTestClient { } } + public void start() { + if (client != null) { + client.start(); + } + } + } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index 83bb46e934..56668bbe56 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -50,63 +50,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources; @DaoSqlTest public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { - protected final String RPC_TRANSPORT_CONFIGURATION = "{\n" + - " \"type\": \"LWM2M\",\n" + - " \"observeAttr\": {\n" + - " \"keyName\": {\n" + - " \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\": \"" + resourceIdName_3_9 + "\",\n" + - " \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\": \"" + resourceIdName_3_14 + "\",\n" + - " \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_0_0 + "\",\n" + - " \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_1_0 + "\"\n" + - " },\n" + - " \"observe\": [\n" + - " \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" + - " \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\"\n" + - " ],\n" + - " \"attribute\": [\n" + - " ],\n" + - " \"telemetry\": [\n" + - " \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" + - " \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\",\n" + - " \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\",\n" + - " \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\"\n" + - " ],\n" + - " \"attributeLwm2m\": {}\n" + - " },\n" + - " \"bootstrap\": {\n" + - " \"servers\": {\n" + - " \"binding\": \"U\",\n" + - " \"shortId\": 123,\n" + - " \"lifetime\": 300,\n" + - " \"notifIfDisabled\": true,\n" + - " \"defaultMinPeriod\": 1\n" + - " },\n" + - " \"lwm2mServer\": {\n" + - " \"host\": \"localhost\",\n" + - " \"port\": 5686,\n" + - " \"serverId\": 123,\n" + - " \"serverPublicKey\": \"\",\n" + - " \"bootstrapServerIs\": false,\n" + - " \"clientHoldOffTime\": 1,\n" + - " \"bootstrapServerAccountTimeout\": 0\n" + - " },\n" + - " \"bootstrapServer\": {\n" + - " \"host\": \"localhost\",\n" + - " \"port\": 5687,\n" + - " \"serverId\": 111,\n" + - " \"securityMode\": \"NO_SEC\",\n" + - " \"serverPublicKey\": \"\",\n" + - " \"bootstrapServerIs\": true,\n" + - " \"clientHoldOffTime\": 1,\n" + - " \"bootstrapServerAccountTimeout\": 0\n" + - " }\n" + - " },\n" + - " \"clientLwM2mSettings\": {\n" + - " \"clientOnlyObserveAfterConnect\": 1,\n" + - " \"fwUpdateStrategy\": 1,\n" + - " \"swUpdateStrategy\": 1\n" + - " }\n" + - "}"; + protected String RPC_TRANSPORT_CONFIGURATION; protected static final String ENDPOINT_RPC = "deviceEndpointRpc"; protected ScheduledExecutorService executor; @@ -137,13 +81,6 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg @Before public void beforeTest() throws Exception { init(); - createDeviceProfile(RPC_TRANSPORT_CONFIGURATION); - - Thread.sleep(1000); - - NoSecClientCredentials credentials = createNoSecClientCredentials(ENDPOINT_RPC); - final Device device = createDevice(credentials); - deviceId = device.getId().getId().toString(); createNewClient (SECURITY, COAP_CONFIG); Thread.sleep(1000); @@ -182,7 +119,76 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg objectInstanceIdVer_3 = (String) expectedObjectIdVerInstances.stream().filter(predicate_3).findFirst().get(); objectInstanceIdVer_5 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + FIRMWARE)).findFirst().get(); objectInstanceIdVer_9 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + SOFTWARE_MANAGEMENT)).findFirst().get(); - } + + RPC_TRANSPORT_CONFIGURATION = "{\n" + + " \"type\": \"LWM2M\",\n" + + " \"observeAttr\": {\n" + + " \"keyName\": {\n" + + " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\": \"" + resourceIdName_3_9 + "\",\n" + + " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\": \"" + resourceIdName_3_14 + "\",\n" + + " \"" + objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_0_0 + "\",\n" + + " \"" + objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_1_0 + "\"\n" + + " },\n" + + " \"observe\": [\n" + + " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" + + " \"" + objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\"\n" + + " ],\n" + + " \"attribute\": [\n" + + " ],\n" + + " \"telemetry\": [\n" + + " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" + + " \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\",\n" + + " \"" + objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\",\n" + + " \"" + objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\"\n" + + " ],\n" + + " \"attributeLwm2m\": {}\n" + + " },\n" + + " \"bootstrap\": {\n" + + " \"servers\": {\n" + + " \"binding\": \"U\",\n" + + " \"shortId\": 123,\n" + + " \"lifetime\": 300,\n" + + " \"notifIfDisabled\": true,\n" + + " \"defaultMinPeriod\": 1\n" + + " },\n" + + " \"lwm2mServer\": {\n" + + " \"host\": \"localhost\",\n" + + " \"port\": 5686,\n" + + " \"serverId\": 123,\n" + + " \"serverPublicKey\": \"\",\n" + + " \"bootstrapServerIs\": false,\n" + + " \"clientHoldOffTime\": 1,\n" + + " \"bootstrapServerAccountTimeout\": 0\n" + + " },\n" + + " \"bootstrapServer\": {\n" + + " \"host\": \"localhost\",\n" + + " \"port\": 5687,\n" + + " \"serverId\": 111,\n" + + " \"securityMode\": \"NO_SEC\",\n" + + " \"serverPublicKey\": \"\",\n" + + " \"bootstrapServerIs\": true,\n" + + " \"clientHoldOffTime\": 1,\n" + + " \"bootstrapServerAccountTimeout\": 0\n" + + " }\n" + + " },\n" + + " \"clientLwM2mSettings\": {\n" + + " \"clientOnlyObserveAfterConnect\": 1,\n" + + " \"fwUpdateStrategy\": 1,\n" + + " \"swUpdateStrategy\": 1\n" + + " }\n" + + "}"; + createDeviceProfile(RPC_TRANSPORT_CONFIGURATION); + + Thread.sleep(1000); + + NoSecClientCredentials credentials = createNoSecClientCredentials(ENDPOINT_RPC); + final Device device = createDevice(credentials); + deviceId = device.getId().getId().toString(); + + Thread.sleep(1000); + + client.start(); + } protected String pathIdVerToObjectId(String pathIdVer) { if (pathIdVer.contains("_")){ @@ -192,4 +198,5 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg } return pathIdVer; } + } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java index 3bc9c075f1..de0a22756d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java @@ -121,7 +121,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); String actualValues = rpcActualResult.get("error").asText(); - String expected = "Resource value is: String. Value of Multi-Instance Resource must be in Json format!"; + String expected = "is bad. Value of Multi-Instance Resource must be in Json format!"; assertTrue(actualValues.contains(expected)); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index e54277060a..cfbc21e57c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.lwm2m.server.attributes; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.ResourceModel; @@ -223,8 +222,15 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { if (!resourceModel.multiple || !(newValProto instanceof JsonElement)) { this.pushUpdateToClientIfNeeded(lwM2MClient, oldResourceValue, newValProto, pathIdVer, logFailedUpdateOfNonChangedValue, resourceModel.type); } else { - pushUpdateMultiToClientIfNeeded(lwM2MClient, resourceModel, (JsonElement) newValProto, - (Map) oldResourceValue, pathIdVer, logFailedUpdateOfNonChangedValue); + try { + pushUpdateMultiToClientIfNeeded(lwM2MClient, resourceModel, (JsonElement) newValProto, + (Map) oldResourceValue, pathIdVer, logFailedUpdateOfNonChangedValue); + } catch (Exception e){ + log.error("Failed update resource ["+lwM2MClient.getEndpoint()+"] onAttributesUpdate:", e); + String logMsg = String.format("%s: Failed update resource onAttributesUpdate %s.", + LOG_LWM2M_ERROR, e.getMessage()); + logService.log(lwM2MClient, logMsg); + } } }); } @@ -250,8 +256,9 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { } private void pushUpdateMultiToClientIfNeeded(LwM2mClient client, ResourceModel resourceModel, JsonElement newValProto, - Map valueOld, String versionedId, boolean logFailedUpdateOfNonChangedValue) { - Map newValues = convertMultiResourceValuesFromJson((JsonObject) newValProto, resourceModel.type, versionedId); + Map valueOld, String versionedId, + boolean logFailedUpdateOfNonChangedValue) throws Exception { + Map newValues = convertMultiResourceValuesFromJson(newValProto, resourceModel.type, versionedId); if (newValues.size() > 0 && valueOld != null && valueOld.size() > 0) { valueOld.values().stream().forEach((v) -> { if (newValues.containsKey(v.getId())) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 2cc69f2c58..b5db43e66e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -334,8 +334,8 @@ public class LwM2mClient implements Serializable { Map values = convertMultiResourceValuesFromRpcBody(value, resourceModel.type, pathRezIdVer); resource = LwM2mMultipleResource.newResource(resourceId, (Map) values, resourceModel.type); } catch (Exception e) { - throw new IllegalArgumentException("Resource id=" + resourceId + ", value = " + value + ", class = " + - value.getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!"); + throw new IllegalArgumentException("Resource id=" + resourceId + ", class = " + + value.getClass().getSimpleName() + ", value = " + value + " is bad. Value of Multi-Instance Resource must be in Json format!"); } } else { Object valueRez = value instanceof Integer ? ((Integer) value).longValue() : value; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 71a27def66..c980fde4ad 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -378,9 +378,10 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im resultIds.getObjectInstanceId(), resultIds.getResourceId(), value, resourceModelWrite.type); } catch (Exception e1) { - callback.onValidationError(toString(request), "Resource id=" + resultIds.toString() + ", value = " + - request.getValue() + ", class = " + request.getValue().getClass().getSimpleName() + - "is bad. Value of Multi-Instance Resource must be in Json format!"); + callback.onValidationError(toString(request), "Resource id=" + resultIds.toString() + + ", class = " + request.getValue().getClass().getSimpleName() + + ", value = " + request.getValue() + " is bad. " + + "Value of Multi-Instance Resource must be in Json format!"); } } } else { @@ -408,43 +409,48 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im validateVersionedId(client, request); CreateRequest downlink = null; LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); - ObjectModel objectModel = client.getObjectModel(request.getObjectId(), this.config.getModelProvider()); + ObjectModel objectModel = client.getObjectModel(request.getVersionedId(), this.config.getModelProvider()); // POST /{Object ID}/{Object Instance ID} && Resources is Mandatory - if (objectModel.multiple) { - // LwM2M CBOR, SenML CBOR, SenML JSON, or TLV (see [LwM2M-CORE]) - ContentFormat contentFormat = getWriteRequestContentFormat(client, request, this.config.getModelProvider()); - if (resultIds.isObject() || resultIds.isObjectInstance()) { - Collection resources; - if (resultIds.isObject()) { - if (request.getValue() != null) { + if (objectModel != null) { + if (objectModel.multiple) { + + // LwM2M CBOR, SenML CBOR, SenML JSON, or TLV (see [LwM2M-CORE]) + ContentFormat contentFormat = getWriteRequestContentFormat(client, request, this.config.getModelProvider()); + if (resultIds.isObject() || resultIds.isObjectInstance()) { + Collection resources; + if (resultIds.isObject()) { + if (request.getValue() != null) { + resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); + downlink = new CreateRequest(contentFormat, resultIds.getObjectId(), resources); + } else if (request.getNodes() != null && request.getNodes().size() > 0) { + Set instances = ConcurrentHashMap.newKeySet(); + request.getNodes().forEach((key, value) -> { + Collection resourcesForInstance = client.getNewResourcesForInstance(request.getVersionedId(), value, this.config.getModelProvider(), this.converter); + LwM2mObjectInstance instance = new LwM2mObjectInstance(Integer.parseInt(key), resourcesForInstance); + instances.add(instance); + }); + LwM2mObjectInstance[] instanceArrays = instances.toArray(new LwM2mObjectInstance[instances.size()]); + downlink = new CreateRequest(contentFormat, resultIds.getObjectId(), instanceArrays); + } + + } else { resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); - downlink = new CreateRequest(contentFormat, resultIds.getObjectId(), resources); - } else if (request.getNodes() != null && request.getNodes().size() > 0) { - Set instances = ConcurrentHashMap.newKeySet(); - request.getNodes().forEach((key, value) -> { - Collection resourcesForInstance = client.getNewResourcesForInstance(request.getVersionedId(), value, this.config.getModelProvider(), this.converter); - LwM2mObjectInstance instance = new LwM2mObjectInstance(Integer.parseInt(key), resourcesForInstance); - instances.add(instance); - }); - LwM2mObjectInstance[] instanceArrays = instances.toArray(new LwM2mObjectInstance[instances.size()]); - downlink = new CreateRequest(contentFormat, resultIds.getObjectId(), instanceArrays); + LwM2mObjectInstance instance = new LwM2mObjectInstance(resultIds.getObjectInstanceId(), resources); + downlink = new CreateRequest(contentFormat, resultIds.getObjectId(), instance); } - + } + if (downlink != null) { + sendSimpleRequest(client, downlink, request.getTimeout(), callback); } else { - resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); - LwM2mObjectInstance instance = new LwM2mObjectInstance(resultIds.getObjectInstanceId(), resources); - downlink = new CreateRequest(contentFormat, resultIds.getObjectId(), instance); + callback.onValidationError(toString(request), "Path " + request.getVersionedId() + + ". Object must be Multiple !"); } - } - if (downlink != null) { - sendSimpleRequest(client, downlink, request.getTimeout(), callback); } else { - callback.onValidationError(toString(request), "Path " + request.getVersionedId() + - ". This operation can only be used for created new ObjectInstance !"); + throw new IllegalArgumentException("Path " + request.getVersionedId() + ". Object must be Multiple !"); } } else { - throw new IllegalArgumentException("Path " + request.getVersionedId() + - ". Object must be Multiple !"); + callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + + " is not configured in the device profile!"); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index a881047f91..6501d443a2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.rpc; +import com.google.gson.JsonElement; import com.google.gson.JsonSyntaxException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -260,8 +261,8 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { Map value = convertMultiResourceValuesFromRpcBody(requestBody.getValue(), resourceModel.type, versionedId); requestBody.setValue(value); } catch (Exception e) { - throw new IllegalArgumentException("Resource id=" + versionedId + ", value = " + requestBody.getValue() + ", class = " + - requestBody.getValue().getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!"); + throw new IllegalArgumentException("Resource id=" + versionedId + ", class = " + + requestBody.getValue().getClass().getSimpleName() + ", value = " + requestBody.getValue() + " is bad. Value of Multi-Instance Resource must be in Json format!"); } } } @@ -300,21 +301,28 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { String versionedId; try { // validate key.toString() - new LwM2mPath(fromVersionedIdToObjectId(key.toString())); - versionedId = key.toString(); + LwM2mPath path = new LwM2mPath(fromVersionedIdToObjectId(key.toString())); + if (path.isResource() || path.isResourceInstance()) { + versionedId = key.toString(); + } + else { + throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " + + "The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString())); + } } catch (Exception e) { versionedId = clientContext.getObjectIdByKeyNameFromProfile(client, key.toString()); } - // validate value. Must be only primitive, not Json + // validate value. Must be only primitive, not JsonObject or JsonArray try { - JsonUtils.parse(value.toString()); - throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " + - "The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString())); + JsonElement element = JsonUtils.parse(value.toString()); + if (!element.isJsonNull() && !element.isJsonPrimitive()) { + throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " + + "The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString())); + } + else if (versionedId != null) { + newNodes.put(fromVersionedIdToObjectId(versionedId), value); + } } catch (JsonSyntaxException jse) { -// if (value instanceof LinkedHashMap) { -// throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " + -// "The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString())); -// } if (versionedId != null) { newNodes.put(fromVersionedIdToObjectId(versionedId), value); } @@ -356,8 +364,8 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { return targetId; } - private String[] getIdsFromParameters(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg rpcRequst) { - RpcReadCompositeRequest requestParams = JacksonUtil.fromString(rpcRequst.getParams(), RpcReadCompositeRequest.class); + private String[] getIdsFromParameters(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { + RpcReadCompositeRequest requestParams = JacksonUtil.fromString(rpcRequest.getParams(), RpcReadCompositeRequest.class); if (requestParams.getKeys() != null && requestParams.getKeys().length > 0) { Set targetIds = ConcurrentHashMap.newKeySet(); for (String key : requestParams.getKeys()) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mTransportUtil.java index b04868c501..23ee5a0a57 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mTransportUtil.java @@ -16,7 +16,7 @@ package org.thingsboard.server.transport.lwm2m.utils; import com.fasterxml.jackson.core.type.TypeReference; -import com.google.gson.JsonObject; +import com.google.gson.JsonElement; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.attributes.Attribute; @@ -214,12 +214,12 @@ public class LwM2mTransportUtil { String[] keyArray = path.split(LWM2M_SEPARATOR_PATH); if (keyArray.length > 1) { String[] keyArrayVer = keyArray[1].split(LWM2M_SEPARATOR_KEY); - return keyArrayVer.length == 2 ? keyArrayVer[1] : null; + return keyArrayVer.length == 2 ? keyArrayVer[1] : LWM2M_OBJECT_VERSION_DEFAULT; } } catch (Exception e) { return null; } - return LWM2M_OBJECT_VERSION_DEFAULT; + return null; } public static String validPathIdVer(String pathIdVer, Registration registration) throws @@ -355,10 +355,11 @@ public class LwM2mTransportUtil { public static Map convertMultiResourceValuesFromRpcBody(Object value, ResourceModel.Type type, String versionedId) throws Exception { String valueJsonStr = JsonUtils.riteValueAsString(value); - return convertMultiResourceValuesFromJson((JsonObject) JsonUtils.parse(valueJsonStr), type, versionedId); + JsonElement element = JsonUtils.parse(valueJsonStr); + return convertMultiResourceValuesFromJson(element, type, versionedId); } - public static Map convertMultiResourceValuesFromJson(JsonObject newValProto, ResourceModel.Type type, String versionedId) { + public static Map convertMultiResourceValuesFromJson(JsonElement newValProto, ResourceModel.Type type, String versionedId) throws Exception{ Map newValues = equalsMultiResourceValuesResourceType(type); newValProto.getAsJsonObject().entrySet().forEach((obj) -> { newValues.put(Integer.valueOf(obj.getKey()), LwM2mValueConverterImpl.getInstance().convertValue(obj.getValue().getAsString(),