diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 5930825894..6fb5fd195e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -22,8 +22,10 @@ import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; +import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.request.SimpleDownlinkRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest; @@ -48,6 +50,7 @@ import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; @@ -371,4 +374,80 @@ public class LwM2mTransportUtil { return lwm2mResourceValue; } + public static Optional contentToString(Object content) { + try { + String value = null; + LwM2mResource resource = null; + String key = null; + if (content instanceof Map) { + Map contentAsMap = (Map) content; + if (contentAsMap.size() == 1) { + for (Map.Entry kv : contentAsMap.entrySet()) { + if (kv.getValue() instanceof LwM2mResource) { + key = kv.getKey().toString(); + resource = (LwM2mResource) kv.getValue(); + } + } + } + } else if (content instanceof LwM2mResource) { + resource = (LwM2mResource) content; + } + if (resource != null && resource.getType() == OPAQUE) { + value = opaqueResourceToString(resource, key); + } + value = value == null ? content.toString() : value; + return Optional.of(value); + } catch (Exception e) { + log.debug("Failed to convert content " + content + " to string", e); + return Optional.ofNullable(content != null ? content.toString() : null); + } + } + + private static String opaqueResourceToString(LwM2mResource resource, String key) { + String value = null; + StringBuilder builder = new StringBuilder(); + if (resource instanceof LwM2mSingleResource) { + builder.append("LwM2mSingleResource"); + if (key == null) { + builder.append(" id=").append(String.valueOf(resource.getId())); + } else { + builder.append(" key=").append(key); + } + builder.append(" value=").append(opaqueToString((byte[]) resource.getValue())); + builder.append(" type=").append(OPAQUE.toString()); + value = builder.toString(); + } else if (resource instanceof LwM2mMultipleResource) { + builder.append("LwM2mMultipleResource"); + if (key == null) { + builder.append(" id=").append(String.valueOf(resource.getId())); + } else { + builder.append(" key=").append(key); + } + builder.append(" values={"); + if (resource.getInstances().size() > 0) { + builder.append(multiInstanceOpaqueToString((LwM2mMultipleResource) resource)); + } + builder.append("}"); + builder.append(" type=").append(OPAQUE.toString()); + value = builder.toString(); + } + return value; + } + + private static String multiInstanceOpaqueToString(LwM2mMultipleResource resource) { + StringBuilder builder = new StringBuilder(); + resource.getInstances().values() + .forEach(v -> builder.append(" id=").append(v.getId()).append(" value=").append(Hex.encodeHexString((byte[]) v.getValue())).append(", ")); + int startInd = builder.lastIndexOf(", "); + if (startInd > 0) { + builder.delete(startInd, startInd + 2); + } + return builder.toString(); + } + + private static String opaqueToString(byte[] value) { + String opaque = Hex.encodeHexString(value); + return opaque.length() > 1024 ? opaque.substring(0, 1024) : opaque; + } + } 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 74edfed357..d88e0131b1 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 @@ -243,74 +243,7 @@ public class LwM2mClient implements Serializable { .getObjectModel(pathIds.getObjectId()) : null; } - public Optional contentToString(Object content) { - String value = null; - LwM2mResource resource = null; - String key = null; - if (content instanceof HashMap && ((HashMap) content).size() == 1) { - key = ((HashMap) content).keySet().toArray()[0].toString(); - if (((HashMap) content).values().toArray()[0] != null) { - if (((HashMap) content).values().toArray()[0] instanceof LwM2mResource) { - resource = (LwM2mResource) ((HashMap) content).values().toArray()[0]; - } - } - } else if (content instanceof LwM2mResource) { - resource = (LwM2mResource) content; - } - if (resource != null && resource.getType() == OPAQUE) { - value = this.resourceToString(resource, key); - } - value = value == null ? content.toString() : value; - return Optional.of(String.format("%s", value)); - } - - public String resourceToString(LwM2mResource resource, String key) { - String value = null; - StringBuilder builder = new StringBuilder(); - if (resource instanceof LwM2mSingleResource) { - builder.append("LwM2mSingleResource"); - if (key == null) { - builder.append(" id=").append(String.valueOf(resource.getId())); - } else { - builder.append(" key=").append(key); - } - builder.append(" value=").append(opaqueToString((byte[]) resource.getValue())); - builder.append(" type=").append(OPAQUE.toString()); - value = builder.toString(); - } else if (resource instanceof LwM2mMultipleResource) { - builder.append("LwM2mMultipleResource"); - if (key == null) { - builder.append(" id=").append(String.valueOf(resource.getId())); - } else { - builder.append(" key=").append(key); - } - builder.append(" values={"); - if (resource.getInstances().size() > 0) { - builder.append(multiInstanceOpaqueToString((LwM2mMultipleResource) resource)); - } - builder.append("}"); - builder.append(" type=").append(OPAQUE.toString()); - value = builder.toString(); - } - return value; - } - private String multiInstanceOpaqueToString(LwM2mMultipleResource resource) { - StringBuilder builder = new StringBuilder(); - resource.getInstances().values().stream().map(v -> { - return builder.append(" id=").append(v.getId()).append(" value=").append(Hex.encodeHexString((byte[]) v.getValue())).append(", "); - }); - int startInd = builder.lastIndexOf(", "); - if (startInd > 0) { - builder.delete(startInd, startInd + 2); - } - return builder.toString(); - } - - private String opaqueToString(byte[] value) { - String opaque = Hex.encodeHexString(value); - return opaque.length() > 1024 ? opaque.substring(0, 1024) : opaque; - } public Collection getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, LwM2mValueConverter converter) { @@ -407,9 +340,5 @@ public class LwM2mClient implements Serializable { this.lock = new ReentrantLock(); } - public boolean isComposite (LwM2mClientContext clientContext) { - return LwM2mVersion.fromVersionStr(registration.getLwM2mVersion()).isComposite() & - clientContext.getProfile(this.profileId).getClientLwM2mSettings().isCompositeOperationsSupport(); - } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java index a7305177e1..643ab61737 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java @@ -60,4 +60,6 @@ public interface LwM2mClientContext { void removeCredentials(TransportProtos.SessionInfoProto sessionInfo); void sendMsgsAfterSleeping(LwM2mClient lwM2MClient); + + boolean isComposite(LwM2mClient client); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index 04c4cc6bcd..b861ce4cd0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -35,6 +35,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; @@ -339,6 +340,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null); } + @Override + public boolean isComposite(LwM2mClient client) { + return LwM2mVersion.fromVersionStr(client.getRegistration().getLwM2mVersion()).isComposite() & + getProfile(client.getProfileId()).getClientLwM2mSettings().isCompositeOperationsSupport(); + } + private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config .getModelProvider()); 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 e0dea149e5..619cbdd7a0 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 @@ -328,8 +328,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im try { if (response.isSuccess()) { callback.onSuccess(request, response); - } - else { + } else { callback.onValidationError(request.getNodes().values().toString(), response.getErrorMessage()); } } catch (Exception e) { 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 147a8a5131..7954cdda98 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 @@ -98,7 +98,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { return; } try { - if (operationType.isHasObjectId()) { + if (operationType.isHasObjectId()) { String objectId = getIdFromParameters(client, rpcRequst); switch (operationType) { case READ: @@ -132,23 +132,21 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); } } else if (operationType.isComposite()) { - if (client.isComposite(clientContext)) { - switch (operationType) { - case READ_COMPOSITE: - sendReadCompositeRequest(client, rpcRequst); - break; - case WRITE_COMPOSITE: - sendWriteCompositeRequest(client, rpcRequst); - break; - default: - throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); - } - } - else { - this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), - ResponseCode.INTERNAL_SERVER_ERROR.getName(), "This device does not support Composite Operation"); - return; - } + if (clientContext.isComposite(client)) { + switch (operationType) { + case READ_COMPOSITE: + sendReadCompositeRequest(client, rpcRequst); + break; + case WRITE_COMPOSITE: + sendWriteCompositeRequest(client, rpcRequst); + break; + default: + throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); + } + } else { + this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), + ResponseCode.INTERNAL_SERVER_ERROR.getName(), "This device does not support Composite Operation"); + } } else { switch (operationType) { case OBSERVE_CANCEL_ALL: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java index 3a53ef5d5d..0a5fa70b5b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java @@ -24,6 +24,8 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCal import java.util.Optional; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.contentToString; + public class RpcReadResponseCallback, T extends ReadResponse> extends RpcLwM2MDownlinkCallback { public RpcReadResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { @@ -32,6 +34,6 @@ public class RpcReadResponseCallback, T extends ReadRe @Override protected Optional serializeSuccessfulResponse(T response) { - return client.contentToString (response.getContent()); + return contentToString(response.getContent()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java index e73fb54ca7..11f6849410 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java @@ -25,6 +25,8 @@ import org.thingsboard.server.transport.lwm2m.server.rpc.RpcLwM2MDownlinkCallbac import java.util.Optional; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.contentToString; + public class RpcReadResponseCompositeCallback, T extends ReadCompositeResponse> extends RpcLwM2MDownlinkCallback { public RpcReadResponseCompositeCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { @@ -33,6 +35,6 @@ public class RpcReadResponseCompositeCallback, T exten @Override protected Optional serializeSuccessfulResponse(T response) { - return client.contentToString (response.getContent()); + return contentToString(response.getContent()); } }