Browse Source

Lwm2m Fix bug review last

pull/5254/head
nickAS21 5 years ago
parent
commit
16a520cd80
  1. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java
  2. 36
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java
  3. 5
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java
  4. 24
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java
  5. 50
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java
  6. 5
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCreateRequest.java
  7. 20
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java
  8. 47
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java
  9. 39
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java
  10. 11
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mTransportUtil.java
  11. 21
      common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java
  12. 3
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java

@ -194,7 +194,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
Map <String, TransportProtos.TsKvProto> attributesUpdate = new ConcurrentHashMap<>(); Map <String, TransportProtos.TsKvProto> attributesUpdate = new ConcurrentHashMap<>();
tsKvProtos.forEach(tsKvProto -> { tsKvProtos.forEach(tsKvProto -> {
try { try {
String pathIdVer = clientContext.getObjectIdByKeyNameFromProfile(lwM2MClient, tsKvProto.getKv().getKey(), false); String pathIdVer = clientContext.getObjectIdByKeyNameFromProfile(lwM2MClient, tsKvProto.getKv().getKey());
if (pathIdVer != null) { if (pathIdVer != null) {
// #1.1 // #1.1
if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) { if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) {

36
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java

@ -61,7 +61,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LWM2M_OBJECT_VERSION_DEFAULT;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertMultiResourceValuesFromRpcBody; import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertMultiResourceValuesFromRpcBody;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId; import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.equalsResourceTypeGetSimpleName;
@ -277,7 +276,7 @@ public class LwM2mClient implements Serializable {
LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer));
String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
String verRez = getVerFromPathIdVerOrId(pathIdVer); String verRez = getVerFromPathIdVerOrId(pathIdVer);
return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
.getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null; .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
} }
@ -294,7 +293,7 @@ public class LwM2mClient implements Serializable {
LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer));
String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
String verRez = getVerFromPathIdVerOrId(pathIdVer); String verRez = getVerFromPathIdVerOrId(pathIdVer);
return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
.getObjectModel(pathIds.getObjectId()) : null; .getObjectModel(pathIds.getObjectId()) : null;
} }
@ -339,7 +338,7 @@ public class LwM2mClient implements Serializable {
value.getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!"); value.getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!");
} }
} else { } else {
Object valueRez = value.getClass().getSimpleName().equals("Integer") ? ((Integer) value).longValue() : value; Object valueRez = value instanceof Integer ? ((Integer) value).longValue() : value;
resource = LwM2mSingleResource.newResource(resourceId, resource = LwM2mSingleResource.newResource(resourceId,
converter.convertValue(valueRez, equalsResourceTypeGetSimpleName(value), resourceModel.type, pathIds), resourceModel.type); converter.convertValue(valueRez, equalsResourceTypeGetSimpleName(value), resourceModel.type, pathIds), resourceModel.type);
} }
@ -370,8 +369,7 @@ public class LwM2mClient implements Serializable {
return String.format("Specified object id %s absent in the list supported objects of the client or is security object!", pathIds.getObjectId()); return String.format("Specified object id %s absent in the list supported objects of the client or is security object!", pathIds.getObjectId());
} else { } else {
String verRez = getVerFromPathIdVerOrId(path); String verRez = getVerFromPathIdVerOrId(path);
if ((verRez != null && !verRez.equals(verSupportedObject)) || if (verRez == null || !verRez.equals(verSupportedObject)) {
(verRez == null && !LWM2M_OBJECT_VERSION_DEFAULT.equals(verSupportedObject))) {
return String.format("Specified resource id %s is not valid version! Must be version: %s", path, verSupportedObject); return String.format("Specified resource id %s is not valid version! Must be version: %s", path, verSupportedObject);
} }
} }
@ -425,23 +423,17 @@ public class LwM2mClient implements Serializable {
} }
} }
private Set<ContentFormat> clientSupportContentFormat(Registration registration) { static private Set<ContentFormat> clientSupportContentFormat(Registration registration) {
Set<ContentFormat> contentFormats = new HashSet<>(); Set<ContentFormat> contentFormats = new HashSet<>();
if (registration == null) { contentFormats.add(ContentFormat.DEFAULT);
contentFormats.add(ContentFormat.DEFAULT); String code = Arrays.stream(registration.getObjectLinks()).filter(link -> link.getUrl().equals("/")).findFirst().get().getAttributes().get("ct");
} else { if (code != null) {
String code = Arrays.stream(registration.getObjectLinks()).filter(link -> link.getUrl().equals("/")). Set<ContentFormat> codes = Stream.of(code.replaceAll("\"", "").split(" ", -1))
findFirst().get().getAttributes().get("ct"); .map(String::trim)
if (code != null) { .map(Integer::parseInt)
Set<ContentFormat> codes = Stream.of(code.replaceAll("\"", "").split(" ", -1)) .map(ContentFormat::fromCode)
.map(String::trim) .collect(Collectors.toSet());
.map(Integer::parseInt) contentFormats.addAll(codes);
.map(ContentFormat::fromCode)
.collect(Collectors.toSet());
contentFormats.addAll(codes);
} else {
contentFormats.add(ContentFormat.DEFAULT);
}
} }
return contentFormats; return contentFormats;
} }

5
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server.client; package org.thingsboard.server.transport.lwm2m.server.client;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
@ -52,7 +51,7 @@ public interface LwM2mClientContext {
LwM2mClient getClientByDeviceId(UUID deviceId); LwM2mClient getClientByDeviceId(UUID deviceId);
String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName, boolean isCompositeOperation); String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName);
void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials);
@ -62,8 +61,6 @@ public interface LwM2mClientContext {
void sendMsgsAfterSleeping(LwM2mClient lwM2MClient); void sendMsgsAfterSleeping(LwM2mClient lwM2MClient);
ContentFormat getContentFormatComposite(LwM2mClient client);
void onUplink(LwM2mClient client); void onUplink(LwM2mClient client);
Long getRequestTimeout(LwM2mClient client); Long getRequestTimeout(LwM2mClient client);

24
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java

@ -19,7 +19,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -58,7 +57,6 @@ import java.util.function.Predicate;
import static org.eclipse.leshan.core.SecurityMode.NO_SEC; import static org.eclipse.leshan.core.SecurityMode.NO_SEC;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId; import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.convertObjectIdToVersionedId;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.getMsgException;
@Slf4j @Slf4j
@Service @Service
@ -289,14 +287,16 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
} }
@Override @Override
public String getObjectIdByKeyNameFromProfile(LwM2mClient client, String keyName, boolean isCompositeOperation) { public String getObjectIdByKeyNameFromProfile(LwM2mClient client, String keyName) {
Lwm2mDeviceProfileTransportConfiguration profile = getProfile(client.getProfileId()); Lwm2mDeviceProfileTransportConfiguration profile = getProfile(client.getProfileId());
Set<String> msgException = ConcurrentHashMap.newKeySet(); for (Map.Entry<String, String> entry : profile.getObserveAttr().getKeyName().entrySet()) {
msgException.add(""); String k = entry.getKey();
return profile.getObserveAttr().getKeyName().entrySet().stream() String v = entry.getValue();
.filter(e -> e.getValue().equals(keyName) && (isCompositeOperation || !msgException.add(client.isValidObjectVersion(e.getKey())))).findFirst().orElseThrow( if (v.equals(keyName) && client.isValidObjectVersion(k).isEmpty()) {
() -> new IllegalArgumentException(getMsgException (keyName, msgException)) return k;
).getKey(); }
}
throw new IllegalArgumentException(keyName + " is not configured in the device profile!");
} }
public Registration getRegistration(String registrationId) { public Registration getRegistration(String registrationId) {
@ -410,12 +410,6 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null); return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null);
} }
@Override
public ContentFormat getContentFormatComposite(LwM2mClient client) {
return client.getClientSupportContentFormats().contains(ContentFormat.SENML_JSON) ? ContentFormat.SENML_JSON :
client.getClientSupportContentFormats().contains(ContentFormat.SENML_CBOR) ? ContentFormat.SENML_CBOR : null;
}
@Override @Override
public boolean isDownlinkAllowed(LwM2mClient client) { public boolean isDownlinkAllowed(LwM2mClient client) {
PowerMode powerMode = client.getPowerMode(); PowerMode powerMode = client.getPowerMode();

50
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java

@ -147,9 +147,9 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
@Override @Override
public void sendReadCompositeRequest(LwM2mClient client, TbLwM2MReadCompositeRequest request, public void sendReadCompositeRequest(LwM2mClient client, TbLwM2MReadCompositeRequest request,
DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback, ContentFormat contentFormatComposite) { DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback, ContentFormat compositeContentFormat) {
try { try {
ReadCompositeRequest downlink = new ReadCompositeRequest(contentFormatComposite, contentFormatComposite, request.getObjectIds()); ReadCompositeRequest downlink = new ReadCompositeRequest(compositeContentFormat, compositeContentFormat, request.getObjectIds());
sendCompositeRequest(client, downlink, this.config.getTimeout(), callback); sendCompositeRequest(client, downlink, this.config.getTimeout(), callback);
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
callback.onValidationError(request.toString(), e.getMessage()); callback.onValidationError(request.toString(), e.getMessage());
@ -207,13 +207,15 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
} }
} }
if (resourceModelExecute == null) { if (resourceModelExecute == null) {
callback.onValidationError(request.toString(), "ResourceModel with " + request.getVersionedId() + " is absent in system. Need ddd Lwm2m Model with id=" + callback.onValidationError(request.toString(), "ResourceModel with " + request.getVersionedId() +
pathIds.getObjectId() + " ver=" + getVerFromPathIdVerOrId(request.getVersionedId()) + " to profile."); " is absent in system. Need ddd Lwm2m Model with id=" + pathIds.getObjectId() + " ver=" +
getVerFromPathIdVerOrId(request.getVersionedId()) + " to profile.");
} }
else if (resourceModelExecute.operations.isExecutable()) { else if (resourceModelExecute.operations.isExecutable()) {
ExecuteRequest downlink; ExecuteRequest downlink;
if (request.getParams() != null && !resourceModelExecute.multiple) { if (request.getParams() != null && !resourceModelExecute.multiple) {
downlink = new ExecuteRequest(request.getObjectId(), (String) this.converter.convertValue(request.getParams(), resourceModelExecute.type, ResourceModel.Type.STRING, new LwM2mPath(request.getObjectId()))); downlink = new ExecuteRequest(request.getObjectId(), (String) this.converter.convertValue(request.getParams(),
resourceModelExecute.type, ResourceModel.Type.STRING, new LwM2mPath(request.getObjectId())));
} else { } else {
downlink = new ExecuteRequest(request.getObjectId()); downlink = new ExecuteRequest(request.getObjectId());
} }
@ -357,9 +359,11 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
* int rscId = resultIds.getObjectInstanceId(); * int rscId = resultIds.getObjectInstanceId();
* contentFormat – Format of the payload (TLV or JSON). * contentFormat – Format of the payload (TLV or JSON).
*/ */
Collection<LwM2mResource> resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); Collection<LwM2mResource> resources = client.getNewResourcesForInstance(request.getVersionedId(),
request.getValue(), this.config.getModelProvider(), this.converter);
if (resources.size() > 0) { if (resources.size() > 0) {
downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
resultIds.getObjectInstanceId(), resources);
} else { } else {
callback.onValidationError(toString(request), "No resources to update!"); callback.onValidationError(toString(request), "No resources to update!");
} }
@ -370,24 +374,30 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
if (resourceModelWrite.multiple) { if (resourceModelWrite.multiple) {
try { try {
Map value = convertMultiResourceValuesFromRpcBody(request.getValue(), resourceModelWrite.type, request.getObjectId()); Map value = convertMultiResourceValuesFromRpcBody(request.getValue(), resourceModelWrite.type, request.getObjectId());
downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
resultIds.getObjectInstanceId(), resultIds.getResourceId(),
value, resourceModelWrite.type); value, resourceModelWrite.type);
} catch (Exception e1) { } 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() + ", value = " +
request.getValue() + ", class = " + request.getValue().getClass().getSimpleName() +
"is bad. Value of Multi-Instance Resource must be in Json format!");
} }
} }
} else { } else {
callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + " is not configured in the device profile!"); callback.onValidationError(toString(request), "Resource " + request.getVersionedId() +
" is not configured in the device profile!");
} }
} }
if (downlink != null) { if (downlink != null) {
sendSimpleRequest(client, downlink, request.getTimeout(), callback); sendSimpleRequest(client, downlink, request.getTimeout(), callback);
} else { } else {
callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + ". This operation can only be used for ObjectInstance or Multi-Instance Resource !"); callback.onValidationError(toString(request), "Resource " + request.getVersionedId() +
". This operation can only be used for ObjectInstance or Multi-Instance Resource !");
} }
} }
else { else {
callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + ". This operation can only be used for ObjectInstance or Resource (multiple)"); callback.onValidationError(toString(request), "Resource " + request.getVersionedId() +
". This operation can only be used for ObjectInstance or Resource (multiple)");
} }
} catch (Exception e) { } catch (Exception e) {
callback.onValidationError(toString(request), e.getMessage()); callback.onValidationError(toString(request), e.getMessage());
@ -406,7 +416,6 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
if (resultIds.isObject() || resultIds.isObjectInstance()) { if (resultIds.isObject() || resultIds.isObjectInstance()) {
Collection<LwM2mResource> resources; Collection<LwM2mResource> resources;
if (resultIds.isObject()) { if (resultIds.isObject()) {
// contentFormat = ContentFormat.TLV;
if (request.getValue() != null) { if (request.getValue() != null) {
resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter);
downlink = new CreateRequest(contentFormat, resultIds.getObjectId(), resources); downlink = new CreateRequest(contentFormat, resultIds.getObjectId(), resources);
@ -584,18 +593,9 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
private static ContentFormat getRequestContentFormat(LwM2mClient client, String versionedId, LwM2mModelProvider modelProvider) { private static ContentFormat getRequestContentFormat(LwM2mClient client, String versionedId, LwM2mModelProvider modelProvider) {
LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(versionedId)); LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(versionedId));
if (pathIds.isResourceInstance()) { if (pathIds.isResource() || pathIds.isResourceInstance()) {
ResourceModel resourceModel = client.getResourceModel(versionedId, modelProvider); ResourceModel resourceModel = client.getResourceModel(versionedId, modelProvider);
if (OBJLNK.equals(resourceModel.type)) { if (resourceModel!= null && (pathIds.isResourceInstance() || (pathIds.isResource() && !resourceModel.multiple))) {
return ContentFormat.LINK;
} else if (OPAQUE.equals(resourceModel.type)) {
return ContentFormat.OPAQUE;
} else {
return findFirst(client.getClientSupportContentFormats(), client.getDefaultContentFormat(), ContentFormat.CBOR, ContentFormat.SENML_CBOR, ContentFormat.SENML_JSON);
}
} else if (pathIds.isResource()) {
ResourceModel resourceModel = client.getResourceModel(versionedId, modelProvider);
if (resourceModel!= null && !resourceModel.multiple) {
if (OBJLNK.equals(resourceModel.type)) { if (OBJLNK.equals(resourceModel.type)) {
return ContentFormat.LINK; return ContentFormat.LINK;
} else if (OPAQUE.equals(resourceModel.type)) { } else if (OPAQUE.equals(resourceModel.type)) {
@ -639,7 +639,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
try { try {
return request != null ? request.toString() : ""; return request != null ? request.toString() : "";
} catch (Exception e) { } catch (Exception e) {
log.trace("Failed to convert request to string", e); log.debug("Failed to convert request to string", e);
return request.getClass().getSimpleName(); return request.getClass().getSimpleName();
} }
} }

5
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCreateRequest.java

@ -30,7 +30,7 @@ public class TbLwM2MCreateRequest extends AbstractTbLwM2MTargetedDownlinkRequest
@Getter @Getter
private final ContentFormat objectContentFormat; private final ContentFormat objectContentFormat;
@Getter @Getter
private final Map<String, Object> nodes;; private final Map<String, Object> nodes;
@Builder @Builder
private TbLwM2MCreateRequest(String versionedId, long timeout, Object value, ContentFormat objectContentFormat, Map<String, Object> nodes) { private TbLwM2MCreateRequest(String versionedId, long timeout, Object value, ContentFormat objectContentFormat, Map<String, Object> nodes) {
@ -44,7 +44,4 @@ public class TbLwM2MCreateRequest extends AbstractTbLwM2MTargetedDownlinkRequest
public LwM2mOperationType getType() { public LwM2mOperationType getType() {
return LwM2mOperationType.CREATE; return LwM2mOperationType.CREATE;
} }
} }

20
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java

@ -16,8 +16,10 @@
package org.thingsboard.server.transport.lwm2m.server.downlink; package org.thingsboard.server.transport.lwm2m.server.downlink;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.request.ReadRequest;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.util.Hex;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
@ -31,8 +33,26 @@ public class TbLwM2MReadCallback extends TbLwM2MUplinkTargetedCallback<ReadReque
@Override @Override
public void onSuccess(ReadRequest request, ReadResponse response) { public void onSuccess(ReadRequest request, ReadResponse response) {
logForBadResponse(response.getCode().getCode(), responseToString (response), request.getClass().getSimpleName());
super.onSuccess(request, response); super.onSuccess(request, response);
handler.onUpdateValueAfterReadResponse(client.getRegistration(), versionedId, response); handler.onUpdateValueAfterReadResponse(client.getRegistration(), versionedId, response);
} }
private String responseToString (ReadResponse response) {
if (response.getContent() instanceof LwM2mSingleResource) {
if (((LwM2mSingleResource) response.getContent()).getType().name().equals("OPAQUE")) {
if (((byte[])((LwM2mSingleResource) response.getContent()).getValue()).length > 0) {
int len = ((byte[])((LwM2mSingleResource) response.getContent()).getValue()).length;
String valueReplace = len + "Bytes";
String valueStr = Hex.encodeHexString((byte[]) (((LwM2mSingleResource) response.getContent()).getValue()));
return response.toString().replace(valueReplace, valueStr);
}
}
return response.toString();
}
else {
return response.toString();
}
}
} }

47
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java

@ -17,10 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.response.LwM2mResponse; import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.util.Hex;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
@ -50,40 +47,22 @@ public abstract class TbLwM2MTargetedCallback<R, T> extends AbstractTbLwM2MReque
@Override @Override
public void onSuccess(R request, T response) { public void onSuccess(R request, T response) {
//TODO convert camelCase to "camel case" using .split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])") //TODO convert camelCase to "camel case" using .split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")
String requestName = request.getClass().getSimpleName(); if (response instanceof LwM2mResponse) {
String responseStr = response.toString(); logForBadResponse(((LwM2mResponse) response).getCode().getCode(), response.toString(), request.getClass().getSimpleName());
int code = 0;
if (response instanceof ReadResponse) {
responseStr = responseToString ((ReadResponse) response);
code = ((ReadResponse) response).getCode().getCode();
}
else if (response instanceof LwM2mResponse) {
code = ((LwM2mResponse) response).getCode().getCode();
}
if (code >= ResponseCode.BAD_REQUEST.getCode()) {
log.error("[{}] [{}] [{}] failed to process successful response [{}] ", client.getEndpoint(), requestName, versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr);
logService.log(client, String.format("[%s]: %s [%s] failed to process successful. Result: %s", LOG_LWM2M_ERROR, requestName, versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr));
}
else {
log.trace("[{}] {} [{}] successful: {}", client.getEndpoint(), requestName, versionedId != null ? versionedId : versionedIds, responseStr);
logService.log(client, String.format("[%s]: %s [%s] successful. Result: %s", LOG_LWM2M_INFO, requestName, versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr));
} }
} }
private String responseToString (ReadResponse response) { public void logForBadResponse(int code, String responseStr, String requestName) {
if (response.getContent() instanceof LwM2mSingleResource) { if (code > ResponseCode.CONTENT_CODE) {
if (((LwM2mSingleResource) response.getContent()).getType().name().equals("OPAQUE")) { log.error("[{}] [{}] [{}] failed to process successful response [{}] ", client.getEndpoint(), requestName,
if (((byte[])((LwM2mSingleResource) response.getContent()).getValue()).length > 0) { versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr);
int len = ((byte[])((LwM2mSingleResource) response.getContent()).getValue()).length; logService.log(client, String.format("[%s]: %s [%s] failed to process successful. Result: %s", LOG_LWM2M_ERROR,
String valueReplace = len + "Bytes"; requestName, versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr));
String valueStr = Hex.encodeHexString((byte[]) (((LwM2mSingleResource) response.getContent()).getValue())); } else {
return response.toString().replace(valueReplace, valueStr); log.trace("[{}] {} [{}] successful: {}", client.getEndpoint(), requestName, versionedId != null ?
} versionedId : versionedIds, responseStr);
} logService.log(client, String.format("[%s]: %s [%s] successful. Result: %s", LOG_LWM2M_INFO, requestName,
return response.toString(); versionedId != null ? versionedId : Arrays.toString(versionedIds), responseStr));
}
else {
return response.toString();
} }
} }
} }

39
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; package org.thingsboard.server.transport.lwm2m.server.rpc;
import com.google.gson.JsonSyntaxException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
@ -25,6 +26,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
@ -103,10 +105,6 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
if (operationType.isHasObjectId()) { if (operationType.isHasObjectId()) {
LwM2MRpcRequestHeader header = JacksonUtil.fromString(rpcRequest.getParams(), LwM2MRpcRequestHeader.class); LwM2MRpcRequestHeader header = JacksonUtil.fromString(rpcRequest.getParams(), LwM2MRpcRequestHeader.class);
String objectId = getIdFromParameters(client, header); String objectId = getIdFromParameters(client, header);
ContentFormat contentFormat = null;
if (StringUtils.isNotEmpty(header.getContentFormat())) {
contentFormat = ContentFormat.fromName(header.getContentFormat());
}
switch (operationType) { switch (operationType) {
case READ: case READ:
sendReadRequest(client, rpcRequest, objectId); sendReadRequest(client, rpcRequest, objectId);
@ -142,7 +140,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); throw new IllegalArgumentException("Unsupported operation: " + operationType.name());
} }
} else if (operationType.isComposite()) { } else if (operationType.isComposite()) {
ContentFormat contentFormatComposite = clientContext.getContentFormatComposite(client); ContentFormat contentFormatComposite = this.getContentFormatComposite(client);
if (contentFormatComposite != null) { if (contentFormatComposite != null) {
switch (operationType) { switch (operationType) {
case READ_COMPOSITE: case READ_COMPOSITE:
@ -305,14 +303,21 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
new LwM2mPath(fromVersionedIdToObjectId(key.toString())); new LwM2mPath(fromVersionedIdToObjectId(key.toString()));
versionedId = key.toString(); versionedId = key.toString();
} catch (Exception e) { } catch (Exception e) {
versionedId = clientContext.getObjectIdByKeyNameFromProfile(client, key.toString(), true); versionedId = clientContext.getObjectIdByKeyNameFromProfile(client, key.toString());
} }
// validate value. Must be only primitive, not Json // validate value. Must be only primitive, not Json
if (value instanceof LinkedHashMap) { try {
JsonUtils.parse(value.toString());
throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " + throw new IllegalArgumentException(String.format("nodes: %s is not validate value. " +
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString())); "The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes.toString()));
} else { } catch (JsonSyntaxException jse) {
newNodes.put(fromVersionedIdToObjectId(versionedId), value); // 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);
}
} }
}); });
return newNodes; return newNodes;
@ -342,7 +347,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
private String getIdFromParameters(LwM2mClient client, LwM2MRpcRequestHeader header) { private String getIdFromParameters(LwM2mClient client, LwM2MRpcRequestHeader header) {
String targetId; String targetId;
if (StringUtils.isNotEmpty(header.getKey())) { if (StringUtils.isNotEmpty(header.getKey())) {
targetId = clientContext.getObjectIdByKeyNameFromProfile(client, header.getKey(), false); targetId = clientContext.getObjectIdByKeyNameFromProfile(client, header.getKey());
} else if (StringUtils.isNotEmpty(header.getId())) { } else if (StringUtils.isNotEmpty(header.getId())) {
targetId = header.getId(); targetId = header.getId();
} else { } else {
@ -356,7 +361,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
if (requestParams.getKeys() != null && requestParams.getKeys().length > 0) { if (requestParams.getKeys() != null && requestParams.getKeys().length > 0) {
Set<String> targetIds = ConcurrentHashMap.newKeySet(); Set<String> targetIds = ConcurrentHashMap.newKeySet();
for (String key : requestParams.getKeys()) { for (String key : requestParams.getKeys()) {
String targetId = clientContext.getObjectIdByKeyNameFromProfile(client, key, true); String targetId = clientContext.getObjectIdByKeyNameFromProfile(client, key);
if (targetId != null) { if (targetId != null) {
targetIds.add(targetId); targetIds.add(targetId);
} }
@ -384,4 +389,16 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) {
log.info("[{}] toServerRpcResponse", toServerResponse); log.info("[{}] toServerRpcResponse", toServerResponse);
} }
private ContentFormat getContentFormatComposite(LwM2mClient client) {
if (client.getClientSupportContentFormats().contains(ContentFormat.SENML_JSON)) {
return ContentFormat.SENML_JSON;
}
else if (client.getClientSupportContentFormats().contains(ContentFormat.SENML_CBOR)) {
return ContentFormat.SENML_CBOR;
}
else {
return null;
}
}
} }

11
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mTransportUtil.java

@ -63,7 +63,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION;
import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN; import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN;
@ -220,7 +219,7 @@ public class LwM2mTransportUtil {
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
return null; return LWM2M_OBJECT_VERSION_DEFAULT;
} }
public static String validPathIdVer(String pathIdVer, Registration registration) throws public static String validPathIdVer(String pathIdVer, Registration registration) throws
@ -354,14 +353,6 @@ public class LwM2mTransportUtil {
} }
} }
public static String getMsgException(String keyName, Set<String> msgException) {
if (msgException.size() == 1) {
msgException.add(" is not configured in the device profile!");
}
msgException.remove("");
return String.format("%s %s", keyName, String.join(",", msgException)).trim();
}
public static Map convertMultiResourceValuesFromRpcBody(Object value, ResourceModel.Type type, String versionedId) throws Exception { public static Map convertMultiResourceValuesFromRpcBody(Object value, ResourceModel.Type type, String versionedId) throws Exception {
String valueJsonStr = JsonUtils.riteValueAsString(value); String valueJsonStr = JsonUtils.riteValueAsString(value);
return convertMultiResourceValuesFromJson((JsonObject) JsonUtils.parse(valueJsonStr), type, versionedId); return convertMultiResourceValuesFromJson((JsonObject) JsonUtils.parse(valueJsonStr), type, versionedId);

21
common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java

@ -23,8 +23,8 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Created by Valerii Sosliuk on 5/12/2017. * Created by Valerii Sosliuk on 5/12/2017.
@ -126,22 +126,35 @@ public class JacksonUtil {
} }
/**
*
* @param {bootstrap:client:host, client, client:port, client:endpoint}
* @param ":"
* @return
* - "bootstrap:client:host"
* -- if in nodeCredentialsValue is absent:
* --- "bootstrap" or "bootstrap:client" or "bootstrap:client:host" -> return "host"
* -- if in nodeCredentialsValue is absent:
* --- "client:port" -> return "port"
* -- if the nodeCredentialsValue contains:
* --- "client" and "client:port" -> "port" not add to Set
*/
public static String validateFieldsToTree(JsonNode nodeCredentialsValue, String[] fields, String delimiter) { public static String validateFieldsToTree(JsonNode nodeCredentialsValue, String[] fields, String delimiter) {
try { try {
Set msgSet = ConcurrentHashMap.newKeySet(); StringBuilder msgSet = new StringBuilder();
for (String field : fields) { for (String field : fields) {
String[] keys = field.split(delimiter); String[] keys = field.split(delimiter);
JsonNode nodeVal = nodeCredentialsValue; JsonNode nodeVal = nodeCredentialsValue;
for (String key : keys) { for (String key : keys) {
if (!nodeVal.hasNonNull(key)) { if (!nodeVal.hasNonNull(key)) {
msgSet.add(keys[keys.length - 1]); msgSet.append(" " + keys[keys.length - 1]);
break; break;
} else { } else {
nodeVal = nodeVal.get(key); nodeVal = nodeVal.get(key);
} }
} }
} }
return String.join(", ", msgSet); return msgSet.toString().trim();
} catch (Exception e) { } catch (Exception e) {
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);
} }

3
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -133,9 +133,6 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
@Autowired @Autowired
private DashboardService dashboardService; private DashboardService dashboardService;
@Autowired
private DeviceCredentialsService deviceCredentialsService;
private final Lock findOrCreateLock = new ReentrantLock(); private final Lock findOrCreateLock = new ReentrantLock();
@Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}") @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}")

Loading…
Cancel
Save