From 4bf0af9e78f8777f5e0a2a851c7d9dde96315024 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 10 Dec 2020 11:36:55 +0200 Subject: [PATCH 01/41] Lwm2m: add logLwm2m to start bootstrap --- .../secure/LwM2MBootstrapSecurityStore.java | 21 ++++- .../lwm2m/server/LwM2MSessionMsgListener.java | 2 +- .../server/LwM2MTransportContextServer.java | 90 ++++++++++++++++++- .../lwm2m/server/LwM2MTransportHandler.java | 12 +-- .../lwm2m/server/LwM2MTransportRequest.java | 6 +- .../lwm2m/server/LwM2MTransportService.java | 54 +++++------ 6 files changed, 146 insertions(+), 39 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index 0b06f02547..b0fff4cf07 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -30,15 +30,21 @@ import org.eclipse.leshan.server.security.SecurityInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; +import org.thingsboard.server.transport.lwm2m.server.LwM2MSessionMsgListener; +import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; +import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler; import org.thingsboard.server.transport.lwm2m.utils.TypeServer; import java.io.IOException; import java.security.GeneralSecurityException; import java.util.Arrays; import java.util.List; +import java.util.UUID; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.*; @@ -52,6 +58,11 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Autowired LwM2MGetSecurityInfo lwM2MGetSecurityInfo; + @Autowired + public LwM2MTransportContextServer context; + + + public LwM2MBootstrapSecurityStore(EditableBootstrapConfigStore bootstrapConfigStore) { this.bootstrapConfigStore = bootstrapConfigStore; } @@ -147,16 +158,21 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class); LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class); LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class); + UUID sessionUUiD = UUID.randomUUID(); + TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo)); if (getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); + String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint()); + context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo); return lwM2MBootstrapConfig; } else { log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint()); log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint()); - String logMsg = String.format(LOG_LW2M_ERROR + " getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint()); -// sentLogsToThingsboard(logMsg, store.getEndPoint()); + String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint()); + context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo); return null; } } catch (JsonProcessingException e) { @@ -165,6 +181,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { } } + /** * Bootstrap security have to sync between (bootstrapServer in credential and bootstrapServer in profile) * and (lwm2mServer in credential and lwm2mServer in profile diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java index bf88427879..2de352bc6c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java @@ -36,7 +36,7 @@ public class LwM2MSessionMsgListener implements GenericFutureListener TransportServiceCallback getPubAckCallbackSentAttrTelemetry(final T msg) { + return new TransportServiceCallback() { + @Override + public void onSuccess(Void dummy) { + log.trace("Success to publish msg: {}, dummy: {}", msg, dummy); + } + + @Override + public void onError(Throwable e) { + log.trace("[{}] Failed to publish msg: {}", msg, e); + } + }; + } + + public void sentParametersOnThingsboard(JsonElement msg, String topicName, TransportProtos.SessionInfoProto sessionInfo) { + try { + if (topicName.equals(LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC)) { + TransportProtos.PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(msg); + TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(postAttributeMsg); + transportService.process(sessionInfo, postAttributeMsg, this.getPubAckCallbackSentAttrTelemetry(call)); + } else if (topicName.equals(LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC)) { + TransportProtos.PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(msg); + TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(postTelemetryMsg); + transportService.process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSentAttrTelemetry(call)); + } + } catch (AdaptorException e) { + log.error("[{}] Failed to process publish msg [{}]", topicName, e); + log.info("[{}] Closing current session due to invalid publish", topicName); + } + } + + public JsonObject getTelemetryMsgObject(String logMsg) { + JsonObject telemetries = new JsonObject(); + telemetries.addProperty(LOG_LW2M_TELEMETRY, logMsg); + return telemetries; + } + + /** + * @return - sessionInfo after access connect client + */ + public TransportProtos.SessionInfoProto getValidateSessionInfo(TransportProtos.ValidateDeviceCredentialsResponseMsg msg, long mostSignificantBits, long leastSignificantBits) { + return TransportProtos.SessionInfoProto.newBuilder() + .setNodeId(this.getNodeId()) + .setSessionIdMSB(mostSignificantBits) + .setSessionIdLSB(leastSignificantBits) + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB()) + .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB()) + .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) + .setDeviceName(msg.getDeviceInfo().getDeviceName()) + .setDeviceType(msg.getDeviceInfo().getDeviceType()) + .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB()) + .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB()) + .build(); + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java index d922f2b1c5..e3c9fef985 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java @@ -16,12 +16,10 @@ package org.thingsboard.server.transport.lwm2m.server; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; +import com.google.gson.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.qpid.proton.engine.Session; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mNode; @@ -39,6 +37,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; import javax.annotation.PostConstruct; @@ -115,7 +118,6 @@ public class LwM2MTransportHandler{ @Autowired private LwM2MTransportService service; - @PostConstruct public void init() { LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index 45a85b16bb..a7e13544db 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -229,7 +229,7 @@ public class LwM2MTransportRequest { if (isSuccess(((Response)response.getCoapResponse()).getCode())) { this.handleResponse(registration, request.getPath().toString(), response, request, lwM2MClient); if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) { - String msg = String.format(LOG_LW2M_INFO + " sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client", + String msg = String.format(LOG_LW2M_INFO + ": sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client", ((Response)response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(), ((LwM2mSingleResource)((WriteRequest) request).getNode()).getValue().toString()); service.sentLogsToThingsboard(msg, registration.getId()); @@ -237,13 +237,13 @@ public class LwM2MTransportRequest { } } else { - String msg = String.format(LOG_LW2M_ERROR + " sendRequest: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", + String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", ((Response)response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); service.sentLogsToThingsboard(msg, registration.getId()); log.error("[{}] - [{}] [{}] error SendRequest", ((Response)response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); } }, e -> { - String msg = String.format(LOG_LW2M_ERROR + " sendRequest: Resource path - %s msg error - %s SendRequest to Client", + String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg error - %s SendRequest to Client", request.getPath().toString(), e.toString()); service.sentLogsToThingsboard(msg, registration.getId()); log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index 7ff68b928c..370423ea4c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -85,8 +85,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportService { - @Autowired - private LwM2MJsonAdaptor adaptor; +// @Autowired +// private LwM2MJsonAdaptor adaptor; @Autowired private TransportService transportService; @@ -263,9 +263,9 @@ public class LwM2MTransportService { log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED); this.closeClientSession(lwM2MClient.getRegistration()); } else { - sessionInfo = SessionInfoProto.newBuilder() + sessionInfo = SessionInfoProto.newBuilder() .setNodeId(this.context.getNodeId()) - .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits()) + .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits() ) .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits()) .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB()) @@ -315,7 +315,7 @@ public class LwM2MTransportService { */ private void updateAttrTelemetry(Registration registration, boolean start, Set paths) { JsonObject attributes = new JsonObject(); - JsonObject telemetrys = new JsonObject(); + JsonObject telemetries = new JsonObject(); if (start) { // #1.1 JsonObject attributeClient = this.getAttributeClient(registration); @@ -327,7 +327,7 @@ public class LwM2MTransportService { } // #1.2 CountDownLatch cancelLatch = new CountDownLatch(1); - this.getParametersFromProfile(attributes, telemetrys, registration, paths); + this.getParametersFromProfile(attributes, telemetries, registration, paths); cancelLatch.countDown(); try { cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); @@ -336,8 +336,8 @@ public class LwM2MTransportService { } if (attributes.getAsJsonObject().entrySet().size() > 0) this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration.getId()); - if (telemetrys.getAsJsonObject().entrySet().size() > 0) - this.updateParametersOnThingsboard(telemetrys, DEVICE_TELEMETRY_TOPIC, registration.getId()); + if (telemetries.getAsJsonObject().entrySet().size() > 0) + this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration.getId()); } /** @@ -432,20 +432,22 @@ public class LwM2MTransportService { public void updateParametersOnThingsboard(JsonElement msg, String topicName, String registrationId) { SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId); if (sessionInfo != null) { - try { - if (topicName.equals(LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC)) { - PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(msg); - TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(-1, postAttributeMsg); - transportService.process(sessionInfo, postAttributeMsg, call); - } else if (topicName.equals(LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC)) { - PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(msg); - TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(-1, postTelemetryMsg); - transportService.process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSentAttrTelemetry(-1, call)); - } - } catch (AdaptorException e) { - log.error("[{}] Failed to process publish msg [{}]", topicName, e); - log.info("[{}] Closing current session due to invalid publish", topicName); - } + context.sentParametersOnThingsboard(msg, topicName, sessionInfo); +// try { +// if (topicName.equals(LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC)) { +// +//// PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(msg); +//// TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(-1, postAttributeMsg); +//// transportService.process(sessionInfo, postAttributeMsg, call); +// } else if (topicName.equals(LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC)) { +// PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(msg); +// TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(-1, postTelemetryMsg); +// transportService.process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSentAttrTelemetry(-1, call)); +// } +// } catch (AdaptorException e) { +// log.error("[{}] Failed to process publish msg [{}]", topicName, e); +// log.info("[{}] Closing current session due to invalid publish", topicName); +// } } else { log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registrationId, sessionInfo); } @@ -669,7 +671,7 @@ public class LwM2MTransportService { } else { log.error(LOG_LW2M_ERROR + ": Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value); - String logMsg = String.format(LOG_LW2M_ERROR + " attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value); + String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value); this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); } } @@ -990,9 +992,9 @@ public class LwM2MTransportService { public void sentLogsToThingsboard(String msg, String registrationId) { if (msg != null) { - JsonObject telemetrys = new JsonObject(); - telemetrys.addProperty(LOG_LW2M_TELEMETRY, msg); - this.updateParametersOnThingsboard(telemetrys, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registrationId); + JsonObject telemetries = new JsonObject(); + telemetries.addProperty(LOG_LW2M_TELEMETRY, msg); + this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registrationId); } } From 85dd4a9416a0dc8938a94d62f6adcb2a62b8ba43 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 11 Dec 2020 19:25:51 +0200 Subject: [PATCH 02/41] Lwm2m: backEnd: add DelayedRequest --- .../secure/LwM2MBootstrapSecurityStore.java | 13 +- .../lwm2m/secure/LwM2MGetSecurityInfo.java | 6 +- .../lwm2m/server/LwM2MSessionMsgListener.java | 2 +- .../server/LwM2MTransportContextServer.java | 8 +- .../lwm2m/server/LwM2MTransportHandler.java | 90 ++-- .../lwm2m/server/LwM2MTransportRequest.java | 61 +-- .../lwm2m/server/LwM2MTransportService.java | 436 +++++++++++------- .../server/adaptors/LwM2MJsonAdaptor.java | 39 ++ .../adaptors/LwM2MTransportAdaptor.java | 4 + .../lwm2m/server/client/LwM2MClient.java | 49 +- .../secure/LwM2MSetSecurityStoreServer.java | 11 +- .../secure/LwM2mInMemorySecurityStore.java | 18 +- 12 files changed, 475 insertions(+), 262 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index b0fff4cf07..a4e49fc546 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -30,7 +30,6 @@ import org.eclipse.leshan.server.security.SecurityInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; @@ -46,7 +45,12 @@ import java.util.Arrays; import java.util.List; import java.util.UUID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.*; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.BOOTSTRAP_SERVER; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LWM2M_SERVER; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVERS; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getBootstrapParametersFromThingsboard; @Slf4j @Component("LwM2MBootstrapSecurityStore") @@ -61,8 +65,6 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Autowired public LwM2MTransportContextServer context; - - public LwM2MBootstrapSecurityStore(EditableBootstrapConfigStore bootstrapConfigStore) { this.bootstrapConfigStore = bootstrapConfigStore; } @@ -161,7 +163,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { UUID sessionUUiD = UUID.randomUUID(); TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo)); - if (getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { + if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint()); @@ -181,7 +183,6 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { } } - /** * Bootstrap security have to sync between (bootstrapServer in credential and bootstrapServer in profile) * and (lwm2mServer in credential and lwm2mServer in profile diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java index ad63e21633..a02107ca45 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java @@ -39,7 +39,11 @@ import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.*; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; + @Slf4j @Component("LwM2MGetSecurityInfo") diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java index 2de352bc6c..97963038b8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java @@ -43,7 +43,7 @@ public class LwM2MSessionMsgListener implements GenericFutureListener 0) { - Lwm2mDeviceProfileTransportConfiguration lwm2mDeviceProfileTransportConfiguration = (Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration(); +// Lwm2mDeviceProfileTransportConfiguration lwm2mDeviceProfileTransportConfiguration = (Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration(); Object observeAttr = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); try { ObjectMapper mapper = new ObjectMapper(); @@ -219,7 +226,6 @@ public class LwM2MTransportHandler{ public static JsonObject getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) { if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { - Lwm2mDeviceProfileTransportConfiguration lwm2mDeviceProfileTransportConfiguration = (Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration(); Object bootstrap = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); try { ObjectMapper mapper = new ObjectMapper(); @@ -278,7 +284,7 @@ public class LwM2MTransportHandler{ jsonValidFlesh = jsonValidFlesh.replaceAll("\n", ""); jsonValidFlesh = jsonValidFlesh.replaceAll("\t", ""); jsonValidFlesh = jsonValidFlesh.replaceAll(" ", ""); - String jsonValid = (jsonValidFlesh.substring(0, 1).equals("\"") && jsonValidFlesh.substring(jsonValidFlesh.length() - 1).equals("\"")) ? jsonValidFlesh.substring(1, jsonValidFlesh.length() - 1) : jsonValidFlesh; + String jsonValid = (jsonValidFlesh.charAt(0) == '"' && jsonValidFlesh.charAt(jsonValidFlesh.length() - 1) == '"') ? jsonValidFlesh.substring(1, jsonValidFlesh.length() - 1) : jsonValidFlesh; try { object = new JsonParser().parse(jsonValid).getAsJsonObject(); } catch (JsonSyntaxException e) { @@ -290,7 +296,7 @@ public class LwM2MTransportHandler{ public static Optional decode(byte[] byteArray) { try { - FSTConfiguration config = FSTConfiguration.createDefaultConfiguration();; + FSTConfiguration config = FSTConfiguration.createDefaultConfiguration(); T msg = (T) config.asObject(byteArray); return Optional.ofNullable(msg); } catch (IllegalArgumentException e) { @@ -301,14 +307,14 @@ public class LwM2MTransportHandler{ /** * Equals to Map for values - * @param map1 - * @param map2 - * @param - * @return + * @param map1 - + * @param map2 - + * @param - + * @return - true if equals */ public static > boolean mapsEquals(Map map1, Map map2) { - List values1 = new ArrayList(map1.values()); - List values2 = new ArrayList(map2.values()); + List values1 = new ArrayList<>(map1.values()); + List values2 = new ArrayList<>(map2.values()); Collections.sort(values1); Collections.sort(values2); return values1.equals(values2); @@ -322,14 +328,28 @@ public class LwM2MTransportHandler{ } public static String splitCamelCaseString(String s){ - LinkedList linkedListOut = new LinkedList(); + LinkedList linkedListOut = new LinkedList<>(); LinkedList linkedList = new LinkedList((Arrays.asList(s.split(" ")))); - linkedList.stream().forEach(str-> { + linkedList.forEach(str-> { String strOut = str.replaceAll("\\W", "").replaceAll("_", "").toUpperCase(); - if (strOut.length()>1) linkedListOut.add(strOut.substring(0, 1) + strOut.substring(1).toLowerCase()); + if (strOut.length()>1) linkedListOut.add(strOut.charAt(0) + strOut.substring(1).toLowerCase()); else linkedListOut.add(strOut); }); linkedListOut.set(0, (linkedListOut.get(0).substring(0, 1).toLowerCase() + linkedListOut.get(0).substring(1))); return StringUtils.join(linkedListOut, ""); } + + public static TransportServiceCallback getAckCallback(LwM2MClient lwM2MClient, int requestId, String typeTopic) { + return new TransportServiceCallback() { + @Override + public void onSuccess(Void dummy) { + log.trace("[{}] [{}] - requestId [{}] - EndPoint , Access AckCallback", typeTopic, requestId, lwM2MClient.getEndPoint()); + } + + @Override + public void onError(Throwable e) { + log.trace("[{}] Failed to publish msg", e.toString()); + } + }; + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index a7e13544db..295c3b9f64 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -113,8 +113,8 @@ public class LwM2MTransportRequest { * @param lwM2MClient * @param observation */ - public void sendAllRequest(LeshanServer lwServer, Registration registration, String target, String typeOper, - String contentFormatParam, LwM2MClient lwM2MClient, Observation observation, Object params, long timeoutInMs) { + public void sendAllRequest(LeshanServer lwServer, Registration registration, String target, String typeOper, String contentFormatParam, + LwM2MClient lwM2MClient, Observation observation, Object params, long timeoutInMs, boolean isDelayedUpdate) { ResultIds resultIds = new ResultIds(target); if (registration != null && resultIds.getObjectId() >= 0) { DownlinkRequest request = null; @@ -212,41 +212,47 @@ public class LwM2MTransportRequest { break; default: } - if (request != null) sendRequest(lwServer, registration, request, lwM2MClient, timeoutInMs); + if (request != null) + this.sendRequest(lwServer, registration, request, lwM2MClient, timeoutInMs, isDelayedUpdate); } } /** * - * @param lwServer - * @param registration - * @param request - * @param lwM2MClient - * @param timeoutInMs + * @param lwServer - + * @param registration - + * @param request - + * @param lwM2MClient - + * @param timeoutInMs - */ - private void sendRequest(LeshanServer lwServer, Registration registration, DownlinkRequest request, LwM2MClient lwM2MClient, long timeoutInMs) { + private void sendRequest(LeshanServer lwServer, Registration registration, DownlinkRequest request, LwM2MClient lwM2MClient, long timeoutInMs, boolean isDelayedUpdate) { lwServer.send(registration, request, timeoutInMs, (ResponseCallback) response -> { - if (isSuccess(((Response)response.getCoapResponse()).getCode())) { - this.handleResponse(registration, request.getPath().toString(), response, request, lwM2MClient); + if (isSuccess(((Response) response.getCoapResponse()).getCode())) { + this.handleResponse(registration, request.getPath().toString(), response, request, lwM2MClient, isDelayedUpdate); if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) { - String msg = String.format(LOG_LW2M_INFO + ": sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client", - ((Response)response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(), - ((LwM2mSingleResource)((WriteRequest) request).getNode()).getValue().toString()); + String delayedUpdateStr = ""; + if (isDelayedUpdate) { + delayedUpdateStr = " (delayedUpdate) "; + } + + String msg = String.format(LOG_LW2M_INFO + ": sendRequest Replace%s: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client", + delayedUpdateStr, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(), + ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue().toString()); service.sentLogsToThingsboard(msg, registration.getId()); - log.info("[{}] - [{}] [{}] [{}] Update SendRequest", ((Response)response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), ((LwM2mSingleResource)((WriteRequest) request).getNode()).getValue()); + log.info("[{}] - [{}] [{}] [{}] Update SendRequest[{}]", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), + ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue(), delayedUpdateStr); } - } - else { + } else { String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", - ((Response)response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); + ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); service.sentLogsToThingsboard(msg, registration.getId()); - log.error("[{}] - [{}] [{}] error SendRequest", ((Response)response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); + log.error("[{}] - [{}] [{}] error SendRequest", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); } }, e -> { String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg error - %s SendRequest to Client", request.getPath().toString(), e.toString()); service.sentLogsToThingsboard(msg, registration.getId()); - log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); + log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); }); } @@ -271,19 +277,18 @@ public class LwM2MTransportRequest { } return null; } catch (NumberFormatException e) { - String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; - log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); - return null; + String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; + log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); + return null; } } - private void handleResponse(Registration registration, final String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient) { + private void handleResponse(Registration registration, final String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient, boolean isDelayedUpdate) { executorService.submit(new Runnable() { @Override public void run() { - try { - sendResponse(registration, path, response, request, lwM2MClient); + sendResponse(registration, path, response, request, lwM2MClient, isDelayedUpdate); } catch (RuntimeException t) { log.error("[{}] endpoint [{}] path [{}] error Unable to after send response.", registration.getEndpoint(), path, t.toString()); } @@ -298,7 +303,7 @@ public class LwM2MTransportRequest { * @param response - * @param lwM2MClient - */ - private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient) { + private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient, boolean isDelayedUpdate) { if (response instanceof ObserveResponse) { service.onObservationResponse(registration, path, (ReadResponse) response); } else if (response instanceof CancelObservationResponse) { @@ -329,7 +334,7 @@ public class LwM2MTransportRequest { log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", path, response); } else if (response instanceof WriteResponse) { log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", path, response); - service.onAttributeUpdateOk(registration, path, (WriteRequest) request); + service.onAttributeUpdateOk(registration, path, (WriteRequest) request, isDelayedUpdate); } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index 370423ea4c..b37d419987 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -15,21 +15,23 @@ */ package org.thingsboard.server.transport.lwm2m.server; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mObject; import org.eclipse.leshan.core.node.LwM2mObjectInstance; -import org.eclipse.leshan.core.node.LwM2mSingleResource; -import org.eclipse.leshan.core.node.LwM2mResource; 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.observation.Observation; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.ReadResponse; -import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.registration.Registration; import org.springframework.beans.factory.annotation.Autowired; @@ -38,36 +40,35 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.transport.TransportService; -import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; -import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; -import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; import org.thingsboard.server.transport.lwm2m.server.client.ModelObject; -import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; +import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore; import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -import java.util.UUID; -import java.util.Random; -import java.util.Map; import java.util.HashMap; -import java.util.Arrays; -import java.util.Set; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; @@ -75,19 +76,26 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import java.util.stream.Collectors; -import java.util.stream.Stream; -import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.*; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.CLIENT_NOT_AUTHORIZED; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEFAULT_TIMEOUT; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getAckCallback; @Slf4j @Service("LwM2MTransportService") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportService { -// @Autowired -// private LwM2MJsonAdaptor adaptor; - @Autowired private TransportService transportService; @@ -100,10 +108,9 @@ public class LwM2MTransportService { @Autowired LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; - @PostConstruct public void init() { - context.getScheduler().scheduleAtFixedRate(() -> checkInactivityAndReportActivity(), new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); + context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); } /** @@ -122,7 +129,7 @@ public class LwM2MTransportService { * @param previousObsersations - may be null */ public void onRegistered(LeshanServer lwServer, Registration registration, Collection previousObsersations) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(lwServer, registration); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration); if (lwM2MClient != null) { lwM2MClient.setLwM2MTransportService(this); lwM2MClient.setLwM2MTransportService(this); @@ -139,10 +146,10 @@ public class LwM2MTransportService { transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client registration", registration.getId()); } else { - log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), sessionInfo); + log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } } else { - log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), lwM2MClient); + log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); } } @@ -155,7 +162,7 @@ public class LwM2MTransportService { if (sessionInfo != null) { log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { - log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), sessionInfo); + log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } } @@ -180,7 +187,7 @@ public class LwM2MTransportService { } log.info("Client: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { - log.error("Client: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), sessionInfo); + log.error("Client: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } } @@ -193,10 +200,9 @@ public class LwM2MTransportService { * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay, * * if you need to do long time processing use a dedicated thread pool. * - * @param registration + * @param registration - */ - - public void onAwakeDev(Registration registration) { + protected void onAwakeDev(Registration registration) { log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); //TODO: associate endpointId with device information. } @@ -244,8 +250,8 @@ public class LwM2MTransportService { Arrays.stream(registration.getObjectLinks()).forEach(url -> { ResultIds pathIds = new ResultIds(url.getUrl()); if (pathIds.instanceId > -1 && pathIds.resourceId == -1) { - lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, - ContentFormat.TLV.getName(), lwM2MClient, null, null, this.context.getCtxServer().getTimeout()); + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); } }); } @@ -256,16 +262,16 @@ public class LwM2MTransportService { */ private SessionInfoProto getValidateSessionInfo(String registrationId) { SessionInfoProto sessionInfo = null; - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registrationId); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registrationId); if (lwM2MClient != null) { ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse(); if (msg == null || msg.getDeviceInfo() == null) { log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED); this.closeClientSession(lwM2MClient.getRegistration()); } else { - sessionInfo = SessionInfoProto.newBuilder() + sessionInfo = SessionInfoProto.newBuilder() .setNodeId(this.context.getNodeId()) - .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits() ) + .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits()) .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits()) .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB()) @@ -284,21 +290,111 @@ public class LwM2MTransportService { /** * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use - * #1 Client`s starting info to send to thingsboard - * #2 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection - * #3 Start observe + * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection + * #2 Start observe * - * @param lwServer - LeshanServer - * @param registration - Registration LwM2M Client + * @param lwM2MClient - LwM2M Client */ - public void updatesAndSentModelParameter(LeshanServer lwServer, Registration registration) { + public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) { // #1 -// this.setParametersToModelClient(registration, modelClient, deviceProfile); + this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null); // #2 - this.updateAttrTelemetry(registration, true, null); - // #3 - this.onSentObserveToClient(lwServer, registration); + this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration()); + + } + + /** + * If there is a difference in values between the current resource values and the shared attribute values + * when the client connects to the server + * #1 get attributes name from profile include name resources in ModelObject if resource isWritable + * #2.1 #1 size > 0 => send Request getAttributes to thingsboard + * #2.2 #1 size == 0 => continue normal process + * + * @param lwM2MClient - LwM2M Client + */ + public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) { + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration().getId()); + if (sessionInfo != null) { + //#1.1 + #1.2 + List attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient); + if (attrSharedNames.size() > 0) { + //#2.1 + try { + TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames); + lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId()); + transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); + } catch (AdaptorException e) { + log.warn("Failed to decode get attributes request", e); + } + } + // #2.2 + else { + lwM2MClient.onSuccessDelayedRequests(null); + } + } + } + + /** + * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values + * #1 Get path resource by result attributesResponse + * #1.1 If two names have equal path => last time attribute + * #2.1 if there is a difference in values between the current resource values and the shared attribute values + * => sent to client Request Update of value (new value from shared attribute) + * and LwM2MClient.delayedRequests.add(path) + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values + * + * @param attributesResponse - + * @param sessionInfo - + */ + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo); + if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) { + attributesResponse.getSharedAttributeListList().forEach(attr -> { + String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey()); + // #1.1 + if (lwM2MClient.getDelayedRequests().keySet().contains(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) { + lwM2MClient.getDelayedRequests().put(path, attr); + } else { + lwM2MClient.getDelayedRequests().put(path, attr); + } + }); + // #2.1 + lwM2MClient.getDelayedRequests().forEach((k, v)->{ + this.putDelayedUpdateResourcesClient (lwM2MClient, lwM2MClient.getResourceValue(k), v.getKv().getStringV(), k); + System.out.printf(" k: %s, v: %s%n, v1: %s%n", k, v.getKv().getStringV(), lwM2MClient.getResourceValue(k)); + }); + lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId()); +// lwM2MClient.onSuccessDelayedRequests(); + } + } + + private void putDelayedUpdateResourcesClient (LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path){ + if (!valueOld.toString().equals(valueNew.toString())) { + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, + ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(), + true); + } + } + + /** + * Get names and keyNames from profile attr resources IsWritable + * + * @param lwM2MClient - + * @return ArrayList names and keyNames from profile attr resources IsWritable + */ + private List getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) { + Set namesIsIsWritable = ConcurrentHashMap.newKeySet(); + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid()); + Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class); + ConcurrentMap keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class); + ConcurrentMap keyNamesIsWritable = keyNamesMap.entrySet() + .stream() + .filter(e -> (attrSet.contains(e.getKey()) && lwM2MClient.getOperation(e.getKey()).isWritable())) + .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)); + namesIsIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values())); + keyNamesIsWritable.keySet().forEach(p -> namesIsIsWritable.add(lwM2MClient.getResourceName(p))); + return new ArrayList<>(namesIsIsWritable); } @@ -320,9 +416,7 @@ public class LwM2MTransportService { // #1.1 JsonObject attributeClient = this.getAttributeClient(registration); if (attributeClient != null) { - attributeClient.entrySet().forEach(p -> { - attributes.add(p.getKey(), p.getValue()); - }); + attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue())); } } // #1.2 @@ -349,9 +443,7 @@ public class LwM2MTransportService { private JsonObject getAttributeClient(Registration registration) { if (registration.getAdditionalRegistrationAttributes().size() > 0) { JsonObject resNameValues = new JsonObject(); - registration.getAdditionalRegistrationAttributes().entrySet().forEach(entry -> { - resNameValues.addProperty(entry.getKey(), entry.getValue()); - }); + registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty); return resNameValues; } return null; @@ -371,7 +463,7 @@ public class LwM2MTransportService { ResultIds pathIds = new ResultIds(p.getAsString().toString()); if (pathIds.getResourceId() > -1) { if (path == null || path.contains(p.getAsString())) { - this.addParameters(pathIds, p.getAsString().toString(), attributes, registration); + this.addParameters(p.getAsString().toString(), attributes, registration); } } }); @@ -379,49 +471,27 @@ public class LwM2MTransportService { ResultIds pathIds = new ResultIds(p.getAsString().toString()); if (pathIds.getResourceId() > -1) { if (path == null || path.contains(p.getAsString())) { - this.addParameters(pathIds, p.getAsString().toString(), telemetry, registration); + this.addParameters(p.getAsString().toString(), telemetry, registration); } } }); } /** - * @param pathIds - path resource * @param parameters - JsonObject attributes/telemetry * @param registration - Registration LwM2M Client */ - private void addParameters(ResultIds pathIds, String path, JsonObject parameters, Registration registration) { - ModelObject modelObject = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getModelObjects().get(pathIds.getObjectId()); + private void addParameters(String path, JsonObject parameters, Registration registration) { JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid()).getPostKeyNameProfile(); String resName = String.valueOf(names.get(path)); - if (modelObject != null && resName != null && !resName.isEmpty()) { - String resValue = this.getResourceValue(modelObject, pathIds); + if (resName != null && !resName.isEmpty()) { + String resValue = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path); if (resValue != null) { parameters.addProperty(resName, resValue); } } } - /** - * @param modelObject - ModelObject of Client - * @param pathIds - path resource - * @return - value of Resource or null - */ - private String getResourceValue(ModelObject modelObject, ResultIds pathIds) { - String resValue = null; - if (modelObject.getInstances().get(pathIds.getInstanceId()) != null) { - LwM2mObjectInstance instance = modelObject.getInstances().get(pathIds.getInstanceId()); - if (instance.getResource(pathIds.getResourceId()) != null) { - resValue = instance.getResource(pathIds.getResourceId()).getType() == OPAQUE ? - Hex.encodeHexString((byte[]) instance.getResource(pathIds.getResourceId()).getValue()).toLowerCase() : - (instance.getResource(pathIds.getResourceId()).isMultiInstances()) ? - instance.getResource(pathIds.getResourceId()).getValues().toString() : - instance.getResource(pathIds.getResourceId()).getValue().toString(); - } - } - return resValue; - } - /** * Prepare Sent to Thigsboard callback - Attribute or Telemetry * @@ -433,53 +503,15 @@ public class LwM2MTransportService { SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId); if (sessionInfo != null) { context.sentParametersOnThingsboard(msg, topicName, sessionInfo); -// try { -// if (topicName.equals(LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC)) { -// -//// PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(msg); -//// TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(-1, postAttributeMsg); -//// transportService.process(sessionInfo, postAttributeMsg, call); -// } else if (topicName.equals(LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC)) { -// PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(msg); -// TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(-1, postTelemetryMsg); -// transportService.process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSentAttrTelemetry(-1, call)); -// } -// } catch (AdaptorException e) { -// log.error("[{}] Failed to process publish msg [{}]", topicName, e); -// log.info("[{}] Closing current session due to invalid publish", topicName); -// } } else { - log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registrationId, sessionInfo); + log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registrationId, null); } } - /** - * Sent to Thingsboard Attribute || Telemetry - * - * @param msgId - always == -1 - * @param msg - JsonObject: [{name: value}] - * @return - dummy - */ - private TransportServiceCallback getPubAckCallbackSentAttrTelemetry(final int msgId, final T msg) { - return new TransportServiceCallback() { - @Override - public void onSuccess(Void dummy) { - log.trace("Success to publish msg: {}, dummy: {}", msg, dummy); - } - - @Override - public void onError(Throwable e) { - log.trace("[{}] Failed to publish msg: {}", msg, e); - } - }; - } - - /** * Start observe * #1 - Analyze: * #1.1 path in observe == (attribute or telemetry) - * #1.2 recourseValue notNull * #2 Analyze after sent request (response): * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation * #2.2 Next: ObservationListener.onResponse * @@ -499,15 +531,11 @@ public class LwM2MTransportService { p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ? p.getAsString().toString() : null; if (target != null) { - // #1.2 - ResultIds pathIds = new ResultIds(target); - ModelObject modelObject = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getModelObjects().get(pathIds.getObjectId()); // #2 - if (modelObject != null) { - if (getResourceValue(modelObject, pathIds) != null) { - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE, - null, null, null, null, this.context.getCtxServer().getTimeout()); - } + if (lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(target) != null) { + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE, + null, null, null, null, this.context.getCtxServer().getTimeout(), + false); } } }); @@ -516,9 +544,7 @@ public class LwM2MTransportService { public void setCancelObservations(LeshanServer lwServer, Registration registration) { if (registration != null) { Set observations = lwServer.getObservationService().getObservations(registration); - observations.forEach(observation -> { - this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()); - }); + observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString())); } } @@ -534,6 +560,7 @@ public class LwM2MTransportService { try { cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { + e.printStackTrace(); } } @@ -599,7 +626,7 @@ public class LwM2MTransportService { private void onObservationSetResourcesValue(Registration registration, Object value, Map values, String path) { ResultIds resultIds = new ResultIds(path); // #1 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registration.getId()); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); ModelObject modelObject = lwM2MClient.getModelObjects().get(resultIds.getObjectId()); Map instancesModelObject = modelObject.getInstances(); LwM2mObjectInstance instanceOld = (instancesModelObject.get(resultIds.instanceId) != null) ? instancesModelObject.get(resultIds.instanceId) : null; @@ -607,7 +634,7 @@ public class LwM2MTransportService { LwM2mResource resourceOld = (resourcesOld != null && resourcesOld.get(resultIds.getResourceId()) != null) ? resourcesOld.get(resultIds.getResourceId()) : null; // #2 LwM2mResource resourceNew; - if (resourceOld.isMultiInstances()) { + if (Objects.requireNonNull(resourceOld).isMultiInstances()) { resourceNew = LwM2mMultipleResource.newResource(resultIds.getResourceId(), values, resourceOld.getType()); } else { resourceNew = LwM2mSingleResource.newResource(resultIds.getResourceId(), value, resourceOld.getType()); @@ -628,8 +655,9 @@ public class LwM2MTransportService { try { respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { + ex.printStackTrace(); } - Set paths = new HashSet(); + Set paths = new HashSet<>(); paths.add(path); this.updateAttrTelemetry(registration, false, paths); } @@ -643,37 +671,36 @@ public class LwM2MTransportService { } /** - * Update - sent request in change value resources in Client (path to resources from profile by keyName) - * Only fo resources W - * Delete - nothing + * Update - sent request in change value resources in Client + * Path to resources from profile equal keyName or from ModelObject equal name + * Only for resources: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase) + * Delete - nothing * * - * @param msg - - * @param sessionInfo - + * @param msg - */ - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, SessionInfoProto sessionInfo) { + public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { if (msg.getSharedUpdatedCount() > 0) { JsonElement el = JsonConverter.toJson(msg); el.getAsJsonObject().entrySet().forEach(de -> { - String profilePath = lwM2mInMemorySecurityStore.getProfiles().get(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())) - .getPostKeyNameProfile().getAsJsonObject().entrySet().stream() - .filter(e -> e.getValue().getAsString().equals(de.getKey())).findFirst().map(Map.Entry::getKey) - .orElse(""); - String path = !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdate(sessionInfo, de.getKey()); - if (path != null) { - ResultIds resultIds = new ResultIds(path); - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); - ResourceModel.Operations operations = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).operations; - String value = ((JsonPrimitive) de.getValue()).getAsString(); - if (operations.isWritable()) { + String path = this.getPathAttributeUpdate(sessionInfo, de.getKey()); + String value = de.getValue().getAsString(); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); + if (path != null && validatePathInAttrProfile(profile.getPostAttributeProfile(), path)) { + if (lwM2MClient.getOperation(path).isWritable()) { lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, - ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout()); + ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(), + false); log.info("[{}] path onAttributeUpdate", path); - } - else { + } else { log.error(LOG_LW2M_ERROR + ": Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value); String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value); this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); } + } else { + log.error(LOG_LW2M_ERROR + ": Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value); + String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value); + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); } }); } else if (msg.getSharedDeletedCount() > 0) { @@ -681,38 +708,86 @@ public class LwM2MTransportService { } } - private String getPathAttributeUpdate(SessionInfoProto sessionInfo, String keyName) { + /** + * Get path to resource from profile equal keyName or from ModelObject equal name + * Only for resource: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase) + * + * @param sessionInfo - + * @param name - + * @return + */ + private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) { + String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name); + return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(sessionInfo, name); + } + + /** + * @param postAttributeProfile - + * @param path - + * @return true if path isPresent in postAttributeProfile + */ + private boolean validatePathInAttrProfile(JsonArray postAttributeProfile, String path) { + Set attributesSet = new Gson().fromJson(postAttributeProfile, Set.class); + return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent(); + } + + + /** + * Get path to resource from profile equal keyName + * + * @param sessionInfo - + * @param name - + * @return + */ + private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); + return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream() + .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey) + .orElse(""); + } + + /** + * Get path to resource from ModelObject equal name + * + * @param name - + * @return true if name isPresent as Resource name (usual format) in ResourceModel + */ + private String getPathAttributeUpdateModelObject(TransportProtos.SessionInfoProto sessionInfo, String name) { try { LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); - Predicate> predicateRes = res -> keyName.equals(splitCamelCaseString(res.getValue().name)); + Predicate> predicateRes = res -> name.equals(res.getValue().name); Predicate> predicateObj = (obj -> { return obj.getValue().getObjectModel().resources.entrySet().stream().filter(predicateRes).findFirst().isPresent(); }); - Stream> objectStream = lwM2MClient.getModelObjects().entrySet().stream().filter(predicateObj); - Predicate> predicateResFinal = (objectStream.count() > 0) ? predicateRes : res -> keyName.equals(res.getValue().name); - Predicate> predicateObjFinal = (obj -> { - return obj.getValue().getObjectModel().resources.entrySet().stream().filter(predicateResFinal).findFirst().isPresent(); - }); - Map.Entry object = lwM2MClient.getModelObjects().entrySet().stream().filter(predicateObjFinal).findFirst().get(); + Map.Entry object = lwM2MClient.getModelObjects().entrySet().stream().filter(predicateObj).findFirst().get(); ModelObject modelObject = object.getValue(); LwM2mObjectInstance instance = modelObject.getInstances().entrySet().stream().findFirst().get().getValue(); - ResourceModel resource = modelObject.getObjectModel().resources.entrySet().stream().filter(predicateResFinal).findFirst().get().getValue(); + ResourceModel resource = modelObject.getObjectModel().resources.entrySet().stream().filter(predicateRes).findFirst().get().getValue(); return new LwM2mPath(object.getKey(), instance.getId(), resource.id).toString(); } catch (NoSuchElementException e) { - log.error("[{}] keyName [{}]", keyName, e.toString()); + log.error("[{}] keyName [{}]", name, e.toString()); return null; } } - public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request) { + /** + * Update resource (attribute) value on thingsboard after update value in client + * + * @param registration - + * @param path - + * @param request - + */ + public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) { ResultIds resultIds = new ResultIds(path); - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registration.getId()); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); LwM2mResource resource = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances().get(resultIds.getInstanceId()).getResource(resultIds.getResourceId()); if (resource.isMultiInstances()) { this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path); } else { this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path); } + + if (isDelayedUpdate) lwM2MClient.onSuccessDelayedRequests (request.getPath().toString()); } /** @@ -781,24 +856,24 @@ public class LwM2MTransportService { * @param deviceProfile - */ public void onDeviceUpdateChangeProfile(String registrationId, DeviceProfile deviceProfile) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registrationId); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registrationId); AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()); if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) { LeshanServer lwServer = lwM2MClient.getLwServer(); Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); // #1 JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile(); - Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class); + Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class); JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile(); - Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class); + Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class); JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile(); JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile(); AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId()); JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile(); - Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class); + Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class); JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile(); - Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class); + Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class); JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile(); JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile(); // #2 @@ -841,8 +916,8 @@ public class LwM2MTransportService { // #5.1 if (!observeOld.equals(observeNew)) { - Set observeSetOld = new Gson().fromJson(observeOld, Set.class); - Set observeSetNew = new Gson().fromJson(observeNew, Set.class); + Set observeSetOld = new Gson().fromJson(observeOld, Set.class); + Set observeSetNew = new Gson().fromJson(observeNew, Set.class); //#5.2 add // path Attr/Telemetry includes newObserve attributeSetOld.addAll(telemetrySetOld); @@ -884,7 +959,7 @@ public class LwM2MTransportService { Set paths = keyNameNew.entrySet() .stream() .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey()))) - .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())).keySet(); + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet(); analyzerParameters.setPathPostParametersAdd(paths); return analyzerParameters; } @@ -892,7 +967,7 @@ public class LwM2MTransportService { private ResultsAnalyzerParameters getAnalyzerParametersIn(Set parametersObserve, Set parameters) { ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); analyzerParameters.setPathPostParametersAdd(parametersObserve - .stream().filter(p -> parameters.contains(p)).collect(Collectors.toSet())); + .stream().filter(parameters::contains).collect(Collectors.toSet())); return analyzerParameters; } @@ -906,23 +981,25 @@ public class LwM2MTransportService { * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] */ private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient, Set targets, String typeOper) { - targets.stream().forEach(target -> { + targets.forEach(target -> { ResultIds pathIds = new ResultIds(target); if (pathIds.resourceId >= 0 && lwM2MClient.getModelObjects().get(pathIds.getObjectId()) .getInstances().get(pathIds.getInstanceId()).getResource(pathIds.getResourceId()).getValue() != null) { if (GET_TYPE_OPER_READ.equals(typeOper)) { lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout()); + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(), + false); } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) { lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, - null, null, null, null, this.context.getCtxServer().getTimeout()); + null, null, null, null, this.context.getCtxServer().getTimeout(), + false); } } }); } private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set paramAnallyzer) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registration.getId()); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); paramAnallyzer.forEach(p -> { if (this.getResourceValue(lwM2MClient, p) != null) { this.setCancelObservationRecourse(lwServer, registration, p); @@ -937,7 +1014,6 @@ public class LwM2MTransportService { if (pathIds.getResourceId() > -1) { LwM2mResource resource = lwM2MClient.getModelObjects().get(pathIds.getObjectId()).getInstances().get(pathIds.getInstanceId()).getResource(pathIds.getResourceId()); if (resource.isMultiInstances()) { - Map values = resource.getValues(); if (resource.getValues().size() > 0) { resourceValue = new ResourceValue(); resourceValue.setMultiInstances(resource.isMultiInstances()); @@ -961,7 +1037,8 @@ public class LwM2MTransportService { */ public void doTrigger(LeshanServer lwServer, Registration registration, String path) { lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE, - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout()); + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(), + false); } /** @@ -979,6 +1056,7 @@ public class LwM2MTransportService { /** * Deregister session in transport + * * @param sessionInfo - lwm2m client */ private void doDisconnect(SessionInfoProto sessionInfo) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java index 47dbe563a2..47c7c01b23 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java @@ -24,6 +24,12 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + @Slf4j @Component("LwM2MJsonAdaptor") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") @@ -46,4 +52,37 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor { throw new AdaptorException(ex); } } + + @Override + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List clientKeys, List sharedKeys) throws AdaptorException { + return processGetAttributeRequestMsg(clientKeys, sharedKeys); + } + + protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(List clientKeys, List sharedKeys) throws AdaptorException { + try { + TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); + Random random = new Random(); + result.setRequestId(random.nextInt()); + if (clientKeys != null) { + result.addAllClientAttributeNames(clientKeys); + } + if (sharedKeys != null) { + result.addAllSharedAttributeNames(sharedKeys); + } + return result.build(); + } catch (RuntimeException e) { + log.warn("Failed to decode get attributes request", e); + throw new AdaptorException(e); + } + } + + private Set toStringSet(JsonElement requestBody, String name) { + JsonElement element = requestBody.getAsJsonObject().get(name); + if (element != null) { + return new HashSet<>(Arrays.asList(element.getAsString().split(","))); + } else { + return null; + } + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java index d4d54f4df8..f1672d2039 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java @@ -19,9 +19,13 @@ import com.google.gson.JsonElement; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos; +import java.util.List; + public interface LwM2MTransportAdaptor { TransportProtos.PostTelemetryMsg convertToPostTelemetry(JsonElement jsonElement) throws AdaptorException; TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException; + + TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List clientKeys, List sharedKeys) throws AdaptorException; } 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 3a84a637ac..a28d7257d5 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 @@ -18,12 +18,15 @@ package org.thingsboard.server.transport.lwm2m.server.client; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.ObjectModel; +import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.response.LwM2mResponse; import org.eclipse.leshan.core.response.ReadResponse; +import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.SecurityInfo; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportService; import org.thingsboard.server.transport.lwm2m.server.ResultIds; @@ -35,6 +38,8 @@ import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; + @Slf4j @Data public class LwM2MClient implements Cloneable { @@ -53,6 +58,8 @@ public class LwM2MClient implements Cloneable { private Map attributes; private Map modelObjects; private Set pendingRequests; + private Map delayedRequests; + private Set delayedRequestsId; private Map responses; public Object clone() throws CloneNotSupportedException { @@ -67,6 +74,8 @@ public class LwM2MClient implements Cloneable { this.attributes = (attributes != null && attributes.size() > 0) ? attributes : new ConcurrentHashMap(); this.modelObjects = (modelObjects != null && modelObjects.size() > 0) ? modelObjects : new ConcurrentHashMap(); this.pendingRequests = ConcurrentHashMap.newKeySet(); + this.delayedRequests = new ConcurrentHashMap<>(); + this.delayedRequestsId = ConcurrentHashMap.newKeySet(); this.profileUuid = profileUuid; /** * Key , response instance -> resources: value...> @@ -84,7 +93,7 @@ public class LwM2MClient implements Cloneable { this.pendingRequests.remove(path); if (this.pendingRequests.size() == 0) { this.initValue(); - this.lwM2MTransportService.updatesAndSentModelParameter(this.lwServer, this.registration); + this.lwM2MTransportService.putDelayedUpdateResourcesThingsboard(this); } } @@ -104,4 +113,42 @@ public class LwM2MClient implements Cloneable { } }); } + + public void onSuccessDelayedRequests (String path) { + if (path != null) this.delayedRequests.remove(path); + if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) { + this.lwM2MTransportService.updatesAndSentModelParameter(this); + } + } + + public ResourceModel.Operations getOperation(String path) { + ResultIds resultIds = new ResultIds(path); + return (this.getModelObjects().get(resultIds.getObjectId()) != null) ? this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).operations : ResourceModel.Operations.NONE; + } + + public String getResourceName (String path) { + ResultIds resultIds = new ResultIds(path); + return (this.getModelObjects().get(resultIds.getObjectId()) != null) ? this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).name : ""; + } + + /** + * @param path - path resource + * @return - value of Resource or null + */public String getResourceValue(String path) { + String resValue = null; + ResultIds pathIds = new ResultIds(path); + ModelObject modelObject = this.getModelObjects().get(pathIds.getObjectId()); + if (modelObject != null && modelObject.getInstances().get(pathIds.getInstanceId()) != null) { + LwM2mObjectInstance instance = modelObject.getInstances().get(pathIds.getInstanceId()); + if (instance.getResource(pathIds.getResourceId()) != null) { + resValue = instance.getResource(pathIds.getResourceId()).getType() == OPAQUE ? + Hex.encodeHexString((byte[]) instance.getResource(pathIds.getResourceId()).getValue()).toLowerCase() : + (instance.getResource(pathIds.getResourceId()).isMultiInstances()) ? + instance.getResource(pathIds.getResourceId()).getValues().toString() : + instance.getResource(pathIds.getResourceId()).getValue().toString(); + } + } + return resValue; + } } + diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java index 7a37ac4005..7e2a9960d1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java @@ -42,10 +42,17 @@ import java.security.GeneralSecurityException; import java.security.KeyStoreException; import java.security.cert.X509Certificate; import java.security.interfaces.ECPublicKey; -import java.security.spec.*; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.KeySpec; import java.util.Arrays; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.*; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.REDIS; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; + @Slf4j @Data diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java index 394e7ee5f1..ceea02e55a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler; @@ -44,7 +45,8 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.*; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.DEFAULT_MODE; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; @Slf4j @Component("LwM2mInMemorySecurityStore") @@ -118,18 +120,22 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { this.listener = listener; } - public LwM2MClient getlwM2MClient(String endPoint, String identity) { + public LwM2MClient getLwM2MClient(String endPoint, String identity) { Map.Entry modelClients = (endPoint != null) ? this.sessions.entrySet().stream().filter(model -> endPoint.equals(model.getValue().getEndPoint())).findAny().orElse(null) : this.sessions.entrySet().stream().filter(model -> identity.equals(model.getValue().getIdentity())).findAny().orElse(null); return (modelClients != null) ? modelClients.getValue() : null; } - public LwM2MClient getlwM2MClient(String registrationId) { + public LwM2MClient getLwM2MClient(String registrationId) { return this.sessions.get(registrationId); } - public LwM2MClient getlwM2MClient(LeshanServer lwServer, Registration registration) { + public LwM2MClient getLwM2MClient(TransportProtos.SessionInfoProto sessionInfo) { + return this.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); + + } + public LwM2MClient updateInSessionsLwM2MClient(LeshanServer lwServer, Registration registration) { writeLock.lock(); try { if (this.sessions.get(registration.getEndpoint()) == null) { @@ -193,6 +199,10 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { return this.profiles; } + public AttrTelemetryObserveValue getProfile(UUID profileUuId) { + return this.profiles.get(profileUuId); + } + public MapsetProfiles(Map profiles) { return this.profiles = profiles; } From ca46f5fb7521fde80975d4f20dc95101a30239c0 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 13 Dec 2020 16:55:48 +0200 Subject: [PATCH 03/41] Lwm2m: backEnd: add DelayedRequest + in the start by to name --- .../transport/DefaultTransportApiService.java | 14 ++-- .../data/security/DeviceCredentialsType.java | 2 - common/queue/src/main/proto/queue.proto | 1 + .../lwm2m/server/LwM2MTransportRequest.java | 69 ++++++++++++++----- .../lwm2m/server/LwM2MTransportService.java | 34 ++++++--- .../lwm2m/server/client/LwM2MClient.java | 2 +- .../transport/adaptor/JsonConverter.java | 3 +- .../device/DeviceCredentialsServiceImpl.java | 9 ++- .../server/dao/device/DeviceServiceImpl.java | 2 + 9 files changed, 94 insertions(+), 42 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 0bdeaf360e..76f77c3889 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -49,34 +49,31 @@ import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceProvisionService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionResponse; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.util.mapping.JacksonUtil; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.DeviceCredentialsProto; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsgOrBuilder; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.state.DeviceStateService; @@ -326,6 +323,7 @@ public class DefaultTransportApiService implements TransportApiService { break; case MQTT_BASIC: case X509_CERTIFICATE: + case LWM2M_CREDENTIALS: provisionResponse.setCredentialsValue(deviceCredentials.getCredentialsValue()); break; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentialsType.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentialsType.java index 84a634a100..6b214ecb38 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentialsType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentialsType.java @@ -21,6 +21,4 @@ public enum DeviceCredentialsType { X509_CERTIFICATE, MQTT_BASIC, LWM2M_CREDENTIALS - - } diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index 08a0d0ba97..1c2a128eb6 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -77,6 +77,7 @@ enum CredentialsType { ACCESS_TOKEN = 0; X509_CERTIFICATE = 1; MQTT_BASIC = 2; + LWM2M_CREDENTIALS = 3; } message KeyValueProto { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index 295c3b9f64..40edf09986 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -23,23 +23,23 @@ import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.ObjectLink; import org.eclipse.leshan.core.observation.Observation; +import org.eclipse.leshan.core.request.CancelObservationRequest; import org.eclipse.leshan.core.request.ContentFormat; -import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.request.DiscoverRequest; import org.eclipse.leshan.core.request.DownlinkRequest; +import org.eclipse.leshan.core.request.ExecuteRequest; import org.eclipse.leshan.core.request.ObserveRequest; -import org.eclipse.leshan.core.request.CancelObservationRequest; import org.eclipse.leshan.core.request.ReadRequest; -import org.eclipse.leshan.core.request.ExecuteRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest; -import org.eclipse.leshan.core.response.ResponseCallback; -import org.eclipse.leshan.core.response.LwM2mResponse; -import org.eclipse.leshan.core.response.ObserveResponse; -import org.eclipse.leshan.core.response.ReadResponse; +import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.CancelObservationResponse; import org.eclipse.leshan.core.response.DeleteResponse; -import org.eclipse.leshan.core.response.ExecuteResponse; import org.eclipse.leshan.core.response.DiscoverResponse; +import org.eclipse.leshan.core.response.ExecuteResponse; +import org.eclipse.leshan.core.response.LwM2mResponse; +import org.eclipse.leshan.core.response.ObserveResponse; +import org.eclipse.leshan.core.response.ReadResponse; +import org.eclipse.leshan.core.response.ResponseCallback; import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.eclipse.leshan.core.response.WriteResponse; import org.eclipse.leshan.core.util.Hex; @@ -65,13 +65,13 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_DISCOVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_ATTRIBUTES; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_UPDATE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_OBSERVE_CANCEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_ATTRIBUTES; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_UPDATE; @Slf4j @Service("LwM2MTransportRequest") @@ -153,11 +153,11 @@ public class LwM2MTransportRequest { case POST_TYPE_OPER_WRITE_REPLACE: // Request to write a String Single-Instance Resource using the TLV content format. if (contentFormat.equals(ContentFormat.TLV) && !resMultiple) { - request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType); + request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType, registration); } // Mode.REPLACE && Request to write a String Single-Instance Resource using the given content format (TEXT, TLV, JSON) else if (!contentFormat.equals(ContentFormat.TLV) && !resMultiple) { - request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType); + request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType, registration); } break; case PUT_TYPE_OPER_WRITE_UPDATE: @@ -212,8 +212,16 @@ public class LwM2MTransportRequest { break; default: } - if (request != null) + if (request != null) { this.sendRequest(lwServer, registration, request, lwM2MClient, timeoutInMs, isDelayedUpdate); + } + else if (request == null && isDelayedUpdate) { + String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg No SendRequest to Client", target); + service.sentLogsToThingsboard(msg, registration.getId()); + log.error("[{}] - [{}] No SendRequest", target); + this.handleResponseError(registration, target, lwM2MClient, isDelayedUpdate); + + } } } @@ -225,6 +233,7 @@ public class LwM2MTransportRequest { * @param lwM2MClient - * @param timeoutInMs - */ + private void sendRequest(LeshanServer lwServer, Registration registration, DownlinkRequest request, LwM2MClient lwM2MClient, long timeoutInMs, boolean isDelayedUpdate) { lwServer.send(registration, request, timeoutInMs, (ResponseCallback) response -> { if (isSuccess(((Response) response.getCoapResponse()).getCode())) { @@ -234,7 +243,6 @@ public class LwM2MTransportRequest { if (isDelayedUpdate) { delayedUpdateStr = " (delayedUpdate) "; } - String msg = String.format(LOG_LW2M_INFO + ": sendRequest Replace%s: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client", delayedUpdateStr, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(), ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue().toString()); @@ -243,20 +251,27 @@ public class LwM2MTransportRequest { ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue(), delayedUpdateStr); } } else { - String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", + String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); service.sentLogsToThingsboard(msg, registration.getId()); log.error("[{}] - [{}] [{}] error SendRequest", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); + if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) { + this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate); + } } + }, e -> { String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg error - %s SendRequest to Client", request.getPath().toString(), e.toString()); service.sentLogsToThingsboard(msg, registration.getId()); log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); + if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) { + this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate); + } }); } - private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, Integer resourceId, Object value, ResourceModel.Type type) { + private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, Integer resourceId, Object value, ResourceModel.Type type, Registration registration) { try { switch (type) { case STRING: // String @@ -270,7 +285,7 @@ public class LwM2MTransportRequest { case FLOAT: // Double return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Double.valueOf(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.valueOf(value.toString())); case TIME: // Date - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, new Date((Long) Integer.toUnsignedLong(Integer.valueOf(value.toString())))) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, new Date((Long) Integer.toUnsignedLong(Integer.valueOf(value.toString())))); + return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, new Date(Long.decode(value.toString()))) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, new Date((Long) Integer.toUnsignedLong(Integer.valueOf(value.toString())))); case OPAQUE: // byte[] value, base64 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())); default: @@ -278,6 +293,9 @@ public class LwM2MTransportRequest { return null; } catch (NumberFormatException e) { String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; + String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", + patn, type, value, e.toString()); + service.sentLogsToThingsboard(msg, registration.getId()); log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); return null; } @@ -296,6 +314,19 @@ public class LwM2MTransportRequest { }); } + private void handleResponseError(Registration registration, final String path, LwM2MClient lwM2MClient, boolean isDelayedUpdate) { + executorService.submit(new Runnable() { + @Override + public void run() { + try { + if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(path); + } catch (RuntimeException t) { + log.error("[{}] endpoint [{}] path [{}] error Unable to after send response.", registration.getEndpoint(), path, t.toString()); + } + } + }); + } + /** * processing a response from a client * @param registration - diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index b37d419987..06cf04a88a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -77,6 +77,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import java.util.stream.Collectors; +import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.CLIENT_NOT_AUTHORIZED; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEFAULT_TIMEOUT; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_REQUEST; @@ -330,7 +331,7 @@ public class LwM2MTransportService { } // #2.2 else { - lwM2MClient.onSuccessDelayedRequests(null); + lwM2MClient.onSuccessOrErrorDelayedRequests(null); } } } @@ -361,11 +362,15 @@ public class LwM2MTransportService { }); // #2.1 lwM2MClient.getDelayedRequests().forEach((k, v)->{ - this.putDelayedUpdateResourcesClient (lwM2MClient, lwM2MClient.getResourceValue(k), v.getKv().getStringV(), k); + List listV = new ArrayList(); + listV.add(v.getKv()); + this.putDelayedUpdateResourcesClient (lwM2MClient, lwM2MClient.getResourceValue(k), getJsonObject(listV).get(v.getKv().getKey()), k); System.out.printf(" k: %s, v: %s%n, v1: %s%n", k, v.getKv().getStringV(), lwM2MClient.getResourceValue(k)); }); lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId()); -// lwM2MClient.onSuccessDelayedRequests(); + if (lwM2MClient.getDelayedRequests().size() == 0) { + lwM2MClient.onSuccessOrErrorDelayedRequests(null); + } } } @@ -686,7 +691,7 @@ public class LwM2MTransportService { String value = de.getValue().getAsString(); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); - if (path != null && validatePathInAttrProfile(profile.getPostAttributeProfile(), path)) { + if (path != null && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) { if (lwM2MClient.getOperation(path).isWritable()) { lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(), @@ -714,7 +719,7 @@ public class LwM2MTransportService { * * @param sessionInfo - * @param name - - * @return + * @return true if path isPresent in postProfile */ private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) { String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name); @@ -722,15 +727,25 @@ public class LwM2MTransportService { } /** - * @param postAttributeProfile - + * @param profile - * @param path - * @return true if path isPresent in postAttributeProfile */ - private boolean validatePathInAttrProfile(JsonArray postAttributeProfile, String path) { - Set attributesSet = new Gson().fromJson(postAttributeProfile, Set.class); + private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) { + Set attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class); return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent(); } + /** + * @param profile - + * @param path - + * @return true if path isPresent in postAttributeProfile + */ + private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) { + Set telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), Set.class); + return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent(); + } + /** * Get path to resource from profile equal keyName @@ -786,8 +801,7 @@ public class LwM2MTransportService { } else { this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path); } - - if (isDelayedUpdate) lwM2MClient.onSuccessDelayedRequests (request.getPath().toString()); + if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(request.getPath().toString()); } /** 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 a28d7257d5..5fd63fa53f 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 @@ -114,7 +114,7 @@ public class LwM2MClient implements Cloneable { }); } - public void onSuccessDelayedRequests (String path) { + public void onSuccessOrErrorDelayedRequests(String path) { if (path != null) this.delayedRequests.remove(path); if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) { this.lwM2MTransportService.updatesAndSentModelParameter(this); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index a20f697bba..d745ebeb65 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -59,7 +59,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; -import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -426,6 +425,8 @@ public class JsonConverter { case MQTT_BASIC: result.add("credentialsValue", JSON_PARSER.parse(payload.getCredentialsValue()).getAsJsonObject()); break; + case LWM2M_CREDENTIALS: + break; } result.addProperty("credentialsType", payload.getCredentialsType().name()); result.addProperty("status", ProvisionResponseStatus.SUCCESS.name()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index ff1763f662..db123746ab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -21,7 +21,6 @@ import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Device; @@ -87,6 +86,9 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen case MQTT_BASIC: formatSimpleMqttCredentials(deviceCredentials); break; + case LWM2M_CREDENTIALS: + formatSimpleLwm2mCredentials(deviceCredentials); + break; } log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials); credentialsValidator.validate(deviceCredentials, id -> tenantId); @@ -129,6 +131,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen deviceCredentials.setCredentialsValue(JacksonUtil.toString(mqttCredentials)); } + private void formatCertData(DeviceCredentials deviceCredentials) { String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); String sha3Hash = EncryptionUtil.getSha3Hash(cert); @@ -136,6 +139,10 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen deviceCredentials.setCredentialsValue(cert); } + private void formatSimpleLwm2mCredentials(DeviceCredentials deviceCredentials) { + + } + @Override @CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, key = "'deviceCredentials_' + #deviceCredentials.credentialsId") public void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index a919f86dfe..9024ed0ee2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -512,6 +512,8 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe case X509_CERTIFICATE: deviceCredentials.setCredentialsValue(provisionRequest.getCredentialsData().getX509CertHash()); break; + case LWM2M_CREDENTIALS: + break; } try { deviceCredentialsService.updateDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); From 1150d4e4f30180ae2c54e3bdca5f36e525c2772a Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 22 Dec 2020 16:23:03 +0200 Subject: [PATCH 04/41] Lwm2m: backEnd: add saveDeviceWithCredential --- .../server/controller/DeviceController.java | 1 - .../controller/DeviceLwm2mController.java | 53 +++++- .../server/dao/device/DeviceService.java | 3 + .../server/common/data/Device.java | 12 +- .../lwm2m/server/LwM2MTransportHandler.java | 65 +++---- .../lwm2m/server/LwM2MTransportRequest.java | 34 ++-- .../lwm2m/server/LwM2MTransportService.java | 171 +++++++++++------- .../lwm2m/server/LwM2mServerListener.java | 11 +- .../lwm2m/server/client/LwM2MClient.java | 54 +++--- .../lwm2m/server/client/ModelObject.java | 6 +- .../server/dao/device/DeviceServiceImpl.java | 45 +++-- .../thingsboard/rest/client/RestClient.java | 19 ++ 12 files changed, 313 insertions(+), 161 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index e2db45884f..68a016bf4e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -278,7 +278,6 @@ public class DeviceController extends BaseController { try { Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS); DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials)); - //log.info("0 LwM2M CredentialsUpdate start) tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null); logEntityAction(device.getId(), device, device.getCustomerId(), diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java index 76915fbed1..c38a163af9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java @@ -15,17 +15,32 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.permission.Resource; import java.util.List; +import java.util.Map; @Slf4j @RestController @@ -72,4 +87,40 @@ public class DeviceLwm2mController extends BaseController { throw handleException(e); } } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/lwm2m/device-credentials", method = RequestMethod.POST) + @ResponseBody + public Device saveDeviceWithCredentials(@RequestBody (required=false) Map, Object> deviceWithDeviceCredentials) throws ThingsboardException { + ObjectMapper mapper = new ObjectMapper(); + Device device = checkNotNull(mapper.convertValue(deviceWithDeviceCredentials.get(Device.class), Device.class)); + DeviceCredentials credentials = checkNotNull(mapper.convertValue( deviceWithDeviceCredentials.get(DeviceCredentials.class), DeviceCredentials.class)); + try { + device.setTenantId(getCurrentUser().getTenantId()); + checkEntity(device.getId(), device, Resource.DEVICE); + Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials); + checkNotNull(savedDevice); + + tbClusterService.onDeviceChange(savedDevice, null); + tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), + savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); + tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), + device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + + logEntityAction(savedDevice.getId(), savedDevice, + savedDevice.getCustomerId(), + device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + + if (device.getId() == null) { + deviceStateService.onDeviceAdded(savedDevice); + } else { + deviceStateService.onDeviceUpdated(savedDevice); + } + return savedDevice; + } catch (Exception e) { + logEntityAction(emptyId(EntityType.DEVICE), device, + null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); + throw handleException(e); + } + } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index 715313538d..a54bb7af7e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import java.util.List; @@ -45,6 +46,8 @@ public interface DeviceService { Device saveDeviceWithAccessToken(Device device, String accessToken); + Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials); + Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId); Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index ca8e5f5575..64c608ecb8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.device.data.DeviceData; -import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -64,6 +63,17 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.setDeviceData(device.getDeviceData()); } + public Device updateDevice(Device device) { + this.tenantId = device.getTenantId(); + this.customerId = device.getCustomerId(); + this.name = device.getName(); + this.type = device.getType(); + this.label = device.getLabel(); + this.deviceProfileId = device.getDeviceProfileId(); + this.setDeviceData(device.getDeviceData()); + return this; + } + public TenantId getTenantId() { return tenantId; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java index 814cac6d9f..d37b429101 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java @@ -45,15 +45,9 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; import javax.annotation.PostConstruct; import java.io.File; import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Date; import java.util.LinkedList; -import java.util.List; -import java.util.Map; import java.util.Optional; @Slf4j @@ -127,14 +121,19 @@ public class LwM2MTransportHandler{ @PostConstruct public void init() { - LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service); - this.lhServerCert.getRegistrationService().addListener(lwM2mServerListener.registrationListener); - this.lhServerCert.getPresenceService().addListener(lwM2mServerListener.presenceListener); - this.lhServerCert.getObservationService().addListener(lwM2mServerListener.observationListener); - lwM2mServerListener = new LwM2mServerListener(lhServerNoSecPskRpk, service); - this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener); - this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener); - this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener); + try { + LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service); + this.lhServerCert.getRegistrationService().addListener(lwM2mServerListener.registrationListener); + this.lhServerCert.getPresenceService().addListener(lwM2mServerListener.presenceListener); + this.lhServerCert.getObservationService().addListener(lwM2mServerListener.observationListener); + lwM2mServerListener = new LwM2mServerListener(lhServerNoSecPskRpk, service); + this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener); + this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener); + this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener); + } + catch (java.lang.NullPointerException e) { + log.error("init [{}]", e.toString()); + } } public static NetworkConfig getCoapConfig() { @@ -150,7 +149,8 @@ public class LwM2MTransportHandler{ return coapConfig; } - public static String getValueTypeToString (Object value, ResourceModel.Type type) { + public static String getValueTypeToString (Object value, ResourceModel.Type type, int val) { + try{ switch (type) { case STRING: // String case OBJLNK: // ObjectLink @@ -160,16 +160,21 @@ public class LwM2MTransportHandler{ case BOOLEAN: // Boolean return Boolean.toString((Boolean) value); case FLOAT: // Double - return Double.toString((Float)value); + return Double.toString((Double) value); case TIME: // Date - String DATE_FORMAT = "MMM d, yyyy HH:mm a"; - DateFormat formatter = new SimpleDateFormat(DATE_FORMAT); - return formatter.format(new Date(Integer.toUnsignedLong((Integer) value))); + return Long.toString(((Date) value).getTime()); +// String DATE_FORMAT = "MMM d, yyyy HH:mm a"; +// DateFormat formatter = new SimpleDateFormat(DATE_FORMAT); +// return formatter.format(new Date(Integer.toUnsignedLong((Integer) value))); case OPAQUE: // byte[] value, base64 return Hex.encodeHexString((byte[])value); default: return null; } + } catch (Exception e) { + log.error(e.getStackTrace().toString()); + return null; + } } public static LwM2mNode getLvM2mNodeToObject(LwM2mNode content) { @@ -305,28 +310,6 @@ public class LwM2MTransportHandler{ } } - /** - * Equals to Map for values - * @param map1 - - * @param map2 - - * @param - - * @return - true if equals - */ - public static > boolean mapsEquals(Map map1, Map map2) { - List values1 = new ArrayList<>(map1.values()); - List values2 = new ArrayList<>(map2.values()); - Collections.sort(values1); - Collections.sort(values2); - return values1.equals(values2); - } - - public static String convertCamelCase (String str) { - str = str.toLowerCase().replace("/[^a-z ]+/g", " "); - str = str.replace("/^(.)|\\s(.)/g", "$1"); - str = str.replace("/[^a-zA-Z]+/g", ""); - return str; - } - public static String splitCamelCaseString(String s){ LinkedList linkedListOut = new LinkedList<>(); LinkedList linkedList = new LinkedList((Arrays.asList(s.split(" ")))); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index 40edf09986..576415da31 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -20,6 +20,7 @@ import org.eclipse.californium.core.coap.Response; import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.model.ResourceModel; +import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.ObjectLink; import org.eclipse.leshan.core.observation.Observation; @@ -50,6 +51,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; import java.util.ArrayList; @@ -79,16 +81,17 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle public class LwM2MTransportRequest { private final ExecutorService executorService; private static final String RESPONSE_CHANNEL = "THINGSBOARD_RESP"; + private final LwM2mValueConverterImpl converter; @Autowired LwM2MTransportService service; public LwM2MTransportRequest() { + this.converter = new LwM2mValueConverterImpl(); executorService = Executors.newCachedThreadPool( new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); } - @PostConstruct public void init() { } @@ -115,12 +118,12 @@ public class LwM2MTransportRequest { */ public void sendAllRequest(LeshanServer lwServer, Registration registration, String target, String typeOper, String contentFormatParam, LwM2MClient lwM2MClient, Observation observation, Object params, long timeoutInMs, boolean isDelayedUpdate) { - ResultIds resultIds = new ResultIds(target); + LwM2mPath resultIds = new LwM2mPath(target); if (registration != null && resultIds.getObjectId() >= 0) { DownlinkRequest request = null; ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null; - ResourceModel resource = (resultIds.resourceId >= 0) ? (lwM2MClient != null) ? - lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.resourceId) : null : null; + ResourceModel resource = (resultIds.getResourceId() !=null && lwM2MClient != null) ? + lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()) : null; ResourceModel.Type resType = (resource == null) ? null : resource.type; boolean resMultiple = (resource == null) ? false : resource.multiple; timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; @@ -132,10 +135,10 @@ public class LwM2MTransportRequest { request = new DiscoverRequest(target); break; case GET_TYPE_OPER_OBSERVE: - if (resultIds.getResourceId() >= 0) { - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId()); - } else if (resultIds.getInstanceId() >= 0) { - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getInstanceId()); + if (resultIds.isResource()) { + request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); + } else if (resultIds.isObjectInstance()) { + request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId()); } else if (resultIds.getObjectId() >= 0) { request = new ObserveRequest(resultIds.getObjectId()); } @@ -145,7 +148,8 @@ public class LwM2MTransportRequest { break; case POST_TYPE_OPER_EXECUTE: if (params != null && !resMultiple) { - request = new ExecuteRequest(target, LwM2MTransportHandler.getValueTypeToString(params, resType)); +// request = new ExecuteRequest(target, LwM2MTransportHandler.getValueTypeToString(params, resType)); + request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resType, ResourceModel.Type.STRING, resultIds)); } else { request = new ExecuteRequest(target); } @@ -153,11 +157,11 @@ public class LwM2MTransportRequest { case POST_TYPE_OPER_WRITE_REPLACE: // Request to write a String Single-Instance Resource using the TLV content format. if (contentFormat.equals(ContentFormat.TLV) && !resMultiple) { - request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType, registration); + request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resType, registration); } // Mode.REPLACE && Request to write a String Single-Instance Resource using the given content format (TEXT, TLV, JSON) else if (!contentFormat.equals(ContentFormat.TLV) && !resMultiple) { - request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType, registration); + request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resType, registration); } break; case PUT_TYPE_OPER_WRITE_UPDATE: @@ -202,10 +206,10 @@ public class LwM2MTransportRequest { Attribute pmin = new Attribute(MINIMUM_PERIOD, Integer.toUnsignedLong(Integer.valueOf("1"))); Attribute[] attrs = {pmin}; AttributeSet attrSet = new AttributeSet(attrs); - if (resultIds.getResourceId() >= 0) { - request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), attrSet); - } else if (resultIds.getInstanceId() >= 0) { - request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getInstanceId(), attrSet); + if (resultIds.isResource()) { + request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), attrSet); + } else if (resultIds.isObjectInstance()) { + request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), attrSet); } else if (resultIds.getObjectId() >= 0) { request = new WriteAttributesRequest(resultIds.getObjectId(), attrSet); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index 06cf04a88a..861afa02a8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -64,7 +64,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; -import java.util.Objects; import java.util.Optional; import java.util.Random; import java.util.Set; @@ -186,9 +185,9 @@ public class LwM2MTransportService { if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) { this.syncSessionsAndProfiles(); } - log.info("Client: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); + log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { - log.error("Client: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); + log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } } @@ -242,15 +241,15 @@ public class LwM2MTransportService { private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) { // #1 Arrays.stream(registration.getObjectLinks()).forEach(url -> { - ResultIds pathIds = new ResultIds(url.getUrl()); - if (pathIds.instanceId > -1 && pathIds.resourceId == -1) { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource()) { lwM2MClient.getPendingRequests().add(url.getUrl()); } }); // #2 Arrays.stream(registration.getObjectLinks()).forEach(url -> { - ResultIds pathIds = new ResultIds(url.getUrl()); - if (pathIds.instanceId > -1 && pathIds.resourceId == -1) { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource()) { lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); } @@ -361,10 +360,10 @@ public class LwM2MTransportService { } }); // #2.1 - lwM2MClient.getDelayedRequests().forEach((k, v)->{ + lwM2MClient.getDelayedRequests().forEach((k, v) -> { List listV = new ArrayList(); listV.add(v.getKv()); - this.putDelayedUpdateResourcesClient (lwM2MClient, lwM2MClient.getResourceValue(k), getJsonObject(listV).get(v.getKv().getKey()), k); + this.putDelayedUpdateResourcesClient(lwM2MClient, lwM2MClient.getResourceValue(k), getJsonObject(listV).get(v.getKv().getKey()), k); System.out.printf(" k: %s, v: %s%n, v1: %s%n", k, v.getKv().getStringV(), lwM2MClient.getResourceValue(k)); }); lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId()); @@ -374,7 +373,7 @@ public class LwM2MTransportService { } } - private void putDelayedUpdateResourcesClient (LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path){ + private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) { if (!valueOld.toString().equals(valueNew.toString())) { lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(), @@ -465,18 +464,20 @@ public class LwM2MTransportService { private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set path) { AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid()); attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> { - ResultIds pathIds = new ResultIds(p.getAsString().toString()); - if (pathIds.getResourceId() > -1) { +// ResultIds pathIds = new ResultIds(p.getAsString().toString()); + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); + if (pathIds.isResource()) { if (path == null || path.contains(p.getAsString())) { - this.addParameters(p.getAsString().toString(), attributes, registration); + this.addParameters(p.getAsString().toString(), attributes, registration, "attributes"); } } }); attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> { - ResultIds pathIds = new ResultIds(p.getAsString().toString()); - if (pathIds.getResourceId() > -1) { +// ResultIds pathIds = new ResultIds(p.getAsString().toString()); + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); + if (pathIds.isResource()) { if (path == null || path.contains(p.getAsString())) { - this.addParameters(p.getAsString().toString(), telemetry, registration); + this.addParameters(p.getAsString().toString(), telemetry, registration, "telemetry"); } } }); @@ -486,13 +487,19 @@ public class LwM2MTransportService { * @param parameters - JsonObject attributes/telemetry * @param registration - Registration LwM2M Client */ - private void addParameters(String path, JsonObject parameters, Registration registration) { + private void addParameters(String path, JsonObject parameters, Registration registration, String nameParam) { JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid()).getPostKeyNameProfile(); String resName = String.valueOf(names.get(path)); if (resName != null && !resName.isEmpty()) { - String resValue = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path); - if (resValue != null) { - parameters.addProperty(resName, resValue); + String resValue = null; + try { + resValue = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path); + if (resValue != null) { +// log.info("addParameters Path: [{}] ResValue : [{}] nameParam [{}]", path, lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path), nameParam); + parameters.addProperty(resName, resValue); + } + } catch (Exception e) { + log.error(e.getStackTrace().toString()); } } } @@ -629,42 +636,73 @@ public class LwM2MTransportService { * @param path - resource */ private void onObservationSetResourcesValue(Registration registration, Object value, Map values, String path) { - ResultIds resultIds = new ResultIds(path); - // #1 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); - ModelObject modelObject = lwM2MClient.getModelObjects().get(resultIds.getObjectId()); - Map instancesModelObject = modelObject.getInstances(); - LwM2mObjectInstance instanceOld = (instancesModelObject.get(resultIds.instanceId) != null) ? instancesModelObject.get(resultIds.instanceId) : null; - Map resourcesOld = (instanceOld != null) ? instanceOld.getResources() : null; - LwM2mResource resourceOld = (resourcesOld != null && resourcesOld.get(resultIds.getResourceId()) != null) ? resourcesOld.get(resultIds.getResourceId()) : null; - // #2 - LwM2mResource resourceNew; - if (Objects.requireNonNull(resourceOld).isMultiInstances()) { - resourceNew = LwM2mMultipleResource.newResource(resultIds.getResourceId(), values, resourceOld.getType()); - } else { - resourceNew = LwM2mSingleResource.newResource(resultIds.getResourceId(), value, resourceOld.getType()); - } - //#3 - Map resourcesNew = new HashMap<>(resourcesOld); - // #4 - resourcesNew.remove(resourceOld); - // #5 - resourcesNew.put(resultIds.getResourceId(), resourceNew); - // #6 - LwM2mObjectInstance instanceNew = new LwM2mObjectInstance(resultIds.instanceId, resourcesNew.values()); - // #7 - CountDownLatch respLatch = new CountDownLatch(1); - lwM2MClient.getModelObjects().get(resultIds.getObjectId()).removeInstance(resultIds.instanceId); - instancesModelObject.put(resultIds.instanceId, instanceNew); - respLatch.countDown(); try { - respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException ex) { - ex.printStackTrace(); + CountDownLatch respLatch = new CountDownLatch(1); + try { + // #1 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); + LwM2mPath resultIds = new LwM2mPath(path); + log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), resultIds, value, values); + ResourceModel.Type resType = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).type; + Map instancesModelObject = (lwM2MClient.getModelObjects().get(resultIds.getObjectId()) != null) ? lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances() : null; + Map resourcesOld = null; + try { + CountDownLatch respResLatch = new CountDownLatch(1); + try { + resourcesOld = (instancesModelObject != null && + instancesModelObject.get(resultIds.getObjectInstanceId()) != null && + instancesModelObject.get(resultIds.getObjectInstanceId()).getResources() != null) ? instancesModelObject.get(resultIds.getObjectInstanceId()).getResources() : null; + + } finally { + respResLatch.countDown(); + } + try { + respResLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + ex.printStackTrace(); + log.error("#1_2 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value); + } + } catch (Exception e) { + e.printStackTrace(); + log.error("#1_2_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value); + } + LwM2mResource resourceOld = (resourcesOld != null && resourcesOld.get(resultIds.getResourceId()) != null) ? resourcesOld.get(resultIds.getResourceId()) : null; + // #2 + LwM2mResource resourceNew = null; + if ((resourceOld != null && resourceOld.isMultiInstances() && !resourceOld.getValues().equals(values)) || + (resourceOld == null && value == null)) { + resourceNew = LwM2mMultipleResource.newResource(resultIds.getResourceId(), values, resType); + } else if ((resourceOld != null && !resourceOld.isMultiInstances() && !resourceOld.getValue().equals(values)) || + (resourceOld == null && value != null)) { + resourceNew = LwM2mSingleResource.newResource(resultIds.getResourceId(), value, resType); + } + if (resourceNew != null) { + //#3 + Map resourcesNew = (resourcesOld == null) ? new HashMap<>() : new HashMap<>(resourcesOld); + // #4 + if ((resourceOld != null)) resourcesNew.remove(resourceOld); + // #5 + resourcesNew.put(resultIds.getResourceId(), resourceNew); + // #6 + LwM2mObjectInstance instanceNew = new LwM2mObjectInstance(resultIds.getObjectInstanceId(), resourcesNew.values()); + // #7 + lwM2MClient.getModelObjects().get(resultIds.getObjectId()).removeInstance(resultIds.getObjectInstanceId()); + instancesModelObject.put(resultIds.getObjectInstanceId(), instanceNew); + Set paths = new HashSet<>(); + paths.add(path); + this.updateAttrTelemetry(registration, false, paths); + } + } finally { + respLatch.countDown(); + } + try { + respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + ex.printStackTrace(); + log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value); + } + } catch (Exception ignored) { } - Set paths = new HashSet<>(); - paths.add(path); - this.updateAttrTelemetry(registration, false, paths); } /** @@ -696,7 +734,7 @@ public class LwM2MTransportService { lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(), false); - log.info("[{}] path onAttributeUpdate", path); +// log.info("[{}] path onAttributeUpdate", path); } else { log.error(LOG_LW2M_ERROR + ": Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value); String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value); @@ -728,7 +766,7 @@ public class LwM2MTransportService { /** * @param profile - - * @param path - + * @param path - * @return true if path isPresent in postAttributeProfile */ private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) { @@ -738,7 +776,7 @@ public class LwM2MTransportService { /** * @param profile - - * @param path - + * @param path - * @return true if path isPresent in postAttributeProfile */ private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) { @@ -793,9 +831,10 @@ public class LwM2MTransportService { * @param request - */ public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) { - ResultIds resultIds = new ResultIds(path); +// ResultIds resultIds = new ResultIds(path); + LwM2mPath resultIds = new LwM2mPath(path); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); - LwM2mResource resource = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances().get(resultIds.getInstanceId()).getResource(resultIds.getResourceId()); + LwM2mResource resource = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances().get(resultIds.getObjectInstanceId()).getResource(resultIds.getResourceId()); if (resource.isMultiInstances()) { this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path); } else { @@ -996,9 +1035,10 @@ public class LwM2MTransportService { */ private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient, Set targets, String typeOper) { targets.forEach(target -> { - ResultIds pathIds = new ResultIds(target); - if (pathIds.resourceId >= 0 && lwM2MClient.getModelObjects().get(pathIds.getObjectId()) - .getInstances().get(pathIds.getInstanceId()).getResource(pathIds.getResourceId()).getValue() != null) { +// ResultIds pathIds = new ResultIds(target); + LwM2mPath pathIds = new LwM2mPath(target); + if (pathIds.isResource() && lwM2MClient.getModelObjects().get(pathIds.getObjectId()) + .getInstances().get(pathIds.getObjectInstanceId()).getResource(pathIds.getResourceId()).getValue() != null) { if (GET_TYPE_OPER_READ.equals(typeOper)) { lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(), @@ -1024,9 +1064,10 @@ public class LwM2MTransportService { private ResourceValue getResourceValue(LwM2MClient lwM2MClient, String path) { ResourceValue resourceValue = null; - ResultIds pathIds = new ResultIds(path); - if (pathIds.getResourceId() > -1) { - LwM2mResource resource = lwM2MClient.getModelObjects().get(pathIds.getObjectId()).getInstances().get(pathIds.getInstanceId()).getResource(pathIds.getResourceId()); +// ResultIds pathIds = new ResultIds(path); + LwM2mPath pathIds = new LwM2mPath(path); + if (pathIds.isResource()) { + LwM2mResource resource = lwM2MClient.getModelObjects().get(pathIds.getObjectId()).getInstances().get(pathIds.getObjectInstanceId()).getResource(pathIds.getResourceId()); if (resource.isMultiInstances()) { if (resource.getValues().size() > 0) { resourceValue = new ResourceValue(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 4d63da3580..ea440a33d0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -44,7 +44,6 @@ public class LwM2mServerListener { @Override public void registered(Registration registration, Registration previousReg, Collection previousObsersations) { - service.onRegistered(lhServer, registration, previousObsersations); } @@ -54,6 +53,7 @@ public class LwM2mServerListener { @Override public void updated(RegistrationUpdate update, Registration updatedRegistration, Registration previousRegistration) { + log.info("updated"); service.updatedReg(lhServer, updatedRegistration); } @@ -63,6 +63,7 @@ public class LwM2mServerListener { @Override public void unregistered(Registration registration, Collection observations, boolean expired, Registration newReg) { + log.info("unregistered"); service.unReg(registration, observations); } @@ -71,11 +72,13 @@ public class LwM2mServerListener { public final PresenceListener presenceListener = new PresenceListener() { @Override public void onSleeping(Registration registration) { + log.info("onSleeping"); service.onSleepingDev(registration); } @Override public void onAwake(Registration registration) { + log.info("onAwake"); service.onAwakeDev(registration); } }; @@ -92,8 +95,10 @@ public class LwM2mServerListener { if (registration != null) { try { service.onObservationResponse(registration, observation.getPath().toString(), response); - } catch (java.lang.NullPointerException e) { - log.error(e.toString()); + } catch (Exception e) { + e.printStackTrace(); + log.error("onResponse"); + } } } 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 5fd63fa53f..4c0f314bc1 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 @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.response.LwM2mResponse; import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.util.Hex; @@ -29,12 +30,11 @@ import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportService; -import org.thingsboard.server.transport.lwm2m.server.ResultIds; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; -import java.util.UUID; import java.util.Map; import java.util.Set; -import java.util.Collection; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -61,6 +61,7 @@ public class LwM2MClient implements Cloneable { private Map delayedRequests; private Set delayedRequestsId; private Map responses; + private final LwM2mValueConverterImpl converter; public Object clone() throws CloneNotSupportedException { return super.clone(); @@ -81,11 +82,13 @@ public class LwM2MClient implements Cloneable { * Key , response instance -> resources: value...> */ this.responses = new ConcurrentHashMap<>(); + this.converter = new LwM2mValueConverterImpl(); } /** - * Fill with data -> Model client - * @param path - + * Fill with data -> Model client + * + * @param path - * @param response - */ public void onSuccessHandler(String path, LwM2mResponse response) { @@ -99,9 +102,9 @@ public class LwM2MClient implements Cloneable { private void initValue() { this.responses.forEach((key, resp) -> { - ResultIds pathIds = new ResultIds(key); - if (pathIds.getObjectId() > -1) { - ObjectModel objectModel = ((Collection) this.lwServer.getModelProvider().getObjectModel(registration).getObjectModels()).stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0); + LwM2mPath pathIds = new LwM2mPath(key); + if (pathIds.isObject() || pathIds.isObjectInstance() || pathIds.isResource()) { + ObjectModel objectModel = this.lwServer.getModelProvider().getObjectModel(registration).getObjectModels().stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0); if (this.modelObjects.get(pathIds.getObjectId()) != null) { this.modelObjects.get(pathIds.getObjectId()).getInstances().put(((ReadResponse) resp).getContent().getId(), (LwM2mObjectInstance) ((ReadResponse) resp).getContent()); } else { @@ -122,30 +125,39 @@ public class LwM2MClient implements Cloneable { } public ResourceModel.Operations getOperation(String path) { - ResultIds resultIds = new ResultIds(path); - return (this.getModelObjects().get(resultIds.getObjectId()) != null) ? this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).operations : ResourceModel.Operations.NONE; + LwM2mPath resultIds = new LwM2mPath(path); + return (this.getModelObjects().get(resultIds.getObjectId()) != null) ? + this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).operations : + ResourceModel.Operations.NONE; } - public String getResourceName (String path) { - ResultIds resultIds = new ResultIds(path); + public String getResourceName(String path) { + LwM2mPath resultIds = new LwM2mPath(path); return (this.getModelObjects().get(resultIds.getObjectId()) != null) ? this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).name : ""; } /** * @param path - path resource * @return - value of Resource or null - */public String getResourceValue(String path) { + */ + public String getResourceValue(String path) { String resValue = null; - ResultIds pathIds = new ResultIds(path); + LwM2mPath pathIds = new LwM2mPath(path); ModelObject modelObject = this.getModelObjects().get(pathIds.getObjectId()); - if (modelObject != null && modelObject.getInstances().get(pathIds.getInstanceId()) != null) { - LwM2mObjectInstance instance = modelObject.getInstances().get(pathIds.getInstanceId()); + + if (modelObject != null && modelObject.getInstances().get(pathIds.getObjectInstanceId()) != null) { + LwM2mObjectInstance instance = modelObject.getInstances().get(pathIds.getObjectInstanceId()); if (instance.getResource(pathIds.getResourceId()) != null) { - resValue = instance.getResource(pathIds.getResourceId()).getType() == OPAQUE ? - Hex.encodeHexString((byte[]) instance.getResource(pathIds.getResourceId()).getValue()).toLowerCase() : - (instance.getResource(pathIds.getResourceId()).isMultiInstances()) ? - instance.getResource(pathIds.getResourceId()).getValues().toString() : - instance.getResource(pathIds.getResourceId()).getValue().toString(); + try { + resValue = instance.getResource(pathIds.getResourceId()).getType() == OPAQUE ? + Hex.encodeHexString((byte[]) instance.getResource(pathIds.getResourceId()).getValue()).toLowerCase() : + (instance.getResource(pathIds.getResourceId()).isMultiInstances()) ? + instance.getResource(pathIds.getResourceId()).getValues().toString() : +// getValueTypeToString(instance.getResource(pathIds.getResourceId()).getValue(), instance.getResource(pathIds.getResourceId()).getType()); + (String) converter.convertValue(instance.getResource(pathIds.getResourceId()).getValue(), instance.getResource(pathIds.getResourceId()).getType(), ResourceModel.Type.STRING, pathIds); + } catch (Exception e) { + log.warn("getResourceValue [{}]", e.getStackTrace().toString()); + } } } return resValue; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ModelObject.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ModelObject.java index 03dcc5013f..fb0ce90e78 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ModelObject.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ModelObject.java @@ -21,7 +21,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance; import java.util.Map; @Data -public class ModelObject { +public class ModelObject implements Cloneable { /** * model one on all instance * for each instance only id resource with parameters of resources (observe, attr, telemetry) @@ -38,4 +38,8 @@ public class ModelObject { LwM2mObjectInstance instance = this.instances.get(id); return this.instances.remove(id, instance); } + + public ModelObject clone() throws CloneNotSupportedException { + return (ModelObject) super.clone(); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 9024ed0ee2..a35691160c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -177,10 +177,40 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe return doSaveDevice(device, null); } + @CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}") + @Override + public Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials) { + if (device.getId() == null) { + Device deviceWithName = this.findDeviceByTenantIdAndName(device.getTenantId(), device.getName()); + device = deviceWithName == null ? device : deviceWithName.updateDevice(device); + } + Device savedDevice = this.saveDeviceWithoutCredentials(device); + deviceCredentials.setDeviceId(savedDevice.getId()); + if (device.getId() == null) { + deviceCredentials = deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); + } + else { + deviceCredentials.setId(deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), savedDevice.getId()).getId()); + deviceCredentials = deviceCredentialsService.updateDeviceCredentials(device.getTenantId(), deviceCredentials); + } + return savedDevice; + } + private Device doSaveDevice(Device device, String accessToken) { + Device savedDevice = this.saveDeviceWithoutCredentials(device); + if (device.getId() == null) { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId())); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + deviceCredentials.setCredentialsId(!StringUtils.isEmpty(accessToken) ? accessToken : RandomStringUtils.randomAlphanumeric(20)); + deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials); + } + return savedDevice; + } + + private Device saveDeviceWithoutCredentials(Device device) { log.trace("Executing saveDevice [{}]", device); deviceValidator.validate(device, Device::getTenantId); - Device savedDevice; try { DeviceProfile deviceProfile; if (device.getDeviceProfileId() == null) { @@ -198,8 +228,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe } device.setType(deviceProfile.getName()); device.setDeviceData(syncDeviceData(deviceProfile, device.getDeviceData())); - - savedDevice = deviceDao.save(device.getTenantId(), device); + return deviceDao.save(device.getTenantId(), device); } catch (Exception t) { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_name_unq_key")) { @@ -208,14 +237,6 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe throw t; } } - if (device.getId() == null) { - DeviceCredentials deviceCredentials = new DeviceCredentials(); - deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId())); - deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); - deviceCredentials.setCredentialsId(!StringUtils.isEmpty(accessToken) ? accessToken : RandomStringUtils.randomAlphanumeric(20)); - deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials); - } - return savedDevice; } private DeviceData syncDeviceData(DeviceProfile deviceProfile, DeviceData deviceData) { @@ -530,7 +551,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Override protected void validateCreate(TenantId tenantId, Device device) { DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); long maxDevices = profileConfiguration.getMaxDevices(); validateNumberOfEntitiesPerTenant(tenantId, deviceDao, maxDevices, EntityType.DEVICE); } diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index e79bb2b713..17fd9c83c5 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -121,6 +121,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -1091,6 +1092,24 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { return restTemplate.postForEntity(baseURL + "/api/device/credentials", deviceCredentials, DeviceCredentials.class).getBody(); } + public Optional saveDeviceWithCredentials(Device device, DeviceCredentials credentials) { + try { + Map, Object> deviceCredentials = new ConcurrentHashMap<>(); + deviceCredentials.put(Device.class, device); + deviceCredentials.put(DeviceCredentials.class, credentials); +// return restTemplate.postForEntity(baseURL + "/api/lwm2m/device-credentials", deviceCredentials, Device.class).getBody(); + ResponseEntity deviceOpt = restTemplate.postForEntity(baseURL + "/api/lwm2m/device-credentials", deviceCredentials, Device.class); + return Optional.ofNullable(deviceOpt.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getTenantDevices(String type, PageLink pageLink) { Map params = new HashMap<>(); params.put("type", type); From 5eeb12ea13a26c96cd6ac58e32cfc2f3dd9dc865 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 22 Dec 2020 16:24:16 +0200 Subject: [PATCH 05/41] Lwm2m: backEnd: add saveDeviceWithCredential --- application/src/main/resources/thingsboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 5dfbdbebd8..b5fe6b29cb 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -433,7 +433,7 @@ spring: database-platform: "${SPRING_JPA_DATABASE_PLATFORM:org.hibernate.dialect.PostgreSQLDialect}" datasource: driverClassName: "${SPRING_DRIVER_CLASS_NAME:org.postgresql.Driver}" - url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard_ce_3_2_2}" + url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard_ce_3_3_test}" username: "${SPRING_DATASOURCE_USERNAME:postgres}" password: "${SPRING_DATASOURCE_PASSWORD:postgres}" hikari: From 9df5be790ac109bb3adb5e3aab1c51b32e57fbb8 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 25 Dec 2020 21:35:13 +0200 Subject: [PATCH 06/41] Lwm2m: backEnd: add ExecutorService to LwM2MTransportService --- .../lwm2m/server/LwM2MTransportHandler.java | 2 + .../lwm2m/server/LwM2MTransportRequest.java | 43 +++-- .../lwm2m/server/LwM2MTransportService.java | 157 +++++++++++------- .../transport/lwm2m/server/ResultIds.java | 38 ----- .../secure/LwM2mInMemorySecurityStore.java | 6 +- .../common/transport/TransportContext.java | 2 + 6 files changed, 126 insertions(+), 122 deletions(-) delete mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ResultIds.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java index d37b429101..f29649de7d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java @@ -107,6 +107,8 @@ public class LwM2MTransportHandler{ public static final String PUT_TYPE_OPER_WRITE_ATTRIBUTES = "wright-attributes"; public static final String EVENT_AWAKE = "AWAKE"; + public static final String SERVICE_CHANNEL = "SERVICE"; + public static final String RESPONSE_CHANNEL = "RESP"; @Autowired @Qualifier("LeshanServerCert") diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index 576415da31..2c7721089c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -74,26 +74,27 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_ATTRIBUTES; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_UPDATE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.RESPONSE_CHANNEL; @Slf4j @Service("LwM2MTransportRequest") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportRequest { - private final ExecutorService executorService; - private static final String RESPONSE_CHANNEL = "THINGSBOARD_RESP"; - private final LwM2mValueConverterImpl converter; + private ExecutorService executorResponse; + private ExecutorService executorResponseError; + private LwM2mValueConverterImpl converter; @Autowired LwM2MTransportService service; - public LwM2MTransportRequest() { - this.converter = new LwM2mValueConverterImpl(); - executorService = Executors.newCachedThreadPool( - new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); - } @PostConstruct public void init() { + this.converter = new LwM2mValueConverterImpl(); + executorResponse = Executors.newCachedThreadPool( + new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); + executorResponseError = Executors.newCachedThreadPool( + new NamedThreadFactory(String.format("LwM2M %s channel response Error", RESPONSE_CHANNEL))); } public Collection doGetRegistrations(LeshanServer lwServer) { @@ -306,27 +307,21 @@ public class LwM2MTransportRequest { } private void handleResponse(Registration registration, final String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient, boolean isDelayedUpdate) { - executorService.submit(new Runnable() { - @Override - public void run() { - try { - sendResponse(registration, path, response, request, lwM2MClient, isDelayedUpdate); - } catch (RuntimeException t) { - log.error("[{}] endpoint [{}] path [{}] error Unable to after send response.", registration.getEndpoint(), path, t.toString()); - } + executorResponse.submit(() -> { + try { + sendResponse(registration, path, response, request, lwM2MClient, isDelayedUpdate); + } catch (Exception e) { + log.error("[{}] endpoint [{}] path [{}] error Unable to after send response.", registration.getEndpoint(), path, e); } }); } private void handleResponseError(Registration registration, final String path, LwM2MClient lwM2MClient, boolean isDelayedUpdate) { - executorService.submit(new Runnable() { - @Override - public void run() { - try { - if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(path); - } catch (RuntimeException t) { - log.error("[{}] endpoint [{}] path [{}] error Unable to after send response.", registration.getEndpoint(), path, t.toString()); - } + executorResponseError.submit(() -> { + try { + if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(path); + } catch (RuntimeException t) { + log.error("[{}] endpoint [{}] path [{}] error Unable to after send response.", registration.getEndpoint(), path, t.toString()); } }); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index 861afa02a8..46282eed07 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -32,6 +32,7 @@ import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.ReadResponse; +import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.registration.Registration; import org.springframework.beans.factory.annotation.Autowired; @@ -71,6 +72,8 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -89,6 +92,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVICE_CHANNEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getAckCallback; @Slf4j @@ -96,6 +100,10 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportService { + private ExecutorService executorRegistered; + private ExecutorService executorUpdateRegistered; + private ExecutorService executorUnRegistered; + @Autowired private TransportService transportService; @@ -111,6 +119,12 @@ public class LwM2MTransportService { @PostConstruct public void init() { context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); + executorRegistered = Executors.newCachedThreadPool( + new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); + executorUpdateRegistered = Executors.newCachedThreadPool( + new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); + executorUnRegistered = Executors.newCachedThreadPool( + new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); } /** @@ -129,28 +143,35 @@ public class LwM2MTransportService { * @param previousObsersations - may be null */ public void onRegistered(LeshanServer lwServer, Registration registration, Collection previousObsersations) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration); - if (lwM2MClient != null) { - lwM2MClient.setLwM2MTransportService(this); - lwM2MClient.setLwM2MTransportService(this); - lwM2MClient.setSessionUuid(UUID.randomUUID()); - this.setLwM2MClient(lwServer, registration, lwM2MClient); - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId()); - if (sessionInfo != null) { - lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); - lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); - lwM2MClient.setDeviceName(sessionInfo.getDeviceName()); - lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType()); - transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); - transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client registration", registration.getId()); - } else { - log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); + executorRegistered.submit(() -> { + try { + log.info("[{}] Client: onRegistered name ", registration.getEndpoint()); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration); + if (lwM2MClient != null) { + lwM2MClient.setLwM2MTransportService(this); + lwM2MClient.setLwM2MTransportService(this); + lwM2MClient.setSessionUuid(UUID.randomUUID()); + this.setLwM2MClient(lwServer, registration, lwM2MClient); + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId()); + if (sessionInfo != null) { + lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); + lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); + lwM2MClient.setDeviceName(sessionInfo.getDeviceName()); + lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType()); + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client registration", registration.getId()); + } else { + log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); + } + } else { + log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); + } + } catch (Throwable t) { + log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t); } - } else { - log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); - } + }); } /** @@ -158,12 +179,18 @@ public class LwM2MTransportService { * @param registration - Registration LwM2M Client */ public void updatedReg(LeshanServer lwServer, Registration registration) { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId()); - if (sessionInfo != null) { - log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); - } else { - log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); - } + executorUpdateRegistered.submit(() -> { + try { + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId()); + if (sessionInfo != null) { + log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); + } else { + log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); + } + } catch (Throwable t) { + log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); + } + }); } /** @@ -172,8 +199,14 @@ public class LwM2MTransportService { * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect */ public void unReg(Registration registration, Collection observations) { - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); - this.closeClientSession(registration); + executorUpdateRegistered.submit(() -> { + try { + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); + this.closeClientSession(registration); + } catch (Throwable t) { + log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); + } + }); } private void closeClientSession(Registration registration) { @@ -848,14 +881,12 @@ public class LwM2MTransportService { * @param deviceProfile - */ public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) { - String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet() + Set registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet() .stream() - .filter(e -> e.getValue().getDeviceUuid().equals(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()))) - .findFirst() - .map(Map.Entry::getKey) - .orElse(""); - if (!registrationId.isEmpty()) { - this.onDeviceUpdateChangeProfile(registrationId, deviceProfile); + .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId())) + .map(Map.Entry::getKey).sorted().collect(Collectors.toSet()); + if (registrationIds.size() > 0) { + this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile); } } @@ -883,19 +914,23 @@ public class LwM2MTransportService { LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId); lwM2MClient.setDeviceName(device.getName()); if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) { - deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationId, deviceProfile)); + Set registrationIds = new HashSet<>(); + registrationIds.add(registrationId); + deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile)); } + + lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId()); } /** * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName) - * #2 Update in lwM2MClient: ...Profile + * #2 Update in lwM2MClient: ...Profile if changes from update device * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName) * #3.1 Attribute isChange (add&del) * #3.2 Telemetry isChange (add&del) * #3.3 KeyName isChange (add) * #4 update - * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value ti thingsboard + * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value to thingsboard * #4.2 del * -- if add attributes includes del telemetry - result del for observe * #5 @@ -905,15 +940,14 @@ public class LwM2MTransportService { * #5.3 Observe.del * -- different between newObserve and oldObserve: sent Request cancel observe to client * - * @param registrationId - - * @param deviceProfile - + * @param registrationIds - + * @param deviceProfile - */ - public void onDeviceUpdateChangeProfile(String registrationId, DeviceProfile deviceProfile) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registrationId); - AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()); + public void onDeviceUpdateChangeProfile(Set registrationIds, DeviceProfile deviceProfile) { + + AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId()); if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) { - LeshanServer lwServer = lwM2MClient.getLwServer(); - Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); + // #1 JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile(); Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class); @@ -929,9 +963,6 @@ public class LwM2MTransportService { Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class); JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile(); JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile(); - // #2 - lwM2MClient.setDeviceProfileName(deviceProfile.getName()); - lwM2MClient.setProfileUuid(deviceProfile.getUuidId()); // #3 ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters(); @@ -957,9 +988,15 @@ public class LwM2MTransportService { // #4.1 add if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) { // update value in Resources - this.updateResourceValueObserve(lwServer, registration, lwM2MClient, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ); - // sent attr/telemetry to tingsboard for new path - this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd()); + registrationIds.forEach(registrationId -> { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registrationId); + LeshanServer lwServer = lwM2MClient.getLwServer(); + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); + log.warn("[{}] # 4.1", registration.getEndpoint()); + this.updateResourceValueObserve(lwServer, registration, lwM2MClient, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ); + // sent attr/telemetry to tingsboard for new path + this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd()); + }); } // #4.2 del if (sentAttrToThingsboard.getPathPostParametersDel().size() > 0) { @@ -980,10 +1017,16 @@ public class LwM2MTransportService { // does not include oldObserve ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd()); // sent Request observe to Client - this.updateResourceValueObserve(lwServer, registration, lwM2MClient, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE); - // 5.3 del - // sent Request cancel observe to Client - this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel()); + registrationIds.forEach(registrationId -> { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registrationId); + LeshanServer lwServer = lwM2MClient.getLwServer(); + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); + log.warn("[{}] # 5.1", registration.getEndpoint()); + this.updateResourceValueObserve(lwServer, registration, lwM2MClient, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE); + // 5.3 del + // sent Request cancel observe to Client + this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel()); + }); } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ResultIds.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ResultIds.java deleted file mode 100644 index b5c2f6638e..0000000000 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ResultIds.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © 2016-2020 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.transport.lwm2m.server; - -import lombok.Builder; -import lombok.Data; - -@Data -public class ResultIds { - @Builder.Default - int objectId = -1; - @Builder.Default - int instanceId = -1; - @Builder.Default - int resourceId = -1; - - public ResultIds (String path) { - String[] paths = path.split("/"); - if (paths != null && paths.length > 1) { - this.objectId = (paths.length > 1) ? Integer.parseInt(paths[1]) : this.objectId; - this.instanceId = (paths.length > 2) ? Integer.parseInt(paths[2]) : this.instanceId; - this.resourceId = (paths.length > 3) ? Integer.parseInt(paths[3]) : this.resourceId; - } - } -} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java index ceea02e55a..5604017d61 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java @@ -173,16 +173,16 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { private SecurityInfo add(String identity) { ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.CLIENT); - UUID profileUuid = (addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null; + UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null; if (store.getSecurityInfo() != null) { if (store.getSecurityMode() < DEFAULT_MODE.code) { String endpoint = store.getSecurityInfo().getEndpoint(); sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, null, profileUuid)); } } else { - if (store.getSecurityMode() == NO_SEC.code) + if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, null, profileUuid)); - else log.error("Registration failed: FORBIDDEN, endpointId: [{}]", identity); + else log.error("Registration failed: FORBIDDEN/profileUuid [{}] , endpointId: [{}]", profileUuid, identity); } return store.getSecurityInfo(); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java index 4b7677dd69..760431b250 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java @@ -43,8 +43,10 @@ public abstract class TransportContext { @Autowired private TransportService transportService; + @Autowired private TbServiceInfoProvider serviceInfoProvider; + @Autowired private SchedulerComponent scheduler; From 9731fb614b1ce09f76e60e46fe6c87a7a9e1525d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 26 Dec 2020 22:29:37 +0200 Subject: [PATCH 07/41] Lwm2m: backEnd: add Error securityConfig if not Device or Profile --- .../server/secure/LwM2mInMemorySecurityStore.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java index 5604017d61..72ba277b18 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.secure; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.InMemorySecurityStore; @@ -69,7 +70,8 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { readLock.lock(); try { String registrationId = this.getByRegistrationId(endPoint, null); - return (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.add(endPoint); + SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.add(endPoint); + return info; } finally { readLock.unlock(); } @@ -182,7 +184,15 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { } else { if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, null, profileUuid)); - else log.error("Registration failed: FORBIDDEN/profileUuid [{}] , endpointId: [{}]", profileUuid, identity); + else { + log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity); + /** + * Return Error securityInfo + */ + byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray()); + SecurityInfo info = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey); + return info; + } } return store.getSecurityInfo(); } From c5d82c2203755e75b79c898b9ba7511c1c9d731a Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 26 Dec 2020 23:09:35 +0200 Subject: [PATCH 08/41] Lwm2m: backEnd: add Error securityConfig --- .../server/transport/lwm2m/server/LwM2MTransportService.java | 2 +- .../lwm2m/server/secure/LwM2mInMemorySecurityStore.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index 46282eed07..59768d1d32 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java index 72ba277b18..07294bccfe 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java @@ -218,7 +218,6 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { } /** - * * @param deviceProfile */ public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) { From ff56bd8863691c241201f5843f01ecf29cbb6dbe Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 29 Dec 2020 11:31:59 +0200 Subject: [PATCH 09/41] Lwm2m: backEnd: add add to LwM2MClient only resources (value) --- .../server/LwM2MTransportContextServer.java | 2 +- .../lwm2m/server/LwM2MTransportHandler.java | 88 ++++--- .../lwm2m/server/LwM2MTransportRequest.java | 27 ++- .../lwm2m/server/LwM2MTransportService.java | 217 ++++++++---------- .../lwm2m/server/client/LwM2MClient.java | 72 ++---- .../lwm2m/server/client/ResourceValue.java | 10 + .../secure/LwM2mInMemorySecurityStore.java | 6 +- .../lwm2m/utils/LwM2mValueConverterImpl.java | 8 + .../lwm2m/LwM2MTransportConfigServer.java | 27 +++ 9 files changed, 236 insertions(+), 221 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportContextServer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportContextServer.java index d107318ad0..7199c3c31c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportContextServer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportContextServer.java @@ -58,7 +58,7 @@ public class LwM2MTransportContextServer extends TransportContext { private LwM2MTransportConfigServer ctxServer; @Autowired - LwM2MTransportConfigServer lwM2MTransportConfigServer; + protected LwM2MTransportConfigServer lwM2MTransportConfigServer; @Autowired private TransportService transportService; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java index f29649de7d..1e84af7d48 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java @@ -27,7 +27,9 @@ import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.LwM2mObject; import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mSingleResource; +import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.californium.LeshanServerBuilder; @@ -53,7 +55,7 @@ import java.util.Optional; @Slf4j @Component("LwM2MTransportHandler") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") -public class LwM2MTransportHandler{ +public class LwM2MTransportHandler { // We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to // send a Confirmable message to the time when an acknowledgement is no longer expected. @@ -132,8 +134,7 @@ public class LwM2MTransportHandler{ this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener); this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener); this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener); - } - catch (java.lang.NullPointerException e) { + } catch (java.lang.NullPointerException e) { log.error("init [{}]", e.toString()); } } @@ -151,31 +152,49 @@ public class LwM2MTransportHandler{ return coapConfig; } - public static String getValueTypeToString (Object value, ResourceModel.Type type, int val) { - try{ +// public static String getValueTypeToString (Object value, ResourceModel.Type type, int val) { +// try{ +// switch (type) { +// case STRING: // String +// case OBJLNK: // ObjectLink +// return value.toString(); +// case INTEGER: // Long +// return Long.toString((long) value); +// case BOOLEAN: // Boolean +// return Boolean.toString((Boolean) value); +// case FLOAT: // Double +// return Double.toString((Double) value); +// case TIME: // Date +// return Long.toString(((Date) value).getTime()); +//// String DATE_FORMAT = "MMM d, yyyy HH:mm a"; +//// DateFormat formatter = new SimpleDateFormat(DATE_FORMAT); +//// return formatter.format(new Date(Integer.toUnsignedLong((Integer) value))); +// case OPAQUE: // byte[] value, base64 +// return Hex.encodeHexString((byte[])value); +// default: +// return null; +// } +// } catch (Exception e) { +// log.error(e.getStackTrace().toString()); +// return null; +// } +// } + + public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException { switch (type) { - case STRING: // String - case OBJLNK: // ObjectLink - return value.toString(); - case INTEGER: // Long - return Long.toString((long) value); - case BOOLEAN: // Boolean - return Boolean.toString((Boolean) value); - case FLOAT: // Double - return Double.toString((Double) value); - case TIME: // Date - return Long.toString(((Date) value).getTime()); -// String DATE_FORMAT = "MMM d, yyyy HH:mm a"; -// DateFormat formatter = new SimpleDateFormat(DATE_FORMAT); -// return formatter.format(new Date(Integer.toUnsignedLong((Integer) value))); - case OPAQUE: // byte[] value, base64 - return Hex.encodeHexString((byte[])value); + case BOOLEAN: + case INTEGER: + case FLOAT: + return String.valueOf(valueOld).equals(String.valueOf(valueNew)); + case TIME: + return ((Date) valueOld).getTime() == ((Date) valueNew).getTime(); + case STRING: + case OBJLNK: + return valueOld.equals(valueNew); + case OPAQUE: + return Hex.decodeHex(((String) valueOld).toCharArray()).equals(Hex.decodeHex(((String) valueNew).toCharArray())); default: - return null; - } - } catch (Exception e) { - log.error(e.getStackTrace().toString()); - return null; + throw new CodecException("Invalid value type for resource %s, type %s", resourcePath, type); } } @@ -198,19 +217,18 @@ public class LwM2MTransportHandler{ attrTelemetryObserveValue.setPostAttributeProfile(profilesConfigData.get(ATTRIBUTE).getAsJsonArray()); attrTelemetryObserveValue.setPostTelemetryProfile(profilesConfigData.get(TELEMETRY).getAsJsonArray()); attrTelemetryObserveValue.setPostObserveProfile(profilesConfigData.get(OBSERVE).getAsJsonArray()); - return attrTelemetryObserveValue; + return attrTelemetryObserveValue; } /** - * @return deviceProfileBody with Observe&Attribute&Telemetry From Thingsboard * Example: with pathResource (use only pathResource) * property: "observeAttr" * {"keyName": { - * "/3/0/1": "modelNumber", - * "/3/0/0": "manufacturer", - * "/3/0/2": "serialNumber" - * }, + * "/3/0/1": "modelNumber", + * "/3/0/0": "manufacturer", + * "/3/0/2": "serialNumber" + * }, * "attribute":["/2/0/1","/3/0/9"], * "telemetry":["/1/0/1","/2/0/1","/6/0/1"], * "observe":["/2/0","/2/0/0","/4/0/2"]} @@ -312,12 +330,12 @@ public class LwM2MTransportHandler{ } } - public static String splitCamelCaseString(String s){ + public static String splitCamelCaseString(String s) { LinkedList linkedListOut = new LinkedList<>(); LinkedList linkedList = new LinkedList((Arrays.asList(s.split(" ")))); - linkedList.forEach(str-> { + linkedList.forEach(str -> { String strOut = str.replaceAll("\\W", "").replaceAll("_", "").toUpperCase(); - if (strOut.length()>1) linkedListOut.add(strOut.charAt(0) + strOut.substring(1).toLowerCase()); + if (strOut.length() > 1) linkedListOut.add(strOut.charAt(0) + strOut.substring(1).toLowerCase()); else linkedListOut.add(strOut); }); linkedListOut.set(0, (linkedListOut.get(0).substring(0, 1).toLowerCase() + linkedListOut.get(0).substring(1))); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index 2c7721089c..f0b96d0225 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -123,10 +123,7 @@ public class LwM2MTransportRequest { if (registration != null && resultIds.getObjectId() >= 0) { DownlinkRequest request = null; ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null; - ResourceModel resource = (resultIds.getResourceId() !=null && lwM2MClient != null) ? - lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()) : null; - ResourceModel.Type resType = (resource == null) ? null : resource.type; - boolean resMultiple = (resource == null) ? false : resource.multiple; + ResourceModel resource = service.context.getCtxServer().getResourceModel(resultIds); timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; switch (typeOper) { case GET_TYPE_OPER_READ: @@ -148,21 +145,23 @@ public class LwM2MTransportRequest { request = new CancelObservationRequest(observation); break; case POST_TYPE_OPER_EXECUTE: - if (params != null && !resMultiple) { + if (params != null && resource != null && !resource.multiple) { // request = new ExecuteRequest(target, LwM2MTransportHandler.getValueTypeToString(params, resType)); - request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resType, ResourceModel.Type.STRING, resultIds)); + request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resource.type, ResourceModel.Type.STRING, resultIds)); } else { request = new ExecuteRequest(target); } break; case POST_TYPE_OPER_WRITE_REPLACE: // Request to write a String Single-Instance Resource using the TLV content format. - if (contentFormat.equals(ContentFormat.TLV) && !resMultiple) { - request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resType, registration); - } - // Mode.REPLACE && Request to write a String Single-Instance Resource using the given content format (TEXT, TLV, JSON) - else if (!contentFormat.equals(ContentFormat.TLV) && !resMultiple) { - request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resType, registration); + if (resource != null) { + if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) { + request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resource.type, registration); + } + // Mode.REPLACE && Request to write a String Single-Instance Resource using the given content format (TEXT, TLV, JSON) + else if (!contentFormat.equals(ContentFormat.TLV) && !resource.multiple) { + request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resource.type, registration); + } } break; case PUT_TYPE_OPER_WRITE_UPDATE: @@ -217,10 +216,10 @@ public class LwM2MTransportRequest { break; default: } + if (request != null) { this.sendRequest(lwServer, registration, request, lwM2MClient, timeoutInMs, isDelayedUpdate); - } - else if (request == null && isDelayedUpdate) { + } else if (request == null && isDelayedUpdate) { String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg No SendRequest to Client", target); service.sentLogsToThingsboard(msg, registration.getId()); log.error("[{}] - [{}] No SendRequest", target); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index 59768d1d32..5e4d4198f0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -19,8 +19,8 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +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.LwM2mObject; @@ -55,12 +55,12 @@ import org.thingsboard.server.transport.lwm2m.server.client.ModelObject; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -103,6 +103,8 @@ public class LwM2MTransportService { private ExecutorService executorRegistered; private ExecutorService executorUpdateRegistered; private ExecutorService executorUnRegistered; + private LwM2mValueConverterImpl converter; + @Autowired private TransportService transportService; @@ -118,13 +120,14 @@ public class LwM2MTransportService { @PostConstruct public void init() { - context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); - executorRegistered = Executors.newCachedThreadPool( + this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); + this.executorRegistered = Executors.newCachedThreadPool( new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); - executorUpdateRegistered = Executors.newCachedThreadPool( + this.executorUpdateRegistered = Executors.newCachedThreadPool( new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); - executorUnRegistered = Executors.newCachedThreadPool( + this.executorUnRegistered = Executors.newCachedThreadPool( new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); + this.converter = new LwM2mValueConverterImpl(); } /** @@ -148,7 +151,6 @@ public class LwM2MTransportService { log.info("[{}] Client: onRegistered name ", registration.getEndpoint()); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration); if (lwM2MClient != null) { - lwM2MClient.setLwM2MTransportService(this); lwM2MClient.setLwM2MTransportService(this); lwM2MClient.setSessionUuid(UUID.randomUUID()); this.setLwM2MClient(lwServer, registration, lwM2MClient); @@ -183,6 +185,8 @@ public class LwM2MTransportService { try { SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId()); if (sessionInfo != null) { +// transportService.reportActivity(sessionInfo); +// transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); @@ -199,7 +203,7 @@ public class LwM2MTransportService { * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect */ public void unReg(Registration registration, Collection observations) { - executorUpdateRegistered.submit(() -> { + executorUnRegistered.submit(() -> { try { this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); this.closeClientSession(registration); @@ -262,6 +266,7 @@ public class LwM2MTransportService { } /** + * #0 Add new ObjectModel to context * Create new LwM2MClient for current session -> setModelClient... * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client * to get the client model with current values @@ -272,6 +277,8 @@ public class LwM2MTransportService { * @param lwM2MClient - object with All parameters off client */ private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) { + // #0 + this.setNewObjectModels(lwServer, registration); // #1 Arrays.stream(registration.getObjectLinks()).forEach(url -> { LwM2mPath pathIds = new LwM2mPath(url.getUrl()); @@ -289,6 +296,16 @@ public class LwM2MTransportService { }); } + private void setNewObjectModels(LeshanServer lwServer, Registration registration) { + Arrays.stream(registration.getObjectLinks()).forEach(url -> { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource() && !context.getCtxServer().getObjectModels().containsKey(pathIds.getObjectId())) { + ObjectModel model = lwServer.getModelProvider().getObjectModel(registration).getObjectModels().stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0); + context.getCtxServer().getObjectModels().put(pathIds.getObjectId(), model); + } + }); + } + /** * @param registrationId - Id of Registration LwM2M Client * @return - sessionInfo after access connect client @@ -396,8 +413,8 @@ public class LwM2MTransportService { lwM2MClient.getDelayedRequests().forEach((k, v) -> { List listV = new ArrayList(); listV.add(v.getKv()); - this.putDelayedUpdateResourcesClient(lwM2MClient, lwM2MClient.getResourceValue(k), getJsonObject(listV).get(v.getKv().getKey()), k); - System.out.printf(" k: %s, v: %s%n, v1: %s%n", k, v.getKv().getStringV(), lwM2MClient.getResourceValue(k)); + this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k); + System.out.printf(" k: %s, v: %s%n, v1: %s%n", k, v.getKv().getStringV(), this.getResourceValueToString(lwM2MClient, k)); }); lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId()); if (lwM2MClient.getDelayedRequests().size() == 0) { @@ -427,10 +444,11 @@ public class LwM2MTransportService { ConcurrentMap keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class); ConcurrentMap keyNamesIsWritable = keyNamesMap.entrySet() .stream() - .filter(e -> (attrSet.contains(e.getKey()) && lwM2MClient.getOperation(e.getKey()).isWritable())) + .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(new LwM2mPath(e.getKey())) != null && + context.getCtxServer().getResourceModel(new LwM2mPath(e.getKey())).operations.isWritable())) .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)); namesIsIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values())); - keyNamesIsWritable.keySet().forEach(p -> namesIsIsWritable.add(lwM2MClient.getResourceName(p))); + keyNamesIsWritable.keySet().forEach(p -> namesIsIsWritable.add(this.getResourceName(p))); return new ArrayList<>(namesIsIsWritable); } @@ -501,7 +519,7 @@ public class LwM2MTransportService { LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); if (pathIds.isResource()) { if (path == null || path.contains(p.getAsString())) { - this.addParameters(p.getAsString().toString(), attributes, registration, "attributes"); + this.addParameters(p.getAsString().toString(), attributes, registration); } } }); @@ -510,7 +528,7 @@ public class LwM2MTransportService { LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); if (pathIds.isResource()) { if (path == null || path.contains(p.getAsString())) { - this.addParameters(p.getAsString().toString(), telemetry, registration, "telemetry"); + this.addParameters(p.getAsString().toString(), telemetry, registration); } } }); @@ -520,13 +538,15 @@ public class LwM2MTransportService { * @param parameters - JsonObject attributes/telemetry * @param registration - Registration LwM2M Client */ - private void addParameters(String path, JsonObject parameters, Registration registration, String nameParam) { - JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid()).getPostKeyNameProfile(); + private void addParameters(String path, JsonObject parameters, Registration registration) { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()); + JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile(); String resName = String.valueOf(names.get(path)); if (resName != null && !resName.isEmpty()) { String resValue = null; try { - resValue = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path); +// resValue = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValueString(path); + resValue = this.getResourceValueToString(lwM2MClient, path); if (resValue != null) { // log.info("addParameters Path: [{}] ResValue : [{}] nameParam [{}]", path, lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path), nameParam); parameters.addProperty(resName, resValue); @@ -577,7 +597,7 @@ public class LwM2MTransportService { p.getAsString().toString() : null; if (target != null) { // #2 - if (lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(target) != null) { + if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) { lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE, null, null, null, null, this.context.getCtxServer().getTimeout(), false); @@ -635,7 +655,7 @@ public class LwM2MTransportService { * @param path - observe * @param response - observe */ - @SneakyThrows + public void onObservationResponse(Registration registration, String path, ReadResponse response) { if (response.getContent() != null) { if (response.getContent() instanceof LwM2mObject) { @@ -655,13 +675,8 @@ public class LwM2MTransportService { /** * Sending observe value of resources to thingsboard - * #1 Return old Resource from ModelObject - * #2 Create new Resource with value from observation - * #3 Create new Resources from old Resources - * #4 Update new Resources (replace old Resource on new Resource) - * #5 Remove old Instance from modelClient - * #6 Create new Instance with new Resources values - * #7 Update modelClient.getModelObjects(idObject) (replace old Instance on new Instance) + * #1 Return old Value Resource from LwM2MClient + * #2 Update new Resources (replace old Resource Value on new Resource Value) * * @param registration - Registration LwM2M Client * @param value - LwM2mSingleResource response.getContent() @@ -669,72 +684,38 @@ public class LwM2MTransportService { * @param path - resource */ private void onObservationSetResourcesValue(Registration registration, Object value, Map values, String path) { + CountDownLatch respLatch = new CountDownLatch(1); + boolean isChange = false; try { - CountDownLatch respLatch = new CountDownLatch(1); - try { - // #1 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); - LwM2mPath resultIds = new LwM2mPath(path); - log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), resultIds, value, values); - ResourceModel.Type resType = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).type; - Map instancesModelObject = (lwM2MClient.getModelObjects().get(resultIds.getObjectId()) != null) ? lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances() : null; - Map resourcesOld = null; - try { - CountDownLatch respResLatch = new CountDownLatch(1); - try { - resourcesOld = (instancesModelObject != null && - instancesModelObject.get(resultIds.getObjectInstanceId()) != null && - instancesModelObject.get(resultIds.getObjectInstanceId()).getResources() != null) ? instancesModelObject.get(resultIds.getObjectInstanceId()).getResources() : null; - - } finally { - respResLatch.countDown(); - } - try { - respResLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException ex) { - ex.printStackTrace(); - log.error("#1_2 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value); - } - } catch (Exception e) { - e.printStackTrace(); - log.error("#1_2_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value); - } - LwM2mResource resourceOld = (resourcesOld != null && resourcesOld.get(resultIds.getResourceId()) != null) ? resourcesOld.get(resultIds.getResourceId()) : null; - // #2 - LwM2mResource resourceNew = null; - if ((resourceOld != null && resourceOld.isMultiInstances() && !resourceOld.getValues().equals(values)) || - (resourceOld == null && value == null)) { - resourceNew = LwM2mMultipleResource.newResource(resultIds.getResourceId(), values, resType); - } else if ((resourceOld != null && !resourceOld.isMultiInstances() && !resourceOld.getValue().equals(values)) || - (resourceOld == null && value != null)) { - resourceNew = LwM2mSingleResource.newResource(resultIds.getResourceId(), value, resType); - } - if (resourceNew != null) { - //#3 - Map resourcesNew = (resourcesOld == null) ? new HashMap<>() : new HashMap<>(resourcesOld); - // #4 - if ((resourceOld != null)) resourcesNew.remove(resourceOld); - // #5 - resourcesNew.put(resultIds.getResourceId(), resourceNew); - // #6 - LwM2mObjectInstance instanceNew = new LwM2mObjectInstance(resultIds.getObjectInstanceId(), resourcesNew.values()); - // #7 - lwM2MClient.getModelObjects().get(resultIds.getObjectId()).removeInstance(resultIds.getObjectInstanceId()); - instancesModelObject.put(resultIds.getObjectInstanceId(), instanceNew); - Set paths = new HashSet<>(); - paths.add(path); - this.updateAttrTelemetry(registration, false, paths); - } - } finally { - respLatch.countDown(); - } - try { - respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException ex) { - ex.printStackTrace(); - log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value); + // #1 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); + LwM2mPath pathIds = new LwM2mPath(path); + log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values); + ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(pathIds); + ResourceValue resValueOld = lwM2MClient.getResources().get(path); + // #2 + if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) { + ResourceValue resourceValue = new ResourceValue ( values, null, true); + lwM2MClient.getResources().put(path, resourceValue); + isChange = true; + } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) { + ResourceValue resourceValue = new ResourceValue ( null, value, false); + lwM2MClient.getResources().put(path, resourceValue); + isChange = true; } - } catch (Exception ignored) { + } finally { + respLatch.countDown(); + } + try { + respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + ex.printStackTrace(); + log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value); + } + if (isChange) { + Set paths = new HashSet<>(); + paths.add(path); + this.updateAttrTelemetry(registration, false, paths); } } @@ -762,19 +743,19 @@ public class LwM2MTransportService { String value = de.getValue().getAsString(); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); + ResourceModel resourceModel = context.getCtxServer().getResourceModel(new LwM2mPath(path)); if (path != null && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) { - if (lwM2MClient.getOperation(path).isWritable()) { + if (resourceModel != null && resourceModel.operations.isWritable()) { lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(), false); -// log.info("[{}] path onAttributeUpdate", path); } else { - log.error(LOG_LW2M_ERROR + ": Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value); + log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value); String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value); this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); } } else { - log.error(LOG_LW2M_ERROR + ": Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value); + log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value); String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value); this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); } @@ -845,7 +826,8 @@ public class LwM2MTransportService { Predicate> predicateObj = (obj -> { return obj.getValue().getObjectModel().resources.entrySet().stream().filter(predicateRes).findFirst().isPresent(); }); - Map.Entry object = lwM2MClient.getModelObjects().entrySet().stream().filter(predicateObj).findFirst().get(); +// Map.Entry object = lwM2MClient.getModelObjects().entrySet().stream().filter(predicateObj).findFirst().get(); + Map.Entry object = null; ModelObject modelObject = object.getValue(); LwM2mObjectInstance instance = modelObject.getInstances().entrySet().stream().findFirst().get().getValue(); ResourceModel resource = modelObject.getObjectModel().resources.entrySet().stream().filter(predicateRes).findFirst().get().getValue(); @@ -867,7 +849,8 @@ public class LwM2MTransportService { // ResultIds resultIds = new ResultIds(path); LwM2mPath resultIds = new LwM2mPath(path); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); - LwM2mResource resource = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances().get(resultIds.getObjectInstanceId()).getResource(resultIds.getResourceId()); +// LwM2mResource resource = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances().get(resultIds.getObjectInstanceId()).getResource(resultIds.getResourceId()); + LwM2mResource resource = null; if (resource.isMultiInstances()) { this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path); } else { @@ -1080,8 +1063,9 @@ public class LwM2MTransportService { targets.forEach(target -> { // ResultIds pathIds = new ResultIds(target); LwM2mPath pathIds = new LwM2mPath(target); - if (pathIds.isResource() && lwM2MClient.getModelObjects().get(pathIds.getObjectId()) - .getInstances().get(pathIds.getObjectInstanceId()).getResource(pathIds.getResourceId()).getValue() != null) { +// if (pathIds.isResource() && lwM2MClient.getModelObjects().get(pathIds.getObjectId()) +// .getInstances().get(pathIds.getObjectInstanceId()).getResource(pathIds.getResourceId()).getValue() != null) { + if (pathIds.isResource()) { if (GET_TYPE_OPER_READ.equals(typeOper)) { lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(), @@ -1098,32 +1082,17 @@ public class LwM2MTransportService { private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set paramAnallyzer) { LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); paramAnallyzer.forEach(p -> { - if (this.getResourceValue(lwM2MClient, p) != null) { + if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) { this.setCancelObservationRecourse(lwServer, registration, p); } } ); } - private ResourceValue getResourceValue(LwM2MClient lwM2MClient, String path) { + private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) { ResourceValue resourceValue = null; -// ResultIds pathIds = new ResultIds(path); - LwM2mPath pathIds = new LwM2mPath(path); if (pathIds.isResource()) { - LwM2mResource resource = lwM2MClient.getModelObjects().get(pathIds.getObjectId()).getInstances().get(pathIds.getObjectInstanceId()).getResource(pathIds.getResourceId()); - if (resource.isMultiInstances()) { - if (resource.getValues().size() > 0) { - resourceValue = new ResourceValue(); - resourceValue.setMultiInstances(resource.isMultiInstances()); - resourceValue.setValues(resource.getValues()); - } - } else { - if (resource.getValue() != null) { - resourceValue = new ResourceValue(); - resourceValue.setMultiInstances(resource.isMultiInstances()); - resourceValue.setValue(resource.getValue()); - } - } + resourceValue = lwM2MClient.getResources().get(pathIds.toString()); } return resourceValue; } @@ -1174,4 +1143,20 @@ public class LwM2MTransportService { } } + private String getResourceName(String path) { + LwM2mPath resultIds = new LwM2mPath(path); + return (context.getCtxServer().getObjectModels().get(resultIds.getObjectId()) != null) ? + context.getCtxServer().getObjectModels().get(resultIds.getObjectId()).resources.get(resultIds.getResourceId()).name : ""; + } + + /** + * @param path - path resource + * @return - value of Resource or null + */ + public String getResourceValueToString(LwM2MClient lwM2MClient, String path) { + LwM2mPath pathIds = new LwM2mPath(path); + ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds); + return (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(pathIds), ResourceModel.Type.STRING, pathIds); + } + } 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 4c0f314bc1..89ef741da9 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 @@ -18,12 +18,10 @@ package org.thingsboard.server.transport.lwm2m.server.client; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.ObjectModel; -import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.response.LwM2mResponse; import org.eclipse.leshan.core.response.ReadResponse; -import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.SecurityInfo; @@ -38,8 +36,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; - @Slf4j @Data public class LwM2MClient implements Cloneable { @@ -56,7 +52,7 @@ public class LwM2MClient implements Cloneable { private Registration registration; private ValidateDeviceCredentialsResponseMsg credentialsResponse; private Map attributes; - private Map modelObjects; + private Map resources; private Set pendingRequests; private Map delayedRequests; private Set delayedRequestsId; @@ -67,15 +63,15 @@ public class LwM2MClient implements Cloneable { return super.clone(); } - public LwM2MClient(String endPoint, String identity, SecurityInfo info, ValidateDeviceCredentialsResponseMsg credentialsResponse, Map attributes, Map modelObjects, UUID profileUuid) { + public LwM2MClient(String endPoint, String identity, SecurityInfo info, ValidateDeviceCredentialsResponseMsg credentialsResponse, Map attributes, UUID profileUuid) { this.endPoint = endPoint; this.identity = identity; this.info = info; this.credentialsResponse = credentialsResponse; this.attributes = (attributes != null && attributes.size() > 0) ? attributes : new ConcurrentHashMap(); - this.modelObjects = (modelObjects != null && modelObjects.size() > 0) ? modelObjects : new ConcurrentHashMap(); this.pendingRequests = ConcurrentHashMap.newKeySet(); this.delayedRequests = new ConcurrentHashMap<>(); + this.resources = new ConcurrentHashMap<>(); this.delayedRequestsId = ConcurrentHashMap.newKeySet(); this.profileUuid = profileUuid; /** @@ -102,21 +98,29 @@ public class LwM2MClient implements Cloneable { private void initValue() { this.responses.forEach((key, resp) -> { - LwM2mPath pathIds = new LwM2mPath(key); + LwM2mPath pathIds = new LwM2mPath(key); if (pathIds.isObject() || pathIds.isObjectInstance() || pathIds.isResource()) { ObjectModel objectModel = this.lwServer.getModelProvider().getObjectModel(registration).getObjectModels().stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0); - if (this.modelObjects.get(pathIds.getObjectId()) != null) { - this.modelObjects.get(pathIds.getObjectId()).getInstances().put(((ReadResponse) resp).getContent().getId(), (LwM2mObjectInstance) ((ReadResponse) resp).getContent()); - } else { - Map instances = new ConcurrentHashMap<>(); - instances.put(((ReadResponse) resp).getContent().getId(), (LwM2mObjectInstance) ((ReadResponse) resp).getContent()); - ModelObject modelObject = new ModelObject(objectModel, instances); - this.modelObjects.put(pathIds.getObjectId(), modelObject); + if (objectModel != null) { + ((LwM2mObjectInstance)((ReadResponse)resp).getContent()).getResources().forEach((k, v) -> { + String rez = pathIds.toString() + "/" + k; + boolean ismulti = objectModel.resources.get(k).multiple; + if (objectModel.resources.get(k).multiple){ + this.resources.put(rez, new ResourceValue(v.getValues(), null, true)); + } + else { + this.resources.put(rez, new ResourceValue(null, v.getValue(), false)); + } + }); } } }); } + /** + * if path != null + * @param path + */ public void onSuccessOrErrorDelayedRequests(String path) { if (path != null) this.delayedRequests.remove(path); if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) { @@ -124,43 +128,5 @@ public class LwM2MClient implements Cloneable { } } - public ResourceModel.Operations getOperation(String path) { - LwM2mPath resultIds = new LwM2mPath(path); - return (this.getModelObjects().get(resultIds.getObjectId()) != null) ? - this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).operations : - ResourceModel.Operations.NONE; - } - - public String getResourceName(String path) { - LwM2mPath resultIds = new LwM2mPath(path); - return (this.getModelObjects().get(resultIds.getObjectId()) != null) ? this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).name : ""; - } - - /** - * @param path - path resource - * @return - value of Resource or null - */ - public String getResourceValue(String path) { - String resValue = null; - LwM2mPath pathIds = new LwM2mPath(path); - ModelObject modelObject = this.getModelObjects().get(pathIds.getObjectId()); - - if (modelObject != null && modelObject.getInstances().get(pathIds.getObjectInstanceId()) != null) { - LwM2mObjectInstance instance = modelObject.getInstances().get(pathIds.getObjectInstanceId()); - if (instance.getResource(pathIds.getResourceId()) != null) { - try { - resValue = instance.getResource(pathIds.getResourceId()).getType() == OPAQUE ? - Hex.encodeHexString((byte[]) instance.getResource(pathIds.getResourceId()).getValue()).toLowerCase() : - (instance.getResource(pathIds.getResourceId()).isMultiInstances()) ? - instance.getResource(pathIds.getResourceId()).getValues().toString() : -// getValueTypeToString(instance.getResource(pathIds.getResourceId()).getValue(), instance.getResource(pathIds.getResourceId()).getType()); - (String) converter.convertValue(instance.getResource(pathIds.getResourceId()).getValue(), instance.getResource(pathIds.getResourceId()).getType(), ResourceModel.Type.STRING, pathIds); - } catch (Exception e) { - log.warn("getResourceValue [{}]", e.getStackTrace().toString()); - } - } - } - return resValue; - } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java index 312973b9d0..d58890dfba 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java @@ -23,4 +23,14 @@ public class ResourceValue { Map values; Object value; boolean multiInstances; + + public ResourceValue ( Map values, Object value, boolean multiInstances) { + this.values = values; + this.value = value; + this.multiInstances = multiInstances; + } + public Object getResourceValue() { + return this.multiInstances ? this.values : this.value; + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java index 07294bccfe..216136be8b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java @@ -179,11 +179,13 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { if (store.getSecurityInfo() != null) { if (store.getSecurityMode() < DEFAULT_MODE.code) { String endpoint = store.getSecurityInfo().getEndpoint(); - sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, null, profileUuid)); +// sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, null, profileUuid)); + sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, profileUuid)); } } else { if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) - sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, null, profileUuid)); +// sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, null, profileUuid)); + sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, profileUuid)); else { log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity); /** diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java index d7cae734a2..44fcfad9b6 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java @@ -26,6 +26,8 @@ import org.eclipse.leshan.core.util.StringUtils; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Date; @Slf4j @@ -120,6 +122,12 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { case INTEGER: case FLOAT: return String.valueOf(value); + case TIME: +// return Long.toString(((Date) value).getTime()); + String DATE_FORMAT = "MMM d, yyyy HH:mm a"; + Long timeValue = ((Date) value).getTime(); + DateFormat formatter = new SimpleDateFormat(DATE_FORMAT); + return formatter.format(new Date(timeValue)); default: break; } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java index d8f305a049..89c1600bf5 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java @@ -20,6 +20,8 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.ObjectLoader; import org.eclipse.leshan.core.model.ObjectModel; +import org.eclipse.leshan.core.model.ResourceModel; +import org.eclipse.leshan.core.node.LwM2mPath; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; @@ -33,6 +35,8 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; @Slf4j @Component @@ -173,6 +177,10 @@ public class LwM2MTransportConfigServer { @Value("${transport.lwm2m.secure.redis_url:}") private String redisUrl; + @Getter + @Setter + private Map objectModels; + @PostConstruct public void init() { modelsValue = ObjectLoader.loadDefault(); @@ -188,6 +196,7 @@ public class LwM2MTransportConfigServer { log.error(" [{}] Read Models", path.getAbsoluteFile()); } getInKeyStore(); + this.objectModels = new ConcurrentHashMap(); } private File getPathModels() { @@ -238,4 +247,22 @@ public class LwM2MTransportConfigServer { } return FULL_FILE_PATH.toUri().getPath(); } + + public ResourceModel getResourceModel(LwM2mPath pathIds) { + return (this.objectModels.size()>0 && + this.objectModels.containsKey(pathIds.getObjectId()) && + this.objectModels.get(pathIds.getObjectId()).resources.containsKey(pathIds.getResourceId())) ? + this.objectModels.get(pathIds.getObjectId()).resources.get(pathIds.getResourceId()) : null; + } + + public ResourceModel.Type getResourceModelType(LwM2mPath pathIds) { + ResourceModel resource = this.getResourceModel(pathIds); + return (resource == null) ? null : resource.type; + } + + public ResourceModel.Operations getOperation(LwM2mPath pathIds) { + ResourceModel resource = this.getResourceModel(pathIds); + return (resource == null) ? ResourceModel.Operations.NONE : resource.operations; + } + } From 4f3707c1e115afe5167c76987a30c415cc91d87f Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 29 Dec 2020 14:26:39 +0200 Subject: [PATCH 10/41] Lwm2m: backEnd: add PUT_TYPE_OPER_WRITE_UPDATE --- .../lwm2m/server/LwM2MTransportHandler.java | 30 ++----------------- .../lwm2m/server/LwM2MTransportRequest.java | 5 ++-- .../lwm2m/server/LwM2MTransportService.java | 7 ----- 3 files changed, 5 insertions(+), 37 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java index 1e84af7d48..14d06eb7e6 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java @@ -99,11 +99,13 @@ public class LwM2MTransportHandler { /** * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see * section 5.3.3 of the LW M2M spec). + * if all resources are to be replaced */ public static final String POST_TYPE_OPER_WRITE_REPLACE = "replace"; /** * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section * 5.3.3 of the LW M2M spec). + * if this is a partial update request */ public static final String PUT_TYPE_OPER_WRITE_UPDATE = "update"; public static final String PUT_TYPE_OPER_WRITE_ATTRIBUTES = "wright-attributes"; @@ -152,34 +154,6 @@ public class LwM2MTransportHandler { return coapConfig; } -// public static String getValueTypeToString (Object value, ResourceModel.Type type, int val) { -// try{ -// switch (type) { -// case STRING: // String -// case OBJLNK: // ObjectLink -// return value.toString(); -// case INTEGER: // Long -// return Long.toString((long) value); -// case BOOLEAN: // Boolean -// return Boolean.toString((Boolean) value); -// case FLOAT: // Double -// return Double.toString((Double) value); -// case TIME: // Date -// return Long.toString(((Date) value).getTime()); -//// String DATE_FORMAT = "MMM d, yyyy HH:mm a"; -//// DateFormat formatter = new SimpleDateFormat(DATE_FORMAT); -//// return formatter.format(new Date(Integer.toUnsignedLong((Integer) value))); -// case OPAQUE: // byte[] value, base64 -// return Hex.encodeHexString((byte[])value); -// default: -// return null; -// } -// } catch (Exception e) { -// log.error(e.getStackTrace().toString()); -// return null; -// } -// } - public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException { switch (type) { case BOOLEAN: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index f0b96d0225..e34cfdd34e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -20,6 +20,7 @@ import org.eclipse.californium.core.coap.Response; import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.model.ResourceModel; +import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.ObjectLink; @@ -146,7 +147,6 @@ public class LwM2MTransportRequest { break; case POST_TYPE_OPER_EXECUTE: if (params != null && resource != null && !resource.multiple) { -// request = new ExecuteRequest(target, LwM2MTransportHandler.getValueTypeToString(params, resType)); request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resource.type, ResourceModel.Type.STRING, resultIds)); } else { request = new ExecuteRequest(target); @@ -168,7 +168,8 @@ public class LwM2MTransportRequest { if (resultIds.getResourceId() >= 0) { ResourceModel resourceModel = lwServer.getModelProvider().getObjectModel(registration).getObjectModel(resultIds.getObjectId()).resources.get(resultIds.getResourceId()); ResourceModel.Type typeRes = resourceModel.type; -// request = getWriteRequestResource(resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, typeRes); + LwM2mNode node = LwM2mSingleResource.newStringResource(resultIds.getResourceId(), (String) this.converter.convertValue(params, resource.type, ResourceModel.Type.STRING, resultIds)); + request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, target, node); } break; case PUT_TYPE_OPER_WRITE_ATTRIBUTES: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index 5e4d4198f0..9c953f4069 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -515,7 +515,6 @@ public class LwM2MTransportService { private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set path) { AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid()); attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> { -// ResultIds pathIds = new ResultIds(p.getAsString().toString()); LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); if (pathIds.isResource()) { if (path == null || path.contains(p.getAsString())) { @@ -524,7 +523,6 @@ public class LwM2MTransportService { } }); attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> { -// ResultIds pathIds = new ResultIds(p.getAsString().toString()); LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); if (pathIds.isResource()) { if (path == null || path.contains(p.getAsString())) { @@ -545,10 +543,8 @@ public class LwM2MTransportService { if (resName != null && !resName.isEmpty()) { String resValue = null; try { -// resValue = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValueString(path); resValue = this.getResourceValueToString(lwM2MClient, path); if (resValue != null) { -// log.info("addParameters Path: [{}] ResValue : [{}] nameParam [{}]", path, lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path), nameParam); parameters.addProperty(resName, resValue); } } catch (Exception e) { @@ -1061,10 +1057,7 @@ public class LwM2MTransportService { */ private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient, Set targets, String typeOper) { targets.forEach(target -> { -// ResultIds pathIds = new ResultIds(target); LwM2mPath pathIds = new LwM2mPath(target); -// if (pathIds.isResource() && lwM2MClient.getModelObjects().get(pathIds.getObjectId()) -// .getInstances().get(pathIds.getObjectInstanceId()).getResource(pathIds.getResourceId()).getValue() != null) { if (pathIds.isResource()) { if (GET_TYPE_OPER_READ.equals(typeOper)) { lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, From 02615cb93dd57c366a54f6cb5076864e2f64981d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 29 Dec 2020 15:24:57 +0200 Subject: [PATCH 11/41] Lwm2m: backEnd: add to updateReg new registerAsyncSession --- .../src/main/resources/thingsboard.yml | 2 +- .../lwm2m/server/LwM2MTransportService.java | 23 ++++++++++++++----- .../common/transport/TransportService.java | 4 ++-- .../service/DefaultTransportService.java | 4 ++-- .../transport/service/SessionMetaData.java | 1 + 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index b5fe6b29cb..065a0a4cb0 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -433,7 +433,7 @@ spring: database-platform: "${SPRING_JPA_DATABASE_PLATFORM:org.hibernate.dialect.PostgreSQLDialect}" datasource: driverClassName: "${SPRING_DRIVER_CLASS_NAME:org.postgresql.Driver}" - url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard_ce_3_3_test}" + url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard}" username: "${SPRING_DATASOURCE_USERNAME:postgres}" password: "${SPRING_DATASOURCE_PASSWORD:postgres}" hikari: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index 9c953f4069..ac2059b561 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -177,6 +177,7 @@ public class LwM2MTransportService { } /** + * if sessionInfo removed from sessions, then new registerAsyncSession * @param lwServer - LeshanServer * @param registration - Registration LwM2M Client */ @@ -185,8 +186,7 @@ public class LwM2MTransportService { try { SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId()); if (sessionInfo != null) { -// transportService.reportActivity(sessionInfo); -// transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); + this.checkInactivity(sessionInfo); log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); @@ -197,6 +197,7 @@ public class LwM2MTransportService { }); } + /** * @param registration - Registration LwM2M Client * @param observations - All paths observations before unReg @@ -691,11 +692,11 @@ public class LwM2MTransportService { ResourceValue resValueOld = lwM2MClient.getResources().get(path); // #2 if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) { - ResourceValue resourceValue = new ResourceValue ( values, null, true); + ResourceValue resourceValue = new ResourceValue(values, null, true); lwM2MClient.getResources().put(path, resourceValue); isChange = true; } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) { - ResourceValue resourceValue = new ResourceValue ( null, value, false); + ResourceValue resourceValue = new ResourceValue(null, value, false); lwM2MClient.getResources().put(path, resourceValue); isChange = true; } @@ -739,7 +740,7 @@ public class LwM2MTransportService { String value = de.getValue().getAsString(); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); - ResourceModel resourceModel = context.getCtxServer().getResourceModel(new LwM2mPath(path)); + ResourceModel resourceModel = context.getCtxServer().getResourceModel(new LwM2mPath(path)); if (path != null && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) { if (resourceModel != null && resourceModel.operations.isWritable()) { lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, @@ -1125,7 +1126,17 @@ public class LwM2MTransportService { } private void checkInactivityAndReportActivity() { - lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> transportService.reportActivity(this.getValidateSessionInfo(key))); + lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key))); + } + + /** + * if sessionInfo removed from sessions, then new registerAsyncSession + * @param sessionInfo + */ + private void checkInactivity(SessionInfoProto sessionInfo) { + if (transportService.reportActivity(sessionInfo) == null) { + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); + } } public void sentLogsToThingsboard(String msg, String registrationId) { diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java index cec9eba5a1..d676874458 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; -import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.common.transport.service.SessionMetaData; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; @@ -98,7 +98,7 @@ public interface TransportService { void registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout); - void reportActivity(SessionInfoProto sessionInfo); + SessionMetaData reportActivity(SessionInfoProto sessionInfo); void deregisterSession(SessionInfoProto sessionInfo); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 246ad7c7c0..3a4790e58a 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -509,8 +509,8 @@ public class DefaultTransportService implements TransportService { } @Override - public void reportActivity(TransportProtos.SessionInfoProto sessionInfo) { - reportActivityInternal(sessionInfo); + public SessionMetaData reportActivity(TransportProtos.SessionInfoProto sessionInfo) { + return reportActivityInternal(sessionInfo); } private SessionMetaData reportActivityInternal(TransportProtos.SessionInfoProto sessionInfo) { diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java index ac293307a9..dba3338d67 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java @@ -25,6 +25,7 @@ import java.util.concurrent.ScheduledFuture; * Created by ashvayka on 15.10.18. */ @Data +public class SessionMetaData { private volatile TransportProtos.SessionInfoProto sessionInfo; From e91cb65399ebadbdae7389ff30bad50939bd603b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 29 Dec 2020 19:12:45 +0200 Subject: [PATCH 12/41] Lwm2m: backEnd: fix bug getResourceValueToString NullPointExaption --- .../transport/lwm2m/server/LwM2MTransportRequest.java | 4 ++-- .../transport/lwm2m/server/LwM2MTransportService.java | 7 +++---- .../server/transport/lwm2m/server/client/LwM2MClient.java | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index e34cfdd34e..134c979529 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -311,7 +311,7 @@ public class LwM2MTransportRequest { try { sendResponse(registration, path, response, request, lwM2MClient, isDelayedUpdate); } catch (Exception e) { - log.error("[{}] endpoint [{}] path [{}] error Unable to after send response.", registration.getEndpoint(), path, e); + log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", registration.getEndpoint(), path, e); } }); } @@ -321,7 +321,7 @@ public class LwM2MTransportRequest { try { if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(path); } catch (RuntimeException t) { - log.error("[{}] endpoint [{}] path [{}] error Unable to after send response.", registration.getEndpoint(), path, t.toString()); + log.error("[{}] endpoint [{}] path [{}] RuntimeException Unable to after send response.", registration.getEndpoint(), path, t); } }); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index ac2059b561..ba3cd1f736 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -415,7 +415,6 @@ public class LwM2MTransportService { List listV = new ArrayList(); listV.add(v.getKv()); this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k); - System.out.printf(" k: %s, v: %s%n, v1: %s%n", k, v.getKv().getStringV(), this.getResourceValueToString(lwM2MClient, k)); }); lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId()); if (lwM2MClient.getDelayedRequests().size() == 0) { @@ -425,7 +424,7 @@ public class LwM2MTransportService { } private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) { - if (!valueOld.toString().equals(valueNew.toString())) { + if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) { lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(), true); @@ -1160,7 +1159,7 @@ public class LwM2MTransportService { public String getResourceValueToString(LwM2MClient lwM2MClient, String path) { LwM2mPath pathIds = new LwM2mPath(path); ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds); - return (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(pathIds), ResourceModel.Type.STRING, pathIds); + return (resourceValue == null) ? null : + (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(pathIds), ResourceModel.Type.STRING, pathIds); } - } 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 89ef741da9..e91f8479e2 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 @@ -104,7 +104,6 @@ public class LwM2MClient implements Cloneable { if (objectModel != null) { ((LwM2mObjectInstance)((ReadResponse)resp).getContent()).getResources().forEach((k, v) -> { String rez = pathIds.toString() + "/" + k; - boolean ismulti = objectModel.resources.get(k).multiple; if (objectModel.resources.get(k).multiple){ this.resources.put(rez, new ResourceValue(v.getValues(), null, true)); } From 3a7101a8a356cb67756d8ca31ce45d0a1658821a Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 30 Dec 2020 15:55:57 +0200 Subject: [PATCH 13/41] Lwm2m: backEnd: model Client from provide --- common/transport/lwm2m/pom.xml | 15 +- .../lwm2m/server/LwM2MTransportRequest.java | 12 +- .../lwm2m/server/LwM2MTransportService.java | 183 +++++++----------- .../secure/LwM2mInMemorySecurityStore.java | 8 +- common/transport/transport-api/pom.xml | 5 +- .../lwm2m/LwM2MTransportConfigServer.java | 37 ++-- pom.xml | 25 +++ transport/lwm2m/pom.xml | 4 - 8 files changed, 135 insertions(+), 154 deletions(-) diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index a904d1e12f..d23f8dbc5d 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -65,15 +65,10 @@ ch.qos.logback logback-classic - org.eclipse.leshan leshan-server-cf - - - - org.eclipse.leshan leshan-client-cf @@ -83,12 +78,6 @@ org.eclipse.leshan leshan-server-redis - - - - - - org.springframework.boot spring-boot-starter-test @@ -107,14 +96,12 @@ org.eclipse.californium californium-core - ${californium.version} - test-jar + test-jar test org.eclipse.californium element-connector - ${californium.version} test-jar test diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index 134c979529..e919835a1a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -124,7 +124,7 @@ public class LwM2MTransportRequest { if (registration != null && resultIds.getObjectId() >= 0) { DownlinkRequest request = null; ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null; - ResourceModel resource = service.context.getCtxServer().getResourceModel(resultIds); + ResourceModel resource = service.context.getCtxServer().getResourceModel(registration, resultIds); timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; switch (typeOper) { case GET_TYPE_OPER_READ: @@ -222,7 +222,7 @@ public class LwM2MTransportRequest { this.sendRequest(lwServer, registration, request, lwM2MClient, timeoutInMs, isDelayedUpdate); } else if (request == null && isDelayedUpdate) { String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg No SendRequest to Client", target); - service.sentLogsToThingsboard(msg, registration.getId()); + service.sentLogsToThingsboard(msg, registration); log.error("[{}] - [{}] No SendRequest", target); this.handleResponseError(registration, target, lwM2MClient, isDelayedUpdate); @@ -251,14 +251,14 @@ public class LwM2MTransportRequest { String msg = String.format(LOG_LW2M_INFO + ": sendRequest Replace%s: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client", delayedUpdateStr, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(), ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue().toString()); - service.sentLogsToThingsboard(msg, registration.getId()); + service.sentLogsToThingsboard(msg, registration); log.info("[{}] - [{}] [{}] [{}] Update SendRequest[{}]", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue(), delayedUpdateStr); } } else { String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); - service.sentLogsToThingsboard(msg, registration.getId()); + service.sentLogsToThingsboard(msg, registration); log.error("[{}] - [{}] [{}] error SendRequest", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) { this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate); @@ -268,7 +268,7 @@ public class LwM2MTransportRequest { }, e -> { String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg error - %s SendRequest to Client", request.getPath().toString(), e.toString()); - service.sentLogsToThingsboard(msg, registration.getId()); + service.sentLogsToThingsboard(msg, registration); log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) { this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate); @@ -300,7 +300,7 @@ public class LwM2MTransportRequest { String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", patn, type, value, e.toString()); - service.sentLogsToThingsboard(msg, registration.getId()); + service.sentLogsToThingsboard(msg, registration); log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); return null; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index ba3cd1f736..d031e63c89 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -20,13 +20,11 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; -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.LwM2mObject; import org.eclipse.leshan.core.node.LwM2mObjectInstance; 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.observation.Observation; import org.eclipse.leshan.core.request.ContentFormat; @@ -51,7 +49,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCre import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; -import org.thingsboard.server.transport.lwm2m.server.client.ModelObject; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore; @@ -62,9 +59,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Optional; import java.util.Random; import java.util.Set; @@ -76,7 +73,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Predicate; import java.util.stream.Collectors; import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject; @@ -148,13 +144,14 @@ public class LwM2MTransportService { public void onRegistered(LeshanServer lwServer, Registration registration, Collection previousObsersations) { executorRegistered.submit(() -> { try { - log.info("[{}] Client: onRegistered name ", registration.getEndpoint()); + log.info("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration); if (lwM2MClient != null) { lwM2MClient.setLwM2MTransportService(this); lwM2MClient.setSessionUuid(UUID.randomUUID()); + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration); this.setLwM2MClient(lwServer, registration, lwM2MClient); - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId()); + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); if (sessionInfo != null) { lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); @@ -163,7 +160,7 @@ public class LwM2MTransportService { transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client registration", registration.getId()); + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); } else { log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } @@ -184,7 +181,7 @@ public class LwM2MTransportService { public void updatedReg(LeshanServer lwServer, Registration registration) { executorUpdateRegistered.submit(() -> { try { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId()); + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); if (sessionInfo != null) { this.checkInactivity(sessionInfo); log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); @@ -206,7 +203,7 @@ public class LwM2MTransportService { public void unReg(Registration registration, Collection observations) { executorUnRegistered.submit(() -> { try { - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); this.closeClientSession(registration); } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); @@ -215,7 +212,7 @@ public class LwM2MTransportService { } private void closeClientSession(Registration registration) { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId()); + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); if (sessionInfo != null) { transportService.deregisterSession(sessionInfo); this.doCloseSession(sessionInfo); @@ -278,9 +275,6 @@ public class LwM2MTransportService { * @param lwM2MClient - object with All parameters off client */ private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) { - // #0 - this.setNewObjectModels(lwServer, registration); - // #1 Arrays.stream(registration.getObjectLinks()).forEach(url -> { LwM2mPath pathIds = new LwM2mPath(url.getUrl()); if (pathIds.isObjectInstance() && !pathIds.isResource()) { @@ -297,30 +291,35 @@ public class LwM2MTransportService { }); } - private void setNewObjectModels(LeshanServer lwServer, Registration registration) { - Arrays.stream(registration.getObjectLinks()).forEach(url -> { - LwM2mPath pathIds = new LwM2mPath(url.getUrl()); - if (pathIds.isObjectInstance() && !pathIds.isResource() && !context.getCtxServer().getObjectModels().containsKey(pathIds.getObjectId())) { - ObjectModel model = lwServer.getModelProvider().getObjectModel(registration).getObjectModels().stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0); - context.getCtxServer().getObjectModels().put(pathIds.getObjectId(), model); - } - }); + /** + * @param registration - Registration LwM2M Client + * @return - sessionInfo after access connect client + */ + private SessionInfoProto getValidateSessionInfo(Registration registration) { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); + return getNewSessionInfoProto(lwM2MClient); + } /** - * @param registrationId - Id of Registration LwM2M Client - * @return - sessionInfo after access connect client + * + * @param registrationId - + * @return - */ private SessionInfoProto getValidateSessionInfo(String registrationId) { - SessionInfoProto sessionInfo = null; - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registrationId); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId); + return getNewSessionInfoProto(lwM2MClient); + } + + private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) { if (lwM2MClient != null) { ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse(); if (msg == null || msg.getDeviceInfo() == null) { log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED); this.closeClientSession(lwM2MClient.getRegistration()); + return null; } else { - sessionInfo = SessionInfoProto.newBuilder() + return SessionInfoProto.newBuilder() .setNodeId(this.context.getNodeId()) .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits()) .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits()) @@ -335,7 +334,7 @@ public class LwM2MTransportService { .build(); } } - return sessionInfo; + return null; } /** @@ -365,7 +364,7 @@ public class LwM2MTransportService { * @param lwM2MClient - LwM2M Client */ public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration().getId()); + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); if (sessionInfo != null) { //#1.1 + #1.2 List attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient); @@ -404,7 +403,7 @@ public class LwM2MTransportService { attributesResponse.getSharedAttributeListList().forEach(attr -> { String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey()); // #1.1 - if (lwM2MClient.getDelayedRequests().keySet().contains(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) { + if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) { lwM2MClient.getDelayedRequests().put(path, attr); } else { lwM2MClient.getDelayedRequests().put(path, attr); @@ -412,7 +411,7 @@ public class LwM2MTransportService { }); // #2.1 lwM2MClient.getDelayedRequests().forEach((k, v) -> { - List listV = new ArrayList(); + ArrayList listV = new ArrayList<>(); listV.add(v.getKv()); this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k); }); @@ -432,24 +431,24 @@ public class LwM2MTransportService { } /** - * Get names and keyNames from profile attr resources IsWritable - * + * Get names and keyNames from profile shared!!!! attr resources IsWritable * @param lwM2MClient - - * @return ArrayList names and keyNames from profile attr resources IsWritable + * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable */ private List getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) { - Set namesIsIsWritable = ConcurrentHashMap.newKeySet(); AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid()); Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class); ConcurrentMap keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class); + ConcurrentMap keyNamesIsWritable = keyNamesMap.entrySet() .stream() - .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(new LwM2mPath(e.getKey())) != null && - context.getCtxServer().getResourceModel(new LwM2mPath(e.getKey())).operations.isWritable())) + .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null && + context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable())) .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)); - namesIsIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values())); - keyNamesIsWritable.keySet().forEach(p -> namesIsIsWritable.add(this.getResourceName(p))); - return new ArrayList<>(namesIsIsWritable); + + Set namesIsWritable = ConcurrentHashMap.newKeySet(); + namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values())); + return new ArrayList<>(namesIsWritable); } @@ -484,9 +483,9 @@ public class LwM2MTransportService { log.error("[{}] updateAttrTelemetry", e.toString()); } if (attributes.getAsJsonObject().entrySet().size() > 0) - this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration.getId()); + this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration); if (telemetries.getAsJsonObject().entrySet().size() > 0) - this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration.getId()); + this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration); } /** @@ -541,9 +540,8 @@ public class LwM2MTransportService { JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile(); String resName = String.valueOf(names.get(path)); if (resName != null && !resName.isEmpty()) { - String resValue = null; try { - resValue = this.getResourceValueToString(lwM2MClient, path); + String resValue = this.getResourceValueToString(lwM2MClient, path); if (resValue != null) { parameters.addProperty(resName, resValue); } @@ -558,14 +556,14 @@ public class LwM2MTransportService { * * @param msg - JsonArray: [{name: value}] * @param topicName - Api Attribute or Telemetry - * @param registrationId - Id of Registration LwM2M Client + * @param registration - Id of Registration LwM2M Client */ - public void updateParametersOnThingsboard(JsonElement msg, String topicName, String registrationId) { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId); + public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) { + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); if (sessionInfo != null) { context.sentParametersOnThingsboard(msg, topicName, sessionInfo); } else { - log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registrationId, null); + log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null); } } @@ -655,10 +653,9 @@ public class LwM2MTransportService { public void onObservationResponse(Registration registration, String path, ReadResponse response) { if (response.getContent() != null) { if (response.getContent() instanceof LwM2mObject) { - LwM2mObject content = (LwM2mObject) response.getContent(); - String target = "/" + content.getId(); +// LwM2mObject content = (LwM2mObject) response.getContent(); } else if (response.getContent() instanceof LwM2mObjectInstance) { - LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent(); +// LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent(); } else if (response.getContent() instanceof LwM2mSingleResource) { LwM2mSingleResource content = (LwM2mSingleResource) response.getContent(); this.onObservationSetResourcesValue(registration, content.getValue(), null, path); @@ -684,10 +681,10 @@ public class LwM2MTransportService { boolean isChange = false; try { // #1 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); LwM2mPath pathIds = new LwM2mPath(path); log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values); - ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(pathIds); + ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds); ResourceValue resValueOld = lwM2MClient.getResources().get(path); // #2 if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) { @@ -739,8 +736,8 @@ public class LwM2MTransportService { String value = de.getValue().getAsString(); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); - ResourceModel resourceModel = context.getCtxServer().getResourceModel(new LwM2mPath(path)); - if (path != null && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) { + ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path)); + if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) { if (resourceModel != null && resourceModel.operations.isWritable()) { lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(), @@ -748,12 +745,12 @@ public class LwM2MTransportService { } else { log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value); String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value); - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); } } else { log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value); String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value); - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); } }); } else if (msg.getSharedDeletedCount() > 0) { @@ -767,11 +764,12 @@ public class LwM2MTransportService { * * @param sessionInfo - * @param name - - * @return true if path isPresent in postProfile + * @return path if path isPresent in postProfile */ private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) { String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name); - return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(sessionInfo, name); +// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name); + return !profilePath.isEmpty() ? profilePath : null; } /** @@ -800,7 +798,7 @@ public class LwM2MTransportService { * * @param sessionInfo - * @param name - - * @return + * @return - */ private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); @@ -809,31 +807,6 @@ public class LwM2MTransportService { .orElse(""); } - /** - * Get path to resource from ModelObject equal name - * - * @param name - - * @return true if name isPresent as Resource name (usual format) in ResourceModel - */ - private String getPathAttributeUpdateModelObject(TransportProtos.SessionInfoProto sessionInfo, String name) { - try { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); - Predicate> predicateRes = res -> name.equals(res.getValue().name); - Predicate> predicateObj = (obj -> { - return obj.getValue().getObjectModel().resources.entrySet().stream().filter(predicateRes).findFirst().isPresent(); - }); -// Map.Entry object = lwM2MClient.getModelObjects().entrySet().stream().filter(predicateObj).findFirst().get(); - Map.Entry object = null; - ModelObject modelObject = object.getValue(); - LwM2mObjectInstance instance = modelObject.getInstances().entrySet().stream().findFirst().get().getValue(); - ResourceModel resource = modelObject.getObjectModel().resources.entrySet().stream().filter(predicateRes).findFirst().get().getValue(); - return new LwM2mPath(object.getKey(), instance.getId(), resource.id).toString(); - } catch (NoSuchElementException e) { - log.error("[{}] keyName [{}]", name, e.toString()); - return null; - } - } - /** * Update resource (attribute) value on thingsboard after update value in client * @@ -842,17 +815,14 @@ public class LwM2MTransportService { * @param request - */ public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) { -// ResultIds resultIds = new ResultIds(path); - LwM2mPath resultIds = new LwM2mPath(path); - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); -// LwM2mResource resource = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances().get(resultIds.getObjectInstanceId()).getResource(resultIds.getResourceId()); - LwM2mResource resource = null; - if (resource.isMultiInstances()) { + ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path)); + if (resource.multiple) { this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path); } else { this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path); } - if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(request.getPath().toString()); + if (isDelayedUpdate) lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null) + .onSuccessOrErrorDelayedRequests(request.getPath().toString()); } /** @@ -863,7 +833,7 @@ public class LwM2MTransportService { Set registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet() .stream() .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId())) - .map(Map.Entry::getKey).sorted().collect(Collectors.toSet()); + .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new)); if (registrationIds.size() > 0) { this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile); } @@ -968,11 +938,11 @@ public class LwM2MTransportService { if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) { // update value in Resources registrationIds.forEach(registrationId -> { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registrationId); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId); LeshanServer lwServer = lwM2MClient.getLwServer(); Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); log.warn("[{}] # 4.1", registration.getEndpoint()); - this.updateResourceValueObserve(lwServer, registration, lwM2MClient, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ); + this.updateResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ); // sent attr/telemetry to tingsboard for new path this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd()); }); @@ -997,11 +967,11 @@ public class LwM2MTransportService { ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd()); // sent Request observe to Client registrationIds.forEach(registrationId -> { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registrationId); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(null, registrationId); LeshanServer lwServer = lwM2MClient.getLwServer(); Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); log.warn("[{}] # 5.1", registration.getEndpoint()); - this.updateResourceValueObserve(lwServer, registration, lwM2MClient, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE); + this.updateResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE); // 5.3 del // sent Request cancel observe to Client this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel()); @@ -1052,10 +1022,9 @@ public class LwM2MTransportService { * * @param lwServer - LeshanServer * @param registration - Registration LwM2M Client - * @param lwM2MClient - object with All parameters off client * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] */ - private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient, Set targets, String typeOper) { + private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, Set targets, String typeOper) { targets.forEach(target -> { LwM2mPath pathIds = new LwM2mPath(target); if (pathIds.isResource()) { @@ -1073,7 +1042,7 @@ public class LwM2MTransportService { } private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set paramAnallyzer) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId()); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); paramAnallyzer.forEach(p -> { if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) { this.setCancelObservationRecourse(lwServer, registration, p); @@ -1130,7 +1099,7 @@ public class LwM2MTransportService { /** * if sessionInfo removed from sessions, then new registerAsyncSession - * @param sessionInfo + * @param sessionInfo - */ private void checkInactivity(SessionInfoProto sessionInfo) { if (transportService.reportActivity(sessionInfo) == null) { @@ -1138,20 +1107,14 @@ public class LwM2MTransportService { } } - public void sentLogsToThingsboard(String msg, String registrationId) { + public void sentLogsToThingsboard(String msg, Registration registration) { if (msg != null) { JsonObject telemetries = new JsonObject(); telemetries.addProperty(LOG_LW2M_TELEMETRY, msg); - this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registrationId); + this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration); } } - private String getResourceName(String path) { - LwM2mPath resultIds = new LwM2mPath(path); - return (context.getCtxServer().getObjectModels().get(resultIds.getObjectId()) != null) ? - context.getCtxServer().getObjectModels().get(resultIds.getObjectId()).resources.get(resultIds.getResourceId()).name : ""; - } - /** * @param path - path resource * @return - value of Resource or null @@ -1160,6 +1123,6 @@ public class LwM2MTransportService { LwM2mPath pathIds = new LwM2mPath(path); ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds); return (resourceValue == null) ? null : - (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(pathIds), ResourceModel.Type.STRING, pathIds); + (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java index 216136be8b..ad8dd4b0f8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java @@ -129,8 +129,12 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { return (modelClients != null) ? modelClients.getValue() : null; } - public LwM2MClient getLwM2MClient(String registrationId) { - return this.sessions.get(registrationId); + public LwM2MClient getLwM2MClientWithReg(Registration registration, String registrationId) { + return registrationId != null ? + this.sessions.get(registrationId) : + this.sessions.containsKey(registration.getId()) ? + this.sessions.get(registration.getId()) : + this.sessions.get(registration.getEndpoint()); } public LwM2MClient getLwM2MClient(TransportProtos.SessionInfoProto sessionInfo) { diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 87a4562f81..77709c3ddc 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -114,9 +114,12 @@ org.eclipse.leshan leshan-core - 1.0.1 compile + + org.eclipse.leshan + leshan-server-cf + diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java index 89c1600bf5..f2e398f063 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java @@ -22,21 +22,25 @@ import org.eclipse.leshan.core.model.ObjectLoader; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mPath; +import org.eclipse.leshan.server.registration.Registration; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; @Slf4j @Component @@ -177,10 +181,6 @@ public class LwM2MTransportConfigServer { @Value("${transport.lwm2m.secure.redis_url:}") private String redisUrl; - @Getter - @Setter - private Map objectModels; - @PostConstruct public void init() { modelsValue = ObjectLoader.loadDefault(); @@ -196,7 +196,6 @@ public class LwM2MTransportConfigServer { log.error(" [{}] Read Models", path.getAbsoluteFile()); } getInKeyStore(); - this.objectModels = new ConcurrentHashMap(); } private File getPathModels() { @@ -248,21 +247,25 @@ public class LwM2MTransportConfigServer { return FULL_FILE_PATH.toUri().getPath(); } - public ResourceModel getResourceModel(LwM2mPath pathIds) { - return (this.objectModels.size()>0 && - this.objectModels.containsKey(pathIds.getObjectId()) && - this.objectModels.get(pathIds.getObjectId()).resources.containsKey(pathIds.getResourceId())) ? - this.objectModels.get(pathIds.getObjectId()).resources.get(pathIds.getResourceId()) : null; + public ResourceModel getResourceModel(Registration registration, LwM2mPath pathIds) { + String pathLink = "/" + pathIds.getObjectId() + "/" + pathIds.getObjectInstanceId(); + return (Arrays.stream(registration.getObjectLinks()).filter(p-> p.getUrl().equals(pathLink)).findFirst().isPresent() && + this.modelsValue.stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).size() > 0) && + this.modelsValue.stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0).resources.containsKey(pathIds.getResourceId()) ? + this.modelsValue.stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0).resources.get(pathIds.getResourceId()) : + null; } - public ResourceModel.Type getResourceModelType(LwM2mPath pathIds) { - ResourceModel resource = this.getResourceModel(pathIds); + public ResourceModel.Type getResourceModelType(Registration registration, LwM2mPath pathIds) { + ResourceModel resource = this.getResourceModel(registration, pathIds); return (resource == null) ? null : resource.type; } - public ResourceModel.Operations getOperation(LwM2mPath pathIds) { - ResourceModel resource = this.getResourceModel(pathIds); + public ResourceModel.Operations getOperation(Registration registration, LwM2mPath pathIds) { + ResourceModel resource = this.getResourceModel(registration, pathIds); return (resource == null) ? ResourceModel.Operations.NONE : resource.operations; } + + } diff --git a/pom.xml b/pom.xml index d87cc038d5..4522ea51bf 100755 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,7 @@ 2.2.6 2.2.3 1.0.1 + 1.0.1 1.0.0 2.6.2 2.3.30 @@ -1160,11 +1161,35 @@ leshan-server-redis ${leshan-server.version} + + org.eclipse.leshan + leshan-core + ${leshan-core.version} + org.eclipse.californium californium-core ${californium.version} + + org.eclipse.californium + californium-core + ${californium.version} + test-jar + test + + + org.eclipse.californium + californium-core + ${californium.version} + + + org.eclipse.californium + element-connector + ${californium.version} + test-jar + test + com.google.code.gson gson diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 60a2d29d58..1d7619a2ad 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -78,14 +78,10 @@ org.eclipse.californium californium-core - ${californium.version} - test-jar - test org.eclipse.californium element-connector - ${californium.version} test-jar test From 58e544c32e6f2274a3084a1ba1509a020fa2d59d Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 5 Jan 2021 10:50:17 +0200 Subject: [PATCH 14/41] Make LwM2mValueConverterImpl singleton --- .../transport/lwm2m/server/LwM2MTransportRequest.java | 2 +- .../lwm2m/server/LwM2MTransportServerConfiguration.java | 4 ++-- .../transport/lwm2m/server/LwM2MTransportService.java | 2 +- .../server/transport/lwm2m/server/client/LwM2MClient.java | 2 +- .../transport/lwm2m/utils/LwM2mValueConverterImpl.java | 6 ++++++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index e919835a1a..5ee37ac380 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -91,7 +91,7 @@ public class LwM2MTransportRequest { @PostConstruct public void init() { - this.converter = new LwM2mValueConverterImpl(); + this.converter = LwM2mValueConverterImpl.getInstance(); executorResponse = Executors.newCachedThreadPool( new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); executorResponseError = Executors.newCachedThreadPool( diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java index 2725e80e2b..c17400ede8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java @@ -73,7 +73,7 @@ public class LwM2MTransportServerConfiguration { builder.setEncoder(new DefaultLwM2mNodeEncoder()); LwM2mNodeDecoder decoder = new DefaultLwM2mNodeDecoder(); builder.setDecoder(decoder); - builder.setEncoder(new DefaultLwM2mNodeEncoder(new LwM2mValueConverterImpl())); + builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); /** Create CoAP Config */ builder.setCoapConfig(getCoapConfig()); @@ -89,7 +89,7 @@ public class LwM2MTransportServerConfiguration { builder.setDtlsConfig(dtlsConfig); /** Use a magic converter to support bad type send by the UI. */ - builder.setEncoder(new DefaultLwM2mNodeEncoder(new LwM2mValueConverterImpl())); + builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); /** Create DTLS security mode * There can be only one DTLS security mode diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index d031e63c89..9b082ce09f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -123,7 +123,7 @@ public class LwM2MTransportService { new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); this.executorUnRegistered = Executors.newCachedThreadPool( new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); - this.converter = new LwM2mValueConverterImpl(); + this.converter = LwM2mValueConverterImpl.getInstance(); } /** 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 e91f8479e2..6519b5cd33 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 @@ -78,7 +78,7 @@ public class LwM2MClient implements Cloneable { * Key , response instance -> resources: value...> */ this.responses = new ConcurrentHashMap<>(); - this.converter = new LwM2mValueConverterImpl(); + this.converter = LwM2mValueConverterImpl.getInstance(); } /** diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java index 44fcfad9b6..cca1effae4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java @@ -33,6 +33,12 @@ import java.util.Date; @Slf4j public class LwM2mValueConverterImpl implements LwM2mValueConverter { + private static final LwM2mValueConverterImpl INSTANCE = new LwM2mValueConverterImpl(); + + public static LwM2mValueConverterImpl getInstance() { + return INSTANCE; + } + @Override public Object convertValue(Object value, Type currentType, Type expectedType, LwM2mPath resourcePath) throws CodecException { From bc4684c907ecebf581e8c6b4dd9763c6fa0af1fa Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 5 Jan 2021 15:50:41 +0200 Subject: [PATCH 15/41] Lwm2m: backEnd: refactoring security --- ...TransportBootstrapServerConfiguration.java | 176 ++- .../secure/LwM2MBootstrapSecurityStore.java | 74 +- .../LwM2MSetSecurityStoreBootstrap.java | 205 --- .../secure/LWM2MGenerationPSkRPkECC.java | 6 +- ...LwM2mValidateCredentialsSecurityInfo.java} | 45 +- .../lwm2m/server/LwM2MSessionMsgListener.java | 4 +- .../lwm2m/server/LwM2MTransportHandler.java | 66 +- .../lwm2m/server/LwM2MTransportRequest.java | 2 +- .../LwM2MTransportServerConfiguration.java | 221 +++- .../LwM2MTransportServerInitializer.java | 47 +- .../lwm2m/server/LwM2MTransportService.java | 1098 +--------------- .../server/LwM2MTransportServiceImpl.java | 1132 +++++++++++++++++ .../lwm2m/server/LwM2mServerListener.java | 17 +- .../lwm2m/server/client/LwM2MClient.java | 8 +- .../secure/LwM2MSetSecurityStoreServer.java | 241 ---- .../secure/LwM2mInMemorySecurityStore.java | 100 +- .../lwm2m/utils/LwM2mValueConverterImpl.java | 1 - netty-mqtt/pom.xml | 7 +- pom.xml | 2 +- 19 files changed, 1750 insertions(+), 1702 deletions(-) delete mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MSetSecurityStoreBootstrap.java rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/{LwM2MGetSecurityInfo.java => LwM2mValidateCredentialsSecurityInfo.java} (80%) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java delete mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java index a1f056ff30..588de9bba2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java @@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.leshan.core.model.StaticModel; +import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager; import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder; @@ -28,18 +29,40 @@ import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore; -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MSetSecurityStoreBootstrap; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; + +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; @Slf4j @Component @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true'&& '${transport.lwm2m.bootstrap.enable}'=='true')") public class LwM2MTransportBootstrapServerConfiguration { + private PublicKey publicKey; + private PrivateKey privateKey; @Autowired private LwM2MTransportContextBootstrap contextBs; @@ -90,7 +113,7 @@ public class LwM2MTransportBootstrapServerConfiguration { builder.setDtlsConfig(dtlsConfig); /** Create credentials */ - new LwM2MSetSecurityStoreBootstrap(builder, contextBs, contextS, dtlsMode); + LwM2MSetSecurityStoreBootstrap(builder, dtlsMode); BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore); builder.setSessionManager(sessionManager); @@ -98,4 +121,151 @@ public class LwM2MTransportBootstrapServerConfiguration { /** Create BootstrapServer */ return builder.build(); } + + public void LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MSecurityMode dtlsMode) { + + /** Set securityStore with new registrationStore */ + + switch (dtlsMode) { + /** Use No_Sec only */ + case NO_SEC: + setServerWithX509Cert(builder, NO_SEC.code); + break; + /** Use PSK/RPK */ + case PSK: + case RPK: + setRPK(builder); + break; + case X509: + setServerWithX509Cert(builder, X509.code); + break; + /** Use X509_EST only */ + case X509_EST: + // TODO support sentinel pool and make pool configurable + break; + /** Use ather X509, PSK, No_Sec ?? */ + default: + break; + } + } + + private void setRPK(LeshanBootstrapServerBuilder builder) { + try { + /** Get Elliptic Curve Parameter spec for secp256r1 */ + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); + algoParameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); + if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) { + /** Get point values */ + byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray()); + byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray()); + /** Create key specs */ + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), + parameterSpec); + /** Get keys */ + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); + } + if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) { + /** Get point values */ + byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray()); + /** Create key specs */ + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); + /** Get keys */ + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); + } + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 && + this.privateKey != null && this.privateKey.getEncoded().length > 0) { + builder.setPublicKey(this.publicKey); + builder.setPrivateKey(this.privateKey); + this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey); + getParamsRPK(); + } + } catch (GeneralSecurityException | IllegalArgumentException e) { + log.error("[{}] Failed generate Server PSK/RPK", e.getMessage()); + throw new RuntimeException(e); + } + } + + private void setServerWithX509Cert(LeshanBootstrapServerBuilder builder, int securityModeCode) { + try { + if (this.contextS.getCtxServer().getKeyStoreValue() != null) { + KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue(); + setBuilderX509(builder); + X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias()); + if (rootCAX509Cert != null && securityModeCode == X509.code) { + X509Certificate[] trustedCertificates = new X509Certificate[1]; + trustedCertificates[0] = rootCAX509Cert; + builder.setTrustedCertificates(trustedCertificates); + } else { + /** by default trust all */ + builder.setTrustedCertificates(new X509Certificate[0]); + } + } + else { + /** by default trust all */ + builder.setTrustedCertificates(new X509Certificate[0]); + log.error("Unable to load X509 files for BootStrapServer"); + } + } catch (KeyStoreException ex) { + log.error("[{}] Unable to load X509 files server", ex.getMessage()); + } + + } + + private void setBuilderX509(LeshanBootstrapServerBuilder builder) { + /** + * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE + * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks + */ + try { + X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias()); + this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray()); + if (this.privateKey != null && this.privateKey.getEncoded().length > 0) { + builder.setPrivateKey(this.privateKey); + } + if (serverCertificate != null) { + builder.setCertificateChain(new X509Certificate[]{serverCertificate}); + this.contextBs.getCtxBootStrap().setBootstrapCertificate(serverCertificate); + infoParamsX509(serverCertificate); + } + } catch (Exception ex) { + log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); + } + } + + private void getParamsRPK() { + if (this.publicKey instanceof ECPublicKey) { + /** Get x coordinate */ + byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray(); + if (x[0] == 0) + x = Arrays.copyOfRange(x, 1, x.length); + + /** Get Y coordinate */ + byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray(); + if (y[0] == 0) + y = Arrays.copyOfRange(y, 1, y.length); + + /** Get Curves params */ + String params = ((ECPublicKey) this.publicKey).getParams().toString(); + log.info( + " \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]", + params, Hex.encodeHexString(x), Hex.encodeHexString(y), + Hex.encodeHexString(this.publicKey.getEncoded()), + Hex.encodeHexString(this.privateKey.getEncoded())); + } else { + throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported)."); + } + } + + private void infoParamsX509(X509Certificate certificate) { + try { + log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]", + Hex.encodeHexString(certificate.getEncoded()), + Hex.encodeHexString(this.privateKey.getEncoded())); + } catch (CertificateEncodingException e) { + log.error("", e); + } + } + + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index a4e49fc546..6d21d48c65 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -29,10 +29,10 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore; import org.eclipse.leshan.server.security.SecurityInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; +import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; import org.thingsboard.server.transport.lwm2m.server.LwM2MSessionMsgListener; import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; @@ -53,14 +53,14 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getBootstrapParametersFromThingsboard; @Slf4j -@Component("LwM2MBootstrapSecurityStore") +@Service("LwM2MBootstrapSecurityStore") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' && '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true' && '${transport.lwm2m.bootstrap.enable}'=='true')") public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { private final EditableBootstrapConfigStore bootstrapConfigStore; @Autowired - LwM2MGetSecurityInfo lwM2MGetSecurityInfo; + LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo; @Autowired public LwM2MTransportContextServer context; @@ -72,8 +72,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Override public List getAllByEndpoint(String endPoint) { String endPointKey = endPoint; - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(endPointKey, TypeServer.BOOTSTRAP); - if (store.getBootstrapJsonCredential() != null) { + ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(endPointKey, TypeServer.BOOTSTRAP); + if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { /** add value to store from BootstrapJson */ this.setBootstrapConfigScurityInfo(store); BootstrapConfig bsConfigNew = store.getBootstrapConfig(); @@ -86,7 +86,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { } bootstrapConfigStore.add(endPoint, bsConfigNew); } catch (InvalidConfigurationException e) { - e.printStackTrace(); + log.error("", e); } return store.getSecurityInfo() == null ? null : Arrays.asList(store.getSecurityInfo()); } @@ -96,17 +96,16 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Override public SecurityInfo getByIdentity(String identity) { - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.BOOTSTRAP); - /** add value to store from BootstrapJson */ - this.setBootstrapConfigScurityInfo(store); - - if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { + ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.BOOTSTRAP); + if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { + /** add value to store from BootstrapJson */ + this.setBootstrapConfigScurityInfo(store); BootstrapConfig bsConfig = store.getBootstrapConfig(); if (bsConfig.security != null) { try { bootstrapConfigStore.add(store.getEndPoint(), bsConfig); } catch (InvalidConfigurationException e) { - e.printStackTrace(); + log.error("", e); } return store.getSecurityInfo(); } @@ -154,33 +153,36 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { private LwM2MBootstrapConfig getParametersBootstrap(ReadResultSecurityStore store) { try { JsonObject bootstrapJsonCredential = store.getBootstrapJsonCredential(); - ObjectMapper mapper = new ObjectMapper(); - LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class); - JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); - lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class); - LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class); - LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class); - UUID sessionUUiD = UUID.randomUUID(); - TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); - context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo)); - if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { - lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); - lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); - String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint()); - context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo); - return lwM2MBootstrapConfig; - } - else { - log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint()); - log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint()); - String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint()); - context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo); - return null; + if (bootstrapJsonCredential != null) { + ObjectMapper mapper = new ObjectMapper(); + LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class); + JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); + lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class); + LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class); + LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class); + UUID sessionUUiD = UUID.randomUUID(); + TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo)); + if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { + lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); + lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); + String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint()); + context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo); + return lwM2MBootstrapConfig; + } else { + log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint()); + log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint()); + String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint()); + context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo); + return null; + } } } catch (JsonProcessingException e) { log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndPoint(), e.getMessage()); return null; } + log.error("Unable to decode Json or Certificate for [{}]", store.getEndPoint()); + return null; } /** @@ -193,7 +195,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { * @return false if not sync between SecurityMode of Bootstrap credential and profile */ private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap profileServerBootstrap, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) { - return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) && + return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) && lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode())); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MSetSecurityStoreBootstrap.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MSetSecurityStoreBootstrap.java deleted file mode 100644 index f648799204..0000000000 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MSetSecurityStoreBootstrap.java +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Copyright © 2016-2020 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.transport.lwm2m.bootstrap.secure; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.util.Hex; -import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder; -import org.eclipse.leshan.server.security.EditableSecurityStore; -import org.thingsboard.server.transport.lwm2m.bootstrap.LwM2MTransportContextBootstrap; -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; -import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; - -import java.math.BigInteger; -import java.security.AlgorithmParameters; -import java.security.KeyStore; -import java.security.PublicKey; -import java.security.PrivateKey; -import java.security.KeyFactory; -import java.security.GeneralSecurityException; -import java.security.KeyStoreException; -import java.security.cert.X509Certificate; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.ECPoint; -import java.security.spec.KeySpec; -import java.security.spec.ECPrivateKeySpec; -import java.util.Arrays; - -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; - -@Slf4j -@Data -public class LwM2MSetSecurityStoreBootstrap { - - private KeyStore keyStore; - private PublicKey publicKey; - private PrivateKey privateKey; - private LwM2MTransportContextBootstrap contextBs; - private LwM2MTransportContextServer contextS; - private LeshanBootstrapServerBuilder builder; - EditableSecurityStore securityStore; - - public LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MTransportContextBootstrap contextBs, LwM2MTransportContextServer contextS, LwM2MSecurityMode dtlsMode) { - this.builder = builder; - this.contextBs = contextBs; - this.contextS = contextS; - /** Set securityStore with new registrationStore */ - - switch (dtlsMode) { - /** Use No_Sec only */ - case NO_SEC: - setServerWithX509Cert(NO_SEC.code); - break; - /** Use PSK/RPK */ - case PSK: - case RPK: - setRPK(); - break; - case X509: - setServerWithX509Cert(X509.code); - break; - /** Use X509_EST only */ - case X509_EST: - // TODO support sentinel pool and make pool configurable - break; - /** Use ather X509, PSK, No_Sec ?? */ - default: - break; - } - } - - private void setRPK() { - try { - /** Get Elliptic Curve Parameter spec for secp256r1 */ - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) { - /** Get point values */ - byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray()); - byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray()); - /** Create key specs */ - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - /** Get keys */ - this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - } - if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) { - /** Get point values */ - byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray()); - /** Create key specs */ - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); - /** Get keys */ - this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); - } - if (this.publicKey != null && this.publicKey.getEncoded().length > 0 && - this.privateKey != null && this.privateKey.getEncoded().length > 0) { - this.builder.setPublicKey(this.publicKey); - this.builder.setPrivateKey(this.privateKey); - this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey); - getParamsRPK(); - } - } catch (GeneralSecurityException | IllegalArgumentException e) { - log.error("[{}] Failed generate Server PSK/RPK", e.getMessage()); - throw new RuntimeException(e); - } - } - - private void setServerWithX509Cert(int securityModeCode) { - try { - if (this.contextS.getCtxServer().getKeyStoreValue() != null) { - KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue(); - setBuilderX509(); - X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias()); - if (rootCAX509Cert != null && securityModeCode == X509.code) { - X509Certificate[] trustedCertificates = new X509Certificate[1]; - trustedCertificates[0] = rootCAX509Cert; - this.builder.setTrustedCertificates(trustedCertificates); - } else { - /** by default trust all */ - this.builder.setTrustedCertificates(new X509Certificate[0]); - } - } - else { - /** by default trust all */ - this.builder.setTrustedCertificates(new X509Certificate[0]); - log.error("Unable to load X509 files for BootStrapServer"); - } - } catch (KeyStoreException ex) { - log.error("[{}] Unable to load X509 files server", ex.getMessage()); - } - - } - - private void setBuilderX509() { - /** - * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE - * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks - */ - try { - X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias()); - this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray()); - if (this.privateKey != null && this.privateKey.getEncoded().length > 0) { - this.builder.setPrivateKey(this.privateKey); - } - if (serverCertificate != null) { - this.builder.setCertificateChain(new X509Certificate[]{serverCertificate}); - this.contextBs.getCtxBootStrap().setBootstrapCertificate(serverCertificate); - } - } catch (Exception ex) { - log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); - } - } - - private void getParamsRPK() { - if (this.publicKey instanceof ECPublicKey) { - /** Get x coordinate */ - byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray(); - if (x[0] == 0) - x = Arrays.copyOfRange(x, 1, x.length); - - /** Get Y coordinate */ - byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray(); - if (y[0] == 0) - y = Arrays.copyOfRange(y, 1, y.length); - - /** Get Curves params */ - String params = ((ECPublicKey) this.publicKey).getParams().toString(); - log.info( - " \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]", - params, Hex.encodeHexString(x), Hex.encodeHexString(y), - Hex.encodeHexString(this.publicKey.getEncoded()), - Hex.encodeHexString(this.privateKey.getEncoded())); - } else { - throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported)."); - } - } - -// private void getParamsX509() { -// try { -// log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]", -// Hex.encodeHexString(this.certificate.getEncoded()), -// Hex.encodeHexString(this.privateKey.getEncoded())); -// } catch (CertificateEncodingException e) { -// e.printStackTrace(); -// } -// } -} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java index 1d70ae2d83..0921df1104 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java @@ -73,15 +73,15 @@ public class LWM2MGenerationPSkRPkECC { try { kpg = KeyPairGenerator.getInstance(algorithm, provider); } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); + log.error("", e); } catch (NoSuchProviderException e) { - e.printStackTrace(); + log.error("", e); } ECGenParameterSpec ecsp = new ECGenParameterSpec(nameParameterSpec); try { kpg.initialize(ecsp); } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); + log.error("", e); } KeyPair kp = kpg.genKeyPair(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java similarity index 80% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java index a02107ca45..0983f9c87f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java @@ -44,11 +44,10 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PS import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; - @Slf4j @Component("LwM2MGetSecurityInfo") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") -public class LwM2MGetSecurityInfo { +public class LwM2mValidateCredentialsSecurityInfo { @Autowired public LwM2MTransportContextServer contextS; @@ -57,7 +56,13 @@ public class LwM2MGetSecurityInfo { public LwM2MTransportContextBootstrap contextBS; - public ReadResultSecurityStore getSecurityInfo(String endPoint, TypeServer keyValue) { + /** + * Request to thingsboard Response from thingsboard ValidateDeviceLwM2MCredentials + * @param endPoint - + * @param keyValue - + * @return ValidateDeviceCredentialsResponseMsg and SecurityInfo + */ + public ReadResultSecurityStore validateCredentialsSecurityInfo(String endPoint, TypeServer keyValue) { CountDownLatch latch = new CountDownLatch(1); final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1]; contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endPoint).build(), @@ -65,7 +70,7 @@ public class LwM2MGetSecurityInfo { @Override public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { String credentialsBody = msg.getCredentialsBody(); - resultSecurityStore[0] = putSecurityInfo(endPoint, msg.getDeviceInfo().getDeviceName(), credentialsBody, keyValue); + resultSecurityStore[0] = createSecurityInfo(endPoint, credentialsBody, keyValue); resultSecurityStore[0].setMsg(msg); Optional deviceProfileOpt = LwM2MTransportHandler.decode(msg.getProfileBody().toByteArray()); deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile)); @@ -74,20 +79,27 @@ public class LwM2MGetSecurityInfo { @Override public void onError(Throwable e) { - log.trace("[{}] Failed to process credentials PSK: {}", endPoint, e); - resultSecurityStore[0] = putSecurityInfo(endPoint, null, null, null); + log.trace("[{}] [{}] Failed to process credentials PSK ", endPoint, e.toString()); + resultSecurityStore[0] = createSecurityInfo(endPoint, null, null); latch.countDown(); } }); try { latch.await(contextS.getCtxServer().getTimeout(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("", e); } return resultSecurityStore[0]; } - private ReadResultSecurityStore putSecurityInfo(String endPoint, String deviceName, String jsonStr, TypeServer keyValue) { + /** + * Create new SecurityInfo + * @param endPoint - + * @param jsonStr - + * @param keyValue - + * @return SecurityInfo + */ + private ReadResultSecurityStore createSecurityInfo(String endPoint, String jsonStr, TypeServer keyValue) { ReadResultSecurityStore result = new ReadResultSecurityStore(); JsonObject objectMsg = LwM2MTransportHandler.validateJson(jsonStr); if (objectMsg != null && !objectMsg.isJsonNull()) { @@ -103,20 +115,21 @@ public class LwM2MGetSecurityInfo { if (keyValue.equals(TypeServer.BOOTSTRAP)) { result.setBootstrapJsonCredential(object); result.setEndPoint(endPoint); + result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code); } else { LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(object.get("securityConfigClientMode").getAsString().toLowerCase()); switch (lwM2MSecurityMode) { case NO_SEC: - getClientSecurityInfoNoSec(result); + createClientSecurityInfoNoSec(result); break; case PSK: - getClientSecurityInfoPSK(result, endPoint, object); + createClientSecurityInfoPSK(result, endPoint, object); break; case RPK: - getClientSecurityInfoRPK(result, endPoint, object); + createClientSecurityInfoRPK(result, endPoint, object); break; case X509: - getClientSecurityInfoX509(result, endPoint); + createClientSecurityInfoX509(result, endPoint); break; default: break; @@ -127,12 +140,12 @@ public class LwM2MGetSecurityInfo { return result; } - private void getClientSecurityInfoNoSec(ReadResultSecurityStore result) { + private void createClientSecurityInfoNoSec(ReadResultSecurityStore result) { result.setSecurityInfo(null); result.setSecurityMode(NO_SEC.code); } - private void getClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) { + private void createClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) { /** PSK Deserialization */ String identity = (object.has("identity") && object.get("identity").isJsonPrimitive()) ? object.get("identity").getAsString() : null; if (identity != null && !identity.isEmpty()) { @@ -152,7 +165,7 @@ public class LwM2MGetSecurityInfo { } } - private void getClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) { + private void createClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) { try { if (object.has("key") && object.get("key").isJsonPrimitive()) { byte[] rpkkey = Hex.decodeHex(object.get("key").getAsString().toLowerCase().toCharArray()); @@ -167,7 +180,7 @@ public class LwM2MGetSecurityInfo { } } - private void getClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) { + private void createClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) { result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint)); result.setSecurityMode(X509.code); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java index 97963038b8..7c3450486e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java @@ -33,10 +33,10 @@ import java.util.Optional; @Slf4j public class LwM2MSessionMsgListener implements GenericFutureListener>, SessionMsgListener { - private LwM2MTransportService service; + private LwM2MTransportServiceImpl service; private TransportProtos.SessionInfoProto sessionInfo; - public LwM2MSessionMsgListener(LwM2MTransportService service, TransportProtos.SessionInfoProto sessionInfo) { + public LwM2MSessionMsgListener(LwM2MTransportServiceImpl service, TransportProtos.SessionInfoProto sessionInfo) { this.service = service; this.sessionInfo = sessionInfo; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java index 14d06eb7e6..76cc2a8015 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java @@ -31,20 +31,14 @@ import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.util.Hex; -import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.nustaq.serialization.FSTConfiguration; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; -import javax.annotation.PostConstruct; import java.io.File; import java.io.IOException; import java.util.Arrays; @@ -53,8 +47,8 @@ import java.util.LinkedList; import java.util.Optional; @Slf4j -@Component("LwM2MTransportHandler") -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") +//@Component("LwM2MTransportHandler") +//@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportHandler { // We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to @@ -114,32 +108,38 @@ public class LwM2MTransportHandler { public static final String SERVICE_CHANNEL = "SERVICE"; public static final String RESPONSE_CHANNEL = "RESP"; - @Autowired - @Qualifier("LeshanServerCert") - private LeshanServer lhServerCert; +// @Autowired +// @Qualifier("LeshanServerCert") +// private LeshanServer lhServerCert; +// +// @Autowired +// @Qualifier("LeshanServerNoSecPskRpk") +// private LeshanServer lhServerNoSecPskRpk; - @Autowired - @Qualifier("leshanServerNoSecPskRpk") - private LeshanServer lhServerNoSecPskRpk; +// @Autowired +// @Qualifier("ServerListenerCert") +// private LwM2mServerListener serverListenerCert; +// +// @Autowired +// @Qualifier("ServerListenerNoSecPskRpk") +// private LwM2mServerListener serverListenerNoSecPskRpk; - @Autowired - private LwM2MTransportService service; - @PostConstruct - public void init() { - try { - LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service); - this.lhServerCert.getRegistrationService().addListener(lwM2mServerListener.registrationListener); - this.lhServerCert.getPresenceService().addListener(lwM2mServerListener.presenceListener); - this.lhServerCert.getObservationService().addListener(lwM2mServerListener.observationListener); - lwM2mServerListener = new LwM2mServerListener(lhServerNoSecPskRpk, service); - this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener); - this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener); - this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener); - } catch (java.lang.NullPointerException e) { - log.error("init [{}]", e.toString()); - } - } +// @PostConstruct +// public void init() { +// try { +// serverListenerCert.init(lhServerCert); +// this.lhServerCert.getRegistrationService().addListener(serverListenerCert.registrationListener); +// this.lhServerCert.getPresenceService().addListener(serverListenerCert.presenceListener); +// this.lhServerCert.getObservationService().addListener(serverListenerCert.observationListener); +// serverListenerNoSecPskRpk.init(lhServerNoSecPskRpk); +// this.lhServerNoSecPskRpk.getRegistrationService().addListener(serverListenerNoSecPskRpk.registrationListener); +// this.lhServerNoSecPskRpk.getPresenceService().addListener(serverListenerNoSecPskRpk.presenceListener); +// this.lhServerNoSecPskRpk.getObservationService().addListener(serverListenerNoSecPskRpk.observationListener); +// } catch (Exception e) { +// log.error("init [{}]", e.toString()); +// } +// } public static NetworkConfig getCoapConfig() { NetworkConfig coapConfig; @@ -217,7 +217,7 @@ public class LwM2MTransportHandler { JsonObject objectMsg = (observeAttrStr != null) ? validateJson(observeAttrStr) : null; return (getValidateCredentialsBodyFromThingsboard(objectMsg)) ? objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject() : null; } catch (IOException e) { - e.printStackTrace(); + log.error("", e); } } return null; @@ -232,7 +232,7 @@ public class LwM2MTransportHandler { JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null; return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null; } catch (IOException e) { - e.printStackTrace(); + log.error("", e); } } return null; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index 5ee37ac380..bc7cbd5b46 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -86,7 +86,7 @@ public class LwM2MTransportRequest { private LwM2mValueConverterImpl converter; @Autowired - LwM2MTransportService service; + LwM2MTransportServiceImpl service; @PostConstruct diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java index c17400ede8..ac2221054c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java @@ -20,10 +20,16 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder; +import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.model.VersionedModelProvider; +import org.eclipse.leshan.server.redis.RedisRegistrationStore; +import org.eclipse.leshan.server.redis.RedisSecurityStore; +import org.eclipse.leshan.server.security.DefaultAuthorizer; +import org.eclipse.leshan.server.security.EditableSecurityStore; +import org.eclipse.leshan.server.security.SecurityChecker; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; @@ -31,11 +37,33 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; -import org.thingsboard.server.transport.lwm2m.server.secure.LwM2MSetSecurityStoreServer; import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.util.Pool; + +import java.math.BigInteger; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyStoreException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.KeySpec; +import java.util.Arrays; + import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; @@ -45,6 +73,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle @Configuration("LwM2MTransportServerConfiguration") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportServerConfiguration { + private PublicKey publicKey; + private PrivateKey privateKey; @Autowired private LwM2MTransportContextServer context; @@ -52,14 +82,26 @@ public class LwM2MTransportServerConfiguration { @Autowired private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; + @Bean + public LwM2mServerListener lwM2mServerListenerCert() { + return new LwM2mServerListener(); + } + + @Bean + public LwM2mServerListener lwM2mServerListenerNoSecPskRpk() { + return new LwM2mServerListener(); + } + @Primary @Bean(name = "LeshanServerCert") public LeshanServer getLeshanServerCert() { log.info("Starting LwM2M transport ServerCert... PostConstruct"); - return getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509); + LeshanServer leshanServerCert = getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509); + + return leshanServerCert; } - @Bean(name = "leshanServerNoSecPskRpk") + @Bean(name = "LeshanServerNoSecPskRpk") public LeshanServer getLeshanServerNoSecPskRpk() { log.info("Starting LwM2M transport ServerNoSecPskRpk... PostConstruct"); return getLeshanServer(this.context.getCtxServer().getServerPort(), this.context.getCtxServer().getServerSecurePort(), RPK); @@ -94,9 +136,178 @@ public class LwM2MTransportServerConfiguration { /** Create DTLS security mode * There can be only one DTLS security mode */ - new LwM2MSetSecurityStoreServer(builder, context, lwM2mInMemorySecurityStore, dtlsMode); + this.LwM2MSetSecurityStoreServer(builder, dtlsMode); /** Create LWM2M server */ return builder.build(); } + + private void LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MSecurityMode dtlsMode) { + /** Set securityStore with new registrationStore */ + EditableSecurityStore securityStore = lwM2mInMemorySecurityStore; + + switch (dtlsMode) { + /** Use PSK only */ + case PSK: + generatePSK_RPK(); + if (this.privateKey != null && this.privateKey.getEncoded().length > 0) { + builder.setPrivateKey(this.privateKey); + builder.setPublicKey(null); + infoParamsPSK(); + } + break; + /** Use RPK only */ + case RPK: + generatePSK_RPK(); + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 && + this.privateKey != null && this.privateKey.getEncoded().length > 0) { + builder.setPublicKey(this.publicKey); + builder.setPrivateKey(this.privateKey); + infoParamsRPK(); + } + break; + /** Use x509 only */ + case X509: + setServerWithX509Cert(builder); + break; + /** No security */ + case NO_SEC: + builder.setTrustedCertificates(new X509Certificate[0]); + break; + /** Use x509 with EST */ + case X509_EST: + // TODO support sentinel pool and make pool configurable + break; + case REDIS: + /** + * Set securityStore with new registrationStore (if use redis store) + * Connect to redis + */ + Pool jedis = null; + try { + jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl())); + securityStore = new RedisSecurityStore(jedis); + builder.setRegistrationStore(new RedisRegistrationStore(jedis)); + } catch (URISyntaxException e) { + log.error("", e); + } + break; + default: + } + + /** Set securityStore with registrationStore (if x509)*/ + if (dtlsMode == X509) { + builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() { + @Override + protected boolean matchX509Identity(String endpoint, String receivedX509CommonName, + String expectedX509CommonName) { + return endpoint.startsWith(expectedX509CommonName); + } + })); + } + + /** Set securityStore with new registrationStore */ + builder.setSecurityStore(securityStore); + } + + private void generatePSK_RPK() { + try { + /** Get Elliptic Curve Parameter spec for secp256r1 */ + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); + algoParameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); + if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) { + /** Get point values */ + byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray()); + byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray()); + /** Create key specs */ + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), + parameterSpec); + /** Get keys */ + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); + } + if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) { + /** Get point values */ + byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray()); + /** Create key specs */ + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); + /** Get keys */ + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); + } + } catch (GeneralSecurityException | IllegalArgumentException e) { + log.error("[{}] Failed generate Server PSK/RPK", e.getMessage()); + throw new RuntimeException(e); + } + } + + private void infoParamsPSK() { + log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]", + Hex.encodeHexString(this.privateKey.getEncoded()), + this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerSecurePort())); + } + + private void infoParamsRPK() { + if (this.publicKey instanceof ECPublicKey) { + /** Get x coordinate */ + byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray(); + if (x[0] == 0) + x = Arrays.copyOfRange(x, 1, x.length); + + /** Get Y coordinate */ + byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray(); + if (y[0] == 0) + y = Arrays.copyOfRange(y, 1, y.length); + + /** Get Curves params */ + String params = ((ECPublicKey) this.publicKey).getParams().toString(); + log.info( + " \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]", + params, Hex.encodeHexString(x), Hex.encodeHexString(y), + Hex.encodeHexString(this.publicKey.getEncoded()), + Hex.encodeHexString(this.privateKey.getEncoded())); + } else { + throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported)."); + } + } + + + private void setServerWithX509Cert(LeshanServerBuilder builder) { + try { + if (this.context.getCtxServer().getKeyStoreValue() != null) { + setBuilderX509(builder); + X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias()); + if (rootCAX509Cert != null) { + X509Certificate[] trustedCertificates = new X509Certificate[1]; + trustedCertificates[0] = rootCAX509Cert; + builder.setTrustedCertificates(trustedCertificates); + } else { + /** by default trust all */ + builder.setTrustedCertificates(new X509Certificate[0]); + } + } else { + /** by default trust all */ + builder.setTrustedCertificates(new X509Certificate[0]); + log.error("Unable to load X509 files for LWM2MServer"); + } + } catch (KeyStoreException ex) { + log.error("[{}] Unable to load X509 files server", ex.getMessage()); + } + } + + private void setBuilderX509(LeshanServerBuilder builder) { + /** + * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE + * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks + */ + try { + X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias()); + PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray()); + builder.setPrivateKey(privateKey); + builder.setCertificateChain(new X509Certificate[]{serverCertificate}); + } catch (Exception ex) { + log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); + } + } + + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java index 4a43407a73..36d39130a3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java @@ -20,14 +20,15 @@ import org.eclipse.leshan.server.californium.LeshanServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Service; +import org.springframework.stereotype.Component; import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; + import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Slf4j -@Service("LwM2MTransportServerInitializer") +@Component("LwM2MTransportServerInitializer") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportServerInitializer { @@ -36,8 +37,18 @@ public class LwM2MTransportServerInitializer { private LeshanServer lhServerCert; @Autowired - @Qualifier("leshanServerNoSecPskRpk") + @Qualifier("LeshanServerNoSecPskRpk") private LeshanServer lhServerNoSecPskRpk; +// +// @Autowired +// @Qualifier("LeshanServerListener") +// private LwM2mServerListener lwM2mServerListener; + + @Autowired + private LwM2mServerListener lwM2mServerListenerNoSecPskRpk; + + @Autowired + private LwM2mServerListener lwM2mServerListenerCert; @Autowired private LwM2MTransportContextServer context; @@ -46,19 +57,33 @@ public class LwM2MTransportServerInitializer { public void init() { if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC(); if (this.context.getCtxServer().isServerStartAll()) { - this.lhServerCert.start(); - this.lhServerNoSecPskRpk.start(); - } - else { + this.startLhServerCert(); + this.startLhServerNoSecPskRpk(); + } else { if (this.context.getCtxServer().getServerDtlsMode() == LwM2MSecurityMode.X509.code) { - this.lhServerCert.start(); - } - else { - this.lhServerNoSecPskRpk.start(); + this.startLhServerCert(); + } else { + this.startLhServerNoSecPskRpk(); } } } + private void startLhServerCert() { + this.lhServerCert.start(); + LwM2mServerListener serverListenerCert = this.lwM2mServerListenerCert.init(this.lhServerCert); + this.lhServerCert.getRegistrationService().addListener(serverListenerCert.registrationListener); + this.lhServerCert.getPresenceService().addListener(serverListenerCert.presenceListener); + this.lhServerCert.getObservationService().addListener(serverListenerCert.observationListener); + } + + private void startLhServerNoSecPskRpk() { + this.lhServerNoSecPskRpk.start(); + LwM2mServerListener serverListenerNoSecPskRpk = this.lwM2mServerListenerNoSecPskRpk.init(this.lhServerNoSecPskRpk); + this.lhServerNoSecPskRpk.getRegistrationService().addListener(serverListenerNoSecPskRpk.registrationListener); + this.lhServerNoSecPskRpk.getPresenceService().addListener(serverListenerNoSecPskRpk.presenceListener); + this.lhServerNoSecPskRpk.getObservationService().addListener(serverListenerNoSecPskRpk.observationListener); + } + @PreDestroy public void shutdown() { log.info("Stopping LwM2M transport Server!"); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index 9b082ce09f..a9fcbcb669 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -15,1114 +15,42 @@ */ package org.thingsboard.server.transport.lwm2m.server; -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.model.ResourceModel; -import org.eclipse.leshan.core.node.LwM2mMultipleResource; -import org.eclipse.leshan.core.node.LwM2mObject; -import org.eclipse.leshan.core.node.LwM2mObjectInstance; -import org.eclipse.leshan.core.node.LwM2mPath; -import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.observation.Observation; -import org.eclipse.leshan.core.request.ContentFormat; -import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.ReadResponse; -import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.registration.Registration; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.transport.TransportService; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; -import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; -import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; -import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; -import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore; -import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; -import javax.annotation.PostConstruct; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Random; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; -import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.CLIENT_NOT_AUTHORIZED; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEFAULT_TIMEOUT; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_REQUEST; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVICE_CHANNEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getAckCallback; +public interface LwM2MTransportService { -@Slf4j -@Service("LwM2MTransportService") -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") -public class LwM2MTransportService { + void onRegistered(LeshanServer lwServer, Registration registration, Collection previousObsersations); - private ExecutorService executorRegistered; - private ExecutorService executorUpdateRegistered; - private ExecutorService executorUnRegistered; - private LwM2mValueConverterImpl converter; + void updatedReg(LeshanServer lwServer, Registration registration); + void unReg(Registration registration, Collection observations); - @Autowired - private TransportService transportService; + void onSleepingDev(Registration registration); - @Autowired - public LwM2MTransportContextServer context; + void setCancelObservations(LeshanServer lwServer, Registration registration); - @Autowired - private LwM2MTransportRequest lwM2MTransportRequest; + void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path); - @Autowired - LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; + void onObservationResponse(Registration registration, String path, ReadResponse response); - @PostConstruct - public void init() { - this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); - this.executorRegistered = Executors.newCachedThreadPool( - new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); - this.executorUpdateRegistered = Executors.newCachedThreadPool( - new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); - this.executorUnRegistered = Executors.newCachedThreadPool( - new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); - this.converter = LwM2mValueConverterImpl.getInstance(); - } + void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); - /** - * Start registration device - * Create session: Map, LwM2MClient> - * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint) - * 1.1 When we initialize the registration, we register the session by endpoint. - * 1.2 If the server has incomplete requests (canceling the registration of the previous session), - * delete the previous session only by the previous registration.getId - * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId - * 1.2 Remove from sessions Model by enpPoint - * Next -> Create new LwM2MClient for current session -> setModelClient... - * - * @param lwServer - LeshanServer - * @param registration - Registration LwM2M Client - * @param previousObsersations - may be null - */ - public void onRegistered(LeshanServer lwServer, Registration registration, Collection previousObsersations) { - executorRegistered.submit(() -> { - try { - log.info("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration); - if (lwM2MClient != null) { - lwM2MClient.setLwM2MTransportService(this); - lwM2MClient.setSessionUuid(UUID.randomUUID()); - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration); - this.setLwM2MClient(lwServer, registration, lwM2MClient); - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); - if (sessionInfo != null) { - lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); - lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); - lwM2MClient.setDeviceName(sessionInfo.getDeviceName()); - lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType()); - transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); - transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); - } else { - log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); - } - } else { - log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); - } - } catch (Throwable t) { - log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t); - } - }); - } + void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile); - /** - * if sessionInfo removed from sessions, then new registerAsyncSession - * @param lwServer - LeshanServer - * @param registration - Registration LwM2M Client - */ - public void updatedReg(LeshanServer lwServer, Registration registration) { - executorUpdateRegistered.submit(() -> { - try { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); - if (sessionInfo != null) { - this.checkInactivity(sessionInfo); - log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); - } else { - log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); - } - } catch (Throwable t) { - log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); - } - }); - } + void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional deviceProfileOpt); + void doTrigger(LeshanServer lwServer, Registration registration, String path); - /** - * @param registration - Registration LwM2M Client - * @param observations - All paths observations before unReg - * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect - */ - public void unReg(Registration registration, Collection observations) { - executorUnRegistered.submit(() -> { - try { - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); - this.closeClientSession(registration); - } catch (Throwable t) { - log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); - } - }); - } + void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); - private void closeClientSession(Registration registration) { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); - if (sessionInfo != null) { - transportService.deregisterSession(sessionInfo); - this.doCloseSession(sessionInfo); - lwM2mInMemorySecurityStore.delRemoveSessionAndListener(registration.getId()); - if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) { - this.syncSessionsAndProfiles(); - } - log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); - } else { - log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); - } - } - public void onSleepingDev(Registration registration) { - log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); - //TODO: associate endpointId with device information. - } - - /** - * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay, - * * if you need to do long time processing use a dedicated thread pool. - * - * @param registration - - */ - protected void onAwakeDev(Registration registration) { - log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); - //TODO: associate endpointId with device information. - } - - /** - * This method is used to sync with sessions - * Removes a profile if not used in sessions - */ - private void syncSessionsAndProfiles() { - Map profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - profilesClone.forEach((k, v) -> { - String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet() - .stream() - .filter(e -> e.getValue().getProfileUuid().equals(k)) - .findFirst() - .map(Map.Entry::getKey) // return the key of the matching entry if found - .orElse(""); - if (registrationId.isEmpty()) { - lwM2mInMemorySecurityStore.getProfiles().remove(k); - } - }); - } - - /** - * #0 Add new ObjectModel to context - * Create new LwM2MClient for current session -> setModelClient... - * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client - * to get the client model with current values - * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse - * - * @param lwServer - LeshanServer - * @param registration - Registration LwM2M Client - * @param lwM2MClient - object with All parameters off client - */ - private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) { - Arrays.stream(registration.getObjectLinks()).forEach(url -> { - LwM2mPath pathIds = new LwM2mPath(url.getUrl()); - if (pathIds.isObjectInstance() && !pathIds.isResource()) { - lwM2MClient.getPendingRequests().add(url.getUrl()); - } - }); - // #2 - Arrays.stream(registration.getObjectLinks()).forEach(url -> { - LwM2mPath pathIds = new LwM2mPath(url.getUrl()); - if (pathIds.isObjectInstance() && !pathIds.isResource()) { - lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), - lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); - } - }); - } - - /** - * @param registration - Registration LwM2M Client - * @return - sessionInfo after access connect client - */ - private SessionInfoProto getValidateSessionInfo(Registration registration) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); - return getNewSessionInfoProto(lwM2MClient); - - } - - /** - * - * @param registrationId - - * @return - - */ - private SessionInfoProto getValidateSessionInfo(String registrationId) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId); - return getNewSessionInfoProto(lwM2MClient); - } - - private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) { - if (lwM2MClient != null) { - ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse(); - if (msg == null || msg.getDeviceInfo() == null) { - log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED); - this.closeClientSession(lwM2MClient.getRegistration()); - return null; - } else { - return SessionInfoProto.newBuilder() - .setNodeId(this.context.getNodeId()) - .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits()) - .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits()) - .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) - .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB()) - .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB()) - .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) - .setDeviceName(msg.getDeviceInfo().getDeviceName()) - .setDeviceType(msg.getDeviceInfo().getDeviceType()) - .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB()) - .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB()) - .build(); - } - } - return null; - } - - /** - * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe - * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use - * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection - * #2 Start observe - * - * @param lwM2MClient - LwM2M Client - */ - - public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) { - // #1 - this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null); - // #2 - this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration()); - - } - - /** - * If there is a difference in values between the current resource values and the shared attribute values - * when the client connects to the server - * #1 get attributes name from profile include name resources in ModelObject if resource isWritable - * #2.1 #1 size > 0 => send Request getAttributes to thingsboard - * #2.2 #1 size == 0 => continue normal process - * - * @param lwM2MClient - LwM2M Client - */ - public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); - if (sessionInfo != null) { - //#1.1 + #1.2 - List attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient); - if (attrSharedNames.size() > 0) { - //#2.1 - try { - TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames); - lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId()); - transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); - } catch (AdaptorException e) { - log.warn("Failed to decode get attributes request", e); - } - } - // #2.2 - else { - lwM2MClient.onSuccessOrErrorDelayedRequests(null); - } - } - } - - /** - * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values - * #1 Get path resource by result attributesResponse - * #1.1 If two names have equal path => last time attribute - * #2.1 if there is a difference in values between the current resource values and the shared attribute values - * => sent to client Request Update of value (new value from shared attribute) - * and LwM2MClient.delayedRequests.add(path) - * #2.1 if there is not a difference in values between the current resource values and the shared attribute values - * - * @param attributesResponse - - * @param sessionInfo - - */ - public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo); - if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) { - attributesResponse.getSharedAttributeListList().forEach(attr -> { - String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey()); - // #1.1 - if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) { - lwM2MClient.getDelayedRequests().put(path, attr); - } else { - lwM2MClient.getDelayedRequests().put(path, attr); - } - }); - // #2.1 - lwM2MClient.getDelayedRequests().forEach((k, v) -> { - ArrayList listV = new ArrayList<>(); - listV.add(v.getKv()); - this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k); - }); - lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId()); - if (lwM2MClient.getDelayedRequests().size() == 0) { - lwM2MClient.onSuccessOrErrorDelayedRequests(null); - } - } - } - - private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) { - if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) { - lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, - ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(), - true); - } - } - - /** - * Get names and keyNames from profile shared!!!! attr resources IsWritable - * @param lwM2MClient - - * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable - */ - private List getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) { - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid()); - Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class); - ConcurrentMap keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class); - - ConcurrentMap keyNamesIsWritable = keyNamesMap.entrySet() - .stream() - .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null && - context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable())) - .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)); - - Set namesIsWritable = ConcurrentHashMap.newKeySet(); - namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values())); - return new ArrayList<>(namesIsWritable); - } - - - /** - * Sent Attribute and Telemetry to Thingsboard - * #1 - get AttrName/TelemetryName with value: - * #1.1 from Client - * #1.2 from LwM2MClient: - * -- resourceId == path from AttrTelemetryObserveValue.postAttributeProfile/postTelemetryProfile/postObserveProfile - * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId) - * #2 - set Attribute/Telemetry - * - * @param registration - Registration LwM2M Client - */ - private void updateAttrTelemetry(Registration registration, boolean start, Set paths) { - JsonObject attributes = new JsonObject(); - JsonObject telemetries = new JsonObject(); - if (start) { - // #1.1 - JsonObject attributeClient = this.getAttributeClient(registration); - if (attributeClient != null) { - attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue())); - } - } - // #1.2 - CountDownLatch cancelLatch = new CountDownLatch(1); - this.getParametersFromProfile(attributes, telemetries, registration, paths); - cancelLatch.countDown(); - try { - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - log.error("[{}] updateAttrTelemetry", e.toString()); - } - if (attributes.getAsJsonObject().entrySet().size() > 0) - this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration); - if (telemetries.getAsJsonObject().entrySet().size() > 0) - this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration); - } - - /** - * get AttrName/TelemetryName with value from Client - * - * @param registration - - * @return - JsonObject, format: {name: value}} - */ - private JsonObject getAttributeClient(Registration registration) { - if (registration.getAdditionalRegistrationAttributes().size() > 0) { - JsonObject resNameValues = new JsonObject(); - registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty); - return resNameValues; - } - return null; - } - - /** - * @param attributes - new JsonObject - * @param telemetry - new JsonObject - * @param registration - Registration LwM2M Client - * result: add to JsonObject those resources to which the user is subscribed and they have a value - * if path==null add All resources else only one - * (attributes/telemetry): new {name(Attr/Telemetry):value} - */ - private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set path) { - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid()); - attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> { - LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); - if (pathIds.isResource()) { - if (path == null || path.contains(p.getAsString())) { - this.addParameters(p.getAsString().toString(), attributes, registration); - } - } - }); - attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> { - LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); - if (pathIds.isResource()) { - if (path == null || path.contains(p.getAsString())) { - this.addParameters(p.getAsString().toString(), telemetry, registration); - } - } - }); - } - - /** - * @param parameters - JsonObject attributes/telemetry - * @param registration - Registration LwM2M Client - */ - private void addParameters(String path, JsonObject parameters, Registration registration) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()); - JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile(); - String resName = String.valueOf(names.get(path)); - if (resName != null && !resName.isEmpty()) { - try { - String resValue = this.getResourceValueToString(lwM2MClient, path); - if (resValue != null) { - parameters.addProperty(resName, resValue); - } - } catch (Exception e) { - log.error(e.getStackTrace().toString()); - } - } - } - - /** - * Prepare Sent to Thigsboard callback - Attribute or Telemetry - * - * @param msg - JsonArray: [{name: value}] - * @param topicName - Api Attribute or Telemetry - * @param registration - Id of Registration LwM2M Client - */ - public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); - if (sessionInfo != null) { - context.sentParametersOnThingsboard(msg, topicName, sessionInfo); - } else { - log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null); - } - } - - /** - * Start observe - * #1 - Analyze: - * #1.1 path in observe == (attribute or telemetry) - * #2 Analyze after sent request (response): - * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation - * #2.2 Next: ObservationListener.onResponse * - * - * @param lwServer - LeshanServer - * @param registration - Registration LwM2M Client - */ - private void onSentObserveToClient(LeshanServer lwServer, Registration registration) { - if (lwServer.getObservationService().getObservations(registration).size() > 0) { - this.setCancelObservations(lwServer, registration); - } - UUID profileUUid = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid(); - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(profileUUid); - attrTelemetryObserveValue.getPostObserveProfile().forEach(p -> { - // #1.1 - String target = (getValidateObserve(attrTelemetryObserveValue.getPostAttributeProfile(), p.getAsString().toString())) ? - p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ? - p.getAsString().toString() : null; - if (target != null) { - // #2 - if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) { - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE, - null, null, null, null, this.context.getCtxServer().getTimeout(), - false); - } - } - }); - } - - public void setCancelObservations(LeshanServer lwServer, Registration registration) { - if (registration != null) { - Set observations = lwServer.getObservationService().getObservations(registration); - observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString())); - } - } - - /** - * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout()); - * At server side this will not remove the observation from the observation store, to do it you need to use - * {@code ObservationService#cancelObservation()} - */ - public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) { - CountDownLatch cancelLatch = new CountDownLatch(1); - lwServer.getObservationService().cancelObservations(registration, path); - cancelLatch.countDown(); - try { - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - /** - * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile - * @param path - recourse from postObserveProfile - * @return rez - true if path observe is in attribute/telemetry - */ - private boolean getValidateObserve(JsonElement parameters, String path) { - AtomicBoolean rez = new AtomicBoolean(false); - if (parameters.isJsonArray()) { - parameters.getAsJsonArray().forEach(p -> { - if (p.getAsString().toString().equals(path)) rez.set(true); - } - ); - } else if (parameters.isJsonObject()) { - rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString()) - .filter(path::equals).findAny().orElse(null) != null); - } - return rez.get(); - } - - /** - * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource - * - * @param registration - Registration LwM2M Client - * @param path - observe - * @param response - observe - */ - - public void onObservationResponse(Registration registration, String path, ReadResponse response) { - if (response.getContent() != null) { - if (response.getContent() instanceof LwM2mObject) { -// LwM2mObject content = (LwM2mObject) response.getContent(); - } else if (response.getContent() instanceof LwM2mObjectInstance) { -// LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent(); - } else if (response.getContent() instanceof LwM2mSingleResource) { - LwM2mSingleResource content = (LwM2mSingleResource) response.getContent(); - this.onObservationSetResourcesValue(registration, content.getValue(), null, path); - } else if (response.getContent() instanceof LwM2mMultipleResource) { - LwM2mSingleResource content = (LwM2mSingleResource) response.getContent(); - this.onObservationSetResourcesValue(registration, null, content.getValues(), path); - } - } - } - - /** - * Sending observe value of resources to thingsboard - * #1 Return old Value Resource from LwM2MClient - * #2 Update new Resources (replace old Resource Value on new Resource Value) - * - * @param registration - Registration LwM2M Client - * @param value - LwM2mSingleResource response.getContent() - * @param values - LwM2mSingleResource response.getContent() - * @param path - resource - */ - private void onObservationSetResourcesValue(Registration registration, Object value, Map values, String path) { - CountDownLatch respLatch = new CountDownLatch(1); - boolean isChange = false; - try { - // #1 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); - LwM2mPath pathIds = new LwM2mPath(path); - log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values); - ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds); - ResourceValue resValueOld = lwM2MClient.getResources().get(path); - // #2 - if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) { - ResourceValue resourceValue = new ResourceValue(values, null, true); - lwM2MClient.getResources().put(path, resourceValue); - isChange = true; - } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) { - ResourceValue resourceValue = new ResourceValue(null, value, false); - lwM2MClient.getResources().put(path, resourceValue); - isChange = true; - } - } finally { - respLatch.countDown(); - } - try { - respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException ex) { - ex.printStackTrace(); - log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value); - } - if (isChange) { - Set paths = new HashSet<>(); - paths.add(path); - this.updateAttrTelemetry(registration, false, paths); - } - } - - /** - * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...) - * config attr/telemetry... in profile - */ - public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) { - log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList()); - } - - /** - * Update - sent request in change value resources in Client - * Path to resources from profile equal keyName or from ModelObject equal name - * Only for resources: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase) - * Delete - nothing * - * - * @param msg - - */ - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { - if (msg.getSharedUpdatedCount() > 0) { - JsonElement el = JsonConverter.toJson(msg); - el.getAsJsonObject().entrySet().forEach(de -> { - String path = this.getPathAttributeUpdate(sessionInfo, de.getKey()); - String value = de.getValue().getAsString(); - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); - ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path)); - if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) { - if (resourceModel != null && resourceModel.operations.isWritable()) { - lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, - ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(), - false); - } else { - log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value); - String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value); - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); - } - } else { - log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value); - String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value); - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); - } - }); - } else if (msg.getSharedDeletedCount() > 0) { - log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); - } - } - - /** - * Get path to resource from profile equal keyName or from ModelObject equal name - * Only for resource: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase) - * - * @param sessionInfo - - * @param name - - * @return path if path isPresent in postProfile - */ - private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) { - String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name); -// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name); - return !profilePath.isEmpty() ? profilePath : null; - } - - /** - * @param profile - - * @param path - - * @return true if path isPresent in postAttributeProfile - */ - private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) { - Set attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class); - return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent(); - } - - /** - * @param profile - - * @param path - - * @return true if path isPresent in postAttributeProfile - */ - private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) { - Set telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), Set.class); - return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent(); - } - - - /** - * Get path to resource from profile equal keyName - * - * @param sessionInfo - - * @param name - - * @return - - */ - private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); - return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream() - .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey) - .orElse(""); - } - - /** - * Update resource (attribute) value on thingsboard after update value in client - * - * @param registration - - * @param path - - * @param request - - */ - public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) { - ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path)); - if (resource.multiple) { - this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path); - } else { - this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path); - } - if (isDelayedUpdate) lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null) - .onSuccessOrErrorDelayedRequests(request.getPath().toString()); - } - - /** - * @param sessionInfo - - * @param deviceProfile - - */ - public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) { - Set registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet() - .stream() - .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId())) - .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new)); - if (registrationIds.size() > 0) { - this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile); - } - } - - /** - * @param sessionInfo - - * @param device - - * @param deviceProfileOpt - - */ - public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional deviceProfileOpt) { - Optional registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream() - .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid())) - .map(Map.Entry::getKey) - .findFirst(); - registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt)); - } - - /** - * Update parameters device in LwM2MClient - * If new deviceProfile != old deviceProfile => update deviceProfile - * - * @param registrationId - - * @param device - - */ - private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional deviceProfileOpt) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId); - lwM2MClient.setDeviceName(device.getName()); - if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) { - Set registrationIds = new HashSet<>(); - registrationIds.add(registrationId); - deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile)); - } - - lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId()); - } - - /** - * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName) - * #2 Update in lwM2MClient: ...Profile if changes from update device - * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName) - * #3.1 Attribute isChange (add&del) - * #3.2 Telemetry isChange (add&del) - * #3.3 KeyName isChange (add) - * #4 update - * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value to thingsboard - * #4.2 del - * -- if add attributes includes del telemetry - result del for observe - * #5 - * #5.1 Observe isChange (add&del) - * #5.2 Observe.add - * -- path Attr/Telemetry includes newObserve and does not include oldObserve: sent Request observe to Client - * #5.3 Observe.del - * -- different between newObserve and oldObserve: sent Request cancel observe to client - * - * @param registrationIds - - * @param deviceProfile - - */ - public void onDeviceUpdateChangeProfile(Set registrationIds, DeviceProfile deviceProfile) { - - AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId()); - if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) { - - // #1 - JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile(); - Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class); - JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile(); - Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class); - JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile(); - JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile(); - - AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId()); - JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile(); - Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class); - JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile(); - Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class); - JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile(); - JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile(); - - // #3 - ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters(); - // #3.1 - if (!attributeOld.equals(attributeNew)) { - ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, Set.class), attributeSetNew); - sentAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd()); - sentAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel()); - } - // #3.2 - if (!attributeOld.equals(attributeNew)) { - ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, Set.class), telemetrySetNew); - sentAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd()); - sentAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel()); - } - // #3.3 - if (!keyNameOld.equals(keyNameNew)) { - ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), ConcurrentHashMap.class), - new Gson().fromJson(keyNameNew.toString(), ConcurrentHashMap.class)); - sentAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd()); - } - - // #4.1 add - if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) { - // update value in Resources - registrationIds.forEach(registrationId -> { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId); - LeshanServer lwServer = lwM2MClient.getLwServer(); - Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); - log.warn("[{}] # 4.1", registration.getEndpoint()); - this.updateResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ); - // sent attr/telemetry to tingsboard for new path - this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd()); - }); - } - // #4.2 del - if (sentAttrToThingsboard.getPathPostParametersDel().size() > 0) { - ResultsAnalyzerParameters sentAttrToThingsboardDel = this.getAnalyzerParameters(sentAttrToThingsboard.getPathPostParametersAdd(), sentAttrToThingsboard.getPathPostParametersDel()); - sentAttrToThingsboard.setPathPostParametersDel(sentAttrToThingsboardDel.getPathPostParametersDel()); - } - - // #5.1 - if (!observeOld.equals(observeNew)) { - Set observeSetOld = new Gson().fromJson(observeOld, Set.class); - Set observeSetNew = new Gson().fromJson(observeNew, Set.class); - //#5.2 add - // path Attr/Telemetry includes newObserve - attributeSetOld.addAll(telemetrySetOld); - ResultsAnalyzerParameters sentObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe - attributeSetNew.addAll(telemetrySetNew); - ResultsAnalyzerParameters sentObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe - // does not include oldObserve - ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd()); - // sent Request observe to Client - registrationIds.forEach(registrationId -> { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(null, registrationId); - LeshanServer lwServer = lwM2MClient.getLwServer(); - Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); - log.warn("[{}] # 5.1", registration.getEndpoint()); - this.updateResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE); - // 5.3 del - // sent Request cancel observe to Client - this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel()); - }); - } - } - } - - /** - * Compare old list with new list after change AttrTelemetryObserve in config Profile - * - * @param parametersOld - - * @param parametersNew - - * @return ResultsAnalyzerParameters: add && new - */ - private ResultsAnalyzerParameters getAnalyzerParameters(Set parametersOld, Set parametersNew) { - ResultsAnalyzerParameters analyzerParameters = null; - if (!parametersOld.equals(parametersNew)) { - analyzerParameters = new ResultsAnalyzerParameters(); - analyzerParameters.setPathPostParametersAdd(parametersNew - .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet())); - analyzerParameters.setPathPostParametersDel(parametersOld - .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet())); - } - return analyzerParameters; - } - - private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap keyNameOld, ConcurrentMap keyNameNew) { - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); - Set paths = keyNameNew.entrySet() - .stream() - .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey()))) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet(); - analyzerParameters.setPathPostParametersAdd(paths); - return analyzerParameters; - } - - private ResultsAnalyzerParameters getAnalyzerParametersIn(Set parametersObserve, Set parameters) { - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); - analyzerParameters.setPathPostParametersAdd(parametersObserve - .stream().filter(parameters::contains).collect(Collectors.toSet())); - return analyzerParameters; - } - - /** - * Update Resource value after change RezAttrTelemetry in config Profile - * sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue() - * - * @param lwServer - LeshanServer - * @param registration - Registration LwM2M Client - * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] - */ - private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, Set targets, String typeOper) { - targets.forEach(target -> { - LwM2mPath pathIds = new LwM2mPath(target); - if (pathIds.isResource()) { - if (GET_TYPE_OPER_READ.equals(typeOper)) { - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(), - false); - } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) { - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, - null, null, null, null, this.context.getCtxServer().getTimeout(), - false); - } - } - }); - } - - private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set paramAnallyzer) { - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); - paramAnallyzer.forEach(p -> { - if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) { - this.setCancelObservationRecourse(lwServer, registration, p); - } - } - ); - } - - private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) { - ResourceValue resourceValue = null; - if (pathIds.isResource()) { - resourceValue = lwM2MClient.getResources().get(pathIds.toString()); - } - return resourceValue; - } - - /** - * Trigger Server path = "/1/0/8" - * - * Trigger bootStrap path = "/1/0/9" - have to implemented on client - */ - public void doTrigger(LeshanServer lwServer, Registration registration, String path) { - lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE, - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(), - false); - } - - /** - * Session device in thingsboard is closed - * - * @param sessionInfo - lwm2m client - */ - private void doCloseSession(SessionInfoProto sessionInfo) { - TransportProtos.SessionEvent event = SessionEvent.CLOSED; - TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder() - .setSessionType(TransportProtos.SessionType.ASYNC) - .setEvent(event).build(); - transportService.process(sessionInfo, msg, null); - } - - /** - * Deregister session in transport - * - * @param sessionInfo - lwm2m client - */ - private void doDisconnect(SessionInfoProto sessionInfo) { - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); - transportService.deregisterSession(sessionInfo); - } - - private void checkInactivityAndReportActivity() { - lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key))); - } - - /** - * if sessionInfo removed from sessions, then new registerAsyncSession - * @param sessionInfo - - */ - private void checkInactivity(SessionInfoProto sessionInfo) { - if (transportService.reportActivity(sessionInfo) == null) { - transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); - } - } - - public void sentLogsToThingsboard(String msg, Registration registration) { - if (msg != null) { - JsonObject telemetries = new JsonObject(); - telemetries.addProperty(LOG_LW2M_TELEMETRY, msg); - this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration); - } - } - - /** - * @param path - path resource - * @return - value of Resource or null - */ - public String getResourceValueToString(LwM2MClient lwM2MClient, String path) { - LwM2mPath pathIds = new LwM2mPath(path); - ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds); - return (resourceValue == null) ? null : - (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds); - } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java new file mode 100644 index 0000000000..2a6af4e19e --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java @@ -0,0 +1,1132 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.model.ResourceModel; +import org.eclipse.leshan.core.node.LwM2mMultipleResource; +import org.eclipse.leshan.core.node.LwM2mObject; +import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mPath; +import org.eclipse.leshan.core.node.LwM2mSingleResource; +import org.eclipse.leshan.core.observation.Observation; +import org.eclipse.leshan.core.request.ContentFormat; +import org.eclipse.leshan.core.request.WriteRequest; +import org.eclipse.leshan.core.response.ReadResponse; +import org.eclipse.leshan.core.util.NamedThreadFactory; +import org.eclipse.leshan.server.californium.LeshanServer; +import org.eclipse.leshan.server.registration.Registration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.transport.service.DefaultTransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; +import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; +import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; +import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Collectors; + +import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.CLIENT_NOT_AUTHORIZED; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEFAULT_TIMEOUT; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVICE_CHANNEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getAckCallback; + +@Slf4j +@Service("LwM2MTransportService") +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") +public class LwM2MTransportServiceImpl implements LwM2MTransportService { + + private ExecutorService executorRegistered; + private ExecutorService executorUpdateRegistered; + private ExecutorService executorUnRegistered; + private LwM2mValueConverterImpl converter; + protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + protected final Lock writeLock = readWriteLock.writeLock(); + + + @Autowired + private TransportService transportService; + + @Autowired + public LwM2MTransportContextServer context; + + @Autowired + private LwM2MTransportRequest lwM2MTransportRequest; + + @Autowired + LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; + + @PostConstruct + public void init() { + this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); + this.executorRegistered = Executors.newCachedThreadPool( + new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); + this.executorUpdateRegistered = Executors.newCachedThreadPool( + new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); + this.executorUnRegistered = Executors.newCachedThreadPool( + new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); + this.converter = LwM2mValueConverterImpl.getInstance(); + } + + /** + * Start registration device + * Create session: Map, LwM2MClient> + * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint) + * 1.1 When we initialize the registration, we register the session by endpoint. + * 1.2 If the server has incomplete requests (canceling the registration of the previous session), + * delete the previous session only by the previous registration.getId + * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId + * 1.2 Remove from sessions Model by enpPoint + * Next -> Create new LwM2MClient for current session -> setModelClient... + * + * @param lwServer - LeshanServer + * @param registration - Registration LwM2M Client + * @param previousObsersations - may be null + */ + public void onRegistered(LeshanServer lwServer, Registration registration, Collection previousObsersations) { + executorRegistered.submit(() -> { + try { + log.info("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration); + if (lwM2MClient != null) { + lwM2MClient.setLwM2MTransportServiceImpl(this); + lwM2MClient.setSessionUuid(UUID.randomUUID()); + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration); + this.setLwM2MClient(lwServer, registration, lwM2MClient); + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); + if (sessionInfo != null) { + lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); + lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); + lwM2MClient.setDeviceName(sessionInfo.getDeviceName()); + lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType()); + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); + } else { + log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); + } + } else { + log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); + } + } catch (Throwable t) { + log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t); + } + }); + } + + /** + * if sessionInfo removed from sessions, then new registerAsyncSession + * @param lwServer - LeshanServer + * @param registration - Registration LwM2M Client + */ + public void updatedReg(LeshanServer lwServer, Registration registration) { + executorUpdateRegistered.submit(() -> { + try { + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); + if (sessionInfo != null) { + this.checkInactivity(sessionInfo); + log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); + } else { + log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); + } + } catch (Throwable t) { + log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); + } + }); + } + + + /** + * @param registration - Registration LwM2M Client + * @param observations - All paths observations before unReg + * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect + */ + public void unReg(Registration registration, Collection observations) { + executorUnRegistered.submit(() -> { + try { + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); + this.closeClientSession(registration); + } catch (Throwable t) { + log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); + } + }); + } + + private void closeClientSession(Registration registration) { + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); + if (sessionInfo != null) { + transportService.deregisterSession(sessionInfo); + this.doCloseSession(sessionInfo); + lwM2mInMemorySecurityStore.delRemoveSessionAndListener(registration.getId()); + if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) { + this.syncSessionsAndProfiles(); + } + log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); + } else { + log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); + } + } + + public void onSleepingDev(Registration registration) { + log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); + //TODO: associate endpointId with device information. + } + + /** + * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay, + * * if you need to do long time processing use a dedicated thread pool. + * + * @param registration - + */ + protected void onAwakeDev(Registration registration) { + log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); + //TODO: associate endpointId with device information. + } + + /** + * This method is used to sync with sessions + * Removes a profile if not used in sessions + */ + private void syncSessionsAndProfiles() { + Map profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + profilesClone.forEach((k, v) -> { + String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet() + .stream() + .filter(e -> e.getValue().getProfileUuid().equals(k)) + .findFirst() + .map(Map.Entry::getKey) // return the key of the matching entry if found + .orElse(""); + if (registrationId.isEmpty()) { + lwM2mInMemorySecurityStore.getProfiles().remove(k); + } + }); + } + + /** + * #0 Add new ObjectModel to context + * Create new LwM2MClient for current session -> setModelClient... + * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client + * to get the client model with current values + * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse + * + * @param lwServer - LeshanServer + * @param registration - Registration LwM2M Client + * @param lwM2MClient - object with All parameters off client + */ + private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) { + Arrays.stream(registration.getObjectLinks()).forEach(url -> { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource()) { + lwM2MClient.getPendingRequests().add(url.getUrl()); + } + }); + // #2 + Arrays.stream(registration.getObjectLinks()).forEach(url -> { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource()) { + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); + } + }); + } + + /** + * @param registration - Registration LwM2M Client + * @return - sessionInfo after access connect client + */ + private SessionInfoProto getValidateSessionInfo(Registration registration) { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); + return getNewSessionInfoProto(lwM2MClient); + + } + + /** + * + * @param registrationId - + * @return - + */ + private SessionInfoProto getValidateSessionInfo(String registrationId) { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId); + return getNewSessionInfoProto(lwM2MClient); + } + + private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) { + if (lwM2MClient != null) { + ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse(); + if (msg == null || msg.getDeviceInfo() == null) { + log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED); + this.closeClientSession(lwM2MClient.getRegistration()); + return null; + } else { + return SessionInfoProto.newBuilder() + .setNodeId(this.context.getNodeId()) + .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits()) + .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB()) + .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB()) + .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) + .setDeviceName(msg.getDeviceInfo().getDeviceName()) + .setDeviceType(msg.getDeviceInfo().getDeviceType()) + .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB()) + .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB()) + .build(); + } + } + return null; + } + + /** + * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe + * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use + * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection + * #2 Start observe + * + * @param lwM2MClient - LwM2M Client + */ + + public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) { + // #1 + this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null); + // #2 + this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration()); + + } + + /** + * If there is a difference in values between the current resource values and the shared attribute values + * when the client connects to the server + * #1 get attributes name from profile include name resources in ModelObject if resource isWritable + * #2.1 #1 size > 0 => send Request getAttributes to thingsboard + * #2.2 #1 size == 0 => continue normal process + * + * @param lwM2MClient - LwM2M Client + */ + public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) { + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); + if (sessionInfo != null) { + //#1.1 + #1.2 + List attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient); + if (attrSharedNames.size() > 0) { + //#2.1 + try { + TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames); + lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId()); + transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); + } catch (AdaptorException e) { + log.warn("Failed to decode get attributes request", e); + } + } + // #2.2 + else { + lwM2MClient.onSuccessOrErrorDelayedRequests(null); + } + } + } + + /** + * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values + * #1 Get path resource by result attributesResponse + * #1.1 If two names have equal path => last time attribute + * #2.1 if there is a difference in values between the current resource values and the shared attribute values + * => sent to client Request Update of value (new value from shared attribute) + * and LwM2MClient.delayedRequests.add(path) + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values + * + * @param attributesResponse - + * @param sessionInfo - + */ + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo); + if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) { + attributesResponse.getSharedAttributeListList().forEach(attr -> { + String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey()); + // #1.1 + if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) { + lwM2MClient.getDelayedRequests().put(path, attr); + } else { + lwM2MClient.getDelayedRequests().put(path, attr); + } + }); + // #2.1 + lwM2MClient.getDelayedRequests().forEach((k, v) -> { + ArrayList listV = new ArrayList<>(); + listV.add(v.getKv()); + this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k); + }); + lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId()); + if (lwM2MClient.getDelayedRequests().size() == 0) { + lwM2MClient.onSuccessOrErrorDelayedRequests(null); + } + } + } + + private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) { + if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) { + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, + ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(), + true); + } + } + + /** + * Get names and keyNames from profile shared!!!! attr resources IsWritable + * @param lwM2MClient - + * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable + */ + private List getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) { + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid()); + Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class); + ConcurrentMap keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class); + + ConcurrentMap keyNamesIsWritable = keyNamesMap.entrySet() + .stream() + .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null && + context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable())) + .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)); + + Set namesIsWritable = ConcurrentHashMap.newKeySet(); + namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values())); + return new ArrayList<>(namesIsWritable); + } + + + /** + * Sent Attribute and Telemetry to Thingsboard + * #1 - get AttrName/TelemetryName with value: + * #1.1 from Client + * #1.2 from LwM2MClient: + * -- resourceId == path from AttrTelemetryObserveValue.postAttributeProfile/postTelemetryProfile/postObserveProfile + * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId) + * #2 - set Attribute/Telemetry + * + * @param registration - Registration LwM2M Client + */ + private void updateAttrTelemetry(Registration registration, boolean start, Set paths) { + JsonObject attributes = new JsonObject(); + JsonObject telemetries = new JsonObject(); + if (start) { + // #1.1 + JsonObject attributeClient = this.getAttributeClient(registration); + if (attributeClient != null) { + attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue())); + } + } + // #1.2 + CountDownLatch cancelLatch = new CountDownLatch(1); + this.getParametersFromProfile(attributes, telemetries, registration, paths); + cancelLatch.countDown(); + try { + cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + log.error("[{}] updateAttrTelemetry", e.toString()); + } + if (attributes.getAsJsonObject().entrySet().size() > 0) + this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration); + if (telemetries.getAsJsonObject().entrySet().size() > 0) + this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration); + } + + /** + * get AttrName/TelemetryName with value from Client + * + * @param registration - + * @return - JsonObject, format: {name: value}} + */ + private JsonObject getAttributeClient(Registration registration) { + if (registration.getAdditionalRegistrationAttributes().size() > 0) { + JsonObject resNameValues = new JsonObject(); + registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty); + return resNameValues; + } + return null; + } + + /** + * @param attributes - new JsonObject + * @param telemetry - new JsonObject + * @param registration - Registration LwM2M Client + * result: add to JsonObject those resources to which the user is subscribed and they have a value + * if path==null add All resources else only one + * (attributes/telemetry): new {name(Attr/Telemetry):value} + */ + private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set path) { + AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid()); + attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> { + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); + if (pathIds.isResource()) { + if (path == null || path.contains(p.getAsString())) { + this.addParameters(p.getAsString().toString(), attributes, registration); + } + } + }); + attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> { + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString()); + if (pathIds.isResource()) { + if (path == null || path.contains(p.getAsString())) { + this.addParameters(p.getAsString().toString(), telemetry, registration); + } + } + }); + } + + /** + * @param parameters - JsonObject attributes/telemetry + * @param registration - Registration LwM2M Client + */ + private void addParameters(String path, JsonObject parameters, Registration registration) { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()); + JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile(); + String resName = String.valueOf(names.get(path)); + if (resName != null && !resName.isEmpty()) { + try { + String resValue = this.getResourceValueToString(lwM2MClient, path); + if (resValue != null) { + parameters.addProperty(resName, resValue); + } + } catch (Exception e) { + log.error(e.getStackTrace().toString()); + } + } + } + + /** + * Prepare Sent to Thigsboard callback - Attribute or Telemetry + * + * @param msg - JsonArray: [{name: value}] + * @param topicName - Api Attribute or Telemetry + * @param registration - Id of Registration LwM2M Client + */ + public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) { + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); + if (sessionInfo != null) { + context.sentParametersOnThingsboard(msg, topicName, sessionInfo); + } else { + log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null); + } + } + + /** + * Start observe + * #1 - Analyze: + * #1.1 path in observe == (attribute or telemetry) + * #2 Analyze after sent request (response): + * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation + * #2.2 Next: ObservationListener.onResponse * + * + * @param lwServer - LeshanServer + * @param registration - Registration LwM2M Client + */ + private void onSentObserveToClient(LeshanServer lwServer, Registration registration) { + if (lwServer.getObservationService().getObservations(registration).size() > 0) { + this.setCancelObservations(lwServer, registration); + } + UUID profileUUid = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid(); + AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(profileUUid); + attrTelemetryObserveValue.getPostObserveProfile().forEach(p -> { + // #1.1 + String target = (getValidateObserve(attrTelemetryObserveValue.getPostAttributeProfile(), p.getAsString().toString())) ? + p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ? + p.getAsString().toString() : null; + if (target != null) { + // #2 + if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) { + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE, + null, null, null, null, this.context.getCtxServer().getTimeout(), + false); + } + } + }); + } + + public void setCancelObservations(LeshanServer lwServer, Registration registration) { + if (registration != null) { + Set observations = lwServer.getObservationService().getObservations(registration); + observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString())); + } + } + + /** + * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout()); + * At server side this will not remove the observation from the observation store, to do it you need to use + * {@code ObservationService#cancelObservation()} + */ + public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) { + CountDownLatch cancelLatch = new CountDownLatch(1); + lwServer.getObservationService().cancelObservations(registration, path); + cancelLatch.countDown(); + try { + cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + log.error("", e); + } + } + + /** + * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile + * @param path - recourse from postObserveProfile + * @return rez - true if path observe is in attribute/telemetry + */ + private boolean getValidateObserve(JsonElement parameters, String path) { + AtomicBoolean rez = new AtomicBoolean(false); + if (parameters.isJsonArray()) { + parameters.getAsJsonArray().forEach(p -> { + if (p.getAsString().toString().equals(path)) rez.set(true); + } + ); + } else if (parameters.isJsonObject()) { + rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString()) + .filter(path::equals).findAny().orElse(null) != null); + } + return rez.get(); + } + + /** + * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource + * + * @param registration - Registration LwM2M Client + * @param path - observe + * @param response - observe + */ + + public void onObservationResponse(Registration registration, String path, ReadResponse response) { + if (response.getContent() != null) { + if (response.getContent() instanceof LwM2mObject) { +// LwM2mObject content = (LwM2mObject) response.getContent(); + } else if (response.getContent() instanceof LwM2mObjectInstance) { +// LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent(); + } else if (response.getContent() instanceof LwM2mSingleResource) { + LwM2mSingleResource content = (LwM2mSingleResource) response.getContent(); + this.onObservationSetResourcesValue(registration, content.getValue(), null, path); + } else if (response.getContent() instanceof LwM2mMultipleResource) { + LwM2mMultipleResource content = (LwM2mMultipleResource) response.getContent(); + this.onObservationSetResourcesValue(registration, null, content.getValues(), path); + } + } + } + + /** + * Sending observe value of resources to thingsboard + * #1 Return old Value Resource from LwM2MClient + * #2 Update new Resources (replace old Resource Value on new Resource Value) + * + * @param registration - Registration LwM2M Client + * @param value - LwM2mSingleResource response.getContent() + * @param values - LwM2mSingleResource response.getContent() + * @param path - resource + */ + private void onObservationSetResourcesValue(Registration registration, Object value, Map values, String path) { + boolean isChange = false; + try { + writeLock.lock(); + try { + // #1 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); + LwM2mPath pathIds = new LwM2mPath(path); + log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values); + ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds); + ResourceValue resValueOld = lwM2MClient.getResources().get(path); + // #2 + if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) { + ResourceValue resourceValue = new ResourceValue(values, null, true); + lwM2MClient.getResources().put(path, resourceValue); + isChange = true; + } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) { + ResourceValue resourceValue = new ResourceValue(null, value, false); + lwM2MClient.getResources().put(path, resourceValue); + isChange = true; + } + } finally { + writeLock.unlock(); + } + } + catch (Exception e) { + log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}] [{}]", path, value, e.toString()); + } + if (isChange) { + Set paths = new HashSet<>(); + paths.add(path); + this.updateAttrTelemetry(registration, false, paths); + } + } + + /** + * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...) + * config attr/telemetry... in profile + */ + public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) { + log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList()); + } + + /** + * Update - sent request in change value resources in Client + * Path to resources from profile equal keyName or from ModelObject equal name + * Only for resources: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase) + * Delete - nothing * + * + * @param msg - + */ + public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { + if (msg.getSharedUpdatedCount() > 0) { + JsonElement el = JsonConverter.toJson(msg); + el.getAsJsonObject().entrySet().forEach(de -> { + String path = this.getPathAttributeUpdate(sessionInfo, de.getKey()); + String value = de.getValue().getAsString(); + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); + ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path)); + if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) { + if (resourceModel != null && resourceModel.operations.isWritable()) { + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, + ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(), + false); + } else { + log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value); + String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value); + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); + } + } else { + log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value); + String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value); + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); + } + }); + } else if (msg.getSharedDeletedCount() > 0) { + log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); + } + } + + /** + * Get path to resource from profile equal keyName or from ModelObject equal name + * Only for resource: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase) + * + * @param sessionInfo - + * @param name - + * @return path if path isPresent in postProfile + */ + private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) { + String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name); +// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name); + return !profilePath.isEmpty() ? profilePath : null; + } + + /** + * @param profile - + * @param path - + * @return true if path isPresent in postAttributeProfile + */ + private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) { + Set attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class); + return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent(); + } + + /** + * @param profile - + * @param path - + * @return true if path isPresent in postAttributeProfile + */ + private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) { + Set telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), Set.class); + return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent(); + } + + + /** + * Get path to resource from profile equal keyName + * + * @param sessionInfo - + * @param name - + * @return - + */ + private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); + return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream() + .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey) + .orElse(""); + } + + /** + * Update resource (attribute) value on thingsboard after update value in client + * + * @param registration - + * @param path - + * @param request - + */ + public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) { + ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path)); + if (resource.multiple) { + this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path); + } else { + this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path); + } + if (isDelayedUpdate) lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null) + .onSuccessOrErrorDelayedRequests(request.getPath().toString()); + } + + /** + * @param sessionInfo - + * @param deviceProfile - + */ + public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) { + Set registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet() + .stream() + .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId())) + .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new)); + if (registrationIds.size() > 0) { + this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile); + } + } + + /** + * @param sessionInfo - + * @param device - + * @param deviceProfileOpt - + */ + public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional deviceProfileOpt) { + Optional registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream() + .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid())) + .map(Map.Entry::getKey) + .findFirst(); + registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt)); + } + + /** + * Update parameters device in LwM2MClient + * If new deviceProfile != old deviceProfile => update deviceProfile + * + * @param registrationId - + * @param device - + */ + private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional deviceProfileOpt) { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId); + lwM2MClient.setDeviceName(device.getName()); + if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) { + Set registrationIds = new HashSet<>(); + registrationIds.add(registrationId); + deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile)); + } + + lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId()); + } + + /** + * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName) + * #2 Update in lwM2MClient: ...Profile if changes from update device + * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName) + * #3.1 Attribute isChange (add&del) + * #3.2 Telemetry isChange (add&del) + * #3.3 KeyName isChange (add) + * #4 update + * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value to thingsboard + * #4.2 del + * -- if add attributes includes del telemetry - result del for observe + * #5 + * #5.1 Observe isChange (add&del) + * #5.2 Observe.add + * -- path Attr/Telemetry includes newObserve and does not include oldObserve: sent Request observe to Client + * #5.3 Observe.del + * -- different between newObserve and oldObserve: sent Request cancel observe to client + * + * @param registrationIds - + * @param deviceProfile - + */ + private void onDeviceUpdateChangeProfile(Set registrationIds, DeviceProfile deviceProfile) { + + AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId()); + if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) { + + // #1 + JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile(); + Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class); + JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile(); + Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class); + JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile(); + JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile(); + + AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId()); + JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile(); + Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class); + JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile(); + Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class); + JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile(); + JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile(); + + // #3 + ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters(); + // #3.1 + if (!attributeOld.equals(attributeNew)) { + ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, Set.class), attributeSetNew); + sentAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd()); + sentAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel()); + } + // #3.2 + if (!attributeOld.equals(attributeNew)) { + ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, Set.class), telemetrySetNew); + sentAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd()); + sentAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel()); + } + // #3.3 + if (!keyNameOld.equals(keyNameNew)) { + ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), ConcurrentHashMap.class), + new Gson().fromJson(keyNameNew.toString(), ConcurrentHashMap.class)); + sentAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd()); + } + + // #4.1 add + if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) { + // update value in Resources + registrationIds.forEach(registrationId -> { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId); + LeshanServer lwServer = lwM2MClient.getLwServer(); + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); + log.warn("[{}] # 4.1", registration.getEndpoint()); + this.updateResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ); + // sent attr/telemetry to tingsboard for new path + this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd()); + }); + } + // #4.2 del + if (sentAttrToThingsboard.getPathPostParametersDel().size() > 0) { + ResultsAnalyzerParameters sentAttrToThingsboardDel = this.getAnalyzerParameters(sentAttrToThingsboard.getPathPostParametersAdd(), sentAttrToThingsboard.getPathPostParametersDel()); + sentAttrToThingsboard.setPathPostParametersDel(sentAttrToThingsboardDel.getPathPostParametersDel()); + } + + // #5.1 + if (!observeOld.equals(observeNew)) { + Set observeSetOld = new Gson().fromJson(observeOld, Set.class); + Set observeSetNew = new Gson().fromJson(observeNew, Set.class); + //#5.2 add + // path Attr/Telemetry includes newObserve + attributeSetOld.addAll(telemetrySetOld); + ResultsAnalyzerParameters sentObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe + attributeSetNew.addAll(telemetrySetNew); + ResultsAnalyzerParameters sentObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe + // does not include oldObserve + ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd()); + // sent Request observe to Client + registrationIds.forEach(registrationId -> { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(null, registrationId); + LeshanServer lwServer = lwM2MClient.getLwServer(); + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId); + log.warn("[{}] # 5.1", registration.getEndpoint()); + this.updateResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE); + // 5.3 del + // sent Request cancel observe to Client + this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel()); + }); + } + } + } + + /** + * Compare old list with new list after change AttrTelemetryObserve in config Profile + * + * @param parametersOld - + * @param parametersNew - + * @return ResultsAnalyzerParameters: add && new + */ + private ResultsAnalyzerParameters getAnalyzerParameters(Set parametersOld, Set parametersNew) { + ResultsAnalyzerParameters analyzerParameters = null; + if (!parametersOld.equals(parametersNew)) { + analyzerParameters = new ResultsAnalyzerParameters(); + analyzerParameters.setPathPostParametersAdd(parametersNew + .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet())); + analyzerParameters.setPathPostParametersDel(parametersOld + .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet())); + } + return analyzerParameters; + } + + private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap keyNameOld, ConcurrentMap keyNameNew) { + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); + Set paths = keyNameNew.entrySet() + .stream() + .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet(); + analyzerParameters.setPathPostParametersAdd(paths); + return analyzerParameters; + } + + private ResultsAnalyzerParameters getAnalyzerParametersIn(Set parametersObserve, Set parameters) { + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); + analyzerParameters.setPathPostParametersAdd(parametersObserve + .stream().filter(parameters::contains).collect(Collectors.toSet())); + return analyzerParameters; + } + + /** + * Update Resource value after change RezAttrTelemetry in config Profile + * sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue() + * + * @param lwServer - LeshanServer + * @param registration - Registration LwM2M Client + * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] + */ + private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, Set targets, String typeOper) { + targets.forEach(target -> { + LwM2mPath pathIds = new LwM2mPath(target); + if (pathIds.isResource()) { + if (GET_TYPE_OPER_READ.equals(typeOper)) { + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(), + false); + } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) { + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, + null, null, null, null, this.context.getCtxServer().getTimeout(), + false); + } + } + }); + } + + private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set paramAnallyzer) { + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); + paramAnallyzer.forEach(p -> { + if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) { + this.setCancelObservationRecourse(lwServer, registration, p); + } + } + ); + } + + private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) { + ResourceValue resourceValue = null; + if (pathIds.isResource()) { + resourceValue = lwM2MClient.getResources().get(pathIds.toString()); + } + return resourceValue; + } + + /** + * Trigger Server path = "/1/0/8" + * + * Trigger bootStrap path = "/1/0/9" - have to implemented on client + */ + public void doTrigger(LeshanServer lwServer, Registration registration, String path) { + lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE, + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(), + false); + } + + /** + * Session device in thingsboard is closed + * + * @param sessionInfo - lwm2m client + */ + private void doCloseSession(SessionInfoProto sessionInfo) { + TransportProtos.SessionEvent event = SessionEvent.CLOSED; + TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC) + .setEvent(event).build(); + transportService.process(sessionInfo, msg, null); + } + + /** + * Deregister session in transport + * + * @param sessionInfo - lwm2m client + */ + public void doDisconnect(SessionInfoProto sessionInfo) { + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); + transportService.deregisterSession(sessionInfo); + } + + private void checkInactivityAndReportActivity() { + lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key))); + } + + /** + * if sessionInfo removed from sessions, then new registerAsyncSession + * @param sessionInfo - + */ + private void checkInactivity(SessionInfoProto sessionInfo) { + if (transportService.reportActivity(sessionInfo) == null) { + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo)); + } + } + + public void sentLogsToThingsboard(String msg, Registration registration) { + if (msg != null) { + JsonObject telemetries = new JsonObject(); + telemetries.addProperty(LOG_LW2M_TELEMETRY, msg); + this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration); + } + } + + /** + * @param path - path resource + * @return - value of Resource or null + */ + private String getResourceValueToString(LwM2MClient lwM2MClient, String path) { + LwM2mPath pathIds = new LwM2mPath(path); + ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds); + return (resourceValue == null) ? null : + (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index ea440a33d0..2028fb4f00 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -24,17 +24,25 @@ import org.eclipse.leshan.server.queue.PresenceListener; import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.RegistrationListener; import org.eclipse.leshan.server.registration.RegistrationUpdate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; import java.util.Collection; @Slf4j +@Component() +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2mServerListener { + private LeshanServer lhServer; - private LwM2MTransportService service; - public LwM2mServerListener(LeshanServer lhServer, LwM2MTransportService service) { + @Autowired + private LwM2MTransportServiceImpl service; + + public LwM2mServerListener init(LeshanServer lhServer) { this.lhServer = lhServer; - this.service = service; + return this; } public final RegistrationListener registrationListener = new RegistrationListener() { @@ -96,8 +104,7 @@ public class LwM2mServerListener { try { service.onObservationResponse(registration, observation.getPath().toString(), response); } catch (Exception e) { - e.printStackTrace(); - log.error("onResponse"); + log.error("[{}] onResponse", e.toString()); } } 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 6519b5cd33..d48e3e4c9e 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 @@ -27,7 +27,7 @@ import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; -import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportService; +import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportServiceImpl; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import java.util.Map; @@ -48,7 +48,7 @@ public class LwM2MClient implements Cloneable { private UUID sessionUuid; private UUID profileUuid; private LeshanServer lwServer; - private LwM2MTransportService lwM2MTransportService; + private LwM2MTransportServiceImpl lwM2MTransportServiceImpl; private Registration registration; private ValidateDeviceCredentialsResponseMsg credentialsResponse; private Map attributes; @@ -92,7 +92,7 @@ public class LwM2MClient implements Cloneable { this.pendingRequests.remove(path); if (this.pendingRequests.size() == 0) { this.initValue(); - this.lwM2MTransportService.putDelayedUpdateResourcesThingsboard(this); + this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this); } } @@ -123,7 +123,7 @@ public class LwM2MClient implements Cloneable { public void onSuccessOrErrorDelayedRequests(String path) { if (path != null) this.delayedRequests.remove(path); if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) { - this.lwM2MTransportService.updatesAndSentModelParameter(this); + this.lwM2MTransportServiceImpl.updatesAndSentModelParameter(this); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java deleted file mode 100644 index 7e2a9960d1..0000000000 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Copyright © 2016-2020 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.transport.lwm2m.server.secure; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.util.Hex; -import org.eclipse.leshan.server.californium.LeshanServerBuilder; -import org.eclipse.leshan.server.redis.RedisRegistrationStore; -import org.eclipse.leshan.server.redis.RedisSecurityStore; -import org.eclipse.leshan.server.security.DefaultAuthorizer; -import org.eclipse.leshan.server.security.EditableSecurityStore; -import org.eclipse.leshan.server.security.SecurityChecker; -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; -import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.util.Pool; - -import java.math.BigInteger; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.KeyStore; -import java.security.PublicKey; -import java.security.PrivateKey; -import java.security.AlgorithmParameters; -import java.security.KeyFactory; -import java.security.GeneralSecurityException; -import java.security.KeyStoreException; -import java.security.cert.X509Certificate; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPrivateKeySpec; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.KeySpec; -import java.util.Arrays; - -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.REDIS; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; - - -@Slf4j -@Data -public class LwM2MSetSecurityStoreServer { - - private KeyStore keyStore; - private X509Certificate certificate; - private PublicKey publicKey; - private PrivateKey privateKey; - private LwM2MTransportContextServer context; - private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; - - private LeshanServerBuilder builder; - EditableSecurityStore securityStore; - - public LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MTransportContextServer context, LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore, LwM2MSecurityMode dtlsMode) { - this.builder = builder; - this.context = context; - this.lwM2mInMemorySecurityStore = lwM2mInMemorySecurityStore; - /** Set securityStore with new registrationStore */ - switch (dtlsMode) { - /** Use PSK only */ - case PSK: - generatePSK_RPK(); - if (this.privateKey != null && this.privateKey.getEncoded().length > 0) { - builder.setPrivateKey(this.privateKey); - builder.setPublicKey(null); - getParamsPSK(); - } - break; - /** Use RPK only */ - case RPK: - generatePSK_RPK(); - if (this.publicKey != null && this.publicKey.getEncoded().length > 0 && - this.privateKey != null && this.privateKey.getEncoded().length > 0) { - builder.setPublicKey(this.publicKey); - builder.setPrivateKey(this.privateKey); - getParamsRPK(); - } - break; - /** Use x509 only */ - case X509: - setServerWithX509Cert(); - break; - /** No security */ - case NO_SEC: - builder.setTrustedCertificates(new X509Certificate[0]); - break; - /** Use x509 with EST */ - case X509_EST: - // TODO support sentinel pool and make pool configurable - break; - case REDIS: - /** - * Set securityStore with new registrationStore (if use redis store) - * Connect to redis - */ - Pool jedis = null; - try { - jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl())); - securityStore = new RedisSecurityStore(jedis); - builder.setRegistrationStore(new RedisRegistrationStore(jedis)); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - break; - default: - } - - /** Set securityStore with new registrationStore (if not redis)*/ - if (dtlsMode.code < REDIS.code) { - securityStore = lwM2mInMemorySecurityStore; - if (dtlsMode == X509) { - builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() { - @Override - protected boolean matchX509Identity(String endpoint, String receivedX509CommonName, - String expectedX509CommonName) { - return endpoint.startsWith(expectedX509CommonName); - } - })); - } - } - - /** Set securityStore with new registrationStore */ - builder.setSecurityStore(securityStore); - } - - private void generatePSK_RPK() { - try { - /** Get Elliptic Curve Parameter spec for secp256r1 */ - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) { - /** Get point values */ - byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray()); - byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray()); - /** Create key specs */ - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - /** Get keys */ - this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - } - if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) { - /** Get point values */ - byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray()); - /** Create key specs */ - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); - /** Get keys */ - this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); - } - } catch (GeneralSecurityException | IllegalArgumentException e) { - log.error("[{}] Failed generate Server PSK/RPK", e.getMessage()); - throw new RuntimeException(e); - } - } - - private void setServerWithX509Cert() { - try { - if (this.context.getCtxServer().getKeyStoreValue() != null) { - setBuilderX509(); - X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias()); - if (rootCAX509Cert != null) { - X509Certificate[] trustedCertificates = new X509Certificate[1]; - trustedCertificates[0] = rootCAX509Cert; - builder.setTrustedCertificates(trustedCertificates); - } else { - /** by default trust all */ - builder.setTrustedCertificates(new X509Certificate[0]); - } - } - else { - /** by default trust all */ - this.builder.setTrustedCertificates(new X509Certificate[0]); - log.error("Unable to load X509 files for LWM2MServer"); - } - } catch (KeyStoreException ex) { - log.error("[{}] Unable to load X509 files server", ex.getMessage()); - } - } - - private void setBuilderX509() { - /** - * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE - * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks - */ - try { - X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias()); - PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray()); - this.builder.setPrivateKey(privateKey); - this.builder.setCertificateChain(new X509Certificate[]{serverCertificate}); - } catch (Exception ex) { - log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); - } - } - - private void getParamsPSK() { - log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]", - Hex.encodeHexString(this.privateKey.getEncoded()), - this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerSecurePort())); - } - - private void getParamsRPK() { - if (this.publicKey instanceof ECPublicKey) { - /** Get x coordinate */ - byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray(); - if (x[0] == 0) - x = Arrays.copyOfRange(x, 1, x.length); - - /** Get Y coordinate */ - byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray(); - if (y[0] == 0) - y = Arrays.copyOfRange(y, 1, y.length); - - /** Get Curves params */ - String params = ((ECPublicKey) this.publicKey).getParams().toString(); - log.info( - " \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]", - params, Hex.encodeHexString(x), Hex.encodeHexString(y), - Hex.encodeHexString(this.publicKey.getEncoded()), - Hex.encodeHexString(this.privateKey.getEncoded())); - } else { - throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported)."); - } - } -} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java index ad8dd4b0f8..fb90b1537c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java @@ -25,32 +25,32 @@ import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityStoreListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo; +import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; +import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler; import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; import org.thingsboard.server.transport.lwm2m.utils.TypeServer; -import java.util.Map; -import java.util.UUID; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.DEFAULT_MODE; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; @Slf4j -@Component("LwM2mInMemorySecurityStore") +@Service("LwM2mInMemorySecurityStore") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { // lock for the two maps @@ -63,26 +63,36 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { private SecurityStoreListener listener; @Autowired - LwM2MGetSecurityInfo lwM2MGetSecurityInfo; + LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo; + /** + * Start after DefaultAuthorizer or LwM2mPskStore + * @param endPoint - + * @return SecurityInfo + */ @Override public SecurityInfo getByEndpoint(String endPoint) { readLock.lock(); try { String registrationId = this.getByRegistrationId(endPoint, null); - SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.add(endPoint); + SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.addLwM2MClientToSession(endPoint); return info; } finally { readLock.unlock(); } } + /** + * Start after LwM2mPskStore + * @param identity - + * @return SecurityInfo + */ @Override public SecurityInfo getByIdentity(String identity) { readLock.lock(); try { String integrationId = this.getByRegistrationId(null, identity); - return (integrationId != null) ? sessions.get(integrationId).getInfo() : add(identity); + return (integrationId != null) ? sessions.get(integrationId).getInfo() : this.addLwM2MClientToSession(identity); } finally { readLock.unlock(); } @@ -141,11 +151,19 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { return this.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); } + + /** + * Update in sessions (LwM2MClient for key registration_Id) after starting registration LwM2MClient in LwM2MTransportServiceImpl + * Remove from sessions LwM2MClient with key registration_Endpoint + * @param lwServer - + * @param registration - + * @return LwM2MClient after adding it to session + */ public LwM2MClient updateInSessionsLwM2MClient(LeshanServer lwServer, Registration registration) { writeLock.lock(); try { if (this.sessions.get(registration.getEndpoint()) == null) { - this.add(registration.getEndpoint()); + this.addLwM2MClientToSession(registration.getEndpoint()); } LwM2MClient lwM2MClient = this.sessions.get(registration.getEndpoint()); lwM2MClient.setLwServer(lwServer); @@ -166,45 +184,43 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null; } - public String getByRegistrationId(String credentialsId) { - List registrationIds = (this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getEndPoint())).map(model -> model.getKey()).collect(Collectors.toList()).size() > 0) ? - this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getEndPoint())).map(model -> model.getKey()).collect(Collectors.toList()) : - this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getIdentity())).map(model -> model.getKey()).collect(Collectors.toList()); - return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null; - } - public Registration getByRegistration(String registrationId) { return this.sessions.get(registrationId).getRegistration(); } - private SecurityInfo add(String identity) { - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.CLIENT); - UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null; - if (store.getSecurityInfo() != null) { - if (store.getSecurityMode() < DEFAULT_MODE.code) { + /** + * Add new LwM2MClient to session + * @param identity- + * @return SecurityInfo. If error - SecurityInfoError + * and log: + * - FORBIDDEN - if there is no authorization + * - profileUuid - if the device does not have a profile + * - device - if the thingsboard does not have a device with a name equal to the identity + */ + private SecurityInfo addLwM2MClientToSession(String identity) { + ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.CLIENT); + if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { + UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null; + if (store.getSecurityInfo() != null && profileUuid != null) { String endpoint = store.getSecurityInfo().getEndpoint(); -// sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, null, profileUuid)); sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, profileUuid)); - } - } else { - if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) -// sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, null, profileUuid)); + } else if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) { sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, profileUuid)); - else { - log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity); - /** - * Return Error securityInfo - */ - byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray()); - SecurityInfo info = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey); - return info; + } else { + log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity); + /** + * Return Error securityInfo + */ + byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray()); + SecurityInfo infoError = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey); + return infoError; + } } - } - return store.getSecurityInfo(); + return store.getSecurityInfo(); } - public Map getSession (UUID sessionUuId){ - return this.sessions.entrySet().stream().filter(e -> e.getValue().getSessionUuid().equals(sessionUuId)).collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); + public Map getSession(UUID sessionUuId) { + return this.sessions.entrySet().stream().filter(e -> e.getValue().getSessionUuid().equals(sessionUuId)).collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); } public Map getSessions() { @@ -219,13 +235,10 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { return this.profiles.get(profileUuId); } - public MapsetProfiles(Map profiles) { + public Map setProfiles(Map profiles) { return this.profiles = profiles; } - /** - * @param deviceProfile - */ public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) { JsonObject profilesConfigData = LwM2MTransportHandler.getObserveAttrTelemetryFromThingsboard(deviceProfile); if (profilesConfigData != null) { @@ -233,5 +246,4 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { } return (profilesConfigData != null); } - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java index cca1effae4..4a52124af9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java @@ -129,7 +129,6 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { case FLOAT: return String.valueOf(value); case TIME: -// return Long.toString(((Date) value).getTime()); String DATE_FORMAT = "MMM d, yyyy HH:mm a"; Long timeValue = ((Date) value).getTime(); DateFormat formatter = new SimpleDateFormat(DATE_FORMAT); diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 793ea83373..bcf36485a9 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -67,11 +67,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.1 - - 1.8 - 1.8 - org.apache.maven.plugins @@ -87,4 +82,4 @@ - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index 4522ea51bf..3cbd57e508 100755 --- a/pom.xml +++ b/pom.xml @@ -579,7 +579,7 @@ org.apache.maven.plugins maven-compiler-plugin - 2.5.1 + 3.8.1 1.8 1.8 From be4475c2cc61935bdb73c76fa97cd1fa8cfef768 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 5 Jan 2021 19:06:14 +0200 Subject: [PATCH 16/41] Lwm2m: backEnd: refactoring service --- .../secure/LwM2MBootstrapSecurityStore.java | 8 +- ...wM2mCredentialsSecurityInfoValidator.java} | 8 +- .../lwm2m/server/LwM2MTransportRequest.java | 7 +- .../LwM2MTransportServerConfiguration.java | 10 -- .../LwM2MTransportServerInitializer.java | 37 +++--- .../server/LwM2MTransportServiceImpl.java | 111 ++++++++++-------- .../lwm2m/server/LwM2mServerListener.java | 20 ++-- .../lwm2m/server/client/LwM2MClient.java | 4 +- .../lwm2m/server/client/ResourceValue.java | 4 +- .../secure/LwM2mInMemorySecurityStore.java | 31 ++--- 10 files changed, 113 insertions(+), 127 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/{LwM2mValidateCredentialsSecurityInfo.java => LwM2mCredentialsSecurityInfoValidator.java} (97%) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index 6d21d48c65..75e6f78d35 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -32,7 +32,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; -import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo; +import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; import org.thingsboard.server.transport.lwm2m.server.LwM2MSessionMsgListener; import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; @@ -60,7 +60,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { private final EditableBootstrapConfigStore bootstrapConfigStore; @Autowired - LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo; + LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator; @Autowired public LwM2MTransportContextServer context; @@ -72,7 +72,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Override public List getAllByEndpoint(String endPoint) { String endPointKey = endPoint; - ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(endPointKey, TypeServer.BOOTSTRAP); + ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(endPointKey, TypeServer.BOOTSTRAP); if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { /** add value to store from BootstrapJson */ this.setBootstrapConfigScurityInfo(store); @@ -96,7 +96,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Override public SecurityInfo getByIdentity(String identity) { - ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.BOOTSTRAP); + ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, TypeServer.BOOTSTRAP); if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { /** add value to store from BootstrapJson */ this.setBootstrapConfigScurityInfo(store); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java similarity index 97% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index 0983f9c87f..80b4772378 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -47,7 +47,7 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X5 @Slf4j @Component("LwM2MGetSecurityInfo") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") -public class LwM2mValidateCredentialsSecurityInfo { +public class LwM2mCredentialsSecurityInfoValidator { @Autowired public LwM2MTransportContextServer contextS; @@ -62,7 +62,7 @@ public class LwM2mValidateCredentialsSecurityInfo { * @param keyValue - * @return ValidateDeviceCredentialsResponseMsg and SecurityInfo */ - public ReadResultSecurityStore validateCredentialsSecurityInfo(String endPoint, TypeServer keyValue) { + public ReadResultSecurityStore createAndValidateCredentialsSecurityInfo(String endPoint, TypeServer keyValue) { CountDownLatch latch = new CountDownLatch(1); final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1]; contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endPoint).build(), @@ -79,7 +79,7 @@ public class LwM2mValidateCredentialsSecurityInfo { @Override public void onError(Throwable e) { - log.trace("[{}] [{}] Failed to process credentials PSK ", endPoint, e.toString()); + log.trace("[{}] [{}] Failed to process credentials ", endPoint, e); resultSecurityStore[0] = createSecurityInfo(endPoint, null, null); latch.countDown(); } @@ -87,7 +87,7 @@ public class LwM2mValidateCredentialsSecurityInfo { try { latch.await(contextS.getCtxServer().getTimeout(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { - log.error("", e); + log.error("Failed to await credentials!", e); } return resultSecurityStore[0]; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index bc7cbd5b46..de03e25767 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -88,13 +88,12 @@ public class LwM2MTransportRequest { @Autowired LwM2MTransportServiceImpl service; - @PostConstruct public void init() { this.converter = LwM2mValueConverterImpl.getInstance(); - executorResponse = Executors.newCachedThreadPool( + executorResponse = Executors.newFixedThreadPool(10, new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); - executorResponseError = Executors.newCachedThreadPool( + executorResponseError = Executors.newFixedThreadPool(10, new NamedThreadFactory(String.format("LwM2M %s channel response Error", RESPONSE_CHANNEL))); } @@ -220,7 +219,7 @@ public class LwM2MTransportRequest { if (request != null) { this.sendRequest(lwServer, registration, request, lwM2MClient, timeoutInMs, isDelayedUpdate); - } else if (request == null && isDelayedUpdate) { + } else if (isDelayedUpdate) { String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg No SendRequest to Client", target); service.sentLogsToThingsboard(msg, registration); log.error("[{}] - [{}] No SendRequest", target); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java index ac2221054c..c809491803 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java @@ -82,16 +82,6 @@ public class LwM2MTransportServerConfiguration { @Autowired private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; - @Bean - public LwM2mServerListener lwM2mServerListenerCert() { - return new LwM2mServerListener(); - } - - @Bean - public LwM2mServerListener lwM2mServerListenerNoSecPskRpk() { - return new LwM2mServerListener(); - } - @Primary @Bean(name = "LeshanServerCert") public LeshanServer getLeshanServerCert() { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java index 36d39130a3..da4cdbf634 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java @@ -32,6 +32,10 @@ import javax.annotation.PreDestroy; @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportServerInitializer { + + @Autowired + private LwM2MTransportServiceImpl service; + @Autowired @Qualifier("LeshanServerCert") private LeshanServer lhServerCert; @@ -39,16 +43,6 @@ public class LwM2MTransportServerInitializer { @Autowired @Qualifier("LeshanServerNoSecPskRpk") private LeshanServer lhServerNoSecPskRpk; -// -// @Autowired -// @Qualifier("LeshanServerListener") -// private LwM2mServerListener lwM2mServerListener; - - @Autowired - private LwM2mServerListener lwM2mServerListenerNoSecPskRpk; - - @Autowired - private LwM2mServerListener lwM2mServerListenerCert; @Autowired private LwM2MTransportContextServer context; @@ -70,28 +64,25 @@ public class LwM2MTransportServerInitializer { private void startLhServerCert() { this.lhServerCert.start(); - LwM2mServerListener serverListenerCert = this.lwM2mServerListenerCert.init(this.lhServerCert); - this.lhServerCert.getRegistrationService().addListener(serverListenerCert.registrationListener); - this.lhServerCert.getPresenceService().addListener(serverListenerCert.presenceListener); - this.lhServerCert.getObservationService().addListener(serverListenerCert.observationListener); + LwM2mServerListener lhServerCertListener = new LwM2mServerListener(this.lhServerCert, service); + this.lhServerCert.getRegistrationService().addListener(lhServerCertListener.registrationListener); + this.lhServerCert.getPresenceService().addListener(lhServerCertListener.presenceListener); + this.lhServerCert.getObservationService().addListener(lhServerCertListener.observationListener); } private void startLhServerNoSecPskRpk() { this.lhServerNoSecPskRpk.start(); - LwM2mServerListener serverListenerNoSecPskRpk = this.lwM2mServerListenerNoSecPskRpk.init(this.lhServerNoSecPskRpk); - this.lhServerNoSecPskRpk.getRegistrationService().addListener(serverListenerNoSecPskRpk.registrationListener); - this.lhServerNoSecPskRpk.getPresenceService().addListener(serverListenerNoSecPskRpk.presenceListener); - this.lhServerNoSecPskRpk.getObservationService().addListener(serverListenerNoSecPskRpk.observationListener); + LwM2mServerListener lhServerNoSecPskRpkListener = new LwM2mServerListener(this.lhServerNoSecPskRpk, service); + this.lhServerNoSecPskRpk.getRegistrationService().addListener(lhServerNoSecPskRpkListener.registrationListener); + this.lhServerNoSecPskRpk.getPresenceService().addListener(lhServerNoSecPskRpkListener.presenceListener); + this.lhServerNoSecPskRpk.getObservationService().addListener(lhServerNoSecPskRpkListener.observationListener); } @PreDestroy public void shutdown() { log.info("Stopping LwM2M transport Server!"); - try { - lhServerCert.destroy(); - lhServerNoSecPskRpk.destroy(); - } finally { - } + lhServerCert.destroy(); + lhServerNoSecPskRpk.destroy(); log.info("LwM2M transport Server stopped!"); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java index 2a6af4e19e..c4575c1a8f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java @@ -20,6 +20,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mObject; @@ -56,7 +57,6 @@ import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; @@ -68,7 +68,6 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -80,7 +79,6 @@ import java.util.stream.Collectors; import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.CLIENT_NOT_AUTHORIZED; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEFAULT_TIMEOUT; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_REQUEST; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC; @@ -122,11 +120,11 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { @PostConstruct public void init() { this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); - this.executorRegistered = Executors.newCachedThreadPool( + this.executorRegistered = Executors.newFixedThreadPool(10, new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); - this.executorUpdateRegistered = Executors.newCachedThreadPool( + this.executorUpdateRegistered = Executors.newFixedThreadPool(10, new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); - this.executorUnRegistered = Executors.newCachedThreadPool( + this.executorUnRegistered = Executors.newFixedThreadPool(10, new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); this.converter = LwM2mValueConverterImpl.getInstance(); } @@ -149,7 +147,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { public void onRegistered(LeshanServer lwServer, Registration registration, Collection previousObsersations) { executorRegistered.submit(() -> { try { - log.info("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); + log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration); if (lwM2MClient != null) { lwM2MClient.setLwM2MTransportServiceImpl(this); @@ -180,6 +178,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { /** * if sessionInfo removed from sessions, then new registerAsyncSession + * * @param lwServer - LeshanServer * @param registration - Registration LwM2M Client */ @@ -280,20 +279,38 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { * @param lwM2MClient - object with All parameters off client */ private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) { - Arrays.stream(registration.getObjectLinks()).forEach(url -> { + // #1 + for (Link url : registration.getObjectLinks()) { LwM2mPath pathIds = new LwM2mPath(url.getUrl()); if (pathIds.isObjectInstance() && !pathIds.isResource()) { lwM2MClient.getPendingRequests().add(url.getUrl()); } - }); + } // #2 - Arrays.stream(registration.getObjectLinks()).forEach(url -> { + for (Link url : registration.getObjectLinks()) { LwM2mPath pathIds = new LwM2mPath(url.getUrl()); if (pathIds.isObjectInstance() && !pathIds.isResource()) { lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); } - }); + } + + // #1 +// Arrays.stream(registration.getObjectLinks()).forEach(url -> { +// LwM2mPath pathIds = new LwM2mPath(url.getUrl()); +// if (pathIds.isObjectInstance() && !pathIds.isResource()) { +// lwM2MClient.getPendingRequests().add(url.getUrl()); +// } +// }); + // #2 + +// Arrays.stream(registration.getObjectLinks()).forEach(url -> { +// LwM2mPath pathIds = new LwM2mPath(url.getUrl()); +// if (pathIds.isObjectInstance() && !pathIds.isResource()) { +// lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), +// lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); +// } +// }); } /** @@ -302,18 +319,17 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { */ private SessionInfoProto getValidateSessionInfo(Registration registration) { LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); - return getNewSessionInfoProto(lwM2MClient); + return getNewSessionInfoProto(lwM2MClient); } /** - * * @param registrationId - * @return - */ private SessionInfoProto getValidateSessionInfo(String registrationId) { LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId); - return getNewSessionInfoProto(lwM2MClient); + return getNewSessionInfoProto(lwM2MClient); } private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) { @@ -437,6 +453,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { /** * Get names and keyNames from profile shared!!!! attr resources IsWritable + * * @param lwM2MClient - * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable */ @@ -479,13 +496,13 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { } } // #1.2 - CountDownLatch cancelLatch = new CountDownLatch(1); - this.getParametersFromProfile(attributes, telemetries, registration, paths); - cancelLatch.countDown(); try { - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - log.error("[{}] updateAttrTelemetry", e.toString()); + writeLock.lock(); + this.getParametersFromProfile(attributes, telemetries, registration, paths); + } catch (Exception e) { + log.error("UpdateAttrTelemetry", e); + } finally { + writeLock.unlock(); } if (attributes.getAsJsonObject().entrySet().size() > 0) this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration); @@ -559,8 +576,8 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { /** * Prepare Sent to Thigsboard callback - Attribute or Telemetry * - * @param msg - JsonArray: [{name: value}] - * @param topicName - Api Attribute or Telemetry + * @param msg - JsonArray: [{name: value}] + * @param topicName - Api Attribute or Telemetry * @param registration - Id of Registration LwM2M Client */ public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) { @@ -618,14 +635,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { * {@code ObservationService#cancelObservation()} */ public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) { - CountDownLatch cancelLatch = new CountDownLatch(1); lwServer.getObservationService().cancelObservations(registration, path); - cancelLatch.countDown(); - try { - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - log.error("", e); - } } /** @@ -685,30 +695,28 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { boolean isChange = false; try { writeLock.lock(); - try { - // #1 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); - LwM2mPath pathIds = new LwM2mPath(path); - log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values); - ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds); - ResourceValue resValueOld = lwM2MClient.getResources().get(path); - // #2 - if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) { - ResourceValue resourceValue = new ResourceValue(values, null, true); - lwM2MClient.getResources().put(path, resourceValue); - isChange = true; - } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) { - ResourceValue resourceValue = new ResourceValue(null, value, false); - lwM2MClient.getResources().put(path, resourceValue); - isChange = true; - } - } finally { - writeLock.unlock(); + // #1 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); + LwM2mPath pathIds = new LwM2mPath(path); + log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values); + ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds); + ResourceValue resValueOld = lwM2MClient.getResources().get(path); + // #2 + if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) { + ResourceValue resourceValue = new ResourceValue(values, null, true); + lwM2MClient.getResources().put(path, resourceValue); + isChange = true; + } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) { + ResourceValue resourceValue = new ResourceValue(null, value, false); + lwM2MClient.getResources().put(path, resourceValue); + isChange = true; } + } catch (Exception e) { + log.error("#1_1 Update ResourcesValue after Observation is unsuccessfully path: [{}] value: [{}] [{}]", path, value, e.toString()); + } finally { + writeLock.unlock(); } - catch (Exception e) { - log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}] [{}]", path, value, e.toString()); - } + if (isChange) { Set paths = new HashSet<>(); paths.add(path); @@ -1103,6 +1111,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { /** * if sessionInfo removed from sessions, then new registerAsyncSession + * * @param sessionInfo - */ private void checkInactivity(SessionInfoProto sessionInfo) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 2028fb4f00..239a40658f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -24,25 +24,18 @@ import org.eclipse.leshan.server.queue.PresenceListener; import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.RegistrationListener; import org.eclipse.leshan.server.registration.RegistrationUpdate; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; import java.util.Collection; @Slf4j -@Component() -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2mServerListener { - private LeshanServer lhServer; + private final LeshanServer lhServer; + private final LwM2MTransportServiceImpl service; - @Autowired - private LwM2MTransportServiceImpl service; - - public LwM2mServerListener init(LeshanServer lhServer) { + public LwM2mServerListener(LeshanServer lhServer, LwM2MTransportServiceImpl service) { this.lhServer = lhServer; - return this; + this.service = service; } public final RegistrationListener registrationListener = new RegistrationListener() { @@ -51,8 +44,8 @@ public class LwM2mServerListener { */ @Override public void registered(Registration registration, Registration previousReg, - Collection previousObsersations) { - service.onRegistered(lhServer, registration, previousObsersations); + Collection previousObservations) { + service.onRegistered(lhServer, registration, previousObservations); } /** @@ -120,4 +113,5 @@ public class LwM2mServerListener { log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint()); } }; + } 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 d48e3e4c9e..72cb052dc7 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 @@ -63,12 +63,12 @@ public class LwM2MClient implements Cloneable { return super.clone(); } - public LwM2MClient(String endPoint, String identity, SecurityInfo info, ValidateDeviceCredentialsResponseMsg credentialsResponse, Map attributes, UUID profileUuid) { + public LwM2MClient(String endPoint, String identity, SecurityInfo info, ValidateDeviceCredentialsResponseMsg credentialsResponse, UUID profileUuid) { this.endPoint = endPoint; this.identity = identity; this.info = info; this.credentialsResponse = credentialsResponse; - this.attributes = (attributes != null && attributes.size() > 0) ? attributes : new ConcurrentHashMap(); + this.attributes = new ConcurrentHashMap<>(); this.pendingRequests = ConcurrentHashMap.newKeySet(); this.delayedRequests = new ConcurrentHashMap<>(); this.resources = new ConcurrentHashMap<>(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java index d58890dfba..2c962a4395 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.client; import lombok.Data; + import java.util.Map; @Data @@ -24,11 +25,12 @@ public class ResourceValue { Object value; boolean multiInstances; - public ResourceValue ( Map values, Object value, boolean multiInstances) { + public ResourceValue(Map values, Object value, boolean multiInstances) { this.values = values; this.value = value; this.multiInstances = multiInstances; } + public Object getResourceValue() { return this.multiInstances ? this.values : this.value; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java index fb90b1537c..088962cb71 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java @@ -29,7 +29,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; -import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo; +import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler; import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; @@ -53,17 +53,18 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO @Service("LwM2mInMemorySecurityStore") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { + private static final boolean INFOS_ARE_COMPROMISED = false; + // lock for the two maps - protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - protected final Lock readLock = readWriteLock.readLock(); - protected final Lock writeLock = readWriteLock.writeLock(); - private final boolean infosAreCompromised = false; - protected Map sessions = new ConcurrentHashMap<>(); - protected Map profiles = new ConcurrentHashMap<>(); + private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private final Lock readLock = readWriteLock.readLock(); + private final Lock writeLock = readWriteLock.writeLock(); + private final Map sessions = new ConcurrentHashMap<>(); + private Map profiles = new ConcurrentHashMap<>(); private SecurityStoreListener listener; @Autowired - LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo; + LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator; /** * Start after DefaultAuthorizer or LwM2mPskStore @@ -75,8 +76,8 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { readLock.lock(); try { String registrationId = this.getByRegistrationId(endPoint, null); - SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.addLwM2MClientToSession(endPoint); - return info; + return (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? + sessions.get(registrationId).getInfo() : this.addLwM2MClientToSession(endPoint); } finally { readLock.unlock(); } @@ -102,7 +103,7 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { public Collection getAll() { readLock.lock(); try { - return Collections.unmodifiableCollection(this.sessions.entrySet().stream().map(model -> model.getValue().getInfo()).collect(Collectors.toList())); + return Collections.unmodifiableCollection(this.sessions.values().stream().map(LwM2MClient::getInfo).collect(Collectors.toList())); } finally { readLock.unlock(); } @@ -118,7 +119,7 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { LwM2MClient lwM2MClient = (sessions.get(registrationId) != null) ? sessions.get(registrationId) : null; if (lwM2MClient != null) { if (listener != null) { - listener.securityInfoRemoved(infosAreCompromised, lwM2MClient.getInfo()); + listener.securityInfoRemoved(INFOS_ARE_COMPROMISED, lwM2MClient.getInfo()); } sessions.remove(registrationId); } @@ -198,14 +199,14 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { * - device - if the thingsboard does not have a device with a name equal to the identity */ private SecurityInfo addLwM2MClientToSession(String identity) { - ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.CLIENT); + ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, TypeServer.CLIENT); if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null; if (store.getSecurityInfo() != null && profileUuid != null) { String endpoint = store.getSecurityInfo().getEndpoint(); - sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, profileUuid)); + sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), profileUuid)); } else if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) { - sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, profileUuid)); + sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), profileUuid)); } else { log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity); /** From 17700fe186bed6f24e90d3603804f48208312c11 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 11 Jan 2021 19:26:12 +0200 Subject: [PATCH 17/41] Lwm2m: backEnd: refactoring after update dependencies: 2.6.0 1.3.0 1.3.0 1.3.0 --- .../service/lwm2m/LwM2MModelsRepository.java | 21 ++-- .../src/main/resources/thingsboard.yml | 50 +++++---- common/transport/lwm2m/pom.xml | 8 ++ ...TransportBootstrapServerConfiguration.java | 47 +++++--- ...2MTransportBootstrapServerInitializer.java | 37 ++++--- .../LwM2mDefaultBootstrapSessionManager.java | 1 - .../lwm2m/server/LwM2MTransportHandler.java | 4 +- .../lwm2m/server/LwM2MTransportRequest.java | 7 +- .../LwM2MTransportServerConfiguration.java | 63 ++++++----- .../LwM2MTransportServerInitializer.java | 67 +++++++----- .../lwm2m/server/LwM2MTransportService.java | 2 +- .../server/LwM2MTransportServiceImpl.java | 83 +++++++++------ .../lwm2m/server/LwM2mServerListener.java | 4 +- .../lwm2m/server/client/LwM2MClient.java | 6 +- .../lwm2m/server/client/ResourceValue.java | 2 +- .../lwm2m/LwM2MTransportConfigBootstrap.java | 48 ++++++--- .../lwm2m/LwM2MTransportConfigServer.java | 100 ++++++++++++------ pom.xml | 13 +-- transport/lwm2m/pom.xml | 60 +++++++---- .../src/main/resources/tb-lwm2m-transport.yml | 51 +++++---- ui-ngx/angular.json | 5 +- ...m2m-object-add-instances-list.component.ts | 2 +- .../lwm2m-object-add-instances.component.ts | 3 +- ...serve-attr-telemetry-resource.component.ts | 4 +- .../lwm2m-observe-attr-telemetry.component.ts | 1 - .../device/lwm2m/profile-config.models.ts | 33 +----- 26 files changed, 417 insertions(+), 305 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java index e314cd8f51..cea898e7a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java @@ -150,53 +150,54 @@ public class LwM2MModelsRepository { */ private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) { ServerSecurityConfig bsServ = new ServerSecurityConfig(); + bsServ.setBootstrapServerIs(bootstrapServerIs); if (bootstrapServerIs) { + bsServ.setServerId(contextBootStrap.getBootstrapServerId()); switch (mode) { case NO_SEC: bsServ.setHost(contextBootStrap.getBootstrapHost()); - bsServ.setPort(contextBootStrap.getBootstrapPort()); + bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk()); bsServ.setServerPublicKey(""); break; case PSK: bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePort()); + bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk()); bsServ.setServerPublicKey(""); break; case RPK: bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePort()); + bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk()); bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); break; case X509: bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortCert()); + bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509()); bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias())); break; default: break; } } else { - bsServ.setBootstrapServerIs(bootstrapServerIs); - bsServ.setServerId(123); + bsServ.setServerId(contextServer.getServerId()); switch (mode) { case NO_SEC: bsServ.setHost(contextServer.getServerHost()); - bsServ.setPort(contextServer.getServerPort()); + bsServ.setPort(contextServer.getServerPortNoSecPsk()); bsServ.setServerPublicKey(""); break; case PSK: bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerSecurePort()); + bsServ.setPort(contextServer.getServerPortPsk()); bsServ.setServerPublicKey(""); break; case RPK: bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerSecurePort()); + bsServ.setPort(contextServer.getServerPortRpk()); bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY())); break; case X509: bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerSecurePortCert()); + bsServ.setPort(contextServer.getServerPortX509()); bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias())); break; default: diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 065a0a4cb0..8c2d7e1702 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -575,7 +575,13 @@ transport: timeout: "${LWM2M_TIMEOUT:120000}" # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}" model_path_file: "${LWM2M_MODEL_PATH_FILE:}" - support_deprecated_ciphers_enable: "${LWM2M_SUPPORT_DEPRECATED_CIPHERS_ENABLED:true}" + recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" + recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:false}" + request_pool_size: "${LWM2M_REQUEST_POOL_SIZE:100}" + request_error_pool_size: "${LWM2M_REQUEST_ERROR_POOL_SIZE:10}" + registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" + update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" + un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" secure: # Only Certificate_x509: # To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format @@ -588,24 +594,19 @@ transport: root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}" enable_gen_psk_rpk: "${ENABLE_GEN_PSK_RPK:true}" server: + id: "${LWM2M_SERVER_ID:123}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" - bind_port: "${LWM2M_BIND_PORT:5685}" - bind_port_cert: "${LWM2M_BIND_PORT_CERT:5687}" + bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_PSK:5685}" + bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_RPK:5687}" + bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_X509:5689}" secure: - start_all: "${START_SERVER_ALL:true}" - #leshan.core (V1_1) - #DTLS security modes: - #0: Pre-Shared Key mode - #1: Raw Public Key mode - #2: Certificate mode X509 - #3: NoSec mode * - #OMA-TS-LightweightM2M_Core-V1_1_1-20190617-A (add) - #4: Certificate mode X509 with EST - # If only startAll == false - dtls_mode: "${LWM2M_SECURITY_MODE:1}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" - bind_port: "${LWM2M_BIND_PORT_SEC:5686}" - bind_port_cert: "${LWM2M_BIND_PORT_SEC_CERT:5688}" + start_psk: "${START_SERVER_PSK:true}" + start_rpk: "${START_SERVER_RPK:true}" + start_x509: "${START_SERVER_X509:true}" + bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK:5686}" + bind_port_rpk: "${LWM2M_BIND_PORT_SEC_RPK:5688}" + bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509:5690}" # Only RPK: Public & Private Key # create_rpk: "${CREATE_RPK:}" public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}" @@ -615,16 +616,19 @@ transport: alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}" bootstrap: enable: "${BOOTSTRAP:true}" + id: "${LWM2M_SERVER_ID:111}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - bind_port: "${LWM2M_BIND_PORT_BS:5689}" - bind_port_cert: "${LWM2M_BIND_PORT_SER_BS:5691}" + bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_BS:5691}" + bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_BS:5693}" + bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_BS:5695}" secure: - start_all: "${START_BOOTSTRAP_ALL:true}" - # If only startAll == false - dtls_mode: "${LWM2M_SECURITY_MODE_BS:1}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - bind_port: "${LWM2M_BIND_PORT_SEC_BS:5690}" - bind_port_cert: "${LWM2M_BIND_PORT_SEC_CERT_BS:5692}" + start_psk: "${START_SERVER_PSK_BS:true}" + start_rpk: "${START_SERVER_RPK_BS:true}" + start_x509: "${START_SERVER_X509_BS:true}" + bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK_BS:5692}" + bind_port_rpk: "${LWM2M_BIND_PORT_SER_RPK_BS:5694}" + bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509_BS:5696}" # Only RPK: Public & Private Key public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}" public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}" diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index d23f8dbc5d..44a764be2b 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -99,6 +99,14 @@ test-jar test + + org.eclipse.californium + californium-core + + + + + org.eclipse.californium element-connector diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java index 588de9bba2..17999326d7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java @@ -52,7 +52,9 @@ import java.security.spec.ECPublicKeySpec; import java.security.spec.KeySpec; import java.util.Arrays; +import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; @@ -76,27 +78,32 @@ public class LwM2MTransportBootstrapServerConfiguration { @Autowired private LwM2MInMemoryBootstrapConfigStore lwM2MInMemoryBootstrapConfigStore; - @Primary - @Bean(name = "leshanBootstrapCert") - public LeshanBootstrapServer getLeshanBootstrapServerCert() { - log.info("Prepare and start BootstrapServerCert... PostConstruct"); - return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortCert(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortCert(), X509); + @Bean(name = "leshanBootstrapX509") + public LeshanBootstrapServer getLeshanBootstrapServerX509() { + log.info("Prepare and start BootstrapServerX509... PostConstruct"); + return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecX509(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortX509(), X509); + } + + @Bean(name = "leshanBootstrapPsk") + public LeshanBootstrapServer getLeshanBootstrapServerPsk() { + log.info("Prepare and start BootstrapServerRsk... PostConstruct"); + return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecPsk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortPsk(), PSK); } - @Bean(name = "leshanBootstrapRPK") - public LeshanBootstrapServer getLeshanBootstrapServerRPK() { - log.info("Prepare and start BootstrapServerRPK... PostConstruct"); - return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPort(), this.contextBs.getCtxBootStrap().getBootstrapSecurePort(), RPK); + @Bean(name = "leshanBootstrapRpk") + public LeshanBootstrapServer getLeshanBootstrapServerRpk() { + log.info("Prepare and start BootstrapServerRpk... PostConstruct"); + return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecRpk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortRpk(), RPK); } - public LeshanBootstrapServer getLeshanBootstrapServer(Integer bootstrapPort, Integer bootstrapSecurePort, LwM2MSecurityMode dtlsMode) { + public LeshanBootstrapServer getLeshanBootstrapServer(Integer bootstrapPortNoSec, Integer bootstrapSecurePort, LwM2MSecurityMode dtlsMode) { LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder(); - builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPort); + builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPortNoSec); builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapSecureHost(), bootstrapSecurePort); /** Create CoAP Config */ - builder.setCoapConfig(getCoapConfig ()); + builder.setCoapConfig(getCoapConfig (bootstrapPortNoSec, bootstrapSecurePort)); /** ConfigStore */ builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore); @@ -107,13 +114,22 @@ public class LwM2MTransportBootstrapServerConfiguration { /** Define model provider (Create Models )*/ builder.setModel(new StaticModel(contextS.getCtxServer().getModelsValue())); + /** Create credentials */ + LwM2MSetSecurityStoreBootstrap(builder, dtlsMode); + /** Create and Set DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); - dtlsConfig.setRecommendedCipherSuitesOnly(contextS.getCtxServer().isSupportDeprecatedCiphersEnable()); + if (dtlsMode==PSK) { + dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers()); + dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getCtxServer().isRecommendedSupportedGroups()); + dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256); + } + else { + dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers()); +// dtlsConfig.setRecommendedSupportedGroupsOnly(false); + } builder.setDtlsConfig(dtlsConfig); - /** Create credentials */ - LwM2MSetSecurityStoreBootstrap(builder, dtlsMode); BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore); builder.setSessionManager(sessionManager); @@ -133,6 +149,7 @@ public class LwM2MTransportBootstrapServerConfiguration { break; /** Use PSK/RPK */ case PSK: + break; case RPK: setRPK(builder); break; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java index cb927a76b9..6ffa3e8bdf 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java @@ -14,13 +14,13 @@ * limitations under the License. */ package org.thingsboard.server.transport.lwm2m.bootstrap; + import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -31,40 +31,39 @@ import javax.annotation.PreDestroy; public class LwM2MTransportBootstrapServerInitializer { @Autowired - @Qualifier("leshanBootstrapCert") + @Qualifier("leshanBootstrapX509") private LeshanBootstrapServer lhBServerCert; @Autowired - @Qualifier("leshanBootstrapRPK") - private LeshanBootstrapServer lhBServerRPK; + @Qualifier("leshanBootstrapPsk") + private LeshanBootstrapServer lhBServerPsk; + + @Autowired + @Qualifier("leshanBootstrapRpk") + private LeshanBootstrapServer lhBServerRpk; @Autowired private LwM2MTransportContextBootstrap contextBS; @PostConstruct public void init() { - if (this.contextBS.getCtxBootStrap().isBootstrapStartAll()) { - this.lhBServerCert.start(); - this.lhBServerRPK.start(); + if (this.contextBS.getCtxBootStrap().getBootstrapStartPsk()) { + this.lhBServerPsk.start(); } - else { - if (this.contextBS.getCtxBootStrap().getBootStrapDtlsMode() == LwM2MSecurityMode.X509.code) { - this.lhBServerCert.start(); - } - else { - this.lhBServerRPK.start(); - } + if (this.contextBS.getCtxBootStrap().getBootstrapStartRpk()) { + this.lhBServerRpk.start(); + } + if (this.contextBS.getCtxBootStrap().getBootstrapStartX509()) { + this.lhBServerCert.start(); } } @PreDestroy public void shutdown() throws InterruptedException { log.info("Stopping LwM2M transport Bootstrap Server!"); - try { - lhBServerCert.destroy(); - lhBServerRPK.destroy(); - } finally { - } + lhBServerPsk.destroy(); + lhBServerRpk.destroy(); + lhBServerCert.destroy(); log.info("LwM2M transport Bootstrap Server stopped!"); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java index db8a520266..6608635ee0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java @@ -49,7 +49,6 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession this.securityChecker = securityChecker; } - @Override public BootstrapSession begin(String endpoint, Identity clientIdentity) { boolean authorized; if (bsSecurityStore != null) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java index 76cc2a8015..dbca7a8f5b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java @@ -141,7 +141,7 @@ public class LwM2MTransportHandler { // } // } - public static NetworkConfig getCoapConfig() { + public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) { NetworkConfig coapConfig; File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME); if (configFile.isFile()) { @@ -151,6 +151,8 @@ public class LwM2MTransportHandler { coapConfig = LeshanServerBuilder.createDefaultNetworkConfig(); coapConfig.store(configFile); } + coapConfig.setString("COAP_PORT", Integer.toString(serverPortNoSec)); + coapConfig.setString("COAP_SECURE_PORT", Integer.toString(serverSecurePort)); return coapConfig; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java index de03e25767..d5884b5b86 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java @@ -88,12 +88,15 @@ public class LwM2MTransportRequest { @Autowired LwM2MTransportServiceImpl service; + @Autowired + public LwM2MTransportContextServer context; + @PostConstruct public void init() { this.converter = LwM2mValueConverterImpl.getInstance(); - executorResponse = Executors.newFixedThreadPool(10, + executorResponse = Executors.newFixedThreadPool(this.context.getCtxServer().getRequestPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); - executorResponseError = Executors.newFixedThreadPool(10, + executorResponseError = Executors.newFixedThreadPool(this.context.getCtxServer().getRequestErrorPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel response Error", RESPONSE_CHANNEL))); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java index c809491803..71fc65967b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java @@ -62,6 +62,8 @@ import java.security.spec.ECPublicKeySpec; import java.security.spec.KeySpec; import java.util.Arrays; +import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; @@ -83,24 +85,27 @@ public class LwM2MTransportServerConfiguration { private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; @Primary - @Bean(name = "LeshanServerCert") - public LeshanServer getLeshanServerCert() { - log.info("Starting LwM2M transport ServerCert... PostConstruct"); - LeshanServer leshanServerCert = getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509); - - return leshanServerCert; + @Bean(name = "leshanServerX509") + public LeshanServer getLeshanServerX509() { + log.info("Starting LwM2M transport ServerX509... PostConstruct"); + return getLeshanServer(this.context.getCtxServer().getServerPortNoSecX509(), this.context.getCtxServer().getServerPortX509(), X509); } - @Bean(name = "LeshanServerNoSecPskRpk") - public LeshanServer getLeshanServerNoSecPskRpk() { - log.info("Starting LwM2M transport ServerNoSecPskRpk... PostConstruct"); - return getLeshanServer(this.context.getCtxServer().getServerPort(), this.context.getCtxServer().getServerSecurePort(), RPK); + @Bean(name = "leshanServerPsk") + public LeshanServer getLeshanServerPsk() { + log.info("Starting LwM2M transport ServerPsk... PostConstruct"); + return getLeshanServer(this.context.getCtxServer().getServerPortNoSecPsk(), this.context.getCtxServer().getServerPortPsk(), PSK); } - private LeshanServer getLeshanServer(Integer serverPort, Integer serverSecurePort, LwM2MSecurityMode dtlsMode) { + @Bean(name = "leshanServerRpk") + public LeshanServer getLeshanServerRpk() { + log.info("Starting LwM2M transport ServerRpk... PostConstruct"); + return getLeshanServer(this.context.getCtxServer().getServerPortNoSecRpk(), this.context.getCtxServer().getServerPortRpk(), RPK); + } + private LeshanServer getLeshanServer(Integer serverPortNoSec, Integer serverSecurePort, LwM2MSecurityMode dtlsMode) { LeshanServerBuilder builder = new LeshanServerBuilder(); - builder.setLocalAddress(this.context.getCtxServer().getServerHost(), serverPort); + builder.setLocalAddress(this.context.getCtxServer().getServerHost(), serverPortNoSec); builder.setLocalSecureAddress(this.context.getCtxServer().getServerSecureHost(), serverSecurePort); builder.setEncoder(new DefaultLwM2mNodeEncoder()); LwM2mNodeDecoder decoder = new DefaultLwM2mNodeDecoder(); @@ -108,43 +113,47 @@ public class LwM2MTransportServerConfiguration { builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); /** Create CoAP Config */ - builder.setCoapConfig(getCoapConfig()); + builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort)); /** Define model provider (Create Models )*/ LwM2mModelProvider modelProvider = new VersionedModelProvider(this.context.getCtxServer().getModelsValue()); builder.setObjectModelProvider(modelProvider); + /** Create DTLS security mode + * There can be only one DTLS security mode + */ + this.LwM2MSetSecurityStoreServer(builder, dtlsMode); + /** Create DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); - dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getCtxServer().isSupportDeprecatedCiphersEnable()); + if (dtlsMode==PSK) { + dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getCtxServer().isRecommendedCiphers()); + dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getCtxServer().isRecommendedSupportedGroups()); + dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256); + } + else { + dtlsConfig.setRecommendedSupportedGroupsOnly(!this.context.getCtxServer().isRecommendedSupportedGroups()); + dtlsConfig.setRecommendedCipherSuitesOnly(!this.context.getCtxServer().isRecommendedCiphers()); + } /** Set DTLS Config */ builder.setDtlsConfig(dtlsConfig); /** Use a magic converter to support bad type send by the UI. */ builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); - /** Create DTLS security mode - * There can be only one DTLS security mode - */ - this.LwM2MSetSecurityStoreServer(builder, dtlsMode); /** Create LWM2M server */ return builder.build(); } - private void LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MSecurityMode dtlsMode) { + private void LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MSecurityMode dtlsMode) { /** Set securityStore with new registrationStore */ EditableSecurityStore securityStore = lwM2mInMemorySecurityStore; - switch (dtlsMode) { /** Use PSK only */ case PSK: generatePSK_RPK(); - if (this.privateKey != null && this.privateKey.getEncoded().length > 0) { - builder.setPrivateKey(this.privateKey); - builder.setPublicKey(null); - infoParamsPSK(); - } + infoParamsPSK(); break; /** Use RPK only */ case RPK: @@ -233,7 +242,7 @@ public class LwM2MTransportServerConfiguration { private void infoParamsPSK() { log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]", Hex.encodeHexString(this.privateKey.getEncoded()), - this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerSecurePort())); + this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerPortPsk())); } private void infoParamsRPK() { @@ -298,6 +307,4 @@ public class LwM2MTransportServerConfiguration { log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); } } - - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java index da4cdbf634..6fbcb78f9c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java @@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC; -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -37,12 +36,16 @@ public class LwM2MTransportServerInitializer { private LwM2MTransportServiceImpl service; @Autowired - @Qualifier("LeshanServerCert") - private LeshanServer lhServerCert; + @Qualifier("leshanServerX509") + private LeshanServer lhServerX509; @Autowired - @Qualifier("LeshanServerNoSecPskRpk") - private LeshanServer lhServerNoSecPskRpk; + @Qualifier("leshanServerPsk") + private LeshanServer lhServerPsk; + + @Autowired + @Qualifier("leshanServerRpk") + private LeshanServer lhServerRpk; @Autowired private LwM2MTransportContextServer context; @@ -50,39 +53,47 @@ public class LwM2MTransportServerInitializer { @PostConstruct public void init() { if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC(); - if (this.context.getCtxServer().isServerStartAll()) { - this.startLhServerCert(); - this.startLhServerNoSecPskRpk(); - } else { - if (this.context.getCtxServer().getServerDtlsMode() == LwM2MSecurityMode.X509.code) { - this.startLhServerCert(); - } else { - this.startLhServerNoSecPskRpk(); - } + if (this.context.getCtxServer().isServerStartPsk()) { + this.startLhServerPsk(); + } + if (this.context.getCtxServer().isServerStartRpk()) { + this.startLhServerRpk(); } + if (this.context.getCtxServer().isServerStartX509()) { + this.startLhServerX509(); + } + } + + private void startLhServerPsk() { + this.lhServerPsk.start(); + LwM2mServerListener lhServerPskListener = new LwM2mServerListener(this.lhServerPsk, service); + this.lhServerPsk.getRegistrationService().addListener(lhServerPskListener.registrationListener); + this.lhServerPsk.getPresenceService().addListener(lhServerPskListener.presenceListener); + this.lhServerPsk.getObservationService().addListener(lhServerPskListener.observationListener); } - private void startLhServerCert() { - this.lhServerCert.start(); - LwM2mServerListener lhServerCertListener = new LwM2mServerListener(this.lhServerCert, service); - this.lhServerCert.getRegistrationService().addListener(lhServerCertListener.registrationListener); - this.lhServerCert.getPresenceService().addListener(lhServerCertListener.presenceListener); - this.lhServerCert.getObservationService().addListener(lhServerCertListener.observationListener); + private void startLhServerRpk() { + this.lhServerRpk.start(); + LwM2mServerListener lhServerRpkListener = new LwM2mServerListener(this.lhServerRpk, service); + this.lhServerRpk.getRegistrationService().addListener(lhServerRpkListener.registrationListener); + this.lhServerRpk.getPresenceService().addListener(lhServerRpkListener.presenceListener); + this.lhServerRpk.getObservationService().addListener(lhServerRpkListener.observationListener); } - private void startLhServerNoSecPskRpk() { - this.lhServerNoSecPskRpk.start(); - LwM2mServerListener lhServerNoSecPskRpkListener = new LwM2mServerListener(this.lhServerNoSecPskRpk, service); - this.lhServerNoSecPskRpk.getRegistrationService().addListener(lhServerNoSecPskRpkListener.registrationListener); - this.lhServerNoSecPskRpk.getPresenceService().addListener(lhServerNoSecPskRpkListener.presenceListener); - this.lhServerNoSecPskRpk.getObservationService().addListener(lhServerNoSecPskRpkListener.observationListener); + private void startLhServerX509() { + this.lhServerX509.start(); + LwM2mServerListener lhServerCertListener = new LwM2mServerListener(this.lhServerX509, service); + this.lhServerX509.getRegistrationService().addListener(lhServerCertListener.registrationListener); + this.lhServerX509.getPresenceService().addListener(lhServerCertListener.presenceListener); + this.lhServerX509.getObservationService().addListener(lhServerCertListener.observationListener); } @PreDestroy public void shutdown() { log.info("Stopping LwM2M transport Server!"); - lhServerCert.destroy(); - lhServerNoSecPskRpk.destroy(); + lhServerPsk.destroy(); + lhServerRpk.destroy(); + lhServerX509.destroy(); log.info("LwM2M transport Server stopped!"); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java index a9fcbcb669..3b21e7003f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java @@ -32,7 +32,7 @@ public interface LwM2MTransportService { void updatedReg(LeshanServer lwServer, Registration registration); - void unReg(Registration registration, Collection observations); + void unReg(LeshanServer lwServer, Registration registration, Collection observations); void onSleepingDev(Registration registration); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java index c4575c1a8f..072c7bc402 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java @@ -20,7 +20,6 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mObject; @@ -57,6 +56,7 @@ import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; @@ -120,11 +120,11 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { @PostConstruct public void init() { this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); - this.executorRegistered = Executors.newFixedThreadPool(10, + this.executorRegistered = Executors.newFixedThreadPool(this.context.getCtxServer().getRegisteredPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); - this.executorUpdateRegistered = Executors.newFixedThreadPool(10, + this.executorUpdateRegistered = Executors.newFixedThreadPool(this.context.getCtxServer().getUpdateRegisteredPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); - this.executorUnRegistered = Executors.newFixedThreadPool(10, + this.executorUnRegistered = Executors.newFixedThreadPool(this.context.getCtxServer().getUnRegisteredPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); this.converter = LwM2mValueConverterImpl.getInstance(); } @@ -147,13 +147,13 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { public void onRegistered(LeshanServer lwServer, Registration registration, Collection previousObsersations) { executorRegistered.submit(() -> { try { - log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); +// log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration); if (lwM2MClient != null) { lwM2MClient.setLwM2MTransportServiceImpl(this); lwM2MClient.setSessionUuid(UUID.randomUUID()); this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration); - this.setLwM2MClient(lwServer, registration, lwM2MClient); +// this.setLwM2MClient(lwServer, registration, lwM2MClient); SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); if (sessionInfo != null) { lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); @@ -204,9 +204,10 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { * @param observations - All paths observations before unReg * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect */ - public void unReg(Registration registration, Collection observations) { + public void unReg(LeshanServer lwServer, Registration registration, Collection observations) { executorUnRegistered.submit(() -> { try { + this.setCancelObservations(lwServer, registration); this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); this.closeClientSession(registration); } catch (Throwable t) { @@ -279,38 +280,49 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { * @param lwM2MClient - object with All parameters off client */ private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) { - // #1 - for (Link url : registration.getObjectLinks()) { - LwM2mPath pathIds = new LwM2mPath(url.getUrl()); - if (pathIds.isObjectInstance() && !pathIds.isResource()) { - lwM2MClient.getPendingRequests().add(url.getUrl()); - } - } - // #2 - for (Link url : registration.getObjectLinks()) { - LwM2mPath pathIds = new LwM2mPath(url.getUrl()); - if (pathIds.isObjectInstance() && !pathIds.isResource()) { - lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), - lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); - } - } - - // #1 // Arrays.stream(registration.getObjectLinks()).forEach(url -> { // LwM2mPath pathIds = new LwM2mPath(url.getUrl()); // if (pathIds.isObjectInstance() && !pathIds.isResource()) { +// // #1 // lwM2MClient.getPendingRequests().add(url.getUrl()); +// // #2 +// lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), +// lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); // } // }); - // #2 -// Arrays.stream(registration.getObjectLinks()).forEach(url -> { +// // #1 +// for (Link url : registration.getObjectLinks()) { +// LwM2mPath pathIds = new LwM2mPath(url.getUrl()); +// if (pathIds.isObjectInstance() && !pathIds.isResource()) { +// lwM2MClient.getPendingRequests().add(url.getUrl()); +// } +// } +// // #2 +// for (Link url : registration.getObjectLinks()) { // LwM2mPath pathIds = new LwM2mPath(url.getUrl()); // if (pathIds.isObjectInstance() && !pathIds.isResource()) { // lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), // lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); // } -// }); +// } + + // #1 + Arrays.stream(registration.getObjectLinks()).forEach(url -> { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource()) { + lwM2MClient.getPendingRequests().add(url.getUrl()); + } + }); + + // #2 + Arrays.stream(registration.getObjectLinks()).forEach(url -> { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource()) { + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); + } + }); } /** @@ -691,24 +703,29 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { * @param values - LwM2mSingleResource response.getContent() * @param path - resource */ - private void onObservationSetResourcesValue(Registration registration, Object value, Map values, String path) { + private void onObservationSetResourcesValue(Registration registration, Object value, Map values, String path) { boolean isChange = false; try { writeLock.lock(); // #1 LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null); LwM2mPath pathIds = new LwM2mPath(path); - log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values); +// log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values); ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds); ResourceValue resValueOld = lwM2MClient.getResources().get(path); // #2 if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) { - ResourceValue resourceValue = new ResourceValue(values, null, true); - lwM2MClient.getResources().put(path, resourceValue); + lwM2MClient.getResources().get(path).setValues(values); +// ResourceValue resourceValue = new ResourceValue(values, null, true); +// lwM2MClient.getResources().put(path, resourceValue); isChange = true; } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) { - ResourceValue resourceValue = new ResourceValue(null, value, false); - lwM2MClient.getResources().put(path, resourceValue); + lwM2MClient.getResources().get(path).setValue(value); +// ResourceValue resourceValueOld = lwM2MClient.getResources().get(path); +// lwM2MClient.getResources().remove(resourceValueOld); +// ResourceValue resourceValue = new ResourceValue(null, value, false); +// lwM2MClient.getResources().put(path, resourceValue); + log.warn("upDateResize: [{}] [{}] [{}] [{}]", lwM2MClient.getEndPoint(), lwM2MClient.getResources().size(), value, path); isChange = true; } } catch (Exception e) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 239a40658f..e7f82db0c4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -54,7 +54,6 @@ public class LwM2mServerListener { @Override public void updated(RegistrationUpdate update, Registration updatedRegistration, Registration previousRegistration) { - log.info("updated"); service.updatedReg(lhServer, updatedRegistration); } @@ -64,8 +63,7 @@ public class LwM2mServerListener { @Override public void unregistered(Registration registration, Collection observations, boolean expired, Registration newReg) { - log.info("unregistered"); - service.unReg(registration, observations); + service.unReg(lhServer, registration, observations); } }; 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 72cb052dc7..fdb2055500 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 @@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.server.client; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.ObjectModel; +import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.response.LwM2mResponse; @@ -104,8 +105,8 @@ public class LwM2MClient implements Cloneable { if (objectModel != null) { ((LwM2mObjectInstance)((ReadResponse)resp).getContent()).getResources().forEach((k, v) -> { String rez = pathIds.toString() + "/" + k; - if (objectModel.resources.get(k).multiple){ - this.resources.put(rez, new ResourceValue(v.getValues(), null, true)); + if (((LwM2mObjectInstance) ((ReadResponse) resp).getContent()).getResource(k) instanceof LwM2mMultipleResource){ +// this.resources.put(rez, new ResourceValue(v.getInstances().values(), null, true)); } else { this.resources.put(rez, new ResourceValue(null, v.getValue(), false)); @@ -114,6 +115,7 @@ public class LwM2MClient implements Cloneable { } } }); + if (this.responses.size() == 0) this.responses = new ConcurrentHashMap<>(); } /** diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java index 2c962a4395..a4a9d54ada 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java @@ -21,7 +21,7 @@ import java.util.Map; @Data public class ResourceValue { - Map values; + Map values; Object value; boolean multiInstances; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java index 2e8a00bc5e..c8215b7ef3 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java @@ -32,39 +32,57 @@ import java.util.Map; @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") public class LwM2MTransportConfigBootstrap { - @Getter - @Value("${transport.lwm2m.bootstrap.bind_address:}") - private String bootstrapHost; + @Value("${transport.lwm2m.bootstrap.enable:}") + private Boolean bootstrapEnable; @Getter - @Value("${transport.lwm2m.bootstrap.bind_port:}") - private Integer bootstrapPort; + @Value("${transport.lwm2m.bootstrap.id:}") + private Integer bootstrapServerId; @Getter - @Value("${transport.lwm2m.bootstrap.bind_port_cert:}") - private Integer bootstrapPortCert; + @Value("${transport.lwm2m.bootstrap.secure.start_psk:}") + private Boolean bootstrapStartPsk; + @Getter + @Value("${transport.lwm2m.bootstrap.secure.start_rpk:}") + private Boolean bootstrapStartRpk; @Getter - @Value("${transport.lwm2m.bootstrap.secure.start_all:}") - private boolean bootstrapStartAll; + @Value("${transport.lwm2m.bootstrap.secure.start_x509:}") + private Boolean bootstrapStartX509; @Getter - @Value("${transport.lwm2m.bootstrap.secure.dtls_mode:}") - private Integer bootStrapDtlsMode; + @Value("${transport.lwm2m.bootstrap.bind_address:}") + private String bootstrapHost; @Getter @Value("${transport.lwm2m.bootstrap.secure.bind_address:}") private String bootstrapSecureHost; @Getter - @Value("${transport.lwm2m.bootstrap.secure.bind_port:}") - private Integer bootstrapSecurePort; + @Value("${transport.lwm2m.bootstrap.bind_port_no_sec_psk:}") + private Integer bootstrapPortNoSecPsk; + + @Getter + @Value("${transport.lwm2m.bootstrap.bind_port_no_sec_rpk:}") + private Integer bootstrapPortNoSecRpk; + + @Getter + @Value("${transport.lwm2m.bootstrap.bind_port_no_sec_x509:}") + private Integer bootstrapPortNoSecX509; + + @Getter + @Value("${transport.lwm2m.bootstrap.secure.bind_port_psk:}") + private Integer bootstrapSecurePortPsk; + + @Getter + @Value("${transport.lwm2m.bootstrap.secure.bind_port_rpk:}") + private Integer bootstrapSecurePortRpk; @Getter - @Value("${transport.lwm2m.bootstrap.secure.bind_port_cert:}") - private Integer bootstrapSecurePortCert; + @Value("${transport.lwm2m.bootstrap.secure.bind_port_x509:}") + private Integer bootstrapSecurePortX509; @Getter @Value("${transport.lwm2m.bootstrap.secure.public_x:}") diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java index f2e398f063..e5643a90f2 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java @@ -47,13 +47,6 @@ import java.util.stream.Collectors; @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") public class LwM2MTransportConfigServer { - @Getter - @Value("${transport.lwm2m.timeout:}") - private Long timeout; - - @Getter - @Value("${transport.sessions.report_timeout}") - private long sessionReportTimeout; @Getter private String MODEL_PATH_DEFAULT = "models"; @@ -85,10 +78,6 @@ public class LwM2MTransportConfigServer { @Getter private String BASE_DIR_PATH = System.getProperty("user.dir"); - @Getter - @Value("${transport.lwm2m.model_path_file:}") - private String modelPathFile; - @Getter // private String PATH_DATA_MICROSERVICE = "/usr/share/tb-lwm2m-transport/data$"; private String PATH_DATA = "data"; @@ -98,8 +87,44 @@ public class LwM2MTransportConfigServer { private List modelsValue; @Getter - @Value("${transport.lwm2m.support_deprecated_ciphers_enable:}") - private boolean supportDeprecatedCiphersEnable; + @Value("${transport.lwm2m.timeout:}") + private Long timeout; + + @Getter + @Value("${transport.sessions.report_timeout}") + private long sessionReportTimeout; + + @Getter + @Value("${transport.lwm2m.model_path_file:}") + private String modelPathFile; + + @Getter + @Value("${transport.lwm2m.recommended_ciphers:}") + private boolean recommendedCiphers; + + @Getter + @Value("${transport.lwm2m.recommended_supported_groups:}") + private boolean recommendedSupportedGroups; + + @Getter + @Value("${transport.lwm2m.request_pool_size:}") + private int requestPoolSize; + + @Getter + @Value("${transport.lwm2m.request_error_pool_size:}") + private int requestErrorPoolSize; + + @Getter + @Value("${transport.lwm2m.registered_pool_size:}") + private int registeredPoolSize; + + @Getter + @Value("${transport.lwm2m.update_registered_pool_size:}") + private int updateRegisteredPoolSize; + + @Getter + @Value("${transport.lwm2m.un_registered_pool_size:}") + private int unRegisteredPoolSize; @Getter @Value("${transport.lwm2m.secure.key_store_type:}") @@ -121,6 +146,18 @@ public class LwM2MTransportConfigServer { @Value("${transport.lwm2m.secure.root_alias:}") private String rootAlias; + @Getter + @Value("${transport.lwm2m.server.secure.start_psk:}") + private boolean serverStartPsk; + + @Getter + @Value("${transport.lwm2m.server.secure.start_rpk:}") + private boolean serverStartRpk; + + @Getter + @Value("${transport.lwm2m.server.secure.start_x509:}") + private boolean serverStartX509; + @Getter @Value("${transport.lwm2m.secure.enable_gen_psk_rpk:}") private Boolean enableGenPskRpk; @@ -130,32 +167,37 @@ public class LwM2MTransportConfigServer { private String serverHost; @Getter - @Value("${transport.lwm2m.server.bind_port:}") - private Integer serverPort; + @Value("${transport.lwm2m.server.id:}") + private Integer serverId; + + @Getter + @Value("${transport.lwm2m.server.secure.bind_address:}") + private String serverSecureHost; + @Getter - @Value("${transport.lwm2m.server.bind_port_cert:}") - private Integer serverPortCert; + @Value("${transport.lwm2m.server.bind_port_no_sec_psk:}") + private Integer serverPortNoSecPsk; @Getter - @Value("${transport.lwm2m.server.secure.start_all:}") - private boolean serverStartAll; + @Value("${transport.lwm2m.server.bind_port_no_sec_rpk:}") + private Integer serverPortNoSecRpk; @Getter - @Value("${transport.lwm2m.server.secure.dtls_mode:}") - private Integer serverDtlsMode; + @Value("${transport.lwm2m.server.bind_port_no_sec_x509:}") + private Integer serverPortNoSecX509; @Getter - @Value("${transport.lwm2m.server.secure.bind_address:}") - private String serverSecureHost; + @Value("${transport.lwm2m.server.secure.bind_port_psk:}") + private Integer serverPortPsk; @Getter - @Value("${transport.lwm2m.server.secure.bind_port:}") - private Integer serverSecurePort; + @Value("${transport.lwm2m.server.secure.bind_port_rpk:}") + private Integer serverPortRpk; @Getter - @Value("${transport.lwm2m.server.secure.bind_port_cert:}") - private Integer serverSecurePortCert; + @Value("${transport.lwm2m.server.secure.bind_port_x509:}") + private Integer serverPortX509; @Getter @Value("${transport.lwm2m.server.secure.public_x:}") @@ -173,10 +215,6 @@ public class LwM2MTransportConfigServer { @Value("${transport.lwm2m.server.secure.alias:}") private String serverAlias; - @Getter - @Value("${transport.lwm2m.bootstrap.enable:}") - private Boolean bootstrapEnable; - @Getter @Value("${transport.lwm2m.secure.redis_url:}") private String redisUrl; diff --git a/pom.xml b/pom.xml index 3cbd57e508..6fc9e94051 100755 --- a/pom.xml +++ b/pom.xml @@ -62,10 +62,10 @@ 2.11.3 2.11.3 2.2.6 - 2.2.3 - 1.0.1 - 1.0.1 - 1.0.0 + 2.6.0 + 1.3.0 + 1.3.0 + 1.3.0 2.6.2 2.3.30 1.6.2 @@ -1166,11 +1166,6 @@ leshan-core ${leshan-core.version} - - org.eclipse.californium - californium-core - ${californium.version} - org.eclipse.californium californium-core diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 1d7619a2ad..addbeb9d7b 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -45,11 +45,38 @@ + + org.thingsboard.common.transport + transport-api + + + org.springframework + spring-context-support + + + org.springframework + spring-context + + + org.slf4j + slf4j-api + + + org.slf4j + log4j-over-slf4j + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + org.eclipse.leshan leshan-server-cf - org.eclipse.leshan leshan-client-cf @@ -59,7 +86,6 @@ org.eclipse.leshan leshan-server-redis - org.springframework.boot spring-boot-starter-test @@ -78,32 +104,22 @@ org.eclipse.californium californium-core - - - org.eclipse.californium - element-connector test-jar test - - org.thingsboard.common.transport - lwm2m - - - org.thingsboard.common - queue - - - org.springframework.boot - spring-boot-starter-web + org.eclipse.californium + californium-core + + + + - com.sun.winsw - winsw - bin - exe - provided + org.eclipse.californium + element-connector + test-jar + test diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index d6fe7b5b1f..f25dd2bb59 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -42,6 +42,7 @@ zk: # LWM2M server parameters transport: + # Local LwM2M transport parameters lwm2m: # Enable/disable lvm2m transport protocol. enabled: "${LWM2M_ENABLED:true}" @@ -51,7 +52,13 @@ transport: timeout: "${LWM2M_TIMEOUT:120000}" # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}" model_path_file: "${LWM2M_MODEL_PATH_FILE:}" - support_deprecated_ciphers_enable: "${LWM2M_SUPPORT_DEPRECATED_CIPHERS_ENABLED:true}" + recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" + recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:false}" + request_pool_size: "${LWM2M_REQUEST_POOL_SIZE:100}" + request_error_pool_size: "${LWM2M_REQUEST_ERROR_POOL_SIZE:10}" + registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" + update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" + un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" secure: # Only Certificate_x509: # To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format @@ -64,24 +71,19 @@ transport: root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}" enable_gen_psk_rpk: "${ENABLE_GEN_PSK_RPK:true}" server: + id: "${LWM2M_SERVER_ID:123}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" - bind_port: "${LWM2M_BIND_PORT:5685}" - bind_port_cert: "${LWM2M_BIND_PORT_CERT:5687}" + bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_PSK:5685}" + bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_RPK:5687}" + bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_X509:5689}" secure: - start_all: "${START_SERVER_ALL:true}" - #leshan.core (V1_1) - #DTLS security modes: - #0: Pre-Shared Key mode - #1: Raw Public Key mode - #2: Certificate mode X509 - #3: NoSec mode * - #OMA-TS-LightweightM2M_Core-V1_1_1-20190617-A (add) - #4: Certificate mode X509 with EST - # If only startAll == false - dtls_mode: "${LWM2M_SECURITY_MODE:1}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" - bind_port: "${LWM2M_BIND_PORT_SEC:5686}" - bind_port_cert: "${LWM2M_BIND_PORT_SEC_CERT:5688}" + start_psk: "${START_SERVER_PSK:true}" + start_rpk: "${START_SERVER_RPK:true}" + start_x509: "${START_SERVER_X509:true}" + bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK:5686}" + bind_port_rpk: "${LWM2M_BIND_PORT_SEC_RPK:5688}" + bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509:5690}" # Only RPK: Public & Private Key # create_rpk: "${CREATE_RPK:}" public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}" @@ -91,16 +93,19 @@ transport: alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}" bootstrap: enable: "${BOOTSTRAP:true}" + id: "${LWM2M_SERVER_ID:111}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - bind_port: "${LWM2M_BIND_PORT_BS:5689}" - bind_port_cert: "${LWM2M_BIND_PORT_SER_BS:5691}" + bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_BS:5691}" + bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_BS:5693}" + bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_BS:5695}" secure: - start_all: "${START_BOOTSTRAP_ALL:true}" - # If only startAll == false - dtls_mode: "${LWM2M_SECURITY_MODE_BS:1}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - bind_port: "${LWM2M_BIND_PORT_SEC_BS:5690}" - bind_port_cert: "${LWM2M_BIND_PORT_SEC_CERT_BS:5692}" + start_psk: "${START_SERVER_PSK_BS:true}" + start_rpk: "${START_SERVER_RPK_BS:true}" + start_x509: "${START_SERVER_X509_BS:true}" + bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK_BS:5692}" + bind_port_rpk: "${LWM2M_BIND_PORT_SER_RPK_BS:5694}" + bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509_BS:5696}" # Only RPK: Public & Private Key public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}" public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}" diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json index 3cad30304a..3cc2d92d57 100644 --- a/ui-ngx/angular.json +++ b/ui-ngx/angular.json @@ -247,6 +247,7 @@ }, "defaultProject": "thingsboard", "cli": { - "packageManager": "yarn" + "packageManager": "yarn", + "analytics": false } -} +} \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts index 20a72d8dcd..31ab078485 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts @@ -25,7 +25,7 @@ import { import { ControlValueAccessor, FormBuilder, - FormGroup, NG_VALIDATORS, + FormGroup, NG_VALUE_ACCESSOR, Validators } from "@angular/forms"; import { coerceBooleanProperty } from "@angular/cdk/coercion"; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts index 62f516b660..9c62b02b49 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts @@ -16,12 +16,11 @@ import { Component, Inject, OnInit } from '@angular/core'; import { DialogComponent } from '@shared/components/dialog.component'; -import { ControlValueAccessor, FormBuilder, FormGroup } from '@angular/forms'; +import { FormBuilder, FormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { Router } from '@angular/router'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { TranslateService } from '@ngx-translate/core'; export interface Lwm2mObjectAddInstancesData { instancesIds: Set; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts index f94cd589ae..9a909a41af 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from "@angular/core"; +import { Component, forwardRef, Input, OnInit } from "@angular/core"; import { ControlValueAccessor, FormArray, FormBuilder, @@ -27,7 +27,7 @@ import { } from '@home/components/profile/device/lwm2m/profile-config.models'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { deepClone, isUndefined } from '@core/utils'; +import { deepClone } from '@core/utils'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; @Component({ diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index 8482fde97a..e506510663 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -44,7 +44,6 @@ import { Lwm2mObjectAddInstancesComponent, Lwm2mObjectAddInstancesData } from '@home/components/profile/device/lwm2m/lwm2m-object-add-instances.component'; -import { Control } from 'leaflet'; @Component({ selector: 'tb-profile-lwm2m-observe-attr-telemetry', diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts index c626138828..20a68765a9 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts @@ -25,11 +25,7 @@ export const DEFAULT_ID_SERVER = 123; export const DEFAULT_ID_BOOTSTRAP = 111; export const DEFAULT_HOST_NAME = "localhost"; export const DEFAULT_PORT_SERVER_NO_SEC = 5685; -export const DEFAULT_PORT_SERVER_SEC = 5686; -export const DEFAULT_PORT_SERVER_SEC_CERT = 5688; -export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5689; -export const DEFAULT_PORT_BOOTSTRAP_SEC = 5690; -export const DEFAULT_PORT_BOOTSTRAP_SEC_CERT = 5692; +export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5691; export const DEFAULT_CLIENT_HOLD_OFF_TIME = 1; export const DEFAULT_LIFE_TIME = 300; export const DEFAULT_DEFAULT_MIN_PERIOD = 1; @@ -118,7 +114,7 @@ export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecu export function getDefaultBootstrapServerSecurityConfig(hostname: any): ServerSecurityConfig { return { host: hostname, - port: getDefaultPortBootstrap(), + port: DEFAULT_PORT_BOOTSTRAP_NO_SEC, bootstrapServerIs: true, securityMode: SECURITY_CONFIG_MODE.NO_SEC.toString(), serverPublicKey: '', @@ -131,20 +127,10 @@ export function getDefaultBootstrapServerSecurityConfig(hostname: any): ServerSe export function getDefaultLwM2MServerSecurityConfig(hostname): ServerSecurityConfig { const DefaultLwM2MServerSecurityConfig = getDefaultBootstrapServerSecurityConfig(hostname); DefaultLwM2MServerSecurityConfig.bootstrapServerIs = false; - DefaultLwM2MServerSecurityConfig.port = getDefaultPortServer(); + DefaultLwM2MServerSecurityConfig.port = DEFAULT_PORT_SERVER_NO_SEC; DefaultLwM2MServerSecurityConfig.serverId = DEFAULT_ID_SERVER; return DefaultLwM2MServerSecurityConfig; } -//ok -export function getDefaultPortBootstrap(securityMode?: string): number { - return (!securityMode || securityMode === SECURITY_CONFIG_MODE.NO_SEC.toString()) ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : - (securityMode === SECURITY_CONFIG_MODE.X509.toString()) ? DEFAULT_PORT_BOOTSTRAP_SEC_CERT : DEFAULT_PORT_BOOTSTRAP_SEC; -} -//ok -export function getDefaultPortServer(securityMode?: string): number { - return (!securityMode || securityMode === SECURITY_CONFIG_MODE.NO_SEC.toString()) ? DEFAULT_PORT_SERVER_NO_SEC : - (securityMode === SECURITY_CONFIG_MODE.X509.toString()) ? DEFAULT_PORT_SERVER_SEC_CERT : DEFAULT_PORT_SERVER_SEC; -} //ok function getDefaultProfileBootstrapSecurityConfig(hostname: any): BootstrapSecurityConfig { @@ -197,16 +183,3 @@ export interface ObjectLwM2M { instances?: Instance [] } -export function getChangeInstancesIds (): ChangeInstancesIds { - let changeInstancesIds: ChangeInstancesIds; - changeInstancesIds.add = new Set(); - changeInstancesIds.del = new Set(); - return changeInstancesIds; - -} - -export interface ChangeInstancesIds { - add: Set, - del: Set -} - From cb3353a32d3ea39b1645f8ee9f3368219b417435 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 12 Jan 2021 11:31:20 +0200 Subject: [PATCH 18/41] Lwm2m: backEnd: refactoring start server/bootstrap without different secure --- application/src/main/resources/thingsboard.yml | 2 +- ...wM2MTransportBootstrapServerConfiguration.java | 5 ++++- .../LwM2MTransportBootstrapServerInitializer.java | 8 ++++---- .../server/LwM2MTransportServerConfiguration.java | 15 +++++++++------ .../server/LwM2MTransportServerInitializer.java | 6 +++--- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 8c2d7e1702..51c1f4f353 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -635,7 +635,7 @@ transport: private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}" # Only Certificate_x509: alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}" - # Redis + # Redis redis_url: "${LWM2M_REDIS_URL:''}" swagger: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java index 17999326d7..09233b5d3b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java @@ -61,7 +61,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle @Slf4j @Component -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true'&& '${transport.lwm2m.bootstrap.enable}'=='true')") +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true')") public class LwM2MTransportBootstrapServerConfiguration { private PublicKey publicKey; private PrivateKey privateKey; @@ -80,18 +80,21 @@ public class LwM2MTransportBootstrapServerConfiguration { @Primary @Bean(name = "leshanBootstrapX509") + @ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_x509:false}'=='true')") public LeshanBootstrapServer getLeshanBootstrapServerX509() { log.info("Prepare and start BootstrapServerX509... PostConstruct"); return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecX509(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortX509(), X509); } @Bean(name = "leshanBootstrapPsk") + @ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_psk:false}'=='true')") public LeshanBootstrapServer getLeshanBootstrapServerPsk() { log.info("Prepare and start BootstrapServerRsk... PostConstruct"); return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecPsk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortPsk(), PSK); } @Bean(name = "leshanBootstrapRpk") + @ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_rpk:false}'=='true')") public LeshanBootstrapServer getLeshanBootstrapServerRpk() { log.info("Prepare and start BootstrapServerRpk... PostConstruct"); return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecRpk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortRpk(), RPK); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java index 6ffa3e8bdf..68bc6ee51c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java @@ -27,18 +27,18 @@ import javax.annotation.PreDestroy; @Slf4j @Service -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled}'=='true'&& '${transport.lwm2m.bootstrap.enable}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true'&& '${transport.lwm2m.bootstrap.enable}'=='true')") +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true')") public class LwM2MTransportBootstrapServerInitializer { - @Autowired + @Autowired(required = false) @Qualifier("leshanBootstrapX509") private LeshanBootstrapServer lhBServerCert; - @Autowired + @Autowired(required = false) @Qualifier("leshanBootstrapPsk") private LeshanBootstrapServer lhBServerPsk; - @Autowired + @Autowired(required = false) @Qualifier("leshanBootstrapRpk") private LeshanBootstrapServer lhBServerRpk; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java index 71fc65967b..1b9c51b9ec 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java @@ -85,24 +85,27 @@ public class LwM2MTransportServerConfiguration { private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; @Primary - @Bean(name = "leshanServerX509") - public LeshanServer getLeshanServerX509() { - log.info("Starting LwM2M transport ServerX509... PostConstruct"); - return getLeshanServer(this.context.getCtxServer().getServerPortNoSecX509(), this.context.getCtxServer().getServerPortX509(), X509); - } - @Bean(name = "leshanServerPsk") + @ConditionalOnExpression("('${transport.lwm2m.server.secure.start_psk:false}'=='true')") public LeshanServer getLeshanServerPsk() { log.info("Starting LwM2M transport ServerPsk... PostConstruct"); return getLeshanServer(this.context.getCtxServer().getServerPortNoSecPsk(), this.context.getCtxServer().getServerPortPsk(), PSK); } @Bean(name = "leshanServerRpk") + @ConditionalOnExpression("('${transport.lwm2m.server.secure.start_rpk:false}'=='true')") public LeshanServer getLeshanServerRpk() { log.info("Starting LwM2M transport ServerRpk... PostConstruct"); return getLeshanServer(this.context.getCtxServer().getServerPortNoSecRpk(), this.context.getCtxServer().getServerPortRpk(), RPK); } + @Bean(name = "leshanServerX509") + @ConditionalOnExpression("('${transport.lwm2m.server.secure.start_x509:false}'=='true')") + public LeshanServer getLeshanServerX509() { + log.info("Starting LwM2M transport ServerX509... PostConstruct"); + return getLeshanServer(this.context.getCtxServer().getServerPortNoSecX509(), this.context.getCtxServer().getServerPortX509(), X509); + } + private LeshanServer getLeshanServer(Integer serverPortNoSec, Integer serverSecurePort, LwM2MSecurityMode dtlsMode) { LeshanServerBuilder builder = new LeshanServerBuilder(); builder.setLocalAddress(this.context.getCtxServer().getServerHost(), serverPortNoSec); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java index 6fbcb78f9c..74a37938b3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java @@ -35,15 +35,15 @@ public class LwM2MTransportServerInitializer { @Autowired private LwM2MTransportServiceImpl service; - @Autowired + @Autowired(required = false) @Qualifier("leshanServerX509") private LeshanServer lhServerX509; - @Autowired + @Autowired(required = false) @Qualifier("leshanServerPsk") private LeshanServer lhServerPsk; - @Autowired + @Autowired(required = false) @Qualifier("leshanServerRpk") private LeshanServer lhServerRpk; From 84698ad554f555b270214e8b00d20dee86e8f2bb Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 14 Jan 2021 16:04:58 +0200 Subject: [PATCH 19/41] Lwm2m: front - fix bug /../../ --- ...TransportBootstrapServerConfiguration.java | 3 +- .../LwM2MTransportServerConfiguration.java | 29 ++++++++++++++++++ .../resources/credentials/serverKeyStore.jks | Bin 5716 -> 5708 bytes .../credentials/shell/lwM2M_credentials.sh | 3 ++ .../credentials/shell/lwM2M_keygen.properties | 2 +- .../lwm2m/LwM2MTransportConfigBootstrap.java | 5 --- .../lwm2m/LwM2MTransportConfigServer.java | 2 +- .../main/data/credentials/serverKeyStore.jks | Bin 5716 -> 5708 bytes .../lwm2m-device-config-server.component.ts | 2 +- ...ofile-transport-configuration.component.ts | 6 ++-- ...m2m-object-add-instances-list.component.ts | 4 +-- .../lwm2m/lwm2m-object-list.component.ts | 8 ++--- 12 files changed, 45 insertions(+), 19 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java index 09233b5d3b..13584cadb5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java @@ -245,8 +245,7 @@ public class LwM2MTransportBootstrapServerConfiguration { } if (serverCertificate != null) { builder.setCertificateChain(new X509Certificate[]{serverCertificate}); - this.contextBs.getCtxBootStrap().setBootstrapCertificate(serverCertificate); - infoParamsX509(serverCertificate); + this.infoParamsX509(serverCertificate); } } catch (Exception ex) { log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java index 1b9c51b9ec..c6ce086446 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java @@ -52,6 +52,7 @@ import java.security.KeyFactory; import java.security.KeyStoreException; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; @@ -306,8 +307,36 @@ public class LwM2MTransportServerConfiguration { PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray()); builder.setPrivateKey(privateKey); builder.setCertificateChain(new X509Certificate[]{serverCertificate}); + this.infoParamsX509(serverCertificate, privateKey); } catch (Exception ex) { log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); } +// /** +// * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE +// * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks +// */ +// try { +// X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerPrivateS()); +// this.privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray()); +// if (this.privateKey != null && this.privateKey.getEncoded().length > 0) { +// builder.setPrivateKey(this.privateKey); +// } +// if (serverCertificate != null) { +// builder.setCertificateChain(new X509Certificate[]{serverCertificate}); +// this.infoParamsX509(serverCertificate); +// } +// } catch (Exception ex) { +// log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); +// } + } + + private void infoParamsX509(X509Certificate certificate, PrivateKey privateKey) { + try { + log.info("Server uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]", + Hex.encodeHexString(certificate.getEncoded()), + Hex.encodeHexString(privateKey.getEncoded())); + } catch (CertificateEncodingException e) { + log.error("", e); + } } } diff --git a/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks b/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks index b5b6c9fa3c43118262f4c89b1ba319ef09b694d0..e1d6deff4b3fd948655f802da4ad65f0ba568f4a 100644 GIT binary patch delta 5500 zcmYM2Wl+=&*T!jBSV}sSl;&oa5|9p+ZjtU@Lb~J+OCuq|QqtYsu}F7FH-dn4E+q)> zJ?}j8JfE(a>ztY2IoJ7iW_iYVm?3b=cNplH{|Y(|h%5YE_zfXCE_x1};sOg!aRPx; z93(KySOyFXE^HOWag!{fQfkM{^n$cphcPhGdNr`#Wt>Q!PDa6mlL-fIR>V+JVL!8t z6X=^CxJmuvKUmO}t4=wD7icL2NvU3iy9#}u2|GX)yjJJ&myU34J;XRUYa_+b3b;ls zCJYD@3qv77La!iDC_j`R5@yYLQt^XkGc~Lo7};0m1nX`Ph<+;prApPQ&uuKCeeY#=-v#&G!-tyqzja z?PT-IcbtWaL_!eim5B-H;^6jgHUM*-G4I&kwb}IfYIF1tkK-iywDN@F=&DR&GZTifsnouggpyc{<&+3#<$DKD#OS`{%gzII<^|pUAg$=My$RP+=yDd3093CqiMN>a7JG7N!Z>bElZk_I`x8f)- z<(&QL?UUDOcA<70GZc7dmyx;QPYMbIU*AWvrl$SM^8k2TN(&6&0@`XKmvFLP0(-@Dkmu?=y+Vo5t%POsjw_UayDi|^4A(xHT zWSrwk=y5|_OhsP}P8n{wj8zmRQW2kDN`@H0q-CI1_R36?Z&FTod*j4Q2@W=~I|J_Z zG#pI;jmh_EuWq)~R`T&Ec(-ZpJOvY?6OI#qN}VINH(dL|pqgKb zEjT*BO8y?mCY*Sa%ZZipIz-}#XUezsw6{Wh+BAxdn4tbV)fV=-vG)ZRCLq@}NwZUl z;y5X5Z^aJA=8n#sD=hq?tdaN_dvX6*b!`}+Q&JgJ8Gh7#P{4!-G*z99v~B8W;4S@eU5`aG>T#I zRLr?5Nd<-g(fXtCLPBIpns}A<Iw4B{4zIU#XvF$-^V6d3C=@@OJ$1i`ztU*=+J;1rKN{v1$nkC`xOShrf5FGf5jv z7ZJ)@!%t>dj7GaGTIGpKXcU}dvfG8pd;lwzAl{G#QqG>IRuwyE;{{DjO3BT`Z#}#G zC5KeU`w=BZ*JonpU>=x^@|YC!I7 z@pzOMc)nSq(<{HN$%pk56@{s`B{W7z!y9u!(U=+b22(#4tPSK@O^J&xEC%OUv+F3vo zbo@l*?CI}tEQU?r^QQy&x2iY7; z|I)xS1*sl}RrQ4jXr#>>;v0RO=L`xd1#LxNTD6}gfhQg=F|-R`Si8OLs?e7?7z=0j z7+PFWfKU7GRHdAmeq-zlChfl4czLfuK01DD_{PO1h6m4c`Wd4gEC3FTkD~s)O+~|Hmv+^v7?gcXW(k+#$=0yE~pvhUNBHXPbOTM?GA z_sOZQ^`aNVtbDVTBT*b@jz+VP29)6p^g`p!E&b=5Bvm$}@x z>gDWT4cT#eGIJz>=!C{x!Rv0FVIm)NNbHIaZ>)ar5I{wWpL*tT{7{+El|kZl$5O*C zKfrf!i3prV+S1iyA*K^mj6d1!;_e~mq%}wtS1`Euc!vXxB12|_RXP zHj9Pv-Zzn24aR;4cf3m`F)L}}Zw1|GwbqYV_@nt(tM__;512+xZ7NM-4jE@v?sfMR zYBth(0^RA4-tB?BPvDdm9B70b{;pxq6aK=B=W4O+{4d??pK=>)AeY`t_h0(x>2yk? zS6x<9_eI~?5~|4c$svli{c|@x5V-l3luJIwkk3j!e;m;Q#CY8q9shX{BB?7;bMr_( z4*%w%D*a-cj1m0BKw1E>QRUQjSM$h~*+y<(77OckpXTxEQU^oO7fJi0HV3ZbM5N@vvIB_(!u$HhGz59mF zNUcOx6hbymgg1aeXgD`d9ilvIuAkzka=T3RTly7($P%GF^**l3gI*!h^zB=YMp@m3 z`3RAZP$>d`d$c|=E7tK0@WLc-N%+$maQ!g08T9Cm-NO5~X*8;HsqP-eEP^Y9qQNx# zJl0cgJIOyBNvVz1_4g};A}?V?AHHubc`kE(KzgLRkpAkm^aEZ(G3=gYU%!UiaS z{3|m5zY@IL&}^@%VK?@8IGeVYHC28U2T|C@epdHphg|I> z#4{1O(Duj=RS_osXsAy0L;Xq;bhPA4B0eoV-FEY0sEyQ2+t%*8H(n3`HHN_L3TY@H zNGaAk`87peDoAYwIS6U8&v!w@u^6RZ-^`7BF>@*&Qw}pu%4;fPFMO@#q@Jhx9f|C9 z#@NN4kDa0Wlq#>Knt~P6l;uKRQy>L8n$pzZ&zJA66;QSPiaZN6N{6#aBOSFEGd~K3 zRLTDKO~Qqp?@R&C$R_|}c{p2piAqGrzu97xM4Zz;j;A#USrRfVz+o~h9738J2Ks%K zaxLyS#gg=uBl~bdel&}zQ94GWf`6fItTchGm8l%OiG_C3KPGf)_m8`fs-hhIiRw_y z>fU&fduMCUC{n>Q+6`WCCd^!)LB7FlM8b+A7VT0BxXT-YRRHCYVERdULy8wcxV$-n z+mcf|a}^&V>~=R?lDaQn-Ofd!r|A^T!`gk%3iE%^><>x5COx6GzNS6~=_w@ycHF<+ zUyfPx389@nqAE;Wen}G4ym?%V)s^4hTGOnO zf`=@*9#&QN%>aK?K`XaEKdl$06iD`iM8bNuv;;LSuFaat%w5I$@%kDn7*2zWD7}12 zw^;0B?pO!%esOrSQreF3ef=^@u%8+GRc`wYw7Oz}rx5Rc-4>{eu)hoyC$rl#;+Pwf zCC?r6W#k%V^@7<}V$K#2YcSqi=MYiW{Mc-cCHa@S579e5!%ax{EybyhpdcpVj^~I&k z){h3fR@d(P* z6h(|M^Aqj|Y?@XLr`aNM`6qh1g?#+T)t=^|!!GxbC9nF6qk?*(JgJY|sV!Ds!Tbi& zJ9@we!vl6@@IF~&U*tJDd%K&SqMF_9f@ig`^yRNSSQ|#;^kvHZ>MmE%idAKE9&9rzEFQQ#;>eAdcL|4! zd0Th=%1yX7^G57|%Vv=o9T_~E%+VB;Ak91bT)5hSV49sbFZx7 z^U?A?Rrt43-``lq((XOifm+)u484}b|J6iID1_uAmJiIuj!@%!LW-yDEfisyhe{lx z*_Yv5b`ePaa4LR$7U)z-a&KA21iYS%iGq!9M-u~j7X3#m0QhQD;>2A_hj|+>;kRP> zotm%cA+8*M#8gqAduC)FBAQU5+(q_|qeG&=3?pDT#aYpf@I#Jk;@?LXFC^nPXB`2~C4HZm3J_iIAw363~$QzlqU}w$Y+?|9{C5}GoPrk?HT90EL z6DQ@^UVz`F_7#;AMfv=!14ek_aI=T*o2P=?y1CCk+7+uGcIeqB_@vJcf`zhf9XC8Y9ns-zYjM0&di(C=9C5JqRQ7}0plZn+f=mAyV<+H9@)5^PiR z+Q6edU1p?uUd%<}+4gRul~L`jg~3w+#nx3a+)b8_uZDT4WS^%DP-prIIQM7H82jLV zkk{&i{aF*!D)kS+iHdhsr+0=AlVsTu4=I$?GB-AVeA|`yM>;f^JdJDz&rIQtxK#Sl z_IyH0W+}|dAW_*{f#^w-x8Y>7S2aw1ExG$}7N7mnx0x225=;F3Rl52OjMMnYE!dfu zw`Xsli`La34ex4H0X^DK zVz0<}c57vfXAg|t|Dak+x0K9p7bizIg8Xnm3YEP6%FNkL`s?S=?C12StI3miYHlc6 zT{x>U#n)Bur$0IK)c%faPVcfnjV* zQ>@}U|KGGu5W2>6KU5fh2911XkGzku{@G`timj_MpG<}G9;@m4;nkq<-A`mJ-_yF> zH@X-}kP!yJPOh7^cCBw)TpE5vAkzhnH)uF*-q{M>QDD*j)ixICAYVi)(~pSJ-UAXsVbT*NQEj_#4%YUaN+}JudbpICah%DXxwg=Qb(XP3{Zt_O z<>FIr$gV1mf3;u8P8U=y3aYeK2NchEII~wWi`)Qj**6}LW8Oy>hOP&H;WhR)!9zRO z=d4Hqj$aS8JBwuD)!T-sPYUC^Pm;YdDQwvHihOvh@Ms`IDfn^+PW~Xl`;VOA?*Lg_ zkvZlx4?^(tD?e0+w7{d!Y^RRt*-7~BVAl0CamJ5p{Lx$V<3IhC^-oUNuS(-n5R9BghPObl9VG%WJz*sJm; vQ>#s@%G3iMrzpqwU^h)Ha{Gvuv)RMKahGuGPrFBz|!5_ zvE*~lJNMl0r)%b%GxIywbv_*w-xwboG>lpY2OIYvAp{ZfMyW_Lgc`9Q&7y8)Y?O zY$T|(^wLH|Ca1@P6*JP;VcvJBJ%xYmca~hIR;7#2`*n^O%p4QME z;|smkY`wqzjj~nQR(9azL~O|i|DnaiL=vSq0iP&zAat68e(HzwTUJE|FI0{%4&; z`seKV|FaSjMoE(*q)P8&c7Rm&0K+C7v}Cx)?}5DMIkbU z7B%vfADeg&df;m?C6=^d|KxbTZVTm);+qN%$%szM%Rg-5U*RV2ms^~|+Is7~gYL>=ZMl(#p zvu`KAzw^UPP#tKX`F=9YL*F>is9oE+YHU?yhx{4!p7nY#ZydWTB<`rf@e7%UuWkN! zpM#Ag9{HUD_pgXTp6rKq6)kZ0Z)(OiHR>TQCp^jc-kF`8op}Dbr<@;qh z!Agv;O?iN3O9|wosjcLvI&vT~O=uUG8MgVHCEUGB{jq2$J||4CL0CE08yOP&R+#Go^vXj5zjiqIJVnpRcpc9I*BB=aA9W;#-UGyf1gm$C zvm-dLe1=&WT^&Zxa zW|*>q>F&`2#@yg@kGxR@Io#TSQ+MtSk+WMiGU=n%K(-)89gSBrAB}b=YpEz)Nu-{< zGFi?h31|xrbbbb&3&`+Yc|mvRarOW&5vWNse_JLGmsn}T8eENb1qDy@olRk1l5xaa zyo@=>h3c+~Np;EB&IviwwDRp_q^o=}u|MP0$vh)k%F)Y1wGKAZQPN-kT1Pp=^YBj% zmN#S$Qr7Chrx)XhcV1GD)q0%@lS{WO%XCFFjLkgrE9Tv{+h+)XI0)V(t2#z&03rts z^=#kb50c1j%e+MvoQaCUsMxDTGbs*l@cR6!ZC^xHv6XfU=i_qGEsQPXnH2clFAbqb zD9$JKsGW2DZt04!acN0cBD0h}QE&@pTQ6o)RVRBKk;FsU8ek#~>V0KIr}Z zvf4gw*t*(MQ%g}@xw-w`w>-lX7^H_+;!WLkZN0S2ZCv{cCUS~6PSTSv9$8cv^DUC0 zYS~yp8KmlTvmCC<=_*|mB@k-!Xkj&nmcNfB@CG8y4qGcKaTN7hrQpi=cZ-5{=9OSKn~OVp;4>NNU$&k-?y0*1>0oNR6eQ!SVQOv@@;*S zPop09P|m2!Agr&8XH7~pi)Q+dCJ(db>h(R?+8!_Ddnr2Dw)VgGi)5qi{qZf$lAeT_ zl>N$>*kh!=%oG|aCw18XmJl}QG*cV<<^Kb+hev(fQ#Y-%l2uFx_W49sSiav```9WE~T==JlvQqRgk+F)EqG>65 zF@Sx0>_LAcquFHT+zPw#Hod%%WP9DSre8Q8i9UXr16*+NIB^!g4WjJLrB;u61MXcj z;~AgnnV>8M3l1HcjEQg;+x~p0wW(=B=68sA2GQx>k`1i+TO)ta0vXOpq}G>N{)+9m zh=tYq;hW7rh`ZAU-9Z!MTW`~G|KF4F$hD6^PX`kng0ad@-074|7PZmB+C6vBm7Df{ zL1ND=1`@1INPPQgg5Y@upXiJci6T4reFb^S1{r^bJ*~v!>!R#5+0=z5dKsbmPdM?2 zhzAnrT&6!(*fVwv04>IXsF1m}X3Ms4FH`nrMIP)W*eJmrRN$haUV%fgXh>D@QH=Xi zI*K!uqokC=ETUAex1t_wmlID~>kHhMF~R(1+$kXCmkiD0Y3Pu*o792y*4mxn-<}b}WK^um-=}#E4baOlc(JePN948$vj2*%fejN>SDxBox20 zn_lJrR%4~|l#I|QGDxsFQh_$rh0$D;ykd5u6iSl;@a|lu-2ZtQGBvZ=%hJp(_aqg3 z@B%3YNg=OyFmo+$5O~MJg5+kavHc#hfJMKM4cg(fcuQY;{Nw)694^VL{VCZ4 z)zI3DuQ9}ZJ3o8x%m+|v+xyX+5zee_#Vr4IU^CupOjOAnlC$3Xr5h%@E6K46r%TwK zcD@7TeJ)+=SX9v4q`M4*q$d+O0@4B@dxbj@SJ^5P;dK$V!>J#agE*~bl^bd!B?GFs z%-os?s1u8eoHiO>>ID_H^CfB<1u>vp@b4Xi+nyLi-i;21tl#FTk}WUNz^4Qeg)zq} zPa`a^Zm|?iZ11|cR$tBeonMOltbOaZrS}IA+B!9ScKvyF0Q!JC&YH#z!dg`kUe3WO zVBye)q^<`mEmA!%nIR#@8d;M-N2#$eCFadi5!iz3guSQ%| z-t0(}W%J5J{iy#WHgu;M(kXmX(DK-eCS^1{SWauj37tp*-}~*(sWObv&@b)_#oz+` z<0eEEhBiInS=(!rzWVrw*Ts|F{Z3;W8-$SsM7O13!*$`3gyG!L9XfUX*Iy3y?r@Y; z4i*%s^&7FV4D)o+B$k80JA~!c+FVS@T{}{C+x@6qYGX^4W5W%hLG-tL!4Ios&25~@8UusDr`M%5$&aky)=W&Ud)VDhDo#^U z1;9-g0?hlEERfxMsk=u^kny!fw?_u%A>}*MySqqo%w`9FQkvd?)&vGJ4HT>1H!#|M zXHww;8A@dH4aa)xH|2E=P1W@`PY8nRS10lbxR+xK4LRSxNuz^OntFv3&tN)X7McDL zC;kGY6jv?X8To^|dzw^@rE`iS1O)u#@@6eHx#gepYd=P`<(Utm=oGX3ack zSt_kki6E~)a9XKVt&bjRBJX&cG$K`bjl^Y^b?3@!bog79K9ZlYY4A0}LR2(3u*-9- z{i-*Ssk35g4kmPFK9uWFnW;5xj|U$+eOqyy)J4jqJbs%cP8S%;0E>J2b1bvn=H`}j zpHImB=|$SI5R}`cpfQ5o#Sn!Po-dTbRAdI?kxCYNrk?yIX}2!u;@Q3Xfo08Ei)jk0 zm2^GLS08jjYx1P;bO3K-h4_*UahT|omvrGA_uTQs=JsTIrlQkqPxN2zi!NIynUd9x zSW-*>n!XhsCGChH>-a4Qi!N3)YZ9bwiMsPuIB4adXep$xl&`EWb{rQsL2<$ z=c3JvtWvU(4p6oxQ>))qxJ>Pg9#=5rRgd;iqsM;ZWF{yDNNcW@7YE1IUW#UU1UIZ~ zHI8xn32jb}5J*zi}Kz5H7|o2PqTd@z+MSwr*t>qtGWDKGM%JJ=h7B zg@+U2_qxa`8==@p@j;|pW{zLO9~QRO!pOe*&WXVMopO--iF0@|vyD)56&B!` z5VOQqGA0WYL(ASm(7+_ok*llwaHW(;eUgtsIh_1Br=u;Rf^$WvL-RHv-;AlbnS=Jg zOw|?~LyKr{0<~93ZZkM6UfUK zUiGP;Zc>^9ana}&=Z|zvJC&cyH#QvC^62&z9s`A+7e4%6aDNLnPI3L2N^wAFhZ6oM6=wPZvyZtyIfJ<5tw`v#U_njWVg!D~<< z1|a>06nK#wY0I5#Ve^L6+_h9xDe4_~g&HqDF>=&h(RtUGZ0z7)2aGy-&hO0S;Vb<3 z85vsS;izzWwjtLkMYa`&)p&n@r}n{Zi|o~bjyDCUppO_tR>Vdy@f&5Dx!WxUZ|8?} z^B*`@QMXUexCR=KbZflP>88oL-d|iM2e9>$2sj_yb;bA{D7O2c75ms|C|Lfu(9Lja zcxkqi%kbJ6C57Jw?*`7idGy(_(DCfGi9B4`KvpxdM7`-$fwT0sVD7Eh6a^Jz`)WJ3 z*3Yl9h8I~HY!J@-Tw=|8{havX^v&coG>%GKV(hh-D!|GG z{3&FtSid5puxUG&5IZ1uuZsF%RvfK2Qg2w_T#EF@+E%Qbem(gc3V!kRhZgq0Qk#a5nE># zlTpGv(@s(A*u?(19UT{Yf3WO}TGC4-%4&7kGihGkS{~)ed$YQTwnmonvwhu>s8wGV z*yJ!75yBAcX4K5{DdO^RGM8skAT7S+$6D1DD|*L{S;a4~#8uih&Zauiif@JAxxC4M z&K1M8b)uO}Y0}yKXPZI20R6-5N+(_CT90bFmX@OEEg^)}Cw~mdAUqNgbGI;~1PC+GG31=Jp3F}m>g`S+j-;Jw@$$F#B z6PWE5OS=t~BCfi%yuh4>$$0p*qnIR5Eb!Aj_r0Wb(8G0C<^5qud0XidwApd2jLBrBG54hdvpW_cjsdNTv$ zfZ~%|$Hw)NG+!J6ROlpM$lubeM}aMlQ?|CKC|SLx$IcO^x8N5nz03z;ar7rsV&#-6 z<`KA=6fzPF4~JYl&o5AuuRjKIaw^MFcV}7m%JaX9f0VU>NNQ$2Q^9Hhr5zi9bA|VP zj^INRj!jCW0b1#s6M}n}AL4P5)4v{m>-1+P5e_HJS3|i0)M~qpLO5ueW9uO6%DsIV zOyn@8_a6HqWi7GN+03{T%U!RY`Fg{kK?Cxwe8G6xj^b0lbY|@ z&r=+gTw?T8IVcR0c6mJ^U!{f7#WqhY2<}s&6LJpTh{wgwXkpcQ)r?w(1RqZy@oDM+ z>I1EI`jX@5Px|s0_1qHsQvNJgDK7ZhE$gG~%ZoQb{YQDc?G~+2X(%g{5QP7P6c>jM z9}ABvLaevF^ah--|CpBLC>`PAGr~HLN97)-C186HY(p5F)V*!Y7gRKM+lPaT9hLV# Dh68^s diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh index fd07df3feb..767e79016b 100755 --- a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh +++ b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh @@ -16,6 +16,9 @@ # # source the properties: +script_dir=$(dirname $0) +echo "script_dir: $script_dir" +cd $script_dir . ./lwM2M_keygen.properties # Generation of the keystore. diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties index 8dd2c30a9a..27e5fa57c7 100644 --- a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties +++ b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties @@ -43,7 +43,7 @@ CLIENT_STORE=clientKeyStore.jks CLIENT_STORE_PWD=client_ks_password CLIENT_ALIAS=client #CLIENT_CN=client_lwm2m_x509 -CLIENT_CN=mobile_lwm2m_x509 +CLIENT_CN=LwX50900000000 CLIENT_SELF_ALIAS=client_self_signed CLIENT_SELF_CN="$DOMAIN_SUFFIX client LwM2M self-signed" diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java index c8215b7ef3..286f555c0c 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java @@ -24,7 +24,6 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.gen.transport.TransportProtos; import java.security.PublicKey; -import java.security.cert.X509Certificate; import java.util.Map; @Slf4j @@ -104,10 +103,6 @@ public class LwM2MTransportConfigBootstrap { @Value("${transport.lwm2m.bootstrap.secure.alias:}") private String bootstrapAlias; - @Getter - @Setter - private X509Certificate bootstrapCertificate; - @Getter @Setter private Map sessions; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java index e5643a90f2..9d3e199aea 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java @@ -233,7 +233,7 @@ public class LwM2MTransportConfigServer { } else { log.error(" [{}] Read Models", path.getAbsoluteFile()); } - getInKeyStore(); + this.getInKeyStore(); } private File getPathModels() { diff --git a/transport/lwm2m/src/main/data/credentials/serverKeyStore.jks b/transport/lwm2m/src/main/data/credentials/serverKeyStore.jks index b5b6c9fa3c43118262f4c89b1ba319ef09b694d0..e1d6deff4b3fd948655f802da4ad65f0ba568f4a 100644 GIT binary patch delta 5500 zcmYM2Wl+=&*T!jBSV}sSl;&oa5|9p+ZjtU@Lb~J+OCuq|QqtYsu}F7FH-dn4E+q)> zJ?}j8JfE(a>ztY2IoJ7iW_iYVm?3b=cNplH{|Y(|h%5YE_zfXCE_x1};sOg!aRPx; z93(KySOyFXE^HOWag!{fQfkM{^n$cphcPhGdNr`#Wt>Q!PDa6mlL-fIR>V+JVL!8t z6X=^CxJmuvKUmO}t4=wD7icL2NvU3iy9#}u2|GX)yjJJ&myU34J;XRUYa_+b3b;ls zCJYD@3qv77La!iDC_j`R5@yYLQt^XkGc~Lo7};0m1nX`Ph<+;prApPQ&uuKCeeY#=-v#&G!-tyqzja z?PT-IcbtWaL_!eim5B-H;^6jgHUM*-G4I&kwb}IfYIF1tkK-iywDN@F=&DR&GZTifsnouggpyc{<&+3#<$DKD#OS`{%gzII<^|pUAg$=My$RP+=yDd3093CqiMN>a7JG7N!Z>bElZk_I`x8f)- z<(&QL?UUDOcA<70GZc7dmyx;QPYMbIU*AWvrl$SM^8k2TN(&6&0@`XKmvFLP0(-@Dkmu?=y+Vo5t%POsjw_UayDi|^4A(xHT zWSrwk=y5|_OhsP}P8n{wj8zmRQW2kDN`@H0q-CI1_R36?Z&FTod*j4Q2@W=~I|J_Z zG#pI;jmh_EuWq)~R`T&Ec(-ZpJOvY?6OI#qN}VINH(dL|pqgKb zEjT*BO8y?mCY*Sa%ZZipIz-}#XUezsw6{Wh+BAxdn4tbV)fV=-vG)ZRCLq@}NwZUl z;y5X5Z^aJA=8n#sD=hq?tdaN_dvX6*b!`}+Q&JgJ8Gh7#P{4!-G*z99v~B8W;4S@eU5`aG>T#I zRLr?5Nd<-g(fXtCLPBIpns}A<Iw4B{4zIU#XvF$-^V6d3C=@@OJ$1i`ztU*=+J;1rKN{v1$nkC`xOShrf5FGf5jv z7ZJ)@!%t>dj7GaGTIGpKXcU}dvfG8pd;lwzAl{G#QqG>IRuwyE;{{DjO3BT`Z#}#G zC5KeU`w=BZ*JonpU>=x^@|YC!I7 z@pzOMc)nSq(<{HN$%pk56@{s`B{W7z!y9u!(U=+b22(#4tPSK@O^J&xEC%OUv+F3vo zbo@l*?CI}tEQU?r^QQy&x2iY7; z|I)xS1*sl}RrQ4jXr#>>;v0RO=L`xd1#LxNTD6}gfhQg=F|-R`Si8OLs?e7?7z=0j z7+PFWfKU7GRHdAmeq-zlChfl4czLfuK01DD_{PO1h6m4c`Wd4gEC3FTkD~s)O+~|Hmv+^v7?gcXW(k+#$=0yE~pvhUNBHXPbOTM?GA z_sOZQ^`aNVtbDVTBT*b@jz+VP29)6p^g`p!E&b=5Bvm$}@x z>gDWT4cT#eGIJz>=!C{x!Rv0FVIm)NNbHIaZ>)ar5I{wWpL*tT{7{+El|kZl$5O*C zKfrf!i3prV+S1iyA*K^mj6d1!;_e~mq%}wtS1`Euc!vXxB12|_RXP zHj9Pv-Zzn24aR;4cf3m`F)L}}Zw1|GwbqYV_@nt(tM__;512+xZ7NM-4jE@v?sfMR zYBth(0^RA4-tB?BPvDdm9B70b{;pxq6aK=B=W4O+{4d??pK=>)AeY`t_h0(x>2yk? zS6x<9_eI~?5~|4c$svli{c|@x5V-l3luJIwkk3j!e;m;Q#CY8q9shX{BB?7;bMr_( z4*%w%D*a-cj1m0BKw1E>QRUQjSM$h~*+y<(77OckpXTxEQU^oO7fJi0HV3ZbM5N@vvIB_(!u$HhGz59mF zNUcOx6hbymgg1aeXgD`d9ilvIuAkzka=T3RTly7($P%GF^**l3gI*!h^zB=YMp@m3 z`3RAZP$>d`d$c|=E7tK0@WLc-N%+$maQ!g08T9Cm-NO5~X*8;HsqP-eEP^Y9qQNx# zJl0cgJIOyBNvVz1_4g};A}?V?AHHubc`kE(KzgLRkpAkm^aEZ(G3=gYU%!UiaS z{3|m5zY@IL&}^@%VK?@8IGeVYHC28U2T|C@epdHphg|I> z#4{1O(Duj=RS_osXsAy0L;Xq;bhPA4B0eoV-FEY0sEyQ2+t%*8H(n3`HHN_L3TY@H zNGaAk`87peDoAYwIS6U8&v!w@u^6RZ-^`7BF>@*&Qw}pu%4;fPFMO@#q@Jhx9f|C9 z#@NN4kDa0Wlq#>Knt~P6l;uKRQy>L8n$pzZ&zJA66;QSPiaZN6N{6#aBOSFEGd~K3 zRLTDKO~Qqp?@R&C$R_|}c{p2piAqGrzu97xM4Zz;j;A#USrRfVz+o~h9738J2Ks%K zaxLyS#gg=uBl~bdel&}zQ94GWf`6fItTchGm8l%OiG_C3KPGf)_m8`fs-hhIiRw_y z>fU&fduMCUC{n>Q+6`WCCd^!)LB7FlM8b+A7VT0BxXT-YRRHCYVERdULy8wcxV$-n z+mcf|a}^&V>~=R?lDaQn-Ofd!r|A^T!`gk%3iE%^><>x5COx6GzNS6~=_w@ycHF<+ zUyfPx389@nqAE;Wen}G4ym?%V)s^4hTGOnO zf`=@*9#&QN%>aK?K`XaEKdl$06iD`iM8bNuv;;LSuFaat%w5I$@%kDn7*2zWD7}12 zw^;0B?pO!%esOrSQreF3ef=^@u%8+GRc`wYw7Oz}rx5Rc-4>{eu)hoyC$rl#;+Pwf zCC?r6W#k%V^@7<}V$K#2YcSqi=MYiW{Mc-cCHa@S579e5!%ax{EybyhpdcpVj^~I&k z){h3fR@d(P* z6h(|M^Aqj|Y?@XLr`aNM`6qh1g?#+T)t=^|!!GxbC9nF6qk?*(JgJY|sV!Ds!Tbi& zJ9@we!vl6@@IF~&U*tJDd%K&SqMF_9f@ig`^yRNSSQ|#;^kvHZ>MmE%idAKE9&9rzEFQQ#;>eAdcL|4! zd0Th=%1yX7^G57|%Vv=o9T_~E%+VB;Ak91bT)5hSV49sbFZx7 z^U?A?Rrt43-``lq((XOifm+)u484}b|J6iID1_uAmJiIuj!@%!LW-yDEfisyhe{lx z*_Yv5b`ePaa4LR$7U)z-a&KA21iYS%iGq!9M-u~j7X3#m0QhQD;>2A_hj|+>;kRP> zotm%cA+8*M#8gqAduC)FBAQU5+(q_|qeG&=3?pDT#aYpf@I#Jk;@?LXFC^nPXB`2~C4HZm3J_iIAw363~$QzlqU}w$Y+?|9{C5}GoPrk?HT90EL z6DQ@^UVz`F_7#;AMfv=!14ek_aI=T*o2P=?y1CCk+7+uGcIeqB_@vJcf`zhf9XC8Y9ns-zYjM0&di(C=9C5JqRQ7}0plZn+f=mAyV<+H9@)5^PiR z+Q6edU1p?uUd%<}+4gRul~L`jg~3w+#nx3a+)b8_uZDT4WS^%DP-prIIQM7H82jLV zkk{&i{aF*!D)kS+iHdhsr+0=AlVsTu4=I$?GB-AVeA|`yM>;f^JdJDz&rIQtxK#Sl z_IyH0W+}|dAW_*{f#^w-x8Y>7S2aw1ExG$}7N7mnx0x225=;F3Rl52OjMMnYE!dfu zw`Xsli`La34ex4H0X^DK zVz0<}c57vfXAg|t|Dak+x0K9p7bizIg8Xnm3YEP6%FNkL`s?S=?C12StI3miYHlc6 zT{x>U#n)Bur$0IK)c%faPVcfnjV* zQ>@}U|KGGu5W2>6KU5fh2911XkGzku{@G`timj_MpG<}G9;@m4;nkq<-A`mJ-_yF> zH@X-}kP!yJPOh7^cCBw)TpE5vAkzhnH)uF*-q{M>QDD*j)ixICAYVi)(~pSJ-UAXsVbT*NQEj_#4%YUaN+}JudbpICah%DXxwg=Qb(XP3{Zt_O z<>FIr$gV1mf3;u8P8U=y3aYeK2NchEII~wWi`)Qj**6}LW8Oy>hOP&H;WhR)!9zRO z=d4Hqj$aS8JBwuD)!T-sPYUC^Pm;YdDQwvHihOvh@Ms`IDfn^+PW~Xl`;VOA?*Lg_ zkvZlx4?^(tD?e0+w7{d!Y^RRt*-7~BVAl0CamJ5p{Lx$V<3IhC^-oUNuS(-n5R9BghPObl9VG%WJz*sJm; vQ>#s@%G3iMrzpqwU^h)Ha{Gvuv)RMKahGuGPrFBz|!5_ zvE*~lJNMl0r)%b%GxIywbv_*w-xwboG>lpY2OIYvAp{ZfMyW_Lgc`9Q&7y8)Y?O zY$T|(^wLH|Ca1@P6*JP;VcvJBJ%xYmca~hIR;7#2`*n^O%p4QME z;|smkY`wqzjj~nQR(9azL~O|i|DnaiL=vSq0iP&zAat68e(HzwTUJE|FI0{%4&; z`seKV|FaSjMoE(*q)P8&c7Rm&0K+C7v}Cx)?}5DMIkbU z7B%vfADeg&df;m?C6=^d|KxbTZVTm);+qN%$%szM%Rg-5U*RV2ms^~|+Is7~gYL>=ZMl(#p zvu`KAzw^UPP#tKX`F=9YL*F>is9oE+YHU?yhx{4!p7nY#ZydWTB<`rf@e7%UuWkN! zpM#Ag9{HUD_pgXTp6rKq6)kZ0Z)(OiHR>TQCp^jc-kF`8op}Dbr<@;qh z!Agv;O?iN3O9|wosjcLvI&vT~O=uUG8MgVHCEUGB{jq2$J||4CL0CE08yOP&R+#Go^vXj5zjiqIJVnpRcpc9I*BB=aA9W;#-UGyf1gm$C zvm-dLe1=&WT^&Zxa zW|*>q>F&`2#@yg@kGxR@Io#TSQ+MtSk+WMiGU=n%K(-)89gSBrAB}b=YpEz)Nu-{< zGFi?h31|xrbbbb&3&`+Yc|mvRarOW&5vWNse_JLGmsn}T8eENb1qDy@olRk1l5xaa zyo@=>h3c+~Np;EB&IviwwDRp_q^o=}u|MP0$vh)k%F)Y1wGKAZQPN-kT1Pp=^YBj% zmN#S$Qr7Chrx)XhcV1GD)q0%@lS{WO%XCFFjLkgrE9Tv{+h+)XI0)V(t2#z&03rts z^=#kb50c1j%e+MvoQaCUsMxDTGbs*l@cR6!ZC^xHv6XfU=i_qGEsQPXnH2clFAbqb zD9$JKsGW2DZt04!acN0cBD0h}QE&@pTQ6o)RVRBKk;FsU8ek#~>V0KIr}Z zvf4gw*t*(MQ%g}@xw-w`w>-lX7^H_+;!WLkZN0S2ZCv{cCUS~6PSTSv9$8cv^DUC0 zYS~yp8KmlTvmCC<=_*|mB@k-!Xkj&nmcNfB@CG8y4qGcKaTN7hrQpi=cZ-5{=9OSKn~OVp;4>NNU$&k-?y0*1>0oNR6eQ!SVQOv@@;*S zPop09P|m2!Agr&8XH7~pi)Q+dCJ(db>h(R?+8!_Ddnr2Dw)VgGi)5qi{qZf$lAeT_ zl>N$>*kh!=%oG|aCw18XmJl}QG*cV<<^Kb+hev(fQ#Y-%l2uFx_W49sSiav```9WE~T==JlvQqRgk+F)EqG>65 zF@Sx0>_LAcquFHT+zPw#Hod%%WP9DSre8Q8i9UXr16*+NIB^!g4WjJLrB;u61MXcj z;~AgnnV>8M3l1HcjEQg;+x~p0wW(=B=68sA2GQx>k`1i+TO)ta0vXOpq}G>N{)+9m zh=tYq;hW7rh`ZAU-9Z!MTW`~G|KF4F$hD6^PX`kng0ad@-074|7PZmB+C6vBm7Df{ zL1ND=1`@1INPPQgg5Y@upXiJci6T4reFb^S1{r^bJ*~v!>!R#5+0=z5dKsbmPdM?2 zhzAnrT&6!(*fVwv04>IXsF1m}X3Ms4FH`nrMIP)W*eJmrRN$haUV%fgXh>D@QH=Xi zI*K!uqokC=ETUAex1t_wmlID~>kHhMF~R(1+$kXCmkiD0Y3Pu*o792y*4mxn-<}b}WK^um-=}#E4baOlc(JePN948$vj2*%fejN>SDxBox20 zn_lJrR%4~|l#I|QGDxsFQh_$rh0$D;ykd5u6iSl;@a|lu-2ZtQGBvZ=%hJp(_aqg3 z@B%3YNg=OyFmo+$5O~MJg5+kavHc#hfJMKM4cg(fcuQY;{Nw)694^VL{VCZ4 z)zI3DuQ9}ZJ3o8x%m+|v+xyX+5zee_#Vr4IU^CupOjOAnlC$3Xr5h%@E6K46r%TwK zcD@7TeJ)+=SX9v4q`M4*q$d+O0@4B@dxbj@SJ^5P;dK$V!>J#agE*~bl^bd!B?GFs z%-os?s1u8eoHiO>>ID_H^CfB<1u>vp@b4Xi+nyLi-i;21tl#FTk}WUNz^4Qeg)zq} zPa`a^Zm|?iZ11|cR$tBeonMOltbOaZrS}IA+B!9ScKvyF0Q!JC&YH#z!dg`kUe3WO zVBye)q^<`mEmA!%nIR#@8d;M-N2#$eCFadi5!iz3guSQ%| z-t0(}W%J5J{iy#WHgu;M(kXmX(DK-eCS^1{SWauj37tp*-}~*(sWObv&@b)_#oz+` z<0eEEhBiInS=(!rzWVrw*Ts|F{Z3;W8-$SsM7O13!*$`3gyG!L9XfUX*Iy3y?r@Y; z4i*%s^&7FV4D)o+B$k80JA~!c+FVS@T{}{C+x@6qYGX^4W5W%hLG-tL!4Ios&25~@8UusDr`M%5$&aky)=W&Ud)VDhDo#^U z1;9-g0?hlEERfxMsk=u^kny!fw?_u%A>}*MySqqo%w`9FQkvd?)&vGJ4HT>1H!#|M zXHww;8A@dH4aa)xH|2E=P1W@`PY8nRS10lbxR+xK4LRSxNuz^OntFv3&tN)X7McDL zC;kGY6jv?X8To^|dzw^@rE`iS1O)u#@@6eHx#gepYd=P`<(Utm=oGX3ack zSt_kki6E~)a9XKVt&bjRBJX&cG$K`bjl^Y^b?3@!bog79K9ZlYY4A0}LR2(3u*-9- z{i-*Ssk35g4kmPFK9uWFnW;5xj|U$+eOqyy)J4jqJbs%cP8S%;0E>J2b1bvn=H`}j zpHImB=|$SI5R}`cpfQ5o#Sn!Po-dTbRAdI?kxCYNrk?yIX}2!u;@Q3Xfo08Ei)jk0 zm2^GLS08jjYx1P;bO3K-h4_*UahT|omvrGA_uTQs=JsTIrlQkqPxN2zi!NIynUd9x zSW-*>n!XhsCGChH>-a4Qi!N3)YZ9bwiMsPuIB4adXep$xl&`EWb{rQsL2<$ z=c3JvtWvU(4p6oxQ>))qxJ>Pg9#=5rRgd;iqsM;ZWF{yDNNcW@7YE1IUW#UU1UIZ~ zHI8xn32jb}5J*zi}Kz5H7|o2PqTd@z+MSwr*t>qtGWDKGM%JJ=h7B zg@+U2_qxa`8==@p@j;|pW{zLO9~QRO!pOe*&WXVMopO--iF0@|vyD)56&B!` z5VOQqGA0WYL(ASm(7+_ok*llwaHW(;eUgtsIh_1Br=u;Rf^$WvL-RHv-;AlbnS=Jg zOw|?~LyKr{0<~93ZZkM6UfUK zUiGP;Zc>^9ana}&=Z|zvJC&cyH#QvC^62&z9s`A+7e4%6aDNLnPI3L2N^wAFhZ6oM6=wPZvyZtyIfJ<5tw`v#U_njWVg!D~<< z1|a>06nK#wY0I5#Ve^L6+_h9xDe4_~g&HqDF>=&h(RtUGZ0z7)2aGy-&hO0S;Vb<3 z85vsS;izzWwjtLkMYa`&)p&n@r}n{Zi|o~bjyDCUppO_tR>Vdy@f&5Dx!WxUZ|8?} z^B*`@QMXUexCR=KbZflP>88oL-d|iM2e9>$2sj_yb;bA{D7O2c75ms|C|Lfu(9Lja zcxkqi%kbJ6C57Jw?*`7idGy(_(DCfGi9B4`KvpxdM7`-$fwT0sVD7Eh6a^Jz`)WJ3 z*3Yl9h8I~HY!J@-Tw=|8{havX^v&coG>%GKV(hh-D!|GG z{3&FtSid5puxUG&5IZ1uuZsF%RvfK2Qg2w_T#EF@+E%Qbem(gc3V!kRhZgq0Qk#a5nE># zlTpGv(@s(A*u?(19UT{Yf3WO}TGC4-%4&7kGihGkS{~)ed$YQTwnmonvwhu>s8wGV z*yJ!75yBAcX4K5{DdO^RGM8skAT7S+$6D1DD|*L{S;a4~#8uih&Zauiif@JAxxC4M z&K1M8b)uO}Y0}yKXPZI20R6-5N+(_CT90bFmX@OEEg^)}Cw~mdAUqNgbGI;~1PC+GG31=Jp3F}m>g`S+j-;Jw@$$F#B z6PWE5OS=t~BCfi%yuh4>$$0p*qnIR5Eb!Aj_r0Wb(8G0C<^5qud0XidwApd2jLBrBG54hdvpW_cjsdNTv$ zfZ~%|$Hw)NG+!J6ROlpM$lubeM}aMlQ?|CKC|SLx$IcO^x8N5nz03z;ar7rsV&#-6 z<`KA=6fzPF4~JYl&o5AuuRjKIaw^MFcV}7m%JaX9f0VU>NNQ$2Q^9Hhr5zi9bA|VP zj^INRj!jCW0b1#s6M}n}AL4P5)4v{m>-1+P5e_HJS3|i0)M~qpLO5ueW9uO6%DsIV zOyn@8_a6HqWi7GN+03{T%U!RY`Fg{kK?Cxwe8G6xj^b0lbY|@ z&r=+gTw?T8IVcR0c6mJ^U!{f7#WqhY2<}s&6LJpTh{wgwXkpcQ)r?w(1RqZy@oDM+ z>I1EI`jX@5Px|s0_1qHsQvNJgDK7ZhE$gG~%ZoQb{YQDc?G~+2X(%g{5QP7P6c>jM z9}ABvLaevF^ah--|CpBLC>`PAGr~HLN97)-C186HY(p5F)V*!Y7gRKM+lPaT9hLV# Dh68^s diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts index 9f1fed9bc9..c5d56aa85a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts @@ -36,7 +36,7 @@ import { import { Store } from "@ngrx/store"; import { AppState } from "@core/core.state"; import { coerceBooleanProperty } from "@angular/cdk/coercion"; -import { WINDOW } from "../../../../../../core/services/window.service"; +import { WINDOW } from "@core/services/window.service"; import { pairwise, startWith } from 'rxjs/operators'; import { DeviceProfileService } from '@core/http/device-profile.service'; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index d724a25332..2cc6fe819e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -35,9 +35,9 @@ import { TELEMETRY, ObjectLwM2M, getDefaultProfileConfig, KEY_NAME, Instance } from "./profile-config.models"; -import { DeviceProfileService } from "../../../../../../core/http/device-profile.service"; -import { deepClone, isUndefined } from "../../../../../../core/utils"; -import { WINDOW } from "../../../../../../core/services/window.service"; +import { DeviceProfileService } from "@core/http/device-profile.service"; +import { deepClone, isUndefined } from "@core/utils"; +import { WINDOW } from "@core/services/window.service"; import { JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; import { isNotNullOrUndefined } from 'codelyzer/util/isNotNullOrUndefined'; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts index 31ab078485..44d9066440 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts @@ -30,14 +30,14 @@ import { } from "@angular/forms"; import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { Store } from "@ngrx/store"; -import { AppState } from "../../../../../../core/core.state"; +import { AppState } from "@core/core.state"; import { MatChipList } from '@angular/material/chips'; import { INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN } from "./profile-config.models"; import { TranslateService } from "@ngx-translate/core"; -import { DeviceProfileService } from "../../../../../../core/http/device-profile.service"; +import { DeviceProfileService } from "@core/http/device-profile.service"; @Component({ selector: 'tb-profile-lwm2m-object-add-instances-list', diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index b0f9ea4ebf..bf9570f95c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -32,16 +32,16 @@ import { } from "@angular/forms"; import {coerceBooleanProperty} from "@angular/cdk/coercion"; import {Store} from "@ngrx/store"; -import {AppState} from "../../../../../../core/core.state"; +import {AppState} from "@core/core.state"; import {MatChipList} from '@angular/material/chips'; import {MatAutocomplete} from "@angular/material/autocomplete"; import {Observable} from "rxjs"; import {filter, map, mergeMap, share, tap} from 'rxjs/operators'; import {ObjectLwM2M} from "./profile-config.models"; import {TranslateService} from "@ngx-translate/core"; -import {DeviceProfileService} from "../../../../../../core/http/device-profile.service"; -import {PageLink} from "../../../../../../shared/models/page/page-link"; -import {Direction} from "../../../../../../shared/models/page/sort-order"; +import {DeviceProfileService} from "@core/http/device-profile.service"; +import {PageLink} from "@shared/models/page/page-link"; +import {Direction} from "@shared/models/page/sort-order"; @Component({ selector: 'tb-profile-lwm2m-object-list', From 33a900145cba25066632d0bb4f8cbf977392269b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 14 Jan 2021 19:24:19 +0200 Subject: [PATCH 20/41] Lwm2m: back: created sh with many certX509 --- .../credentials/shell/lwM2M_credentials.sh | 308 ++++++++++--- .../credentials/shell/lwM2M_keygen.properties | 19 +- .../credentials/shell/lwM2M_credentials.sh | 403 ++++++++++++++++++ .../credentials/shell/lwM2M_keygen.properties | 57 +++ .../LwM2M_BinaryAppDataContainer-v1_0_1.xml | 17 - 5 files changed, 719 insertions(+), 85 deletions(-) create mode 100755 transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh create mode 100644 transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh index 767e79016b..6a1ad298ac 100755 --- a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh +++ b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Copyright © 2016-2020 The Thingsboard Authors # @@ -15,12 +15,147 @@ # limitations under the License. # -# source the properties: +#p) CLIENT_CN=LwX50900000000 +#s) client_start=0 +#f) client_finish=1 +#a) CLIENT_ALIAS=client_alias_00000000 +#b) BOOTSTRAP_ALIAS=bootstrap +#d) SERVER_ALIAS=server +#j) SERVER_STORE=serverKeyStore.jks +#k) CLIENT_STORE=clientKeyStore.jks +#c) CLIENT_STORE_PWD=client_ks_password +#w) SERVER_STORE_PWD=server_ks_password + +#while test $# -gt 0; do +# case "$1" in +# -h|--help) +# echo "$package - attempt to capture frames" +# echo " " +# echo "$package [options] application [arguments]" +# echo " " +# echo "options:" +# echo "-h, --help show brief help" +# echo "-a, --action=ACTION specify an action to use" +# echo "-o, --output-dir=DIR specify a directory to store output in" +# exit 0 +# ;; +# -a) +# shift +# if test $# -gt 0; then +# export PROCESS=$1 +# else +# echo "no process specified" +# exit 1 +# fi +# shift +# ;; +# --action*) +# export PROCESS=`echo $1 | sed -e 's/^[^=]*=//g'` +# shift +# ;; +# -o) +# shift +# if test $# -gt 0; then +# export OUTPUT=$1 +# else +# echo "no output dir specified" +# exit 1 +# fi +# shift +# ;; +# --output-dir*) +# export OUTPUT=`echo $1 | sed -e 's/^[^=]*=//g'` +# shift +# ;; +# *) +# break +# ;; +# esac +#done + + +while getopts p:s:f:a:b:d:j:k:c:w: flag; do + case "${flag}" in + p) client_prefix=${OPTARG} ;; + s) client_start=${OPTARG} ;; + f) client_finish=${OPTARG} ;; + a) client_alias=${OPTARG} ;; + b) bootstrap_alias=${OPTARG} ;; + d) server_alias=${OPTARG} ;; + j) key_store_server_file=${OPTARG} ;; + k) key_store_client_file=${OPTARG} ;; + c) client_key_store_pwd=${OPTARG} ;; + w) server_key_store_pwd=${OPTARG} ;; + esac +done + +# cd to dir of script script_dir=$(dirname $0) echo "script_dir: $script_dir" cd $script_dir +# source the properties: . ./lwM2M_keygen.properties + +if [ -n "$client_prefix" ]; then + CLIENT_PREFIX=$client_prefix +fi + +if [ -z "$client_start" ]; then + client_start=0 +fi + +if [ -z "$client_finish" ]; then + client_finish=1 +fi + +if [ -n "$client_alias" ]; then + CLIENT_ALIAS=$client_alias +fi + +if [ -n "$bootstrap_alias" ]; then + BOOTSTRAP_ALIAS=$bootstrap_alias +fi + +if [ -n "$server_alias" ]; then + SERVER_ALIAS=$server_alias +fi + +if [ -n "$key_store_server_file" ]; then + SERVER_STORE=$key_store_server_file +fi + +if [ -n "$key_store_client_file" ]; then + CLIENT_STORE=$key_store_client_file +fi + +if [ -n "$client_key_store_pwd" ]; then + CLIENT_STORE_PWD=$client_key_store_pwd +fi + +if [ -n "$server_key_store_pwd" ]; then + SERVER_STORE_PWD=$server_key_store_pwd +fi + +echo "==Start==" +echo "CLIENT_PREFIX: $CLIENT_PREFIX" +echo "client_start: $client_start" +echo "client_finish: $client_finish" +echo "CLIENT_ALIAS: $CLIENT_ALIAS" +echo "BOOTSTRAP_ALIAS: $BOOTSTRAP_ALIAS" +echo "SERVER_ALIAS: $SERVER_ALIAS" +echo "SERVER_STORE: $SERVER_STORE" +echo "CLIENT_STORE: $CLIENT_STORE" +echo "CLIENT_STORE_PWD: $CLIENT_STORE_PWD" +echo "SERVER_STORE_PWD: $SERVER_STORE_PWD" + +end_point() { + echo "$CLIENT_PREFIX$(printf "%08d" $CLIENT_NUMBER)" +} +client_alias_point() { + echo "$CLIENT_ALIAS$(printf "%08d" $CLIENT_NUMBER)" +} + # Generation of the keystore. echo "${H0}====START========${RESET}" echo "${H1}Server Keystore : ${RESET}" @@ -55,7 +190,7 @@ keytool \ -exportcert \ -alias $SERVER_ALIAS \ -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | \ + -storepass $SERVER_STORE_PWD | keytool \ -importcert \ -alias $SERVER_SELF_ALIAS \ @@ -70,22 +205,22 @@ keytool \ -alias $SERVER_ALIAS \ -dname "CN=$SERVER_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | \ + -storepass $SERVER_STORE_PWD | keytool \ -gencert \ -alias $ROOT_KEY_ALIAS \ -keystore $SERVER_STORE \ -storepass $SERVER_STORE_PWD \ -storetype $STORETYPE \ - -validity $VALIDITY | \ - keytool \ - -importcert \ - -alias $SERVER_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD + -validity $VALIDITY | + keytool \ + -importcert \ + -alias $SERVER_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD echo -echo "${H2}Creating server key and self-signed certificate ...${RESET}" +echo "${H2}Creating bootstrap key and self-signed certificate ...${RESET}" keytool \ -genkeypair \ -alias $BOOTSTRAP_ALIAS \ @@ -100,7 +235,7 @@ keytool \ -exportcert \ -alias $BOOTSTRAP_ALIAS \ -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | \ + -storepass $SERVER_STORE_PWD | keytool \ -importcert \ -alias $BOOTSTRAP_SELF_ALIAS \ @@ -115,54 +250,53 @@ keytool \ -alias $BOOTSTRAP_ALIAS \ -dname "CN=$BOOTSTRAP_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | \ + -storepass $SERVER_STORE_PWD | keytool \ -gencert \ -alias $ROOT_KEY_ALIAS \ -keystore $SERVER_STORE \ -storepass $SERVER_STORE_PWD \ -storetype $STORETYPE \ - -validity $VALIDITY | \ - keytool \ - -importcert \ - -alias $BOOTSTRAP_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD - + -validity $VALIDITY | + keytool \ + -importcert \ + -alias $BOOTSTRAP_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD echo echo "${H1}Client Keystore : ${RESET}" echo "${H1}==================${RESET}" -echo "${H2}Creating client key and self-signed certificate with expected CN...${RESET}" -keytool \ - -genkeypair \ - -alias $CLIENT_ALIAS \ - -keyalg EC \ - -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -validity $VALIDITY \ - -storetype $STORETYPE \ - -keypass $CLIENT_STORE_PWD \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD -keytool \ - -exportcert \ - -alias $CLIENT_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD | \ - keytool \ - -importcert \ - -alias $CLIENT_SELF_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD \ - -noprompt +#echo "${H2}Creating client key and self-signed certificate with expected CN...${RESET}" +#keytool \ +# -genkeypair \ +# -alias $CLIENT_ALIAS \ +# -keyalg EC \ +# -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ +# -validity $VALIDITY \ +# -storetype $STORETYPE \ +# -keypass $CLIENT_STORE_PWD \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD +#keytool \ +# -exportcert \ +# -alias $CLIENT_ALIAS \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD | \ +# keytool \ +# -importcert \ +# -alias $CLIENT_SELF_ALIAS \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD \ +# -noprompt echo -echo "${H2}Import root certificate just to be able to import ned by root CA with expected CN...${RESET}" +echo "${H2}Import root certificate just to be able to import need by root CA with expected CN...${RESET}" keytool \ -exportcert \ -alias $ROOT_KEY_ALIAS \ -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | \ + -storepass $SERVER_STORE_PWD | keytool \ -importcert \ -alias $ROOT_KEY_ALIAS \ @@ -170,27 +304,85 @@ keytool \ -storepass $CLIENT_STORE_PWD \ -noprompt -echo -echo "${H2}Creating client certificate signed by root CA with expected CN...${RESET}" -keytool \ - -certreq \ - -alias $CLIENT_ALIAS \ - -dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD | \ +#echo +#echo "${H2}Creating client certificate signed by root CA with expected CN...${RESET}" +#keytool \ +# -certreq \ +# -alias $CLIENT_ALIAS \ +# -dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD | \ +# keytool \ +# -gencert \ +# -alias $ROOT_KEY_ALIAS \ +# -keystore $SERVER_STORE \ +# -storepass $SERVER_STORE_PWD \ +# -storetype $STORETYPE \ +# -validity $VALIDITY | \ +# keytool \ +# -importcert \ +# -alias $CLIENT_ALIAS \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD \ +# -noprompt + +cert_end_point() { + echo "${H2}Creating client key and self-signed certificate with expected CN $CLIENT_SELF_CN ${RESET}" keytool \ - -gencert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD \ + -genkeypair \ + -alias $CLIENT_CN_ALIAS \ + -keyalg EC \ + -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -validity $VALIDITY \ -storetype $STORETYPE \ - -validity $VALIDITY | \ + -keypass $CLIENT_STORE_PWD \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD + keytool \ + -exportcert \ + -alias $CLIENT_CN_ALIAS \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD | + keytool \ + -importcert \ + -alias $CLIENT_SELF_ALIAS \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD \ + -noprompt + + echo + echo "${H2}Creating client certificate signed by root CA with expected $CLIENT_CN_NAME ${RESET}" + keytool \ + -certreq \ + -alias $CLIENT_CN_ALIAS \ + -dname "CN=$CLIENT_CN_NAME, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD | + keytool \ + -gencert \ + -alias $ROOT_KEY_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD \ + -storetype $STORETYPE \ + -validity $VALIDITY | keytool \ -importcert \ - -alias $CLIENT_ALIAS \ + -alias $CLIENT_CN_ALIAS \ -keystore $CLIENT_STORE \ -storepass $CLIENT_STORE_PWD \ -noprompt +} + +while [ "$CLIENT_NUMBER" != "$client_finish" ]; do + CLIENT_CN_NAME=$(end_point) + CLIENT_CN_ALIAS=$(client_alias_point) + echo "$CLIENT_CN_NAME" + echo "$CLIENT_CN_ALIAS" + cert_end_point + CLIENT_NUMBER=$(($CLIENT_NUMBER + 1)) + echo "number $CLIENT_NUMBER" + echo "finish $client_finish" +done echo echo "${H0}!!! Warning ${H2}Migrate ${H1}${SERVER_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties index 27e5fa57c7..18f851e4dd 100644 --- a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties +++ b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties @@ -17,7 +17,7 @@ # Keystore common parameters ROOT_KEY_ALIAS=rootCA DOMAIN_SUFFIX="$(hostname)" -ROOT_CN="$DOMAIN_SUFFIX rootCA" +ROOT_CN="$DOMAIN_SUFFIX $ROOT_KEY_ALIAS" ORGANIZATIONAL_UNIT=Thingsboard ORGANIZATION=Thingsboard CITY=SF @@ -27,23 +27,22 @@ VALIDITY=36500 #days STORETYPE="JKS" #Server -SERVER_STORE=serverKeyStore.jks -SERVER_STORE_PWD=server_ks_password -SERVER_ALIAS=server +SERVER_STORE=serverKeyStore1.jks +SERVER_STORE_PWD=server_ks_password1 +SERVER_ALIAS=server1 SERVER_CN="$DOMAIN_SUFFIX server LwM2M signed by root CA" SERVER_SELF_ALIAS=server_self_signed SERVER_SELF_CN="$DOMAIN_SUFFIX server LwM2M self-signed" -BOOTSTRAP_ALIAS=bootstrap +BOOTSTRAP_ALIAS=bootstrap1 BOOTSTRAP_CN="$DOMAIN_SUFFIX bootstrap server LwM2M signed by root CA" BOOTSTRAP_SELF_ALIAS=bootstrap_self_signed BOOTSTRAP_SELF_CN="$DOMAIN_SUFFIX bootstrap server LwM2M self-signed" # Client -CLIENT_STORE=clientKeyStore.jks -CLIENT_STORE_PWD=client_ks_password -CLIENT_ALIAS=client -#CLIENT_CN=client_lwm2m_x509 -CLIENT_CN=LwX50900000000 +CLIENT_STORE=clientKeyStore1.jks +CLIENT_STORE_PWD=client_ks_password1 +CLIENT_ALIAS=client_alias_1 +CLIENT_PREFIX=LwX509_ CLIENT_SELF_ALIAS=client_self_signed CLIENT_SELF_CN="$DOMAIN_SUFFIX client LwM2M self-signed" diff --git a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh new file mode 100755 index 0000000000..6a1ad298ac --- /dev/null +++ b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh @@ -0,0 +1,403 @@ +#!/bin/bash +# +# Copyright © 2016-2020 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#p) CLIENT_CN=LwX50900000000 +#s) client_start=0 +#f) client_finish=1 +#a) CLIENT_ALIAS=client_alias_00000000 +#b) BOOTSTRAP_ALIAS=bootstrap +#d) SERVER_ALIAS=server +#j) SERVER_STORE=serverKeyStore.jks +#k) CLIENT_STORE=clientKeyStore.jks +#c) CLIENT_STORE_PWD=client_ks_password +#w) SERVER_STORE_PWD=server_ks_password + +#while test $# -gt 0; do +# case "$1" in +# -h|--help) +# echo "$package - attempt to capture frames" +# echo " " +# echo "$package [options] application [arguments]" +# echo " " +# echo "options:" +# echo "-h, --help show brief help" +# echo "-a, --action=ACTION specify an action to use" +# echo "-o, --output-dir=DIR specify a directory to store output in" +# exit 0 +# ;; +# -a) +# shift +# if test $# -gt 0; then +# export PROCESS=$1 +# else +# echo "no process specified" +# exit 1 +# fi +# shift +# ;; +# --action*) +# export PROCESS=`echo $1 | sed -e 's/^[^=]*=//g'` +# shift +# ;; +# -o) +# shift +# if test $# -gt 0; then +# export OUTPUT=$1 +# else +# echo "no output dir specified" +# exit 1 +# fi +# shift +# ;; +# --output-dir*) +# export OUTPUT=`echo $1 | sed -e 's/^[^=]*=//g'` +# shift +# ;; +# *) +# break +# ;; +# esac +#done + + +while getopts p:s:f:a:b:d:j:k:c:w: flag; do + case "${flag}" in + p) client_prefix=${OPTARG} ;; + s) client_start=${OPTARG} ;; + f) client_finish=${OPTARG} ;; + a) client_alias=${OPTARG} ;; + b) bootstrap_alias=${OPTARG} ;; + d) server_alias=${OPTARG} ;; + j) key_store_server_file=${OPTARG} ;; + k) key_store_client_file=${OPTARG} ;; + c) client_key_store_pwd=${OPTARG} ;; + w) server_key_store_pwd=${OPTARG} ;; + esac +done + +# cd to dir of script +script_dir=$(dirname $0) +echo "script_dir: $script_dir" +cd $script_dir +# source the properties: +. ./lwM2M_keygen.properties + + +if [ -n "$client_prefix" ]; then + CLIENT_PREFIX=$client_prefix +fi + +if [ -z "$client_start" ]; then + client_start=0 +fi + +if [ -z "$client_finish" ]; then + client_finish=1 +fi + +if [ -n "$client_alias" ]; then + CLIENT_ALIAS=$client_alias +fi + +if [ -n "$bootstrap_alias" ]; then + BOOTSTRAP_ALIAS=$bootstrap_alias +fi + +if [ -n "$server_alias" ]; then + SERVER_ALIAS=$server_alias +fi + +if [ -n "$key_store_server_file" ]; then + SERVER_STORE=$key_store_server_file +fi + +if [ -n "$key_store_client_file" ]; then + CLIENT_STORE=$key_store_client_file +fi + +if [ -n "$client_key_store_pwd" ]; then + CLIENT_STORE_PWD=$client_key_store_pwd +fi + +if [ -n "$server_key_store_pwd" ]; then + SERVER_STORE_PWD=$server_key_store_pwd +fi + +echo "==Start==" +echo "CLIENT_PREFIX: $CLIENT_PREFIX" +echo "client_start: $client_start" +echo "client_finish: $client_finish" +echo "CLIENT_ALIAS: $CLIENT_ALIAS" +echo "BOOTSTRAP_ALIAS: $BOOTSTRAP_ALIAS" +echo "SERVER_ALIAS: $SERVER_ALIAS" +echo "SERVER_STORE: $SERVER_STORE" +echo "CLIENT_STORE: $CLIENT_STORE" +echo "CLIENT_STORE_PWD: $CLIENT_STORE_PWD" +echo "SERVER_STORE_PWD: $SERVER_STORE_PWD" + +end_point() { + echo "$CLIENT_PREFIX$(printf "%08d" $CLIENT_NUMBER)" +} +client_alias_point() { + echo "$CLIENT_ALIAS$(printf "%08d" $CLIENT_NUMBER)" +} + +# Generation of the keystore. +echo "${H0}====START========${RESET}" +echo "${H1}Server Keystore : ${RESET}" +echo "${H1}==================${RESET}" +echo "${H2}Creating the trusted root CA key and certificate...${RESET}" +# -keysize +# 1024 (when using -genkeypair) +keytool \ + -genkeypair \ + -alias $ROOT_KEY_ALIAS \ + -keyalg EC \ + -dname "CN=$ROOT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -validity $VALIDITY \ + -storetype $STORETYPE \ + -keypass $SERVER_STORE_PWD \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD + +echo +echo "${H2}Creating server key and self-signed certificate ...${RESET}" +keytool \ + -genkeypair \ + -alias $SERVER_ALIAS \ + -keyalg EC \ + -dname "CN=$SERVER_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -validity $VALIDITY \ + -storetype $STORETYPE \ + -keypass $SERVER_STORE_PWD \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD +keytool \ + -exportcert \ + -alias $SERVER_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD | + keytool \ + -importcert \ + -alias $SERVER_SELF_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD \ + -noprompt + +echo +echo "${H2}Creating server certificate signed by root CA...${RESET}" +keytool \ + -certreq \ + -alias $SERVER_ALIAS \ + -dname "CN=$SERVER_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD | + keytool \ + -gencert \ + -alias $ROOT_KEY_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD \ + -storetype $STORETYPE \ + -validity $VALIDITY | + keytool \ + -importcert \ + -alias $SERVER_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD + +echo +echo "${H2}Creating bootstrap key and self-signed certificate ...${RESET}" +keytool \ + -genkeypair \ + -alias $BOOTSTRAP_ALIAS \ + -keyalg EC \ + -dname "CN=$BOOTSTRAP_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -validity $VALIDITY \ + -storetype $STORETYPE \ + -keypass $SERVER_STORE_PWD \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD +keytool \ + -exportcert \ + -alias $BOOTSTRAP_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD | + keytool \ + -importcert \ + -alias $BOOTSTRAP_SELF_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD \ + -noprompt + +echo +echo "${H2}Creating bootstrap certificate signed by root CA...${RESET}" +keytool \ + -certreq \ + -alias $BOOTSTRAP_ALIAS \ + -dname "CN=$BOOTSTRAP_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD | + keytool \ + -gencert \ + -alias $ROOT_KEY_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD \ + -storetype $STORETYPE \ + -validity $VALIDITY | + keytool \ + -importcert \ + -alias $BOOTSTRAP_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD + +echo +echo "${H1}Client Keystore : ${RESET}" +echo "${H1}==================${RESET}" +#echo "${H2}Creating client key and self-signed certificate with expected CN...${RESET}" +#keytool \ +# -genkeypair \ +# -alias $CLIENT_ALIAS \ +# -keyalg EC \ +# -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ +# -validity $VALIDITY \ +# -storetype $STORETYPE \ +# -keypass $CLIENT_STORE_PWD \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD +#keytool \ +# -exportcert \ +# -alias $CLIENT_ALIAS \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD | \ +# keytool \ +# -importcert \ +# -alias $CLIENT_SELF_ALIAS \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD \ +# -noprompt + +echo +echo "${H2}Import root certificate just to be able to import need by root CA with expected CN...${RESET}" +keytool \ + -exportcert \ + -alias $ROOT_KEY_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD | + keytool \ + -importcert \ + -alias $ROOT_KEY_ALIAS \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD \ + -noprompt + +#echo +#echo "${H2}Creating client certificate signed by root CA with expected CN...${RESET}" +#keytool \ +# -certreq \ +# -alias $CLIENT_ALIAS \ +# -dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD | \ +# keytool \ +# -gencert \ +# -alias $ROOT_KEY_ALIAS \ +# -keystore $SERVER_STORE \ +# -storepass $SERVER_STORE_PWD \ +# -storetype $STORETYPE \ +# -validity $VALIDITY | \ +# keytool \ +# -importcert \ +# -alias $CLIENT_ALIAS \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD \ +# -noprompt + +cert_end_point() { + echo "${H2}Creating client key and self-signed certificate with expected CN $CLIENT_SELF_CN ${RESET}" + keytool \ + -genkeypair \ + -alias $CLIENT_CN_ALIAS \ + -keyalg EC \ + -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -validity $VALIDITY \ + -storetype $STORETYPE \ + -keypass $CLIENT_STORE_PWD \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD + keytool \ + -exportcert \ + -alias $CLIENT_CN_ALIAS \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD | + keytool \ + -importcert \ + -alias $CLIENT_SELF_ALIAS \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD \ + -noprompt + + echo + echo "${H2}Creating client certificate signed by root CA with expected $CLIENT_CN_NAME ${RESET}" + keytool \ + -certreq \ + -alias $CLIENT_CN_ALIAS \ + -dname "CN=$CLIENT_CN_NAME, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD | + keytool \ + -gencert \ + -alias $ROOT_KEY_ALIAS \ + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD \ + -storetype $STORETYPE \ + -validity $VALIDITY | + keytool \ + -importcert \ + -alias $CLIENT_CN_ALIAS \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD \ + -noprompt +} + +while [ "$CLIENT_NUMBER" != "$client_finish" ]; do + CLIENT_CN_NAME=$(end_point) + CLIENT_CN_ALIAS=$(client_alias_point) + echo "$CLIENT_CN_NAME" + echo "$CLIENT_CN_ALIAS" + cert_end_point + CLIENT_NUMBER=$(($CLIENT_NUMBER + 1)) + echo "number $CLIENT_NUMBER" + echo "finish $client_finish" +done + +echo +echo "${H0}!!! Warning ${H2}Migrate ${H1}${SERVER_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" +keytool \ + -importkeystore \ + -srckeystore $SERVER_STORE \ + -destkeystore $SERVER_STORE \ + -deststoretype pkcs12 \ + -srcstorepass $SERVER_STORE_PWD + +echo +echo "${H0}!!! Warning ${H2}Migrate ${H1}${CLIENT_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" +keytool \ + -importkeystore \ + -srckeystore $CLIENT_STORE \ + -destkeystore $CLIENT_STORE \ + -deststoretype pkcs12 \ + -srcstorepass $CLIENT_STORE_PWD diff --git a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties new file mode 100644 index 0000000000..18f851e4dd --- /dev/null +++ b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties @@ -0,0 +1,57 @@ +# +# Copyright © 2016-2017 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Keystore common parameters +ROOT_KEY_ALIAS=rootCA +DOMAIN_SUFFIX="$(hostname)" +ROOT_CN="$DOMAIN_SUFFIX $ROOT_KEY_ALIAS" +ORGANIZATIONAL_UNIT=Thingsboard +ORGANIZATION=Thingsboard +CITY=SF +STATE_OR_PROVINCE=CA +TWO_LETTER_COUNTRY_CODE=US +VALIDITY=36500 #days +STORETYPE="JKS" + +#Server +SERVER_STORE=serverKeyStore1.jks +SERVER_STORE_PWD=server_ks_password1 +SERVER_ALIAS=server1 +SERVER_CN="$DOMAIN_SUFFIX server LwM2M signed by root CA" +SERVER_SELF_ALIAS=server_self_signed +SERVER_SELF_CN="$DOMAIN_SUFFIX server LwM2M self-signed" +BOOTSTRAP_ALIAS=bootstrap1 +BOOTSTRAP_CN="$DOMAIN_SUFFIX bootstrap server LwM2M signed by root CA" +BOOTSTRAP_SELF_ALIAS=bootstrap_self_signed +BOOTSTRAP_SELF_CN="$DOMAIN_SUFFIX bootstrap server LwM2M self-signed" + +# Client +CLIENT_STORE=clientKeyStore1.jks +CLIENT_STORE_PWD=client_ks_password1 +CLIENT_ALIAS=client_alias_1 +CLIENT_PREFIX=LwX509_ +CLIENT_SELF_ALIAS=client_self_signed +CLIENT_SELF_CN="$DOMAIN_SUFFIX client LwM2M self-signed" + +# Color output stuff +red=`tput setaf 1` +green=`tput setaf 2` +blue=`tput setaf 4` +bold=`tput bold` +H0=${red}${bold} +H1=${green}${bold} +H2=${blue} +RESET=`tput sgr0` diff --git a/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml b/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml index 374dac72bc..b111d3b321 100644 --- a/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml +++ b/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml @@ -1,20 +1,3 @@ -  --> From 9cdd916276757ed2ca5e41c502af7c46e8e457be Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 15 Jan 2021 10:45:18 +0200 Subject: [PATCH 22/41] Lwm2m: back: for test2 --- .../src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml b/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml index 6381ec2245..cd6182596c 100644 --- a/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml +++ b/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml @@ -59,7 +59,6 @@ The above license is used as a license under copyright only. Please reference the OMA IPR Policy for patent licensing terms: https://www.omaspecworks.org/about/intellectual-property-rights/ ---> --> From cd6a76de862efc81fc4c9f6138d76244b896d994 Mon Sep 17 00:00:00 2001 From: Vladyslav Date: Fri, 15 Jan 2021 13:06:39 +0200 Subject: [PATCH 23/41] Improvement/lwm2m/refactoring 1 (#3956) UI: Refactoring lwm2m --- .../app/core/http/device-profile.service.ts | 21 +- .../device/device-credentials.component.html | 3 +- .../device/device-credentials.component.ts | 37 +-- .../home/components/home-components.module.ts | 2 +- .../lwm2m-device-config-server.component.html | 57 ++-- .../lwm2m-device-config-server.component.ts | 45 ++- ...ile-transport-configuration.component.html | 9 +- ...ofile-transport-configuration.component.ts | 269 +++++++++--------- .../device/lwm2m/security-config.models.ts | 1 - 9 files changed, 228 insertions(+), 216 deletions(-) diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index 16f6edb512..22ff52e99b 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -22,9 +22,7 @@ import { Observable } from 'rxjs'; import { PageData } from '@shared/models/page/page-data'; import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; import { isDefinedAndNotNull } from '@core/utils'; -import { - ObjectLwM2M, ServerSecurityConfig -} from "../../modules/home/components/profile/device/lwm2m/profile-config.models"; +import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/profile-config.models'; @Injectable({ providedIn: 'root' @@ -43,16 +41,23 @@ export class DeviceProfileService { return this.http.get(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); } - public getLwm2mObjects(objectIds: number [], config?: RequestConfig): Observable { - return this.http.get(`/api/lwm2m/deviceProfile/${objectIds}`, defaultHttpOptionsFromConfig(config)); + public getLwm2mObjects(objectIds: number[], config?: RequestConfig): Observable> { + return this.http.get>(`/api/lwm2m/deviceProfile/${objectIds}`, defaultHttpOptionsFromConfig(config)); } - public getLwm2mBootstrapSecurityInfo(securityMode: string, bootstrapServerIs: boolean, config?: RequestConfig): Observable { - return this.http.get(`/api/lwm2m/deviceProfile/bootstrap/${securityMode}/${bootstrapServerIs}`, defaultHttpOptionsFromConfig(config)); + public getLwm2mBootstrapSecurityInfo(securityMode: string, bootstrapServerIs: boolean, + config?: RequestConfig): Observable { + return this.http.get( + `/api/lwm2m/deviceProfile/bootstrap/${securityMode}/${bootstrapServerIs}`, + defaultHttpOptionsFromConfig(config) + ); } public getLwm2mObjectsPage(pageLink: PageLink, config?: RequestConfig): Observable> { - return this.http.get>(`/api/lwm2m/deviceProfile/objects${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); + return this.http.get>( + `/api/lwm2m/deviceProfile/objects${pageLink.toQuery()}`, + defaultHttpOptionsFromConfig(config) + ); } public saveDeviceProfile(deviceProfile: DeviceProfile, config?: RequestConfig): Observable { diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html index 44337d30a5..9243feb18a 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html @@ -90,9 +90,8 @@
diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts index 4c622747ef..b6eb364b2f 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Inject, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -41,8 +41,7 @@ import { DeviceCredentialsDialogLwm2mData, END_POINT, getDefaultSecurityConfig, - JSON_ALL_CONFIG, - SecurityConfigModels + JSON_ALL_CONFIG } from '@home/pages/device/lwm2m/security-config.models'; import { TranslateService } from '@ngx-translate/core'; import { MatDialog } from '@angular/material/dialog'; @@ -197,6 +196,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false}); break; case DeviceCredentialsType.X509_CERTIFICATE: + case DeviceCredentialsType.LWM2M_CREDENTIALS: this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]); this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false}); this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]); @@ -211,13 +211,6 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]); this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false}); break; - case DeviceCredentialsType.LWM2M_CREDENTIALS: - this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]); - this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false}); - this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]); - this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false}); - this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false}); - break; } } @@ -245,27 +238,39 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, }); } - openSecurityInfoLwM2mDialog($event: Event, value: string, id: string): void { + openSecurityInfoLwM2mDialog($event: Event): void { if ($event) { $event.stopPropagation(); $event.preventDefault(); } + let credentialsValue = this.deviceCredentialsFormGroup.get('credentialsValue').value; + if (credentialsValue === null || credentialsValue.length === 0) { + credentialsValue = getDefaultSecurityConfig(); + } else { + credentialsValue = JSON.parse(credentialsValue); + } + const credentialsId = this.deviceCredentialsFormGroup.get('credentialsId').value || DEFAULT_END_POINT; this.dialog.open(SecurityConfigComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - jsonAllConfig: (value === null || value.length === 0) ? getDefaultSecurityConfig() as SecurityConfigModels : JSON.parse(value) as SecurityConfigModels, - endPoint: (id === null) ? DEFAULT_END_POINT : id, - isNew: (id === null || value === null || value.length === 0) + jsonAllConfig: credentialsValue, + endPoint: credentialsId } }).afterClosed().subscribe( (res) => { if (res) { - this.deviceCredentialsFormGroup.get('credentialsValue').patchValue((Object.keys(res[JSON_ALL_CONFIG]).length === 0 || JSON.stringify(res[JSON_ALL_CONFIG]) === "[{}]") ? null : JSON.stringify(res[JSON_ALL_CONFIG])); - this.deviceCredentialsFormGroup.get('credentialsId').patchValue((Object.keys(res[END_POINT]).length === 0 || JSON.stringify(res[END_POINT]) === "[{}]") ? null : JSON.stringify(res[END_POINT]).split('\"').join('')); + this.deviceCredentialsFormGroup.patchValue({ + credentialsValue: this.isDefautLw2mResponse(res[JSON_ALL_CONFIG]) ? null : JSON.stringify(res[JSON_ALL_CONFIG]), + credentialsId: this.isDefautLw2mResponse(res[END_POINT]) ? null : JSON.stringify(res[END_POINT]).split('\"').join('') + }); this.deviceCredentialsFormGroup.get('credentialsValue').markAsDirty(); } } ); } + + private isDefautLw2mResponse(response: object): boolean { + return Object.keys(response).length === 0 || JSON.stringify(response) === '[{}]'; + } } diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 67f86cb812..03124d7a65 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -307,7 +307,7 @@ import { Lwm2mProfileComponentsModule } from '@home/components/profile/device/lw EditAlarmDetailsDialogComponent, DeviceProfileProvisionConfigurationComponent, AlarmScheduleComponent, - Lwm2mProfileComponentsModule, + // Lwm2mProfileComponentsModule, SmsProviderConfigurationComponent, AwsSnsProviderConfigurationComponent, TwilioSmsProviderConfigurationComponent diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html index 0e10a8e843..5f8c1080bc 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html @@ -21,10 +21,9 @@
{{ 'device-profile.lwm2m.mode' | translate }} - + + [value]="securityMode"> {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }} @@ -87,33 +86,31 @@ {{ 'device-profile.lwm2m.bootstrap-server' | translate }}
-
-
- - {{ 'device-profile.lwm2m.server-public-key' | translate }} - - {{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}} - - {{ 'device-profile.lwm2m.server-public-key' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - {{ 'device-profile.lwm2m.client-key' | translate }} - {{ 'device-profile.lwm2m.pattern_hex_dec_182' | translate }} - - - {{ 'device-profile.lwm2m.client-key' | translate }} - {{ 'device-profile.lwm2m.pattern_hex_dec' | translate }} - - -
+
+ + {{ 'device-profile.lwm2m.server-public-key' | translate }} + + {{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}} + + {{ 'device-profile.lwm2m.server-public-key' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + {{ 'device-profile.lwm2m.client-key' | translate }} + {{ 'device-profile.lwm2m.pattern_hex_dec_182' | translate }} + + + {{ 'device-profile.lwm2m.client-key' | translate }} + {{ 'device-profile.lwm2m.pattern_hex_dec' | translate }} + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts index c5d56aa85a..01352af36e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts @@ -43,7 +43,6 @@ import { DeviceProfileService } from '@core/http/device-profile.service'; @Component({ selector: 'tb-profile-lwm2m-device-config-server', templateUrl: './lwm2m-device-config-server.component.html', - styleUrls: [], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -55,8 +54,8 @@ import { DeviceProfileService } from '@core/http/device-profile.service'; export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAccessor, Validators { - valuePrev = null as any; private requiredValue: boolean; + valuePrev = null; serverFormGroup: FormGroup; securityConfigLwM2MType = SECURITY_CONFIG_MODE; securityConfigLwM2MTypes = Object.keys(SECURITY_CONFIG_MODE); @@ -69,7 +68,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc disabled: boolean; @Input() - bootstrapServerIs: boolean + bootstrapServerIs: boolean; get required(): boolean { return this.requiredValue; @@ -107,7 +106,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc } updateValueFields(serverData: ServerSecurityConfig): void { - serverData['bootstrapServerIs'] = this.bootstrapServerIs; + serverData.bootstrapServerIs = this.bootstrapServerIs; this.serverFormGroup.patchValue(serverData, {emitEvent: false}); this.serverFormGroup.get('bootstrapServerIs').disable(); const securityMode = this.serverFormGroup.get('securityMode').value as SECURITY_CONFIG_MODE; @@ -132,21 +131,22 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc this.serverFormGroup.get('serverPublicKey').setValidators([Validators.required, Validators.pattern(KEY_PUBLIC_REGEXP_X509)]); break; } - this.checkValueWithNewValidate(); + this.serverFormGroup.updateValueAndValidity(); + // this.checkValueWithNewValidate(); } - checkValueWithNewValidate(): void { - this.serverFormGroup.patchValue({ - host: this.serverFormGroup.get('host').value, - port: this.serverFormGroup.get('port').value, - bootstrapServerIs: this.serverFormGroup.get('bootstrapServerIs').value, - serverPublicKey: this.serverFormGroup.get('serverPublicKey').value, - clientHoldOffTime: this.serverFormGroup.get('clientHoldOffTime').value, - serverId: this.serverFormGroup.get('serverId').value, - bootstrapServerAccountTimeout: this.serverFormGroup.get('bootstrapServerAccountTimeout').value, - }, - {emitEvent: true}); - } + // checkValueWithNewValidate(): void { + // this.serverFormGroup.patchValue({ + // host: this.serverFormGroup.get('host').value, + // port: this.serverFormGroup.get('port').value, + // bootstrapServerIs: this.serverFormGroup.get('bootstrapServerIs').value, + // serverPublicKey: this.serverFormGroup.get('serverPublicKey').value, + // clientHoldOffTime: this.serverFormGroup.get('clientHoldOffTime').value, + // serverId: this.serverFormGroup.get('serverId').value, + // bootstrapServerAccountTimeout: this.serverFormGroup.get('bootstrapServerAccountTimeout').value, + // }, + // {emitEvent: true}); + // } writeValue(value: any): void { if (value) { @@ -154,8 +154,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc } } - private propagateChange = (v: any) => { - }; + private propagateChange = (v: any) => {}; registerOnChange(fn: any): void { this.propagateChange = fn; @@ -164,8 +163,8 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc private propagateChangeState(value: any): void { if (value !== undefined) { if (this.valuePrev === null) { - this.valuePrev = "init"; - } else if (this.valuePrev === "init") { + this.valuePrev = 'init'; + } else if (this.valuePrev === 'init') { this.valuePrev = value; } else if (JSON.stringify(value) !== JSON.stringify(this.valuePrev)) { this.valuePrev = value; @@ -192,7 +191,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc } getServerGroup(): FormGroup { - const port = (this.bootstrapServerIs) ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC; + const port = this.bootstrapServerIs ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC; return this.fb.group({ host: [this.window.location.hostname, this.required ? [Validators.required] : []], port: [port, this.required ? [Validators.required] : []], @@ -202,7 +201,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc clientHoldOffTime: [DEFAULT_CLIENT_HOLD_OFF_TIME, this.required ? [Validators.required] : []], serverId: [DEFAULT_ID_SERVER, this.required ? [Validators.required] : []], bootstrapServerAccountTimeout: ['', this.required ? [Validators.required] : []], - }) + }); } getLwm2mBootstrapSecurityInfo(mode: string) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 846f991e3e..5e9c5d27f4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -40,8 +40,7 @@ -
{{ 'device-profile.lwm2m.servers' | translate | uppercase }}
+
{{ 'device-profile.lwm2m.servers' | translate | uppercase }}
@@ -93,8 +92,7 @@ -
{{ 'device-profile.lwm2m.bootstrap-server' | translate | uppercase }}
+
{{ 'device-profile.lwm2m.bootstrap-server' | translate | uppercase }}
@@ -110,8 +108,7 @@ -
{{ 'device-profile.lwm2m.lwm2m-server' | translate | uppercase }}
+
{{ 'device-profile.lwm2m.lwm2m-server' | translate | uppercase }}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 2cc6fe819e..9071fc4a2b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -33,7 +33,7 @@ import { OBSERVE, OBSERVE_ATTR, TELEMETRY, - ObjectLwM2M, getDefaultProfileConfig, KEY_NAME, Instance + ObjectLwM2M, getDefaultProfileConfig, KEY_NAME, Instance, ProfileConfigModels, ResourceLwM2M } from "./profile-config.models"; import { DeviceProfileService } from "@core/http/device-profile.service"; import { deepClone, isUndefined } from "@core/utils"; @@ -44,7 +44,6 @@ import { isNotNullOrUndefined } from 'codelyzer/util/isNotNullOrUndefined'; @Component({ selector: 'tb-profile-lwm2m-device-transport-configuration', templateUrl: './lwm2m-device-profile-transport-configuration.component.html', - styleUrls: [], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Lwm2mDeviceProfileTransportConfigurationComponent), @@ -53,6 +52,10 @@ import { isNotNullOrUndefined } from 'codelyzer/util/isNotNullOrUndefined'; }) export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, Validators { + private configurationValue: ProfileConfigModels; + private requiredValue: boolean; + private disabled = false; + lwm2mDeviceProfileTransportConfFormGroup: FormGroup; observeAttr = OBSERVE_ATTR as string; observe = OBSERVE as string; @@ -62,9 +65,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServers: string; bootstrapServer: string; lwm2mServer: string; - private configurationValue: {}; - private requiredValue: boolean; - private disabled = false as boolean; sortFunction = this.sortObjectKeyPathJson; get required(): boolean { @@ -76,8 +76,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.requiredValue = coerceBooleanProperty(value); } - private propagateChange = (v: any) => { - }; + private propagateChange = (v: any) => { }; constructor(private store: Store, private fb: FormBuilder, @@ -85,17 +84,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @Inject(WINDOW) private window: Window) { this.lwm2mDeviceProfileTransportConfFormGroup = this.fb.group({ objectIds: [{}, Validators.required], - observeAttrTelemetry: [{'clientLwM2M': [] as ObjectLwM2M []}, Validators.required], + observeAttrTelemetry: [{clientLwM2M: [] as ObjectLwM2M[]}, Validators.required], shortId: [null, Validators.required], lifetime: [null, Validators.required], defaultMinPeriod: [null, Validators.required], notifIfDisabled: [true, []], - binding: ["U", Validators.required], + binding: ['U', Validators.required], bootstrapServer: [null, Validators.required], lwm2mServer: [null, Validators.required], configurationJson: [null, Validators.required], }); - this.lwm2mDeviceProfileTransportConfFormGroup.valueChanges.subscribe(value => { + this.lwm2mDeviceProfileTransportConfFormGroup.valueChanges.subscribe(() => { if (!this.disabled) { this.updateModel(); } @@ -122,7 +121,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } writeValue(value: any | null): void { - value = (Object.keys(value).length == 0) ? getDefaultProfileConfig() : value; + value = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value; this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ configurationJson: value }, @@ -132,14 +131,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private initWriteValue(): void { - let modelValue = {"objectIds": null, "objectsList": []}; + const modelValue = {objectIds: null, objectsList: []}; modelValue.objectIds = this.getObjectsFromJsonAllConfig(); if (modelValue.objectIds !== null) { this.deviceProfileService.getLwm2mObjects(modelValue.objectIds).subscribe( - (objectsList) => { - modelValue.objectsList = objectsList; - this.updateWriteValue(modelValue); - } + (objectsList) => { + modelValue.objectsList = objectsList; + this.updateWriteValue(modelValue); + } ); } else { this.updateWriteValue(modelValue); @@ -147,17 +146,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateWriteValue(value: any): void { - let objectsList = deepClone(value.objectsList); + const objectsList = deepClone(value.objectsList); this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ objectIds: value, observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)}, - shortId: this.configurationValue['bootstrap'].servers.shortId, - lifetime: this.configurationValue['bootstrap'].servers.lifetime, - defaultMinPeriod: this.configurationValue['bootstrap'].servers.defaultMinPeriod, - notifIfDisabled: this.configurationValue['bootstrap'].servers.notifIfDisabled, - binding: this.configurationValue['bootstrap'].servers.binding, - bootstrapServer: this.configurationValue['bootstrap'].bootstrapServer, - lwm2mServer: this.configurationValue['bootstrap'].lwm2mServer + shortId: this.configurationValue.bootstrap.servers.shortId, + lifetime: this.configurationValue.bootstrap.servers.lifetime, + defaultMinPeriod: this.configurationValue.bootstrap.servers.defaultMinPeriod, + notifIfDisabled: this.configurationValue.bootstrap.servers.notifIfDisabled, + binding: this.configurationValue.bootstrap.servers.binding, + bootstrapServer: this.configurationValue.bootstrap.bootstrapServer, + lwm2mServer: this.configurationValue.bootstrap.lwm2mServer }, {emitEvent: false}); } @@ -174,10 +173,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private updateObserveAttrTelemetryObjectFormGroup(objectsList: ObjectLwM2M[]) { this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ - observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)} - }, - {emitEvent: false}); - this.lwm2mDeviceProfileTransportConfFormGroup.get("observeAttrTelemetry").markAsPristine({ + observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)} + }, + {emitEvent: false}); + this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').markAsPristine({ onlySelf: true }); } @@ -188,9 +187,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } upDateValueToJsonTab_0(): void { - if (!this.lwm2mDeviceProfileTransportConfFormGroup.get("observeAttrTelemetry").pristine) { - this.upDateObserveAttrTelemetryFromGroupToJson(this.lwm2mDeviceProfileTransportConfFormGroup.get("observeAttrTelemetry").value['clientLwM2M']); - this.lwm2mDeviceProfileTransportConfFormGroup.get("observeAttrTelemetry").markAsPristine({ + if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').pristine) { + this.upDateObserveAttrTelemetryFromGroupToJson( + this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M + ); + this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); @@ -200,14 +201,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro upDateValueToJsonTab_1(): void { this.upDateValueServersToJson(); if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('bootstrapServer').pristine) { - this.configurationValue['bootstrap'].bootstrapServer = this.lwm2mDeviceProfileTransportConfFormGroup.get('bootstrapServer').value; + this.configurationValue.bootstrap.bootstrapServer = this.lwm2mDeviceProfileTransportConfFormGroup.get('bootstrapServer').value; this.lwm2mDeviceProfileTransportConfFormGroup.get('bootstrapServer').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('lwm2mServer').pristine) { - this.configurationValue['bootstrap'].lwm2mServer = this.lwm2mDeviceProfileTransportConfFormGroup.get('lwm2mServer').value; + this.configurationValue.bootstrap.lwm2mServer = this.lwm2mDeviceProfileTransportConfFormGroup.get('lwm2mServer').value; this.lwm2mDeviceProfileTransportConfFormGroup.get('lwm2mServer').markAsPristine({ onlySelf: true }); @@ -216,36 +217,37 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } upDateValueServersToJson(): void { + const bootstrapServers = this.configurationValue.bootstrap.servers; if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('shortId').pristine) { - this.configurationValue['bootstrap'].servers.shortId = this.lwm2mDeviceProfileTransportConfFormGroup.get('shortId').value; + bootstrapServers.shortId = this.lwm2mDeviceProfileTransportConfFormGroup.get('shortId').value; this.lwm2mDeviceProfileTransportConfFormGroup.get('shortId').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('lifetime').pristine) { - this.configurationValue['bootstrap'].servers.lifetime = this.lwm2mDeviceProfileTransportConfFormGroup.get('lifetime').value; + bootstrapServers.lifetime = this.lwm2mDeviceProfileTransportConfFormGroup.get('lifetime').value; this.lwm2mDeviceProfileTransportConfFormGroup.get('lifetime').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('defaultMinPeriod').pristine) { - this.configurationValue['bootstrap'].servers.defaultMinPeriod = this.lwm2mDeviceProfileTransportConfFormGroup.get('defaultMinPeriod').value; + bootstrapServers.defaultMinPeriod = this.lwm2mDeviceProfileTransportConfFormGroup.get('defaultMinPeriod').value; this.lwm2mDeviceProfileTransportConfFormGroup.get('defaultMinPeriod').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('notifIfDisabled').pristine) { - this.configurationValue['bootstrap'].servers.notifIfDisabled = this.lwm2mDeviceProfileTransportConfFormGroup.get('notifIfDisabled').value; + bootstrapServers.notifIfDisabled = this.lwm2mDeviceProfileTransportConfFormGroup.get('notifIfDisabled').value; this.lwm2mDeviceProfileTransportConfFormGroup.get('notifIfDisabled').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('binding').pristine) { - this.configurationValue['bootstrap'].servers.binding = this.lwm2mDeviceProfileTransportConfFormGroup.get('binding').value; + bootstrapServers.binding = this.lwm2mDeviceProfileTransportConfFormGroup.get('binding').value; this.lwm2mDeviceProfileTransportConfFormGroup.get('binding').markAsPristine({ onlySelf: true }); @@ -254,122 +256,130 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } getObserveAttrTelemetryObjects(listObject: ObjectLwM2M[]): ObjectLwM2M [] { - let clientObserveAttr = deepClone(listObject) as ObjectLwM2M[]; + const clientObserveAttr = deepClone(listObject); if (this.configurationValue[this.observeAttr]) { - let observeArray = this.configurationValue[this.observeAttr][this.observe] as Array; - let attributeArray = this.configurationValue[this.observeAttr][this.attribute] as Array; - let telemetryArray = this.configurationValue[this.observeAttr][this.telemetry] as Array; - let keyNameJson = this.configurationValue[this.observeAttr][this.keyName] as JsonObject; - if (this.includesInstancesNo(attributeArray, telemetryArray, clientObserveAttr)) { + const observeArray = this.configurationValue[this.observeAttr][this.observe] as Array; + const attributeArray = this.configurationValue[this.observeAttr][this.attribute] as Array; + const telemetryArray = this.configurationValue[this.observeAttr][this.telemetry] as Array; + const keyNameJson = this.configurationValue[this.observeAttr][this.keyName] as JsonObject; + if (this.includesInstancesNo(attributeArray, telemetryArray)) { this.addInstances(attributeArray, telemetryArray, clientObserveAttr); } - if (observeArray) this.updateObserveAttrTelemetryObjects(observeArray, clientObserveAttr, "observe"); - if (attributeArray) this.updateObserveAttrTelemetryObjects(attributeArray, clientObserveAttr, "attribute"); - if (telemetryArray) this.updateObserveAttrTelemetryObjects(telemetryArray, clientObserveAttr, "telemetry"); - if (keyNameJson) this.updateKeyNameObjects(deepClone(keyNameJson), clientObserveAttr); + if (observeArray) { + this.updateObserveAttrTelemetryObjects(observeArray, clientObserveAttr, 'observe'); + } + if (attributeArray) { + this.updateObserveAttrTelemetryObjects(attributeArray, clientObserveAttr, 'attribute'); + } + if (telemetryArray) { + this.updateObserveAttrTelemetryObjects(telemetryArray, clientObserveAttr, 'telemetry'); + } + if (keyNameJson) { + this.updateKeyNameObjects(deepClone(keyNameJson), clientObserveAttr); + } } clientObserveAttr.forEach(obj => { - obj.instances.sort((a,b) => a.id - b.id);; - }) + obj.instances.sort((a, b) => a.id - b.id); + }); return clientObserveAttr; } - includesInstancesNo(attributeArray: Array, telemetryArray: Array, clientObserveAttr: ObjectLwM2M[]): boolean { - let isIdIndex = (element) => !element.includes("/0/"); - return attributeArray.findIndex(isIdIndex) >= 0 || telemetryArray.findIndex(isIdIndex) >= 0 - + includesInstancesNo(attributeArray: Array, telemetryArray: Array): boolean { + const isIdIndex = (element) => !element.includes('/0/'); + return attributeArray.findIndex(isIdIndex) >= 0 || telemetryArray.findIndex(isIdIndex) >= 0; } addInstances(attributeArray: Array, telemetryArray: Array, clientObserveAttr: ObjectLwM2M[]): void { - let attr = [] as Array; - [...attributeArray].filter(x => (!x.includes("/0/"))).forEach(x => { + const attr = [] as Array; + [...attributeArray].filter(x => (!x.includes('/0/'))).forEach(x => { attr.push(this.convertPathToInstance(x)); }); - let telemetry = [] as Array; - [...telemetryArray].filter(x => (!x.includes("/0/"))).forEach(x => { + const telemetry = [] as Array; + [...telemetryArray].filter(x => (!x.includes('/0/'))).forEach(x => { telemetry.push(this.convertPathToInstance(x)); }); - let instancesNoZero = new Set(attr.concat(telemetry).sort()); + const instancesNoZero = new Set(attr.concat(telemetry).sort()); instancesNoZero.forEach(path => { - let pathParameter = Array.from(path.split('/'), Number); - let objectLwM2M = clientObserveAttr.find(x => (x.id === pathParameter[0])); + const pathParameter = Array.from(path.split('/'), Number); + const objectLwM2M = clientObserveAttr.find(x => (x.id === pathParameter[0])); if (objectLwM2M) { - let instance = deepClone(objectLwM2M.instances[0]) as Instance; + const instance = deepClone(objectLwM2M.instances[0]) as Instance; instance.id = pathParameter[1]; objectLwM2M.instances.push(instance); } - }) + }); } convertPathToInstance(path: string): string { - let newX = Array.from(path.substring(1).split('/'), Number); - return [newX[0], newX[1]].join("/"); + const newX = Array.from(path.substring(1).split('/'), Number); + return [newX[0], newX[1]].join('/'); } updateObserveAttrTelemetryObjects(isParameter: Array, clientObserveAttr: ObjectLwM2M[], nameParameter: string): void { isParameter.forEach(attr => { - let idKeys = Array.from(attr.substring(1).split('/'), Number); + const idKeys = Array.from(attr.substring(1).split('/'), Number); clientObserveAttr - .forEach(e => { - if (e.id == idKeys[0]) { - let instance = e.instances.find(e => e.id == idKeys[1]); - if (isNotNullOrUndefined(instance)) { - instance.resources.find(e => e.id == idKeys[2])[nameParameter] = true; + .forEach(e => { + if (e.id === idKeys[0]) { + const instance = e.instances.find(itrInstance => itrInstance.id === idKeys[1]); + if (isNotNullOrUndefined(instance)) { + instance.resources.find(resource => resource.id === idKeys[2])[nameParameter] = true; + } } - } - }); + }); }); } updateKeyNameObjects(nameJson: JsonObject, clientObserveAttr: ObjectLwM2M[]): void { - let keyName = JSON.parse(JSON.stringify(nameJson)); - Object.keys(keyName).forEach(function (key) { - let idKeys = Array.from(key.substring(1).split('/'), Number); + const keyName = JSON.parse(JSON.stringify(nameJson)); + Object.keys(keyName).forEach(key => { + const idKeys = Array.from(key.substring(1).split('/'), Number); clientObserveAttr - .forEach(e => { - if (e.id == idKeys[0]) { - e.instances.find(e => e.id == idKeys[1]).resources - .find(e => e.id == idKeys[2]).keyName = keyName[key]; - } - }); + .forEach(e => { + if (e.id === idKeys[0]) { + e.instances + .find(instance => instance.id === idKeys[1]).resources + .find(resource => resource.id === idKeys[2]).keyName = keyName[key]; + } + }); }); } - upDateObserveAttrTelemetryFromGroupToJson(val: ObjectLwM2M []): void { - let observeArray = [] as Array; - let attributeArray = [] as Array; - let telemetryArray = [] as Array; - let observeJson = JSON.parse(JSON.stringify(val)); + upDateObserveAttrTelemetryFromGroupToJson(val: ObjectLwM2M[]): void { + const observeArray: Array = []; + const attributeArray: Array = []; + const telemetryArray: Array = []; + const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); let pathObj; let pathInst; - let pathRes + let pathRes; observeJson.forEach(obj => { Object.entries(obj).forEach(([key, value]) => { if (key === 'id') { pathObj = value; } if (key === 'instances') { - let instancesJson = JSON.parse(JSON.stringify(value)) as []; + const instancesJson = JSON.parse(JSON.stringify(value)) as Instance[]; if (instancesJson.length > 0) { instancesJson.forEach(instance => { - Object.entries(instance).forEach(([key, value]) => { - if (key === 'id') { - pathInst = value; + Object.entries(instance).forEach(([instanceKey, instanceValue]) => { + if (instanceKey === 'id') { + pathInst = instanceValue; } - if (key === 'resources') { - let resourcesJson = JSON.parse(JSON.stringify(value)) as []; + if (instanceKey === 'resources') { + const resourcesJson = JSON.parse(JSON.stringify(instanceValue)) as ResourceLwM2M[]; if (resourcesJson.length > 0) { resourcesJson.forEach(res => { - Object.entries(res).forEach(([key, value]) => { - if (key === 'id') { - // pathRes = value - pathRes = '/' + pathObj + '/' + pathInst + '/' + value; - } else if (key === 'observe' && value) { - observeArray.push(pathRes) - } else if (key === 'attribute' && value) { - attributeArray.push(pathRes) - } else if (key === 'telemetry' && value) { - telemetryArray.push(pathRes) + Object.entries(res).forEach(([resourceKey, resourceValue]) => { + if (resourceKey === 'id') { + // pathRes = resourceValue + pathRes = '/' + pathObj + '/' + pathInst + '/' + resourceValue; + } else if (resourceKey === 'observe' && resourceValue) { + observeArray.push(pathRes); + } else if (resourceKey === 'attribute' && resourceValue) { + attributeArray.push(pathRes); + } else if (resourceKey === 'telemetry' && resourceValue) { + telemetryArray.push(pathRes); } }); }); @@ -396,19 +406,19 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } sortObjectKeyPathJson(key, value) { - if (key == "keyName") { + if (key === 'keyName') { return Object.keys(value).sort((a, b) => { - let aLC = Array.from(a.substring(1).split('/'), Number); - let bLC = Array.from(b.substring(1).split('/'), Number); - return aLC[0] == bLC[0] ? aLC[1] - bLC[1] : aLC[0] - bLC[0]; - }).reduce((r, k) => (r[k] = value[k], r), {}); + const aLC = Array.from(a.substring(1).split('/'), Number); + const bLC = Array.from(b.substring(1).split('/'), Number); + return aLC[0] === bLC[0] ? aLC[1] - bLC[1] : aLC[0] - bLC[0]; + }).reduce((r, k) => r[k] = value[k], {}); } else { - return value + return value; } } updateKeyName(): void { - let paths = new Set(); + const paths = new Set(); if (this.configurationValue[this.observeAttr][this.attribute]) { this.configurationValue[this.observeAttr][this.attribute].forEach(path => { paths.add(path); @@ -419,31 +429,32 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro paths.add(path); }); } - let keyNameNew = {}; + const keyNameNew = {}; paths.forEach(path => { - let pathParameter = this.findIndexsForIds(path); + const pathParameter = this.findIndexsForIds(path); if (pathParameter.length === 3) { - let value = this.lwm2mDeviceProfileTransportConfFormGroup.get("observeAttrTelemetry").value['clientLwM2M'][pathParameter[0]].instances[pathParameter[1]].resources[pathParameter[2]][this.keyName]; - keyNameNew[path] = value; + keyNameNew[path] = this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value + .clientLwM2M[pathParameter[0]].instances[pathParameter[1]].resources[pathParameter[2]][this.keyName]; } }); - this.configurationValue[this.observeAttr][this.keyName] = this.sortObjectKeyPathJson("keyName", keyNameNew); + this.configurationValue[this.observeAttr][this.keyName] = this.sortObjectKeyPathJson('keyName', keyNameNew); } findIndexsForIds(path: string): number[] { - let pathParameter = Array.from(path.substring(1).split('/'), Number); - let pathParameterIndexes = [] as number[]; - let objectsOld = deepClone(this.lwm2mDeviceProfileTransportConfFormGroup.get("observeAttrTelemetry").value.clientLwM2M) as ObjectLwM2M[]; + const pathParameter = Array.from(path.substring(1).split('/'), Number); + const pathParameterIndexes: number[] = []; + const objectsOld = deepClone( + this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M) as ObjectLwM2M[]; let isIdIndex = (element) => element.id === pathParameter[0]; - let objIndex = objectsOld.findIndex(isIdIndex); + const objIndex = objectsOld.findIndex(isIdIndex); if (objIndex >= 0) { pathParameterIndexes.push(objIndex); isIdIndex = (element) => element.id === pathParameter[1]; - let instIndex = objectsOld[objIndex].instances.findIndex(isIdIndex); + const instIndex = objectsOld[objIndex].instances.findIndex(isIdIndex); if (instIndex >= 0) { pathParameterIndexes.push(instIndex); isIdIndex = (element) => element.id === pathParameter[2]; - let resIndex = objectsOld[objIndex].instances[instIndex].resources.findIndex(isIdIndex); + const resIndex = objectsOld[objIndex].instances[instIndex].resources.findIndex(isIdIndex); if (resIndex >= 0) { pathParameterIndexes.push(resIndex); } @@ -453,7 +464,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } getObjectsFromJsonAllConfig(): number [] { - let objectsIds = new Set(); + const objectsIds = new Set(); if (this.configurationValue[this.observeAttr]) { if (this.configurationValue[this.observeAttr][this.observe]) { this.configurationValue[this.observeAttr][this.observe].forEach(obj => { @@ -488,9 +499,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } removeObjectsList(value: ObjectLwM2M): void { - let objectsOld = deepClone(this.lwm2mDeviceProfileTransportConfFormGroup.get("observeAttrTelemetry").value.clientLwM2M); + const objectsOld = deepClone(this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M); const isIdIndex = (element) => element.id === value.id; - let index = objectsOld.findIndex(isIdIndex); + const index = objectsOld.findIndex(isIdIndex); if (index >= 0) { objectsOld.splice(index, 1); } @@ -504,7 +515,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } removeObserveAttrTelemetryFromJson(observeAttrTel: string, id: number): void { - let isIdIndex = (element) => Array.from(element.substring(1).split('/'), Number)[0] === id; + const isIdIndex = (element) => Array.from(element.substring(1).split('/'), Number)[0] === id; let index = this.configurationValue[this.observeAttr][observeAttrTel].findIndex(isIdIndex); while (index >= 0) { this.configurationValue[this.observeAttr][observeAttrTel].splice(index, 1); @@ -513,9 +524,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } removeKeyNameFromJson(id: number): void { - let keyNmaeJson = this.configurationValue[this.observeAttr][this.keyName]; - Object.keys(keyNmaeJson).forEach(function (key) { - let idKey = Array.from(key.substring(1).split('/'), Number)[0]; + const keyNmaeJson = this.configurationValue[this.observeAttr][this.keyName]; + Object.keys(keyNmaeJson).forEach(key => { + const idKey = Array.from(key.substring(1).split('/'), Number)[0]; if (idKey === id) { delete keyNmaeJson[key]; } diff --git a/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.models.ts b/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.models.ts index cd323e2f90..220426f357 100644 --- a/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.models.ts +++ b/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.models.ts @@ -33,7 +33,6 @@ export const KEY_PUBLIC_REGEXP_X509 = /^[0-9a-fA-F]{0,3000}$/; export interface DeviceCredentialsDialogLwm2mData { jsonAllConfig?: SecurityConfigModels; endPoint?: string; - isNew?: boolean; } export enum SECURITY_CONFIG_MODE { From 79f8b87133eebd25212f9647df5204a73a531905 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 15 Jan 2021 14:51:55 +0200 Subject: [PATCH 24/41] Lwm2m: back: for test3 --- .../LwM2M_BinaryAppDataContainer-v1_0_1.xml | 77 ++++++++---------- .../LwM2M_BinaryAppDataContainer-v1_0_1.xml | 79 +++++++++---------- 2 files changed, 71 insertions(+), 85 deletions(-) diff --git a/common/transport/lwm2m/src/main/resources/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml b/common/transport/lwm2m/src/main/resources/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml index b111d3b321..0508674743 100644 --- a/common/transport/lwm2m/src/main/resources/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml +++ b/common/transport/lwm2m/src/main/resources/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml @@ -3,61 +3,54 @@ FILE INFORMATION OMA Permanent Document - File: OMA-SUP-LwM2M_BinaryAppDataContainer-V1_0_1-20190221-A + File: OMA-SUP-XML_LWM2M_Bearer_Selection-V1_0_1-20190917-A Type: xml Public Reachable Information Path: http://www.openmobilealliance.org/tech/profiles - Name: LwM2M_BinaryAppDataContainer-v1_0_1.xml + Name: LWM2M_Bearer_Selection-v1_0_1.xml NORMATIVE INFORMATION + This file is not part of a Technical Specification document. - Information about this file can be found in the latest revision of - - OMA-TS-LWM2M_BinaryAppDataContainer-V1_0_1 - - This is available at http://www.openmobilealliance.org/ + The file is also available at http://www.openmobilealliance.org/ Send comments to https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues -CHANGE HISTORY - -15062018 Status changed to Approved by DM, Doc Ref # OMA-DM&SE-2018-0061-INP_LWM2M_APPDATA_V1_0_ERP_for_final_Approval -21022019 Status changed to Approved by IPSO, Doc Ref # OMA-IPSO-2019-0025-INP_LwM2M_Object_App_Data_Container_1_0_1_for_Final_Approval LEGAL DISCLAIMER -Copyright 2019 Open Mobile Alliance. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its -contributors may be used to endorse or promote products derived -from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -The above license is used as a license under copyright only. Please -reference the OMA IPR Policy for patent licensing terms: -https://www.omaspecworks.org/about/intellectual-property-rights/ + Copyright 2019 Open Mobile Alliance. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + The above license is used as a license under copyright only. Please + reference the OMA IPR Policy for patent licensing terms: + https://www.omaspecworks.org/about/intellectual-property-rights/ --> diff --git a/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml b/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml index cd6182596c..0508674743 100644 --- a/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml +++ b/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml @@ -1,63 +1,56 @@ - + From efb671f4806c08196a0a7b05dcfe77e0ee400e29 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 15 Jan 2021 15:08:31 +0200 Subject: [PATCH 25/41] Lwm2m: back: for test4 --- .../LwM2M_BinaryAppDataContainer-v1_0_1.xml | 140 ------------------ .../LwM2M_BinaryAppDataContainer-v1_0_1.xml | 140 ------------------ 2 files changed, 280 deletions(-) delete mode 100644 common/transport/lwm2m/src/main/resources/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml delete mode 100644 transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml diff --git a/common/transport/lwm2m/src/main/resources/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml b/common/transport/lwm2m/src/main/resources/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml deleted file mode 100644 index 0508674743..0000000000 --- a/common/transport/lwm2m/src/main/resources/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - BinaryAppDataContainer - - 19 - urn:oma:lwm2m:oma:19 - 1.0 - 1.0 - Multiple - Optional - - Data - RW - - - - Multiple - Mandatory - Opaque - - - - - Data Priority - RW - Single - Optional - Integer - 1 bytes - - - - Data Creation Time - RW - Single - Optional - Time - - - - - Data Description - RW - Single - Optional - String - 32 bytes - - - - Data Format - RW - Single - Optional - String - 32 bytes - - - - App ID - RW - Single - Optional - Integer - 2 bytes - - - - - - diff --git a/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml b/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml deleted file mode 100644 index 0508674743..0000000000 --- a/transport/lwm2m/src/main/data/models/LwM2M_BinaryAppDataContainer-v1_0_1.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - BinaryAppDataContainer - - 19 - urn:oma:lwm2m:oma:19 - 1.0 - 1.0 - Multiple - Optional - - Data - RW - - - - Multiple - Mandatory - Opaque - - - - - Data Priority - RW - Single - Optional - Integer - 1 bytes - - - - Data Creation Time - RW - Single - Optional - Time - - - - - Data Description - RW - Single - Optional - String - 32 bytes - - - - Data Format - RW - Single - Optional - String - 32 bytes - - - - App ID - RW - Single - Optional - Integer - 2 bytes - - - - - - From cfa9a5778a9ae7b26c92dc80cb7e4d9265a8a11f Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 19 Jan 2021 16:24:58 +0200 Subject: [PATCH 26/41] Lwm2m: front: refactoring --- .../lwm2m-device-config-server.component.html | 27 ++- .../lwm2m-device-config-server.component.ts | 76 +++--- ...ile-transport-configuration.component.html | 218 +++++++++--------- ...ofile-transport-configuration.component.ts | 132 ++++++----- ...m2m-object-add-instances-list.component.ts | 68 +++--- .../lwm2m-object-add-instances.component.html | 2 +- .../lwm2m-object-add-instances.component.ts | 6 +- .../lwm2m/lwm2m-object-list.component.ts | 82 +++---- ...serve-attr-telemetry-resource.component.ts | 41 ++-- .../lwm2m-observe-attr-telemetry.component.ts | 153 ++++++------ .../device/lwm2m/profile-config.models.ts | 108 ++++----- .../home/pages/device/device.module.ts | 3 +- .../security-config-server.component.html | 57 +++-- .../lwm2m/security-config-server.component.ts | 62 +++-- .../lwm2m/security-config.component.html | 206 +++++++++-------- .../device/lwm2m/security-config.component.ts | 185 +++++++-------- .../device/lwm2m/security-config.models.ts | 54 +++-- .../components/json-object-edit.component.ts | 6 +- .../assets/locale/locale.constant-en_US.json | 10 +- 19 files changed, 778 insertions(+), 718 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html index 5f8c1080bc..f0fe7d8dcd 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html @@ -90,9 +90,14 @@ serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509"> {{ 'device-profile.lwm2m.server-public-key' | translate }} - {{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}} @@ -101,14 +106,18 @@ {{ 'device-profile.lwm2m.required' | translate }} - {{ 'device-profile.lwm2m.client-key' | translate }} - {{ 'device-profile.lwm2m.pattern_hex_dec_182' | translate }} + (serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK || + serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509)"> + {{ 'device-profile.lwm2m.server-public-key' | translate }} + {{ translate.get('device-profile.lwm2m.pattern_hex_dec', { + count: 0}) | async }} - - {{ 'device-profile.lwm2m.client-key' | translate }} - {{ 'device-profile.lwm2m.pattern_hex_dec' | translate }} + + {{ 'device-profile.lwm2m.server-public-key' | translate }} + {{ translate.get('device-profile.lwm2m.pattern_hex_dec', { + count: lenMaxServerPublicKey }) | async }}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts index 01352af36e..e02a4e7c3a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts @@ -14,31 +14,28 @@ /// limitations under the License. /// -import { Component, forwardRef, Inject, Input, OnInit } from "@angular/core"; +import { Component, forwardRef, Inject, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, - FormBuilder, FormGroup, NG_VALUE_ACCESSOR, NgModel, Validators -} from "@angular/forms"; + FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators +} from '@angular/forms'; import { SECURITY_CONFIG_MODE, SECURITY_CONFIG_MODE_NAMES, - KEY_PUBLIC_REGEXP_PSK, ServerSecurityConfig, - LEN_MAX_PUBLIC_KEY_PSK, - LEN_MAX_PUBLIC_KEY_RPK_X509, - KEY_PUBLIC_REGEXP_X509, DEFAULT_PORT_BOOTSTRAP_NO_SEC, DEFAULT_PORT_SERVER_NO_SEC, DEFAULT_CLIENT_HOLD_OFF_TIME, - DEFAULT_ID_SERVER -} from "./profile-config.models"; -import { Store } from "@ngrx/store"; -import { AppState } from "@core/core.state"; -import { coerceBooleanProperty } from "@angular/cdk/coercion"; -import { WINDOW } from "@core/services/window.service"; + DEFAULT_ID_SERVER, LEN_MAX_PUBLIC_KEY_RPK, LEN_MAX_PUBLIC_KEY_X509, KEY_REGEXP_HEX_DEC +} from './profile-config.models'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { WINDOW } from '@core/services/window.service'; import { pairwise, startWith } from 'rxjs/operators'; import { DeviceProfileService } from '@core/http/device-profile.service'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-profile-lwm2m-device-config-server', @@ -55,12 +52,14 @@ import { DeviceProfileService } from '@core/http/device-profile.service'; export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAccessor, Validators { private requiredValue: boolean; + valuePrev = null; serverFormGroup: FormGroup; securityConfigLwM2MType = SECURITY_CONFIG_MODE; securityConfigLwM2MTypes = Object.keys(SECURITY_CONFIG_MODE); credentialTypeLwM2MNamesMap = SECURITY_CONFIG_MODE_NAMES; - lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_PSK; + lenMinServerPublicKey = 0; + lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_RPK; currentSecurityMode = null; @@ -80,6 +79,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc } constructor(protected store: Store, + public translate: TranslateService, public fb: FormBuilder, private deviceProfileService: DeviceProfileService, @Inject(WINDOW) private window: Window) { @@ -105,7 +105,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc ngOnInit(): void { } - updateValueFields(serverData: ServerSecurityConfig): void { + private updateValueFields = (serverData: ServerSecurityConfig): void => { serverData.bootstrapServerIs = this.bootstrapServerIs; this.serverFormGroup.patchValue(serverData, {emitEvent: false}); this.serverFormGroup.get('bootstrapServerIs').disable(); @@ -113,40 +113,38 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc this.updateValidate(securityMode); } - updateValidate(securityMode: SECURITY_CONFIG_MODE): void { + private updateValidate = (securityMode: SECURITY_CONFIG_MODE): void => { switch (securityMode) { case SECURITY_CONFIG_MODE.NO_SEC: - this.serverFormGroup.get('serverPublicKey').setValidators([]); + this.setValidatorsNoSecPsk(); break; case SECURITY_CONFIG_MODE.PSK: - this.lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_PSK; - this.serverFormGroup.get('serverPublicKey').setValidators([]); + this.setValidatorsNoSecPsk(); break; case SECURITY_CONFIG_MODE.RPK: - this.lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_RPK_X509; - this.serverFormGroup.get('serverPublicKey').setValidators([Validators.required, Validators.pattern(KEY_PUBLIC_REGEXP_PSK)]); + this.lenMinServerPublicKey = LEN_MAX_PUBLIC_KEY_RPK; + this.lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_RPK; + this.setValidatorsRpkX509(); break; case SECURITY_CONFIG_MODE.X509: - this.lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_RPK_X509; - this.serverFormGroup.get('serverPublicKey').setValidators([Validators.required, Validators.pattern(KEY_PUBLIC_REGEXP_X509)]); + this.lenMinServerPublicKey = 0; + this.lenMaxServerPublicKey = LEN_MAX_PUBLIC_KEY_X509; + this.setValidatorsRpkX509(); break; } this.serverFormGroup.updateValueAndValidity(); - // this.checkValueWithNewValidate(); } - // checkValueWithNewValidate(): void { - // this.serverFormGroup.patchValue({ - // host: this.serverFormGroup.get('host').value, - // port: this.serverFormGroup.get('port').value, - // bootstrapServerIs: this.serverFormGroup.get('bootstrapServerIs').value, - // serverPublicKey: this.serverFormGroup.get('serverPublicKey').value, - // clientHoldOffTime: this.serverFormGroup.get('clientHoldOffTime').value, - // serverId: this.serverFormGroup.get('serverId').value, - // bootstrapServerAccountTimeout: this.serverFormGroup.get('bootstrapServerAccountTimeout').value, - // }, - // {emitEvent: true}); - // } + private setValidatorsNoSecPsk = (): void => { + this.serverFormGroup.get('serverPublicKey').setValidators([]); + } + + private setValidatorsRpkX509 = (): void => { + this.serverFormGroup.get('serverPublicKey').setValidators([Validators.required, + Validators.pattern(KEY_REGEXP_HEX_DEC), + Validators.minLength(this.lenMinServerPublicKey), + Validators.maxLength(this.lenMaxServerPublicKey)]); + } writeValue(value: any): void { if (value) { @@ -160,7 +158,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc this.propagateChange = fn; } - private propagateChangeState(value: any): void { + private propagateChangeState = (value: any): void => { if (value !== undefined) { if (this.valuePrev === null) { this.valuePrev = 'init'; @@ -190,7 +188,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc registerOnTouched(fn: any): void { } - getServerGroup(): FormGroup { + private getServerGroup = (): FormGroup => { const port = this.bootstrapServerIs ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC; return this.fb.group({ host: [this.window.location.hostname, this.required ? [Validators.required] : []], @@ -204,7 +202,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc }); } - getLwm2mBootstrapSecurityInfo(mode: string) { + private getLwm2mBootstrapSecurityInfo = (mode: string): void => { this.deviceProfileService.getLwm2mBootstrapSecurityInfo(mode, this.serverFormGroup.get('bootstrapServerIs').value).subscribe( (serverSecurityConfig) => { this.serverFormGroup.patchValue({ diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 5e9c5d27f4..3eadd43c53 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -19,118 +19,126 @@
-
- - -
-
- - -
+ +
+ + +
+
+ + +
+
-
- - - - -
{{ 'device-profile.lwm2m.servers' | translate | uppercase }}
-
-
-
-
- - {{ 'device-profile.lwm2m.short-id' | translate }} - - - {{ 'device-profile.lwm2m.short-id' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.lifetime' | translate }} - - - {{ 'device-profile.lwm2m.lifetime' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - + +
+ + + + +
{{ 'device-profile.lwm2m.servers' | translate | uppercase }}
+
+
+
+
+ + {{ 'device-profile.lwm2m.short-id' | translate }} + + + {{ 'device-profile.lwm2m.short-id' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + + {{ 'device-profile.lwm2m.lifetime' | translate }} + + + {{ 'device-profile.lwm2m.lifetime' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + +
+
+ + {{ 'device-profile.lwm2m.default-min-period' | translate }} + + + {{ 'device-profile.lwm2m.default-min-period' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + + {{ 'device-profile.lwm2m.binding' | translate }} + + + {{ 'device-profile.lwm2m.binding' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + +
+
+ + {{ 'device-profile.lwm2m.notif-if-disabled' | translate }} + +
-
- - {{ 'device-profile.lwm2m.default-min-period' | translate }} - - - {{ 'device-profile.lwm2m.default-min-period' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.binding' | translate }} - - - {{ 'device-profile.lwm2m.binding' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - + + + + + + +
{{ 'device-profile.lwm2m.bootstrap-server' | translate | uppercase }}
+
+
+
+ +
-
- - {{ 'device-profile.lwm2m.notif-if-disabled' | translate }} - + + + + + + +
{{ 'device-profile.lwm2m.lwm2m-server' | translate | uppercase }}
+
+
+
+ +
-
-
-
- - - - -
{{ 'device-profile.lwm2m.bootstrap-server' | translate | uppercase }}
-
-
-
- - -
-
-
- - - - -
{{ 'device-profile.lwm2m.lwm2m-server' | translate | uppercase }}
-
-
-
- - -
-
-
-
+
+
+
+
-
- - -
+ +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 9071fc4a2b..b1f87568ad 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -34,10 +34,10 @@ import { OBSERVE_ATTR, TELEMETRY, ObjectLwM2M, getDefaultProfileConfig, KEY_NAME, Instance, ProfileConfigModels, ResourceLwM2M -} from "./profile-config.models"; -import { DeviceProfileService } from "@core/http/device-profile.service"; -import { deepClone, isUndefined } from "@core/utils"; -import { WINDOW } from "@core/services/window.service"; +} from './profile-config.models'; +import { DeviceProfileService } from '@core/http/device-profile.service'; +import { deepClone, isUndefined } from '@core/utils'; +import { WINDOW } from '@core/services/window.service'; import { JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; import { isNotNullOrUndefined } from 'codelyzer/util/isNotNullOrUndefined'; @@ -65,7 +65,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServers: string; bootstrapServer: string; lwm2mServer: string; - sortFunction = this.sortObjectKeyPathJson; + sortFunction: {}; get required(): boolean { return this.requiredValue; @@ -76,7 +76,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.requiredValue = coerceBooleanProperty(value); } - private propagateChange = (v: any) => { }; + private propagateChange = (v: any) => { }; constructor(private store: Store, private fb: FormBuilder, @@ -109,6 +109,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } ngOnInit() { + this.sortFunction = this.sortObjectKeyPathJson; } setDisabledState(isDisabled: boolean): void { @@ -130,22 +131,22 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.initWriteValue(); } - private initWriteValue(): void { + private initWriteValue = (): void => { const modelValue = {objectIds: null, objectsList: []}; modelValue.objectIds = this.getObjectsFromJsonAllConfig(); if (modelValue.objectIds !== null) { this.deviceProfileService.getLwm2mObjects(modelValue.objectIds).subscribe( - (objectsList) => { - modelValue.objectsList = objectsList; - this.updateWriteValue(modelValue); - } + (objectsList) => { + modelValue.objectsList = objectsList; + this.updateWriteValue(modelValue); + } ); } else { this.updateWriteValue(modelValue); } } - private updateWriteValue(value: any): void { + private updateWriteValue = (value: any): void => { const objectsList = deepClone(value.objectsList); this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ objectIds: value, @@ -161,7 +162,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro {emitEvent: false}); } - private updateModel() { + private updateModel = (): void => { let configuration: DeviceProfileTransportConfiguration = null; if (this.lwm2mDeviceProfileTransportConfFormGroup.valid) { this.upDateValueToJson(); @@ -171,25 +172,25 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.propagateChange(configuration); } - private updateObserveAttrTelemetryObjectFormGroup(objectsList: ObjectLwM2M[]) { + private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => { this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ - observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)} - }, - {emitEvent: false}); + observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)} + }, + {emitEvent: false}); this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').markAsPristine({ onlySelf: true }); } - upDateValueToJson(): void { - this.upDateValueToJsonTab_0(); - this.upDateValueToJsonTab_1(); + private upDateValueToJson = (): void => { + this.upDateValueToJsonTab0(); + this.upDateValueToJsonTab1(); } - upDateValueToJsonTab_0(): void { + private upDateValueToJsonTab0 = (): void => { if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').pristine) { this.upDateObserveAttrTelemetryFromGroupToJson( - this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M + this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M ); this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').markAsPristine({ onlySelf: true @@ -198,7 +199,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } } - upDateValueToJsonTab_1(): void { + private upDateValueToJsonTab1 = (): void => { this.upDateValueServersToJson(); if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('bootstrapServer').pristine) { this.configurationValue.bootstrap.bootstrapServer = this.lwm2mDeviceProfileTransportConfFormGroup.get('bootstrapServer').value; @@ -216,7 +217,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } } - upDateValueServersToJson(): void { + private upDateValueServersToJson = (): void => { const bootstrapServers = this.configurationValue.bootstrap.servers; if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('shortId').pristine) { bootstrapServers.shortId = this.lwm2mDeviceProfileTransportConfFormGroup.get('shortId').value; @@ -255,7 +256,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } } - getObserveAttrTelemetryObjects(listObject: ObjectLwM2M[]): ObjectLwM2M [] { + private getObserveAttrTelemetryObjects = (listObject: ObjectLwM2M[]): ObjectLwM2M [] => { const clientObserveAttr = deepClone(listObject); if (this.configurationValue[this.observeAttr]) { const observeArray = this.configurationValue[this.observeAttr][this.observe] as Array; @@ -284,12 +285,13 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro return clientObserveAttr; } - includesInstancesNo(attributeArray: Array, telemetryArray: Array): boolean { + private includesInstancesNo = (attributeArray: Array, telemetryArray: Array): boolean => { const isIdIndex = (element) => !element.includes('/0/'); return attributeArray.findIndex(isIdIndex) >= 0 || telemetryArray.findIndex(isIdIndex) >= 0; } - addInstances(attributeArray: Array, telemetryArray: Array, clientObserveAttr: ObjectLwM2M[]): void { + private addInstances = (attributeArray: Array, telemetryArray: Array, + clientObserveAttr: ObjectLwM2M[]): void => { const attr = [] as Array; [...attributeArray].filter(x => (!x.includes('/0/'))).forEach(x => { attr.push(this.convertPathToInstance(x)); @@ -310,42 +312,43 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro }); } - convertPathToInstance(path: string): string { + private convertPathToInstance = (path: string): string => { const newX = Array.from(path.substring(1).split('/'), Number); return [newX[0], newX[1]].join('/'); } - updateObserveAttrTelemetryObjects(isParameter: Array, clientObserveAttr: ObjectLwM2M[], nameParameter: string): void { + private updateObserveAttrTelemetryObjects = (isParameter: Array, clientObserveAttr: ObjectLwM2M[], + nameParameter: string): void => { isParameter.forEach(attr => { const idKeys = Array.from(attr.substring(1).split('/'), Number); clientObserveAttr - .forEach(e => { - if (e.id === idKeys[0]) { - const instance = e.instances.find(itrInstance => itrInstance.id === idKeys[1]); - if (isNotNullOrUndefined(instance)) { - instance.resources.find(resource => resource.id === idKeys[2])[nameParameter] = true; - } + .forEach(e => { + if (e.id === idKeys[0]) { + const instance = e.instances.find(itrInstance => itrInstance.id === idKeys[1]); + if (isNotNullOrUndefined(instance)) { + instance.resources.find(resource => resource.id === idKeys[2])[nameParameter] = true; } - }); + } + }); }); } - updateKeyNameObjects(nameJson: JsonObject, clientObserveAttr: ObjectLwM2M[]): void { + private updateKeyNameObjects = (nameJson: JsonObject, clientObserveAttr: ObjectLwM2M[]): void => { const keyName = JSON.parse(JSON.stringify(nameJson)); Object.keys(keyName).forEach(key => { const idKeys = Array.from(key.substring(1).split('/'), Number); clientObserveAttr - .forEach(e => { - if (e.id === idKeys[0]) { - e.instances - .find(instance => instance.id === idKeys[1]).resources - .find(resource => resource.id === idKeys[2]).keyName = keyName[key]; - } - }); + .forEach(e => { + if (e.id === idKeys[0]) { + e.instances + .find(instance => instance.id === idKeys[1]).resources + .find(resource => resource.id === idKeys[2]).keyName = keyName[key]; + } + }); }); } - upDateObserveAttrTelemetryFromGroupToJson(val: ObjectLwM2M[]): void { + private upDateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => { const observeArray: Array = []; const attributeArray: Array = []; const telemetryArray: Array = []; @@ -405,19 +408,22 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.updateKeyName(); } - sortObjectKeyPathJson(key, value) { + sortObjectKeyPathJson = (key: string, value: object): object => { if (key === 'keyName') { return Object.keys(value).sort((a, b) => { const aLC = Array.from(a.substring(1).split('/'), Number); const bLC = Array.from(b.substring(1).split('/'), Number); return aLC[0] === bLC[0] ? aLC[1] - bLC[1] : aLC[0] - bLC[0]; - }).reduce((r, k) => r[k] = value[k], {}); + }).reduce((obj, keySort) => { + obj[keySort] = value[keySort]; + return obj; + }, {}); } else { return value; } } - updateKeyName(): void { + private updateKeyName = (): void => { const paths = new Set(); if (this.configurationValue[this.observeAttr][this.attribute]) { this.configurationValue[this.observeAttr][this.attribute].forEach(path => { @@ -431,20 +437,20 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } const keyNameNew = {}; paths.forEach(path => { - const pathParameter = this.findIndexsForIds(path); + const pathParameter = this.findIndexesForIds(path); if (pathParameter.length === 3) { keyNameNew[path] = this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value - .clientLwM2M[pathParameter[0]].instances[pathParameter[1]].resources[pathParameter[2]][this.keyName]; + .clientLwM2M[pathParameter[0]].instances[pathParameter[1]].resources[pathParameter[2]][this.keyName]; } }); this.configurationValue[this.observeAttr][this.keyName] = this.sortObjectKeyPathJson('keyName', keyNameNew); } - findIndexsForIds(path: string): number[] { + private findIndexesForIds = (path: string): number[] => { const pathParameter = Array.from(path.substring(1).split('/'), Number); const pathParameterIndexes: number[] = []; const objectsOld = deepClone( - this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M) as ObjectLwM2M[]; + this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M) as ObjectLwM2M[]; let isIdIndex = (element) => element.id === pathParameter[0]; const objIndex = objectsOld.findIndex(isIdIndex); if (objIndex >= 0) { @@ -463,7 +469,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro return pathParameterIndexes; } - getObjectsFromJsonAllConfig(): number [] { + private getObjectsFromJsonAllConfig = (): number [] => { const objectsIds = new Set(); if (this.configurationValue[this.observeAttr]) { if (this.configurationValue[this.observeAttr][this.observe]) { @@ -485,7 +491,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro return (objectsIds.size > 0) ? Array.from(objectsIds) : null; } - upDateJsonAllConfig(): void { + private upDateJsonAllConfig = (): void => { this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ configurationJson: this.configurationValue }, {emitEvent: false}); @@ -494,11 +500,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro }); } - addObjectsList(value: ObjectLwM2M[]): void { + addObjectsList = (value: ObjectLwM2M[]): void => { this.updateObserveAttrTelemetryObjectFormGroup(deepClone(value)); } - removeObjectsList(value: ObjectLwM2M): void { + removeObjectsList = (value: ObjectLwM2M): void => { const objectsOld = deepClone(this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M); const isIdIndex = (element) => element.id === value.id; const index = objectsOld.findIndex(isIdIndex); @@ -514,7 +520,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.upDateJsonAllConfig(); } - removeObserveAttrTelemetryFromJson(observeAttrTel: string, id: number): void { + private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, id: number): void => { const isIdIndex = (element) => Array.from(element.substring(1).split('/'), Number)[0] === id; let index = this.configurationValue[this.observeAttr][observeAttrTel].findIndex(isIdIndex); while (index >= 0) { @@ -523,12 +529,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } } - removeKeyNameFromJson(id: number): void { - const keyNmaeJson = this.configurationValue[this.observeAttr][this.keyName]; - Object.keys(keyNmaeJson).forEach(key => { + private removeKeyNameFromJson = (id: number): void => { + const keyNameJson = this.configurationValue[this.observeAttr][this.keyName]; + Object.keys(keyNameJson).forEach(key => { const idKey = Array.from(key.substring(1).split('/'), Number)[0]; if (idKey === id) { - delete keyNmaeJson[key]; + delete keyNameJson[key]; } }); } @@ -538,10 +544,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro if (!isPath) { isPath = this.findPathInJson(path, this.telemetry); } - return (isPath) ? true : false; + return !!isPath; } - findPathInJson(path: string, side: string): string { + private findPathInJson = (path: string, side: string): string => { if (this.configurationValue[this.observeAttr]) { if (this.configurationValue[this.observeAttr][side]) { return this.configurationValue[this.observeAttr][side].find( diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts index 44d9066440..acbebca313 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts @@ -21,44 +21,45 @@ import { OnInit, ViewChild, ElementRef, -} from "@angular/core"; +} from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators -} from "@angular/forms"; -import { coerceBooleanProperty } from "@angular/cdk/coercion"; -import { Store } from "@ngrx/store"; -import { AppState } from "@core/core.state"; +} from '@angular/forms'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; import { MatChipList } from '@angular/material/chips'; import { INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN -} from "./profile-config.models"; -import { TranslateService } from "@ngx-translate/core"; -import { DeviceProfileService } from "@core/http/device-profile.service"; +} from './profile-config.models'; +import { TranslateService } from '@ngx-translate/core'; +import { DeviceProfileService } from '@core/http/device-profile.service'; @Component({ selector: 'tb-profile-lwm2m-object-add-instances-list', templateUrl: './lwm2m-object-add-instances-list.component.html', styleUrls: ['./lwm2m-object-add-instances-list.component.scss'], providers: [{ - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => Lwm2mObjectAddInstancesListComponent), - multi: true - }] + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => Lwm2mObjectAddInstancesListComponent), + multi: true + }] }) export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccessor, OnInit, Validators { - lwm2mObjectListFormGroup: FormGroup; private requiredValue: boolean; - private instancesIdsList: Set | null; - filteredObjectsList: Array; private disabled = false as boolean; private dirty = false as boolean; - instanceIdValueMin = INSTANCES_ID_VALUE_MIN as number - instanceIdValueMax = INSTANCES_ID_VALUE_MAX as number + + lwm2mObjectListFormGroup: FormGroup; + instancesIdsList: Set | null; + filteredObjectsList: Array; + instanceIdValueMin = INSTANCES_ID_VALUE_MIN as number; + instanceIdValueMax = INSTANCES_ID_VALUE_MAX as number; get required(): boolean { return this.requiredValue; @@ -76,8 +77,7 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso @ViewChild('instanceIdInput') instanceIdInput: ElementRef; @ViewChild('chipList', {static: true}) chipList: MatChipList; - private propagateChange = (v: any) => { - }; + private propagateChange = (v: any) => { }; constructor(private store: Store, public translate: TranslateService, @@ -89,7 +89,7 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso }); } - updateValidators() { + private updateValidators = (): void => { this.lwm2mObjectListFormGroup.get('instanceIdInput').setValidators([ Validators.min(this.instanceIdValueMin), Validators.max(this.instanceIdValueMax)]); @@ -128,41 +128,41 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso this.dirty = false; } - add(value: number): void { + add = (value: number): void => { if (!isNaN(value) && this.lwm2mObjectListFormGroup.get('instanceIdInput').valid) { this.instancesIdsList.add(value); this.lwm2mObjectListFormGroup.get('instancesIdsList').setValue(this.instancesIdsList); this.propagateChange(this.instancesIdsList); - this.dirty = true + this.dirty = true; } this.clear(); } - remove(object: number) { + remove = (object: number): void => { this.instancesIdsList.delete(object); this.lwm2mObjectListFormGroup.get('instancesIdsList').setValue(this.instancesIdsList); this.propagateChange(this.instancesIdsList); - this.dirty = true + this.dirty = true; this.clear(); } + // + // displayFn(object?: number): number | undefined { + // return object ? object : undefined; + // } - displayFn(object?: number): number | undefined { - return object ? object : undefined; - } - - clear() { + private clear = (): void => { this.lwm2mObjectListFormGroup.get('instanceIdInput').patchValue(null, {emitEvent: true}); - this.instanceIdInput.nativeElement.value = ""; + this.instanceIdInput.nativeElement.value = ''; setTimeout(() => { this.instanceIdInput.nativeElement.blur(); this.instanceIdInput.nativeElement.focus(); }, 0); } - onkeydown(e: KeyboardEvent) { - if (e.keyCode == 189 || e.keyCode == 187 || e.keyCode == 109 || e.keyCode == 107) { + onkeydown = (e: KeyboardEvent): boolean => { + if (e.keyCode === 189 || e.keyCode === 187 || e.keyCode === 109 || e.keyCode === 107) { return false; - } else if (e.keyCode == 8) { + } else if (e.keyCode === 8) { if (this.lwm2mObjectListFormGroup.get('instanceIdInput').value == null) { this.clear(); } @@ -170,7 +170,7 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso } } - onFocus() { + onFocus = (): void => { if (this.dirty) { this.lwm2mObjectListFormGroup.get('instanceIdInput').updateValueAndValidity({onlySelf: true, emitEvent: true}); this.dirty = false; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html index b8d10689e1..6dafeb6024 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html @@ -42,7 +42,7 @@ -
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts index 64beea1bdb..57c7e3e2b5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts @@ -35,7 +35,7 @@ export interface Lwm2mObjectAddInstancesData { }) export class Lwm2mObjectAddInstancesComponent extends DialogComponent implements OnInit { - jsonFormGroup: FormGroup; + instancesFormGroup: FormGroup; submitted = false; constructor(protected store: Store, @@ -46,9 +46,8 @@ export class Lwm2mObjectAddInstancesComponent extends DialogComponent | null; objectsList: Array = []; filteredObjectsList: Observable>; - disabled = false as boolean; - searchText = '' as string; + disabled = false; + searchText = ''; get required(): boolean { return this.requiredValue; @@ -210,9 +196,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V direction: Direction.ASC }); return this.deviceProfileService.getLwm2mObjectsPage(pageLink, {ignoreLoading: true}).pipe( - map(pageData => { - return pageData.data; - }) + map(pageData => pageData.data) ); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html index 322fba8086..c406cfb17f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html @@ -17,12 +17,10 @@ -->
-
-
-
- -
+ *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams"> +
+
+
device-profile.lwm2m.observe-label @@ -72,10 +70,9 @@ {{ 'device-profile.lwm2m.key-name_label' | translate }} - {{ 'device-profile.lwm2m.key-name' | translate }} {{ 'device-profile.lwm2m.required' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts index 12eeaab029..8d3fa0c57b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts @@ -14,20 +14,12 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; -import { - ControlValueAccessor, - FormArray, FormBuilder, - FormGroup, - NG_VALUE_ACCESSOR, Validators -} from '@angular/forms'; -import { - CAMEL_CASE_REGEXP, - ResourceLwM2M -} from '@home/components/profile/device/lwm2m/profile-config.models'; +import { Component, forwardRef, Input } from '@angular/core'; +import { ControlValueAccessor, FormArray, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { ResourceLwM2M } from '@home/components/profile/device/lwm2m/profile-config.models'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { deepClone } from '@core/utils'; +import _ from 'lodash'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; @Component({ @@ -43,12 +35,12 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; ] }) -export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueAccessor, OnInit, Validators { +export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueAccessor { private requiredValue: boolean; resourceFormGroup: FormGroup; - disabled = false as boolean; + disabled = false; get required(): boolean { return this.requiredValue; @@ -61,9 +53,12 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA this.requiredValue = newVal; } } + constructor(private store: Store, private fb: FormBuilder) { - this.resourceFormGroup = this.fb.group({resources: this.fb.array([])}); + this.resourceFormGroup = this.fb.group({ + resources: this.fb.array([]) + }); this.resourceFormGroup.valueChanges.subscribe(value => { if (!this.disabled) { this.propagateChangeState(value.resources); @@ -71,9 +66,6 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA }); } - ngOnInit(): void { - } - registerOnTouched(fn: any): void { } @@ -85,10 +77,6 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA return this.resourceFormGroup.get('resources') as FormArray; } - resourceLwm2mFormArray = (instance: FormGroup): FormArray => { - return instance.get('resources') as FormArray; - } - setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (isDisabled) { @@ -98,31 +86,16 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA } } - getDisabledState = (): boolean => { - return this.disabled; - } - - updateValueKeyName = (event: any, z: number): void => { - this.resourceFormArray.at(z).patchValue( {keyName: this.keysToCamel(deepClone(event.target.value))} ); - } - - private keysToCamel = (o: any): string => { - const val = o.split(' '); - const playStore = []; - val.forEach((item, k) => { - item = item.replace(CAMEL_CASE_REGEXP, ''); - item = (k === 0) ? item.charAt(0).toLowerCase() + item.substr(1) : item.charAt(0).toUpperCase() + item.substr(1); - playStore.push(item); - }); - return playStore.join(''); + updateValueKeyName = (event: Event, index: number): void => { + this.resourceFormArray.at(index).patchValue({keyName: _.camelCase((event.target as HTMLInputElement).value)}); } - private createResourceLwM2M = (resourcesLwM2MJson: ResourceLwM2M []): void => { - if (resourcesLwM2MJson.length === this.resourceFormArray.length) { - this.resourceFormArray.patchValue(resourcesLwM2MJson, {emitEvent: false}); + createResourceLwM2M(resourcesLwM2M: ResourceLwM2M[]): void { + if (resourcesLwM2M.length === this.resourceFormArray.length) { + this.resourceFormArray.patchValue(resourcesLwM2M, {emitEvent: false}); } else { this.resourceFormArray.clear(); - resourcesLwM2MJson.forEach(resourceLwM2M => { + resourcesLwM2M.forEach(resourceLwM2M => { this.resourceFormArray.push(this.fb.group({ id: resourceLwM2M.id, name: resourceLwM2M.name, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html index ea3233f769..eb0e4bc88d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html @@ -19,7 +19,7 @@
@@ -34,8 +34,8 @@ -
- + +
-
- - -
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index faed152a1b..11ffdf0fe5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -15,7 +15,7 @@ /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -28,16 +28,8 @@ import { import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { - ATTR, - Instance, - ObjectLwM2M, - OBSERVE, - ResourceLwM2M, - TELEMETRY -} from './profile-config.models'; -import { isNotNullOrUndefined } from 'codelyzer/util/isNotNullOrUndefined'; -import { deepClone, isUndefined } from '@core/utils'; +import { Instance, ObjectLwM2M, ResourceLwM2M } from './profile-config.models'; +import { deepClone, isDefinedAndNotNull, isEqual, isUndefined } from '@core/utils'; import { MatDialog } from '@angular/material/dialog'; import { TranslateService } from '@ngx-translate/core'; import { @@ -58,24 +50,17 @@ import { ] }) -export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, OnInit, Validators { +export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor { private requiredValue: boolean; valuePrev = null as any; observeAttrTelemetryFormGroup: FormGroup; - observe = OBSERVE as string; - attribute = ATTR as string; - telemetry = TELEMETRY as string; get required(): boolean { return this.requiredValue; } - @Input() - disabled: boolean; - - // tslint:disable-next-line:adjacent-overload-signatures @Input() set required(value: boolean) { const newVal = coerceBooleanProperty(value); @@ -85,6 +70,9 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, } } + @Input() + disabled: boolean; + constructor(private store: Store, private fb: FormBuilder, private dialog: MatDialog, @@ -99,9 +87,6 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, }); } - ngOnInit(): void { - } - private propagateChange = (v: any) => { }; @@ -139,10 +124,6 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, } } - getDisabledState = (): boolean => { - return this.disabled; - } - writeValue(value: any): void { this.buildClientObjectsLwM2M(value.clientLwM2M); } @@ -153,8 +134,8 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, ); } - private createObjectsLwM2M = (objectsLwM2MJson: ObjectLwM2M []): FormArray => { - return this.fb.array(objectsLwM2MJson.map((objectLwM2M) => { + private createObjectsLwM2M = (objectsLwM2M: ObjectLwM2M[]): FormArray => { + return this.fb.array(objectsLwM2M.map((objectLwM2M) => { return this.fb.group({ id: objectLwM2M.id, name: objectLwM2M.name, @@ -165,20 +146,17 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, })); } - private createInstanceLwM2M = (instanceLwM2MJson: Instance []): FormArray => { - return this.fb.array(instanceLwM2MJson.map((instanceLwM2M) => { + private createInstanceLwM2M = (instancesLwM2M: Instance[]): FormArray => { + return this.fb.array(instancesLwM2M.map((instanceLwM2M) => { return this.fb.group({ id: instanceLwM2M.id, - [this.observe]: {value: false, disabled: this.disabled}, - [this.attribute]: {value: false, disabled: this.disabled}, - [this.telemetry]: {value: false, disabled: this.disabled}, resources: {value: instanceLwM2M.resources, disabled: this.disabled} }); })); } - clientLwM2MFormArray = (formGroup: FormGroup): FormArray => { - return formGroup.get('clientLwM2M') as FormArray; + get clientLwM2MFormArray(): FormArray { + return this.observeAttrTelemetryFormGroup.get('clientLwM2M') as FormArray; } instancesLwm2mFormArray = (objectLwM2M: AbstractControl): FormArray => { @@ -186,7 +164,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, } changeInstanceResourcesCheckBox = (value: boolean, instance: AbstractControl, type: string): void => { - const resources = instance.get('resources').value as ResourceLwM2M []; + const resources = instance.get('resources').value as ResourceLwM2M[]; resources.forEach(resource => resource[type] = value); instance.get('resources').patchValue(resources); this.propagateChange(this.observeAttrTelemetryFormGroup.value); @@ -202,26 +180,18 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, } getIndeterminate = (instance: AbstractControl, type: string): boolean => { - const resources = instance.get('resources').value as ResourceLwM2M []; - if (isNotNullOrUndefined(resources)) { - const isType = (element) => element[type] === true; - const checkedResource = resources.filter(isType); - if (checkedResource.length === 0) { return false; } - else if (checkedResource.length === resources.length) { - instance.patchValue({[type]: true}); - return false; - } else { return true; } + const resources = instance.get('resources').value as ResourceLwM2M[]; + if (isDefinedAndNotNull(resources)) { + const checkedResource = resources.filter(resource => resource[type]); + return checkedResource.length !== 0 && checkedResource.length !== resources.length; } return false; } getChecked = (instance: AbstractControl, type: string): boolean => { - const resources = instance.get('resources').value as ResourceLwM2M []; - if (isNotNullOrUndefined(resources)) { - return resources.some(resource => resource[type]); - } - return false; + const resources = instance.get('resources').value as ResourceLwM2M[]; + return isDefinedAndNotNull(resources) && resources.every(resource => resource[type]); } getExpended = (objectLwM2M: AbstractControl): boolean => { @@ -265,7 +235,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, } }).afterClosed().subscribe( (res: Lwm2mObjectAddInstancesData | undefined) => { - if (isNotNullOrUndefined(res)) { + if (isDefinedAndNotNull(res)) { this.updateInstancesIds(res); } } @@ -278,43 +248,41 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, data.instancesIds.forEach(value => { valueNew.add(value); }); - const oldInstances = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M []). - find(e => e.id === data.objectId).instances; + const oldInstances = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M []) + .find(e => e.id === data.objectId).instances; oldInstances.forEach(inst => { valueOld.add(inst.id); }); - if (JSON.stringify(Array.from(valueOld)) !== JSON.stringify(Array.from(valueNew))) { - const idsDel = this.diffBetweenSet(valueNew, this.deepCloneSet(valueOld)); - const idsAdd = this.diffBetweenSet(valueOld, this.deepCloneSet(valueNew)); + if (!isEqual(valueOld, valueNew)) { + const idsDel = this.diffBetweenSet(valueOld, valueNew); + const idsAdd = this.diffBetweenSet(valueNew, valueOld); if (idsAdd.size) { this.addInstancesNew(data.objectId, idsAdd); } if (idsDel.size) { - this.delInstances(data.objectId, idsDel); + this.deleteInstances(data.objectId, idsDel); } } } - private delInstances = (objectId: number, idsDel: Set): void => { - let isIdIndex = (element) => element.id === objectId; - const objectIndex = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M []).findIndex(isIdIndex); + private deleteInstances = (objectId: number, idsDel: Set): void => { + const objectIndex = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M[]) + .findIndex(element => element.id === objectId); idsDel.forEach(x => { - isIdIndex = (element) => element.value.id === x; const instancesFormArray = ((this.observeAttrTelemetryFormGroup.get('clientLwM2M') as FormArray) - .controls[objectIndex].get('instances') as FormArray); - const instanceIndex = instancesFormArray.controls.findIndex(isIdIndex); + .at(objectIndex).get('instances') as FormArray); + const instanceIndex = instancesFormArray.value.findIndex(element => element.id === x); instancesFormArray.removeAt(instanceIndex); }); } private addInstancesNew = (objectId: number, idsAdd: Set): void => { const instancesValue = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M []) - .find(e => e.id === objectId).instances; + .find(objectLwM2M => objectLwM2M.id === objectId).instances; const instancesFormArray = ((this.observeAttrTelemetryFormGroup.get('clientLwM2M') as FormArray).controls .find(e => e.value.id === objectId).get('instances') as FormArray) as FormArray; idsAdd.forEach(x => { const instanceNew = deepClone(instancesValue[0]) as Instance; - instanceNew.id = x; instanceNew.resources.forEach(r => { r.attribute = false; r.telemetry = false; @@ -322,28 +290,14 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, }); instancesFormArray.push(this.fb.group({ id: x, - [this.observe]: {value: false, disabled: this.disabled}, - [this.attribute]: {value: false, disabled: this.disabled}, - [this.telemetry]: {value: false, disabled: this.disabled}, resources: {value: instanceNew.resources, disabled: this.disabled} })); }); (instancesFormArray.controls as FormGroup[]).sort((a, b) => a.value.id - b.value.id); } - private deepCloneSet = (oldSet: Set): Set => { - const newSet = new Set(); - oldSet.forEach(p => { - newSet.add(p); - }); - return newSet; - } - private diffBetweenSet = (firstSet: Set, secondSet: Set): Set => { - firstSet.forEach(p => { - secondSet.delete(p); - }); - return secondSet; + return new Set([...Array.from(firstSet)].filter(x => !secondSet.has(x))); } private setInstancesIds = (instances: Instance []): Set => { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts index 1cbc1bdf03..681e2fa295 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts @@ -33,7 +33,6 @@ export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0; export const LEN_MAX_PUBLIC_KEY_RPK = 182; export const LEN_MAX_PUBLIC_KEY_X509 = 3000; export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; -export const CAMEL_CASE_REGEXP = /[-_&@.,*+!?^${}()|[\]\\]/g; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; @@ -82,12 +81,14 @@ interface BootstrapSecurityConfig { export interface ProfileConfigModels { bootstrap: BootstrapSecurityConfig; - observeAttr: { - observe: string [], - attribute: string [], - telemetry: string [], - keyName: [] - }; + observeAttr: ObservableAttributes; +} + +export interface ObservableAttributes { + observe: string[]; + attribute: string[]; + telemetry: string[]; + keyName: string[]; } export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html index 19f6b87471..8340857d8d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html @@ -35,7 +35,7 @@ required > -
profile-tab +
{{'gateway.security-type' | translate }} diff --git a/ui-ngx/src/app/shared/components/json-object-edit.component.ts b/ui-ngx/src/app/shared/components/json-object-edit.component.ts index c1b1e4ab06..61861bc6f6 100644 --- a/ui-ngx/src/app/shared/components/json-object-edit.component.ts +++ b/ui-ngx/src/app/shared/components/json-object-edit.component.ts @@ -61,8 +61,7 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va @Input() editorStyle: { [klass: string]: any }; - // tslint:disable-next-line:ban-types - @Input() sort: Function; + @Input() sort: (key: string, value: any) => any; private requiredValue: boolean; @@ -227,10 +226,9 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va try { if (this.modelValue) { - this.contentValue = JSON.stringify(this.modelValue, isUndefined(this.sort) ? null : - // tslint:disable-next-line:no-shadowed-variable - (key, value) => { - return this.sort(key, value); + this.contentValue = JSON.stringify(this.modelValue, isUndefined(this.sort) ? undefined : + (key, objectValue) => { + return this.sort(key, objectValue); }, 2); this.objectValid = true; } else { diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 0f07cadd36..4ff9fadbba 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -454,8 +454,6 @@ export interface DeviceCredentials extends BaseData { credentialsType: DeviceCredentialsType; credentialsId: string; credentialsValue: string; - credentialsLwKey: string; - credentialsLwValue: string; } export interface DeviceCredentialMQTTBasic { From 6cfc79215608b3c08e09f653e76c647eb4f455be Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 20 Jan 2021 09:12:05 +0200 Subject: [PATCH 28/41] Lwm2m: front: refactoring --- ...ile-transport-configuration.component.html | 35 +++++++++++-------- .../lwm2m/security-config.component.html | 34 ++++++++++-------- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 58eaff4fdc..7b49f8ffd6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -60,7 +60,8 @@ {{ 'device-profile.lwm2m.lifetime' | translate }} - + {{ 'device-profile.lwm2m.lifetime' | translate }} {{ 'device-profile.lwm2m.required' | translate }} @@ -102,13 +103,15 @@ class="tb-panel-title">{{ 'device-profile.lwm2m.bootstrap-server' | translate | uppercase }}
-
- - -
+ +
+ + +
+
@@ -118,13 +121,15 @@
{{ 'device-profile.lwm2m.lwm2m-server' | translate | uppercase }}
-
- - -
+ +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.html b/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.html index f5e52d41fc..e2b95cdb73 100644 --- a/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.html @@ -83,13 +83,15 @@ {{ 'device.lwm2m-security-config.client-key' | translate }} {{ translate.get('device.lwm2m-security-config.pattern_hex_dec', { - count: 0 }) | async }} + count: 0 + }) | async }} {{ 'device.lwm2m-security-config.client-key' | translate }} {{ translate.get('device.lwm2m-security-config.pattern_hex_dec', { - count: lenMaxKeyClient }) | async }} + count: lenMaxKeyClient + }) | async }}
@@ -112,12 +114,14 @@ class="tb-panel-title">{{ 'device.lwm2m-security-config.bootstrap-server' | translate | uppercase }}
-
- - -
+ +
+ + +
+
@@ -128,12 +132,14 @@ class="tb-panel-title">{{ 'device.lwm2m-security-config.lwm2m-server' | translate | uppercase }}
-
- - -
+ +
+ + +
+
From e6a3ee272581a599c45d50cc0546b0832f211138 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 20 Jan 2021 16:19:58 +0200 Subject: [PATCH 29/41] UI: Refactoring lwm2m --- .../lwm2m-device-config-server.component.ts | 27 ++-- ...ofile-transport-configuration.component.ts | 21 ++- ...m-object-add-instances-list.component.html | 81 +++++------ ...m-object-add-instances-list.component.scss | 22 --- ...m2m-object-add-instances-list.component.ts | 127 ++++-------------- .../lwm2m-object-add-instances.component.html | 8 +- .../lwm2m/lwm2m-object-list.component.html | 4 +- .../lwm2m/lwm2m-object-list.component.ts | 53 +++----- .../lwm2m-observe-attr-telemetry.component.ts | 49 +++---- .../device/lwm2m/profile-config.models.ts | 1 + 10 files changed, 129 insertions(+), 264 deletions(-) delete mode 100644 ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.scss diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts index 29589d2388..fc766683f2 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Inject, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Inject, Input } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { DEFAULT_CLIENT_HOLD_OFF_TIME, @@ -48,7 +48,7 @@ import { TranslateService } from '@ngx-translate/core'; ] }) -export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAccessor, Validators { +export class Lwm2mDeviceConfigServerComponent implements ControlValueAccessor { private requiredValue: boolean; @@ -82,7 +82,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc public fb: FormBuilder, private deviceProfileService: DeviceProfileService, @Inject(WINDOW) private window: Window) { - this.serverFormGroup = this.getServerGroup(); + this.serverFormGroup = this.initServerGroup(); this.serverFormGroup.get('securityMode').valueChanges.pipe( startWith(null), pairwise() @@ -101,9 +101,6 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc }); } - ngOnInit(): void { - } - private updateValueFields = (serverData: ServerSecurityConfig): void => { serverData.bootstrapServerIs = this.bootstrapServerIs; this.serverFormGroup.patchValue(serverData, {emitEvent: false}); @@ -187,17 +184,17 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc registerOnTouched(fn: any): void { } - private getServerGroup = (): FormGroup => { + private initServerGroup = (): FormGroup => { const port = this.bootstrapServerIs ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC; return this.fb.group({ - host: [this.window.location.hostname, this.required ? [Validators.required] : []], - port: [port, this.required ? [Validators.required] : []], - bootstrapServerIs: [this.bootstrapServerIs, []], - securityMode: [this.fb.control(SECURITY_CONFIG_MODE.NO_SEC), []], - serverPublicKey: ['', this.required ? [Validators.required] : []], - clientHoldOffTime: [DEFAULT_CLIENT_HOLD_OFF_TIME, this.required ? [Validators.required] : []], - serverId: [DEFAULT_ID_SERVER, this.required ? [Validators.required] : []], - bootstrapServerAccountTimeout: ['', this.required ? [Validators.required] : []], + host: [this.window.location.hostname, this.required ? Validators.required : ''], + port: [port, this.required ? Validators.required : ''], + bootstrapServerIs: [this.bootstrapServerIs, ''], + securityMode: [this.fb.control(SECURITY_CONFIG_MODE.NO_SEC)], + serverPublicKey: ['', this.required ? Validators.required : ''], + clientHoldOffTime: [DEFAULT_CLIENT_HOLD_OFF_TIME, this.required ? Validators.required : ''], + serverId: [DEFAULT_ID_SERVER, this.required ? Validators.required : ''], + bootstrapServerAccountTimeout: ['', this.required ? Validators.required : ''], }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index ea7da72966..feb248d5cc 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -15,7 +15,7 @@ /// import { DeviceProfileTransportConfiguration, DeviceTransportType } from '@shared/models/device.models'; -import { Component, forwardRef, Inject, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Inject, Input } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; @@ -54,11 +54,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private disabled = false; lwm2mDeviceProfileTransportConfFormGroup: FormGroup; - observeAttr = OBSERVE_ATTR as string; - observe = OBSERVE as string; - attribute = ATTR as string; - telemetry = TELEMETRY as string; - keyName = KEY_NAME as string; + observeAttr = OBSERVE_ATTR; + observe = OBSERVE; + attribute = ATTR; + telemetry = TELEMETRY; + keyName = KEY_NAME; bootstrapServers: string; bootstrapServer: string; lwm2mServer: string; @@ -73,7 +73,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.requiredValue = coerceBooleanProperty(value); } - private propagateChange = (v: any) => { }; + private propagateChange = (v: any) => { }; constructor(private store: Store, private fb: FormBuilder, @@ -81,7 +81,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @Inject(WINDOW) private window: Window) { this.lwm2mDeviceProfileTransportConfFormGroup = this.fb.group({ objectIds: [{}, Validators.required], - observeAttrTelemetry: [{clientLwM2M: [] as ObjectLwM2M[]}, Validators.required], + observeAttrTelemetry: [{clientLwM2M: []}, Validators.required], shortId: [null, Validators.required], lifetime: [null, Validators.required], defaultMinPeriod: [null, Validators.required], @@ -116,12 +116,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } writeValue(value: any | null): void { - value = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value; + this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value; this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ - configurationJson: value + configurationJson: this.configurationValue }, {emitEvent: false}); - this.configurationValue = this.lwm2mDeviceProfileTransportConfFormGroup.getRawValue().configurationJson; this.initWriteValue(); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html index cddc6f1ef5..5a03f3f54d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html @@ -15,43 +15,46 @@ limitations under the License. --> - - {{ 'device-profile.lwm2m.instances-list' | translate }} - - - {{instanceIdInputFor}} - close - - - {{ 'device-profile.lwm2m.instances-input' | translate }} - - - {{ translate.get('device-profile.lwm2m.valid-id-instance-no-min', { - instance: instanceIdInput.value.toString(), min: instanceIdValueMin.toString() - }) | async }} - - - {{ translate.get('device-profile.lwm2m.valid-id-instance-no-max', { - instance: instanceIdInput.value.toString(), max: instanceIdValueMax.toString() - }) | async }} - - - {{ translate.get('device-profile.lwm2m.instances-input-holder', { - instance: instanceIdInput.value.toString(), max: instanceIdValueMax.toString() - }) | async }} - - - - +
+ + {{ 'device-profile.lwm2m.instances-list' | translate }} + + + {{instanceId}} + close + + + + + {{ 'device-profile.lwm2m.instances-input' | translate }} + + + + {{ translate.get('device-profile.lwm2m.valid-id-instance-no-min', { + instance: instanceId, min: instanceIdValueMin + }) | async }} + + + {{ translate.get('device-profile.lwm2m.valid-id-instance-no-max', { + instance: instanceId, max: instanceIdValueMax + }) | async }} + + + {{ translate.get('device-profile.lwm2m.instances-input-holder', { + instance: instanceId, max: instanceIdValueMax + }) | async }} + + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.scss deleted file mode 100644 index 45e601db09..0000000000 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.scss +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright © 2016-2020 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -:host ::ng-deep { - mat-form-field.lwm2m-instances-list { - & > .mat-form-field-wrapper > .mat-form-field-underline { - display: none; - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts index 6e09a6d254..1375b49112 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts @@ -14,35 +14,18 @@ /// limitations under the License. /// -import { - Component, - forwardRef, - Input, - OnInit, - ViewChild, - ElementRef, -} from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - FormGroup, - NG_VALUE_ACCESSOR, Validators -} from '@angular/forms'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { Component, forwardRef, } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { MatChipList } from '@angular/material/chips'; -import { - INSTANCES_ID_VALUE_MAX, - INSTANCES_ID_VALUE_MIN -} from './profile-config.models'; +import { INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER } from './profile-config.models'; import { TranslateService } from '@ngx-translate/core'; import { DeviceProfileService } from '@core/http/device-profile.service'; @Component({ selector: 'tb-profile-lwm2m-object-add-instances-list', templateUrl: './lwm2m-object-add-instances-list.component.html', - styleUrls: ['./lwm2m-object-add-instances-list.component.scss'], + styleUrls: [], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Lwm2mObjectAddInstancesListComponent), @@ -51,31 +34,13 @@ import { DeviceProfileService } from '@core/http/device-profile.service'; }) export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccessor { - private requiredValue: boolean; - private disabled = false as boolean; - private dirty = false as boolean; + private disabled = false; + private dirty = false; - lwm2mObjectListFormGroup: FormGroup; - instancesIdsList: Set | null; - filteredObjectsList: Array; - instanceIdValueMin = INSTANCES_ID_VALUE_MIN as number; - instanceIdValueMax = INSTANCES_ID_VALUE_MAX as number; - - get required(): boolean { - return this.requiredValue; - } - - @Input() - set required(value: boolean) { - const newVal = coerceBooleanProperty(value); - if (this.requiredValue !== newVal) { - this.requiredValue = newVal; - this.updateValidators(); - } - } - - @ViewChild('instanceIdInput') instanceIdInput: ElementRef; - @ViewChild('chipList', {static: true}) chipList: MatChipList; + instancesListFormGroup: FormGroup; + instancesId = new Set(); + instanceIdValueMin = INSTANCES_ID_VALUE_MIN; + instanceIdValueMax = INSTANCES_ID_VALUE_MAX; private propagateChange = (v: any) => { }; @@ -83,19 +48,14 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso public translate: TranslateService, private deviceProfileService: DeviceProfileService, private fb: FormBuilder) { - this.lwm2mObjectListFormGroup = this.fb.group({ - instancesIdsList: [null], - instanceIdInput: [null] + this.instancesListFormGroup = this.fb.group({ + instanceIdInput: [null, [ + Validators.min(this.instanceIdValueMin), + Validators.max(this.instanceIdValueMax), + Validators.pattern(KEY_REGEXP_NUMBER)]] }); } - private updateValidators = (): void => { - this.lwm2mObjectListFormGroup.get('instanceIdInput').setValidators([ - Validators.min(this.instanceIdValueMin), - Validators.max(this.instanceIdValueMax)]); - this.lwm2mObjectListFormGroup.get('instanceIdInput').updateValueAndValidity(); - } - registerOnChange(fn: any): void { this.propagateChange = fn; } @@ -106,68 +66,35 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (isDisabled) { - this.lwm2mObjectListFormGroup.disable({emitEvent: false}); + this.instancesListFormGroup.disable({emitEvent: false}); } else { - this.lwm2mObjectListFormGroup.enable({emitEvent: false}); + this.instancesListFormGroup.enable({emitEvent: false}); } } writeValue(value: Set): void { - this.instancesIdsList = new Set(); if (value && value.size) { - value.forEach(item => this.instancesIdsList.add(item)); - this.lwm2mObjectListFormGroup.get('instancesIdsList').setValue(this.instancesIdsList); + this.instancesId = value; } - this.updateValidators(); this.dirty = false; } - add = (value: number): void => { - if (!isNaN(value) && this.lwm2mObjectListFormGroup.get('instanceIdInput').valid) { - this.instancesIdsList.add(value); - this.lwm2mObjectListFormGroup.get('instancesIdsList').setValue(this.instancesIdsList); - this.propagateChange(this.instancesIdsList); + add = (): void => { + if (this.instancesListFormGroup.get('instanceIdInput').valid) { + this.instancesId.add(this.instanceId); + this.instancesListFormGroup.get('instanceIdInput').setValue(null); + this.propagateChange(this.instancesId); this.dirty = true; } - this.clear(); } remove = (object: number): void => { - this.instancesIdsList.delete(object); - this.lwm2mObjectListFormGroup.get('instancesIdsList').setValue(this.instancesIdsList); - this.propagateChange(this.instancesIdsList); + this.instancesId.delete(object); + this.propagateChange(this.instancesId); this.dirty = true; - this.clear(); - } - // - // displayFn(object?: number): number | undefined { - // return object ? object : undefined; - // } - - private clear = (): void => { - this.lwm2mObjectListFormGroup.get('instanceIdInput').patchValue(null, {emitEvent: true}); - this.instanceIdInput.nativeElement.value = ''; - setTimeout(() => { - this.instanceIdInput.nativeElement.blur(); - this.instanceIdInput.nativeElement.focus(); - }, 0); } - onkeydown = (e: KeyboardEvent): boolean => { - if (e.keyCode === 189 || e.keyCode === 187 || e.keyCode === 109 || e.keyCode === 107) { - return false; - } else if (e.keyCode === 8) { - if (this.lwm2mObjectListFormGroup.get('instanceIdInput').value == null) { - this.clear(); - } - this.instanceIdInput.nativeElement.focus(); - } - } - - onFocus = (): void => { - if (this.dirty) { - this.lwm2mObjectListFormGroup.get('instanceIdInput').updateValueAndValidity({onlySelf: true, emitEvent: true}); - this.dirty = false; - } + get instanceId(): number { + return this.instancesListFormGroup.get('instanceIdInput').value; } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html index 6c7706071f..303e38f180 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html @@ -30,11 +30,9 @@
-
- - -
+ +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html index 7ad661752b..260f0016f5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html @@ -16,7 +16,7 @@ --> - + - + {{ 'device-profile.lwm2m.object-list-empty' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index f5a84638ed..d0f6c3cc47 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -19,8 +19,6 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Valida import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { MatChipList } from '@angular/material/chips'; -import { MatAutocomplete } from '@angular/material/autocomplete'; import { Observable } from 'rxjs'; import { filter, map, mergeMap, share, tap } from 'rxjs/operators'; import { ObjectLwM2M } from './profile-config.models'; @@ -43,9 +41,9 @@ import { Direction } from '@shared/models/page/sort-order'; export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, Validators { private requiredValue: boolean; - private dirty = false as boolean; + private dirty = false; - lwm2mObjectListFormGroup: FormGroup; + lwm2mListFormGroup: FormGroup; modelValue: Array | null; objectsList: Array = []; filteredObjectsList: Observable>; @@ -72,8 +70,6 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V removeList = new EventEmitter(); @ViewChild('objectInput') objectInput: ElementRef; - @ViewChild('objectAutocomplete') matAutocomplete: MatAutocomplete; - @ViewChild('chipList', {static: true}) chipList: MatChipList; private propagateChange = (v: any) => { }; @@ -81,15 +77,15 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V public translate: TranslateService, private deviceProfileService: DeviceProfileService, private fb: FormBuilder) { - this.lwm2mObjectListFormGroup = this.fb.group({ + this.lwm2mListFormGroup = this.fb.group({ objectsList: [this.objectsList], objectLwm2m: [null, this.required ? [Validators.required] : []] }); } private updateValidators = (): void => { - this.lwm2mObjectListFormGroup.get('objectLwm2m').setValidators(this.required ? [Validators.required] : []); - this.lwm2mObjectListFormGroup.get('objectLwm2m').updateValueAndValidity(); + this.lwm2mListFormGroup.get('objectLwm2m').setValidators(this.required ? [Validators.required] : []); + this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity(); } registerOnChange(fn: any): void { @@ -100,7 +96,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } ngOnInit() { - this.filteredObjectsList = this.lwm2mObjectListFormGroup.get('objectLwm2m').valueChanges + this.filteredObjectsList = this.lwm2mListFormGroup.get('objectLwm2m').valueChanges .pipe( tap((value) => { if (value && typeof value !== 'string') { @@ -110,21 +106,17 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } }), filter((value) => typeof value === 'string'), - map((value) => value ? (typeof value === 'string' ? value : value.name) : ''), mergeMap(name => this.fetchListObjects(name)), share() ); } - ngAfterViewInit = (): void => { - } - setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (isDisabled) { - this.lwm2mObjectListFormGroup.disable({emitEvent: false}); + this.lwm2mListFormGroup.disable({emitEvent: false}); } else { - this.lwm2mObjectListFormGroup.enable({emitEvent: false}); + this.lwm2mListFormGroup.enable({emitEvent: false}); } } @@ -134,35 +126,22 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V if (value.hasOwnProperty(objectIds) && value[objectIds] != null && value[objectIds].length > 0) { this.modelValue = [...value[objectIds]]; this.objectsList = value.objectsList; - this.lwm2mObjectListFormGroup.get('objectsList').setValue(this.objectsList); } else { this.objectsList = []; - this.lwm2mObjectListFormGroup.get('objectsList').setValue(this.objectsList); this.modelValue = null; } + this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); this.dirty = true; } - // reset() { - // this.objectsList = []; - // this.lwm2mObjectListFormGroup.get('objectsList').setValue(this.objectsList); - // this.modelValue = null; - // if (this.objectInput) { - // this.objectInput.nativeElement.value = ''; - // } - // this.lwm2mObjectListFormGroup.get('objectLwm2m').patchValue('', {emitEvent: false}); - // this.propagateChange(this.modelValue); - // this.dirty = true; - // } - - private add = (object: ObjectLwM2M): void => { + private add(object: ObjectLwM2M): void { if (!this.modelValue || this.modelValue.indexOf(object.id) === -1) { if (!this.modelValue) { this.modelValue = []; } this.modelValue.push(object.id); this.objectsList.push(object); - this.lwm2mObjectListFormGroup.get('objectsList').setValue(this.objectsList); + this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); this.addList.next(this.objectsList); } this.propagateChange(this.modelValue); @@ -173,7 +152,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V let index = this.objectsList.indexOf(object); if (index >= 0) { this.objectsList.splice(index, 1); - this.lwm2mObjectListFormGroup.get('objectsList').setValue(this.objectsList); + this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); index = this.modelValue.indexOf(object.id); this.modelValue.splice(index, 1); this.removeList.next(object); @@ -189,9 +168,11 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V return object ? object.name : undefined; } + // @TODO: add support page size + // @TODO: filter for id + name private fetchListObjects = (searchText?: string): Observable> => { this.searchText = searchText; - const pageLink = new PageLink(10, 0, searchText, { + const pageLink = new PageLink(50, 0, searchText, { property: 'name', direction: Direction.ASC }); @@ -202,14 +183,14 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V onFocus = (): void => { if (this.dirty) { - this.lwm2mObjectListFormGroup.get('objectLwm2m').updateValueAndValidity({onlySelf: true, emitEvent: true}); + this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity({onlySelf: true, emitEvent: true}); this.dirty = false; } } private clear = (value: string = ''): void => { this.objectInput.nativeElement.value = value; - this.lwm2mObjectListFormGroup.get('objectLwm2m').patchValue(value, {emitEvent: true}); + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value, {emitEvent: true}); setTimeout(() => { this.objectInput.nativeElement.blur(); this.objectInput.nativeElement.focus(); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index 11ffdf0fe5..f6a600ae99 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -171,7 +171,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor } private updateValidators = (): void => { - this.observeAttrTelemetryFormGroup.get('clientLwM2M').setValidators(this.required ? [Validators.required] : []); + this.observeAttrTelemetryFormGroup.get('clientLwM2M').setValidators(this.required ? Validators.required : []); this.observeAttrTelemetryFormGroup.get('clientLwM2M').updateValueAndValidity(); } @@ -188,7 +188,6 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor return false; } - getChecked = (instance: AbstractControl, type: string): boolean => { const resources = instance.get('resources').value as ResourceLwM2M[]; return isDefinedAndNotNull(resources) && resources.every(resource => resource[type]); @@ -221,15 +220,11 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor $event.stopPropagation(); $event.preventDefault(); } - const instancesIds = new Set(); - object.instances.forEach(inst => { - instancesIds.add(inst.id); - }); this.dialog.open(Lwm2mObjectAddInstancesComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - instancesIds: this.setInstancesIds(object.instances), + instancesIds: this.instancesToSetId(object.instances), objectName: object.name, objectId: object.id } @@ -243,21 +238,15 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor } private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => { - const valueNew = new Set(); - const valueOld = new Set(); - data.instancesIds.forEach(value => { - valueNew.add(value); - }); - const oldInstances = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M []) - .find(e => e.id === data.objectId).instances; - oldInstances.forEach(inst => { - valueOld.add(inst.id); - }); - if (!isEqual(valueOld, valueNew)) { - const idsDel = this.diffBetweenSet(valueOld, valueNew); - const idsAdd = this.diffBetweenSet(valueNew, valueOld); + const instances = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M[]) + .find(objectLwM2M => objectLwM2M.id === data.objectId).instances; + const valueOld = this.instancesToSetId(instances); + + if (!isEqual(valueOld, data.instancesIds)) { + const idsDel = this.diffBetweenSet(valueOld, data.instancesIds); + const idsAdd = this.diffBetweenSet(data.instancesIds, valueOld); if (idsAdd.size) { - this.addInstancesNew(data.objectId, idsAdd); + this.addInstancesNew(data.objectId, idsAdd, instances.find(instance => instance.id === 0)); } if (idsDel.size) { this.deleteInstances(data.objectId, idsDel); @@ -276,13 +265,11 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor }); } - private addInstancesNew = (objectId: number, idsAdd: Set): void => { - const instancesValue = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M []) - .find(objectLwM2M => objectLwM2M.id === objectId).instances; + private addInstancesNew = (objectId: number, idsAdd: Set, instance: Instance): void => { const instancesFormArray = ((this.observeAttrTelemetryFormGroup.get('clientLwM2M') as FormArray).controls .find(e => e.value.id === objectId).get('instances') as FormArray) as FormArray; idsAdd.forEach(x => { - const instanceNew = deepClone(instancesValue[0]) as Instance; + const instanceNew = deepClone(instance); instanceNew.resources.forEach(r => { r.attribute = false; r.telemetry = false; @@ -296,17 +283,11 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor (instancesFormArray.controls as FormGroup[]).sort((a, b) => a.value.id - b.value.id); } - private diffBetweenSet = (firstSet: Set, secondSet: Set): Set => { + private diffBetweenSet(firstSet: Set, secondSet: Set): Set { return new Set([...Array.from(firstSet)].filter(x => !secondSet.has(x))); } - private setInstancesIds = (instances: Instance []): Set => { - const instancesIds = new Set(); - if (instances && instances.length) { - instances.forEach(inst => { - instancesIds.add(inst.id); - }); - } - return instancesIds; + private instancesToSetId = (instances: Instance[]): Set => { + return new Set(instances.map(x => x.id)); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts index 681e2fa295..1849fd3279 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts @@ -33,6 +33,7 @@ export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0; export const LEN_MAX_PUBLIC_KEY_RPK = 182; export const LEN_MAX_PUBLIC_KEY_X509 = 3000; export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; +export const KEY_REGEXP_NUMBER = /^\d*$/; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; From e6d4e91e25a529afedd16b5b102684f318c75f8d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 20 Jan 2021 19:17:13 +0200 Subject: [PATCH 30/41] Lwm2m: front: refactoring n number validators --- .../server/LwM2MTransportServiceImpl.java | 59 ++++++++++--------- .../lwm2m/server/client/LwM2MClient.java | 2 +- ...m-object-add-instances-list.component.html | 16 +++-- ...m2m-object-add-instances-list.component.ts | 4 +- .../device/lwm2m/profile-config.models.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 1 + 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java index 072c7bc402..3255d24760 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java @@ -20,6 +20,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mObject; @@ -153,7 +154,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { lwM2MClient.setLwM2MTransportServiceImpl(this); lwM2MClient.setSessionUuid(UUID.randomUUID()); this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration); -// this.setLwM2MClient(lwServer, registration, lwM2MClient); +// this.setLwM2mFromClientValue(lwServer, registration, lwM2MClient); SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); if (sessionInfo != null) { lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); @@ -164,6 +165,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); + this.putDelayedUpdateResourcesThingsboard(lwM2MClient); } else { log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } @@ -198,7 +200,6 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { }); } - /** * @param registration - Registration LwM2M Client * @param observations - All paths observations before unReg @@ -279,33 +280,33 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { * @param registration - Registration LwM2M Client * @param lwM2MClient - object with All parameters off client */ - private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) { -// Arrays.stream(registration.getObjectLinks()).forEach(url -> { -// LwM2mPath pathIds = new LwM2mPath(url.getUrl()); -// if (pathIds.isObjectInstance() && !pathIds.isResource()) { -// // #1 -// lwM2MClient.getPendingRequests().add(url.getUrl()); -// // #2 -// lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), -// lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); -// } -// }); - -// // #1 -// for (Link url : registration.getObjectLinks()) { -// LwM2mPath pathIds = new LwM2mPath(url.getUrl()); -// if (pathIds.isObjectInstance() && !pathIds.isResource()) { -// lwM2MClient.getPendingRequests().add(url.getUrl()); -// } -// } -// // #2 -// for (Link url : registration.getObjectLinks()) { -// LwM2mPath pathIds = new LwM2mPath(url.getUrl()); -// if (pathIds.isObjectInstance() && !pathIds.isResource()) { -// lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), -// lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); -// } -// } + private void setLwM2mFromClientValue(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) { + Arrays.stream(registration.getObjectLinks()).forEach(url -> { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource()) { + // #1 + lwM2MClient.getPendingRequests().add(url.getUrl()); + // #2 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); + } + }); + + // #1 + for (Link url : registration.getObjectLinks()) { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource()) { + lwM2MClient.getPendingRequests().add(url.getUrl()); + } + } + // #2 + for (Link url : registration.getObjectLinks()) { + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); + if (pathIds.isObjectInstance() && !pathIds.isResource()) { + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false); + } + } // #1 Arrays.stream(registration.getObjectLinks()).forEach(url -> { 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 fdb2055500..f20853299d 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 @@ -93,7 +93,7 @@ public class LwM2MClient implements Cloneable { this.pendingRequests.remove(path); if (this.pendingRequests.size() == 0) { this.initValue(); - this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this); +// this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html index 5a03f3f54d..34d77c299b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html @@ -31,9 +31,7 @@ {{ 'device-profile.lwm2m.instances-input' | translate }} - - {{ translate.get('device-profile.lwm2m.valid-id-instance-no-min', { - instance: instanceId, min: instanceIdValueMin + {{ translate.get('device-profile.lwm2m.valid-id-instance', { + count: 2, instance: instanceId, min: instanceIdValueMin }) | async }} - {{ translate.get('device-profile.lwm2m.valid-id-instance-no-max', { - instance: instanceId, max: instanceIdValueMax + {{ translate.get('device-profile.lwm2m.valid-id-instance', { + count: 1, instance: instanceId, max: instanceIdValueMax }) | async }} - {{ translate.get('device-profile.lwm2m.instances-input-holder', { - instance: instanceId, max: instanceIdValueMax + {{ translate.get('device-profile.lwm2m.valid-id-instance', { + count: 0, instance: instanceId, max: instanceIdValueMax }) | async }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts index 1375b49112..1921db396d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts @@ -80,8 +80,8 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso } add = (): void => { - if (this.instancesListFormGroup.get('instanceIdInput').valid) { - this.instancesId.add(this.instanceId); + if (this.instancesListFormGroup.get('instanceIdInput').valid && Number.isFinite(Number(this.instanceId))) { + this.instancesId.add(Number(this.instanceId)); this.instancesListFormGroup.get('instanceIdInput').setValue(null); this.propagateChange(this.instancesId); this.dirty = true; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts index 1849fd3279..11c16ac802 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts @@ -33,7 +33,7 @@ export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0; export const LEN_MAX_PUBLIC_KEY_RPK = 182; export const LEN_MAX_PUBLIC_KEY_X509 = 3000; export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; -export const KEY_REGEXP_NUMBER = /^\d*$/; +export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 1ff1e0a6e0..b26fb40b1e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1102,6 +1102,7 @@ "no-objects-matching": "No objects matching '{{object}}' were found.", "valid-id-instance-no-min": "Instance number '{{instance}}' no validated. Min value='{{min}}'", "valid-id-instance-no-max": "Instance number '{{instance}}' no validated. Max value='{{max}}'", + "valid-id-instance": "Instance number '{{instance}}' no validated. { count, plural, 1 {Max value='{{max}}'} 2 {Min value='{{min}}'} other {Must be only number} }", "model-tab": "LWM2M Model", "add-instances-tip": "Add new instances", "instances-list": "Instances list", From 573837c4b126e19428a67933dbc6b2d9bbe10240 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 21 Jan 2021 17:38:45 +0200 Subject: [PATCH 31/41] Lwm2m: front: refactoring sort json and add instances --- .../LWM2M_Connectivity_Monitoring-v1_0_2.xml | 208 ------------------ .../LWM2M_Connectivity_Monitoring-v1_0_2.xml | 167 -------------- ...ofile-transport-configuration.component.ts | 20 +- .../lwm2m/lwm2m-object-list.component.html | 2 +- .../lwm2m-observe-attr-telemetry.component.ts | 62 +++--- 5 files changed, 48 insertions(+), 411 deletions(-) delete mode 100644 common/transport/lwm2m/src/main/resources/models/LWM2M_Connectivity_Monitoring-v1_0_2.xml delete mode 100644 transport/lwm2m/src/main/data/models/LWM2M_Connectivity_Monitoring-v1_0_2.xml diff --git a/common/transport/lwm2m/src/main/resources/models/LWM2M_Connectivity_Monitoring-v1_0_2.xml b/common/transport/lwm2m/src/main/resources/models/LWM2M_Connectivity_Monitoring-v1_0_2.xml deleted file mode 100644 index 28957f5a5d..0000000000 --- a/common/transport/lwm2m/src/main/resources/models/LWM2M_Connectivity_Monitoring-v1_0_2.xml +++ /dev/null @@ -1,208 +0,0 @@ - - - - - Connectivity Monitoring - - This LwM2M Object enables monitoring of parameters related to network connectivity. In this general connectivity Object, the Resources are limited to the most general cases common to most network bearers. It is recommended to read the description, which refers to relevant standard development organizations (e.g. 3GPP, IEEE). The goal of the Connectivity Monitoring Object is to carry information reflecting the more up to date values of the current connection for monitoring purposes. Resources such as Link Quality, Radio Signal Strength, Cell ID are retrieved during connected mode at least for cellular networks. - - 4 - urn:oma:lwm2m:oma:4 - 1.0 - 1.0 - Single - Optional - - - Network Bearer - R - Single - Mandatory - Integer - 0-50 - - - Indicates the network bearer used for the current LwM2M communication session from the network bearer list below. The number range is split into three categories: 0 - 20 are Cellular Bearers 21 - 40 are Wireless Bearers 41 - 50 are Wireline Bearers More specifically: 0: GSM cellular network 1: TD-SCDMA cellular network 2: WCDMA cellular network 3: CDMA2000 cellular network 4: WiMAX cellular network 5: LTE-TDD cellular network 6: LTE-FDD cellular network 7: NB-IoT 8 - 20: Reserved for other types of cellular network 21: WLAN network 22: Bluetooth network 23: IEEE 802.15.4 network 24 - 40: Reserved for other types of local wireless network 41: Ethernet 42: DSL 43: PLC 44 - 50: reserved for other types of wireline networks. - - - - Available Network Bearer - R - Multiple - Mandatory - Integer - 0-50 - - - Indicates a list of current available network bearer. Each Resource Instance has a value from the network bearer list. - - - - Radio Signal Strength - R - Single - Mandatory - Integer - - dBm - - Indicates the average value of the received signal strength indication used in the current network bearer (as indicated by Resource 0 of this Object). For the following network bearers the signal strength parameters indicated below are represented by this resource: GSM: RSSI UMTS: RSCP LTE: RSRP NB-IoT: NRSRP For more details on Network Measurement Report, refer to the appropriate Cellular or Wireless Network standards, (e.g. for LTE Cellular Network refer to 3GPP TS 36.133 specification). - - - - Link Quality - R - Single - Optional - Integer - - - - This contains received link quality e.g. LQI for IEEE 802.15.4 (range 0...255), RxQual Downlink for GSM (range 0...7, refer to [3GPP 44.018] for more details on Network Measurement Report encoding), RSRQ for LTE, (refer to [3GPP 36.214]), NRSRQ for NB-IoT (refer to [3GPP 36.214]). - - - - IP Addresses - R - Multiple - Mandatory - String - - - - The IP addresses assigned to the connectivity interface. (e.g. IPv4, IPv6, etc.) - - - - Router IP Addresses - R - Multiple - Optional - String - - - - The IP address of the next-hop IP router, on each of the interfaces specified in resource 4 (IP Addresses). Note: This IP Address doesn’t indicate the Server IP address. - - - - Link Utilization - R - Single - Optional - Integer - 0-100 - % - - The percentage indicating the average utilization of the link to the next-hop IP router. - - - - APN - R - Multiple - Optional - String - - - - Access Point Name in case Network Bearer Resource is a Cellular Network. - - - - Cell ID - R - Single - Optional - Integer - - - - Serving Cell ID in case Network Bearer Resource is a Cellular Network. As specified in TS [3GPP 23.003] and in [3GPP. 24.008]. Range (0...65535) in GSM/EDGE UTRAN Cell ID has a length of 28 bits. Cell Identity in WCDMA/TD-SCDMA. Range: (0...268435455). LTE Cell ID has a length of 28 bits. Parameter definitions in [3GPP 25.331]. - - - - SMNC - R - Single - Optional - Integer - 0-999 - % - - Serving Mobile Network Code. This is applicable when the Network Bearer Resource value is referring to a cellular network. As specified in TS [3GPP 23.003]. - - - - SMCC - R - Single - Optional - Integer - 0-999 - - - Serving Mobile Country Code. This is applicable when the Network Bearer Resource value is referring to a cellular network. As specified in TS [3GPP 23.003]. - - - - - - diff --git a/transport/lwm2m/src/main/data/models/LWM2M_Connectivity_Monitoring-v1_0_2.xml b/transport/lwm2m/src/main/data/models/LWM2M_Connectivity_Monitoring-v1_0_2.xml deleted file mode 100644 index 48e942983d..0000000000 --- a/transport/lwm2m/src/main/data/models/LWM2M_Connectivity_Monitoring-v1_0_2.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - Connectivity Monitoring - - This LwM2M Object enables monitoring of parameters related to network connectivity. In this general connectivity Object, the Resources are limited to the most general cases common to most network bearers. It is recommended to read the description, which refers to relevant standard development organizations (e.g. 3GPP, IEEE). The goal of the Connectivity Monitoring Object is to carry information reflecting the more up to date values of the current connection for monitoring purposes. Resources such as Link Quality, Radio Signal Strength, Cell ID are retrieved during connected mode at least for cellular networks. - - 4 - urn:oma:lwm2m:oma:4 - 1.0 - 1.0 - Single - Optional - - - Network Bearer - R - Single - Mandatory - Integer - 0-50 - - - Indicates the network bearer used for the current LwM2M communication session from the network bearer list below. The number range is split into three categories: 0 - 20 are Cellular Bearers 21 - 40 are Wireless Bearers 41 - 50 are Wireline Bearers More specifically: 0: GSM cellular network 1: TD-SCDMA cellular network 2: WCDMA cellular network 3: CDMA2000 cellular network 4: WiMAX cellular network 5: LTE-TDD cellular network 6: LTE-FDD cellular network 7: NB-IoT 8 - 20: Reserved for other types of cellular network 21: WLAN network 22: Bluetooth network 23: IEEE 802.15.4 network 24 - 40: Reserved for other types of local wireless network 41: Ethernet 42: DSL 43: PLC 44 - 50: reserved for other types of wireline networks. - - - - Available Network Bearer - R - Multiple - Mandatory - Integer - 0-50 - - - Indicates a list of current available network bearer. Each Resource Instance has a value from the network bearer list. - - - - Radio Signal Strength - R - Single - Mandatory - Integer - - dBm - - Indicates the average value of the received signal strength indication used in the current network bearer (as indicated by Resource 0 of this Object). For the following network bearers the signal strength parameters indicated below are represented by this resource: GSM: RSSI UMTS: RSCP LTE: RSRP NB-IoT: NRSRP For more details on Network Measurement Report, refer to the appropriate Cellular or Wireless Network standards, (e.g. for LTE Cellular Network refer to 3GPP TS 36.133 specification). - - - - Link Quality - R - Single - Optional - Integer - - - - This contains received link quality e.g. LQI for IEEE 802.15.4 (range 0...255), RxQual Downlink for GSM (range 0...7, refer to [3GPP 44.018] for more details on Network Measurement Report encoding), RSRQ for LTE, (refer to [3GPP 36.214]), NRSRQ for NB-IoT (refer to [3GPP 36.214]). - - - - IP Addresses - R - Multiple - Mandatory - String - - - - The IP addresses assigned to the connectivity interface. (e.g. IPv4, IPv6, etc.) - - - - Router IP Addresses - R - Multiple - Optional - String - - - - The IP address of the next-hop IP router, on each of the interfaces specified in resource 4 (IP Addresses). Note: This IP Address doesn’t indicate the Server IP address. - - - - Link Utilization - R - Single - Optional - Integer - 0-100 - % - - The percentage indicating the average utilization of the link to the next-hop IP router. - - - - APN - R - Multiple - Optional - String - - - - Access Point Name in case Network Bearer Resource is a Cellular Network. - - - - Cell ID - R - Single - Optional - Integer - - - - Serving Cell ID in case Network Bearer Resource is a Cellular Network. As specified in TS [3GPP 23.003] and in [3GPP. 24.008]. Range (0...65535) in GSM/EDGE UTRAN Cell ID has a length of 28 bits. Cell Identity in WCDMA/TD-SCDMA. Range: (0...268435455). LTE Cell ID has a length of 28 bits. Parameter definitions in [3GPP 25.331]. - - - - SMNC - R - Single - Optional - Integer - 0-999 - % - - Serving Mobile Network Code. This is applicable when the Network Bearer Resource value is referring to a cellular network. As specified in TS [3GPP 23.003]. - - - - SMCC - R - Single - Optional - Integer - 0-999 - - - Serving Mobile Country Code. This is applicable when the Network Bearer Resource value is referring to a cellular network. As specified in TS [3GPP 23.003]. - - - - - - diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index feb248d5cc..5ce7620e32 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -73,7 +73,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.requiredValue = coerceBooleanProperty(value); } - private propagateChange = (v: any) => { }; + private propagateChange = (v: any) => { + }; constructor(private store: Store, private fb: FormBuilder, @@ -403,19 +404,26 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro sortObjectKeyPathJson = (key: string, value: object): object => { if (key === 'keyName') { - return Object.keys(value).sort((a, b) => { - const aLC = Array.from(a.substring(1).split('/'), Number); - const bLC = Array.from(b.substring(1).split('/'), Number); - return aLC[0] === bLC[0] ? aLC[1] - bLC[1] : aLC[0] - bLC[0]; - }).reduce((obj, keySort) => { + return Object.keys(value).sort(this.sortPath).reduce((obj, keySort) => { obj[keySort] = value[keySort]; return obj; }, {}); + } else if (key === 'observe' || key === 'attribute' || key === 'telemetry') { + return Object.values(value).sort(this.sortPath).reduce((arr, arrValue) => { + arr.push(arrValue); + return arr; + }, []); } else { return value; } } + private sortPath = (a, b): number => { + const aLC = Array.from(a.substring(1).split('/'), Number); + const bLC = Array.from(b.substring(1).split('/'), Number); + return aLC[0] === bLC[0] ? aLC[1] - bLC[1] : aLC[0] - bLC[0]; + } + private updateKeyName = (): void => { const paths = new Set(); if (this.configurationValue[this.observeAttr][this.attribute]) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html index 260f0016f5..09b2abfe44 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html @@ -41,7 +41,7 @@ class="tb-autocomplete" [displayWith]="displayObjectLwm2mFn"> - + diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index f6a600ae99..4122971e50 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -89,7 +89,6 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor private propagateChange = (v: any) => { }; - registerOnChange(fn: any): void { this.propagateChange = fn; } @@ -164,7 +163,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor } changeInstanceResourcesCheckBox = (value: boolean, instance: AbstractControl, type: string): void => { - const resources = instance.get('resources').value as ResourceLwM2M[]; + const resources = deepClone(instance.get('resources').value as ResourceLwM2M[]); resources.forEach(resource => resource[type] = value); instance.get('resources').patchValue(resources); this.propagateChange(this.observeAttrTelemetryFormGroup.value); @@ -238,49 +237,54 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor } private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => { - const instances = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M[]) - .find(objectLwM2M => objectLwM2M.id === data.objectId).instances; - const valueOld = this.instancesToSetId(instances); - + const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get('clientLwM2M') as FormArray).controls + .find(e => e.value.id === data.objectId) as FormGroup; + const instancesArray = objectLwM2MFormGroup.value.instances as Instance []; + const instancesFormArray = objectLwM2MFormGroup.get('instances') as FormArray; + const instance0 = deepClone(instancesFormArray.at(0).value as Instance); + instance0.resources.forEach(r => { + r.attribute = false; + r.telemetry = false; + r.observe = false; + }); + const valueOld = this.instancesToSetId(instancesArray); if (!isEqual(valueOld, data.instancesIds)) { const idsDel = this.diffBetweenSet(valueOld, data.instancesIds); const idsAdd = this.diffBetweenSet(data.instancesIds, valueOld); if (idsAdd.size) { - this.addInstancesNew(data.objectId, idsAdd, instances.find(instance => instance.id === 0)); + this.addInstancesNew(idsAdd, objectLwM2MFormGroup, instancesFormArray, instance0); } if (idsDel.size) { - this.deleteInstances(data.objectId, idsDel); + this.deleteInstances(idsDel, objectLwM2MFormGroup, instancesFormArray, instance0); } } } - private deleteInstances = (objectId: number, idsDel: Set): void => { - const objectIndex = (this.observeAttrTelemetryFormGroup.get('clientLwM2M').value as ObjectLwM2M[]) - .findIndex(element => element.id === objectId); + private addInstancesNew = (idsAdd: Set, objectLwM2MFormGroup: FormGroup, instancesFormArray: FormArray, + instanceNew: Instance): void => { + idsAdd.forEach(x => { + this.pushInstance(instancesFormArray, x, instanceNew); + }); + (instancesFormArray.controls as FormGroup[]).sort((a, b) => a.value.id - b.value.id); + } + + private deleteInstances = (idsDel: Set, objectLwM2MFormGroup: FormGroup, instancesFormArray: FormArray, + instance0: Instance): void => { idsDel.forEach(x => { - const instancesFormArray = ((this.observeAttrTelemetryFormGroup.get('clientLwM2M') as FormArray) - .at(objectIndex).get('instances') as FormArray); const instanceIndex = instancesFormArray.value.findIndex(element => element.id === x); instancesFormArray.removeAt(instanceIndex); }); + if (instancesFormArray.length === 0) { + this.pushInstance(instancesFormArray, 0, instance0); + } + (instancesFormArray.controls as FormGroup[]).sort((a, b) => a.value.id - b.value.id); } - private addInstancesNew = (objectId: number, idsAdd: Set, instance: Instance): void => { - const instancesFormArray = ((this.observeAttrTelemetryFormGroup.get('clientLwM2M') as FormArray).controls - .find(e => e.value.id === objectId).get('instances') as FormArray) as FormArray; - idsAdd.forEach(x => { - const instanceNew = deepClone(instance); - instanceNew.resources.forEach(r => { - r.attribute = false; - r.telemetry = false; - r.observe = false; - }); - instancesFormArray.push(this.fb.group({ - id: x, - resources: {value: instanceNew.resources, disabled: this.disabled} - })); - }); - (instancesFormArray.controls as FormGroup[]).sort((a, b) => a.value.id - b.value.id); + private pushInstance = (instancesFormArray: FormArray, x: number, instanceNew: Instance): void => { + instancesFormArray.push(this.fb.group({ + id: x, + resources: {value: instanceNew.resources, disabled: this.disabled} + })); } private diffBetweenSet(firstSet: Set, secondSet: Set): Set { From 15abe85b748eb6e08945faff865f92fd517a56ce Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 21 Jan 2021 18:21:55 +0200 Subject: [PATCH 32/41] UI: Refactoring LwM2M --- ...ofile-transport-configuration.component.ts | 15 +++--- .../lwm2m/security-config-server.component.ts | 18 ++++---- .../device/lwm2m/security-config.component.ts | 46 +++++++++---------- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 5ce7620e32..1711f11ade 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -73,8 +73,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.requiredValue = coerceBooleanProperty(value); } - private propagateChange = (v: any) => { - }; + private propagateChange = (v: any) => { }; constructor(private store: Store, private fb: FormBuilder, @@ -141,7 +140,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateWriteValue = (value: any): void => { - const objectsList = deepClone(value.objectsList); + const objectsList = value.objectsList; this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ objectIds: value, observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)}, @@ -251,12 +250,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private getObserveAttrTelemetryObjects = (listObject: ObjectLwM2M[]): ObjectLwM2M [] => { - const clientObserveAttr = deepClone(listObject); + const clientObserveAttr = listObject; if (this.configurationValue[this.observeAttr]) { - const observeArray = this.configurationValue[this.observeAttr][this.observe] as Array; - const attributeArray = this.configurationValue[this.observeAttr][this.attribute] as Array; - const telemetryArray = this.configurationValue[this.observeAttr][this.telemetry] as Array; - const keyNameJson = this.configurationValue[this.observeAttr][this.keyName] as JsonObject; + const observeArray = this.configurationValue[this.observeAttr][this.observe]; + const attributeArray = this.configurationValue[this.observeAttr][this.attribute]; + const telemetryArray = this.configurationValue[this.observeAttr][this.telemetry]; + const keyNameJson = this.configurationValue[this.observeAttr][this.keyName]; if (this.includesInstancesNo(attributeArray, telemetryArray)) { this.addInstances(attributeArray, telemetryArray, clientObserveAttr); } diff --git a/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config-server.component.ts b/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config-server.component.ts index b9e1ddf6b0..d88115e266 100644 --- a/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config-server.component.ts @@ -15,19 +15,17 @@ /// import { Component, forwardRef, Inject, Input, OnInit } from '@angular/core'; - -import { - ControlValueAccessor, - FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators -} from '@angular/forms'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { - SECURITY_CONFIG_MODE, - SECURITY_CONFIG_MODE_NAMES, - KEY_REGEXP_HEX_DEC, - ServerSecurityConfig, DeviceCredentialsDialogLwm2mData, + KEY_REGEXP_HEX_DEC, + LEN_MAX_PRIVATE_KEY, LEN_MAX_PSK, - LEN_MAX_PRIVATE_KEY, LEN_MAX_PUBLIC_KEY_RPK, LEN_MAX_PUBLIC_KEY_X509 + LEN_MAX_PUBLIC_KEY_RPK, + LEN_MAX_PUBLIC_KEY_X509, + SECURITY_CONFIG_MODE, + SECURITY_CONFIG_MODE_NAMES, + ServerSecurityConfig } from '@home/pages/device/lwm2m/security-config.models'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; diff --git a/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.ts b/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.ts index 3fcd0ae01d..32e1f0736f 100644 --- a/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.ts @@ -15,7 +15,7 @@ /// -import {Component, Inject, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { DialogComponent } from '@shared/components/dialog.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -24,22 +24,22 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { - SECURITY_CONFIG_MODE_NAMES, - SECURITY_CONFIG_MODE, - SecurityConfigModels, - ClientSecurityConfigPSK, - ClientSecurityConfigRPK, - JSON_ALL_CONFIG, - KEY_REGEXP_HEX_DEC, - DeviceCredentialsDialogLwm2mData, BOOTSTRAP_SERVER, BOOTSTRAP_SERVERS, - LWM2M_SERVER, - ClientSecurityConfigX509, ClientSecurityConfigNoSEC, + ClientSecurityConfigPSK, + ClientSecurityConfigRPK, + ClientSecurityConfigX509, + DeviceCredentialsDialogLwm2mData, getDefaultClientSecurityConfigType, + JSON_ALL_CONFIG, + KEY_REGEXP_HEX_DEC, LEN_MAX_PSK, - LEN_MAX_PUBLIC_KEY_RPK + LEN_MAX_PUBLIC_KEY_RPK, + LWM2M_SERVER, + SECURITY_CONFIG_MODE, + SECURITY_CONFIG_MODE_NAMES, + SecurityConfigModels } from './security-config.models'; import { WINDOW } from '@core/services/window.service'; import { MatTabChangeEvent } from '@angular/material/tabs'; @@ -65,9 +65,9 @@ export class SecurityConfigComponent extends DialogComponent, protected router: Router, @@ -111,23 +111,23 @@ export class SecurityConfigComponent extends DialogComponent { - switch (jsonAllConfig.client.securityConfigClientMode.toString()) { - case SECURITY_CONFIG_MODE.NO_SEC.toString(): + switch (jsonAllConfig.client.securityConfigClientMode) { + case SECURITY_CONFIG_MODE.NO_SEC: break; - case SECURITY_CONFIG_MODE.PSK.toString(): + case SECURITY_CONFIG_MODE.PSK: const clientSecurityConfigPSK = jsonAllConfig.client as ClientSecurityConfigPSK; this.lwm2mConfigFormGroup.patchValue({ identityPSK: clientSecurityConfigPSK.identity, clientKey: clientSecurityConfigPSK.key, }, {emitEvent: false}); break; - case SECURITY_CONFIG_MODE.RPK.toString(): + case SECURITY_CONFIG_MODE.RPK: const clientSecurityConfigRPK = jsonAllConfig.client as ClientSecurityConfigRPK; this.lwm2mConfigFormGroup.patchValue({ clientKey: clientSecurityConfigRPK.key, }, {emitEvent: false}); break; - case SECURITY_CONFIG_MODE.X509.toString(): + case SECURITY_CONFIG_MODE.X509: const clientSecurityConfigX509 = jsonAllConfig.client as ClientSecurityConfigX509; this.lwm2mConfigFormGroup.patchValue({ clientCertificate: clientSecurityConfigX509.x509 @@ -295,7 +295,7 @@ export class SecurityConfigComponent extends DialogComponent { const securityMode = 'securityMode'; - if (this.lwm2mConfigFormGroup.get('bootstrapServer').value[securityMode] === SECURITY_CONFIG_MODE.PSK.toString()) { + if (this.lwm2mConfigFormGroup.get('bootstrapServer').value[securityMode] === SECURITY_CONFIG_MODE.PSK) { this.lwm2mConfigFormGroup.get('bootstrapFormGroup').patchValue({ clientPublicKeyOrId: this.lwm2mConfigFormGroup.get('identityPSK').value }); @@ -303,7 +303,7 @@ export class SecurityConfigComponent extends DialogComponent Date: Fri, 22 Jan 2021 14:46:55 +0200 Subject: [PATCH 33/41] Lwm2m: front: refactoring5 --- .../controller/DeviceLwm2mController.java | 10 +- .../service/lwm2m/LwM2MModelsRepository.java | 323 ++++++++++-------- .../server/common/data/lwm2m/LwM2mObject.java | 4 + .../app/core/http/device-profile.service.ts | 14 +- ...ofile-transport-configuration.component.ts | 7 +- 5 files changed, 207 insertions(+), 151 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java index c38a163af9..a12bfb8900 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java @@ -48,13 +48,15 @@ import java.util.Map; @RequestMapping("/api") public class DeviceLwm2mController extends BaseController { - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/lwm2m/deviceProfile/{objectIds}", method = RequestMethod.GET) + @RequestMapping(value = "/lwm2m/deviceProfile", params = {"objectIds"}, method = RequestMethod.GET) @ResponseBody - public List getLwm2mListObjects(@PathVariable("objectIds") int[] objectIds) throws ThingsboardException { + public List getLwm2mListObjects(@RequestParam int[] objectIds, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder) throws ThingsboardException { try { - return lwM2MModelsRepository.getLwm2mObjects(objectIds, null); + return lwM2MModelsRepository.getLwm2mObjects(objectIds, textSearch, sortProperty, sortOrder); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java index cea898e7a1..4057d613b9 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java @@ -23,8 +23,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.data.domain.PageImpl; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.lwm2m.*; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.lwm2m.LwM2mInstance; +import org.thingsboard.server.common.data.lwm2m.LwM2mObject; +import org.thingsboard.server.common.data.lwm2m.LwM2mResource; +import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap; @@ -32,17 +35,23 @@ import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer; import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; +import java.lang.reflect.Field; import java.math.BigInteger; -import java.security.*; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyStoreException; +import java.security.PublicKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; -import java.security.spec.ECPublicKeySpec; import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; import java.security.spec.KeySpec; -import java.util.List; import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -70,22 +79,26 @@ public class LwM2MModelsRepository { * Filter by Predicate (uses objectIds, if objectIds is null then it uses textSearch, * if textSearch is null then it uses AllList from List) */ - public List getLwm2mObjects(int[] objectIds, String textSearch) { - return getLwm2mObjects((objectIds != null && objectIds.length > 0) ? - (ObjectModel element) -> IntStream.of(objectIds).anyMatch(x -> x == element.id) : - (textSearch != null && !textSearch.isEmpty()) ? (ObjectModel element) -> element.name.contains(textSearch) : null); + public List getLwm2mObjects(int[] objectIds, String textSearch, String sortProperty, String sortOrder) { + return getLwm2mObjects((objectIds != null && objectIds.length > 0 && textSearch != null && !textSearch.isEmpty()) ? + (ObjectModel element) -> IntStream.of(objectIds).anyMatch(x -> x == element.id) || element.name.contains(textSearch) : + (objectIds != null && objectIds.length > 0) ? + (ObjectModel element) -> IntStream.of(objectIds).anyMatch(x -> x == element.id) : + (textSearch != null && !textSearch.isEmpty()) ? (ObjectModel element) -> element.name.contains(textSearch) : null, + sortProperty, sortOrder); } /** * @param predicate * @return list of LwM2mObject */ - private List getLwm2mObjects(Predicate predicate) { + private List getLwm2mObjects(Predicate predicate, String sortProperty, String sortOrder) { List lwM2mObjects = new ArrayList<>(); List listObjects = (predicate == null) ? this.contextServer.getModelsValue() : contextServer.getModelsValue().stream() .filter(predicate) .collect(Collectors.toList()); + listObjects.forEach(obj -> { LwM2mObject lwM2mObject = new LwM2mObject(); lwM2mObject.setId(obj.id); @@ -105,153 +118,177 @@ public class LwM2MModelsRepository { lwM2mObject.setInstances(new LwM2mInstance[]{instance}); lwM2mObjects.add(lwM2mObject); }); + try { + Field field = LwM2mObject.class.getField(sortProperty); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + switch (sortProperty) { + case "name": + switch (sortOrder) { + case "ASC": + // ASC + lwM2mObjects.sort((o1, o2) -> o1.getName().compareTo(o2.getName())); + break; + case "DESC": + // DESC + lwM2mObjects.stream().sorted(Comparator.comparing(LwM2mObject::getName).reversed()); + break; + } + case "id": + switch (sortOrder) { + case "ASC": + // ASC + lwM2mObjects.sort((o1, o2) -> Long.compare(o1.getId(), o2.getId())); + break; + case "DESC": + // DESC + lwM2mObjects.sort((o1, o2) -> Long.compare(o2.getId(), o1.getId())); + } + } return lwM2mObjects; } - /** - * @param tenantId - * @param pageLink - * @return List of LwM2mObject in PageData format - */ - public PageData findDeviceLwm2mObjects(TenantId tenantId, PageLink pageLink) { - log.trace("Executing findDeviceProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validatePageLink(pageLink); - return this.findLwm2mListObjects(pageLink); - } + /** + * @param tenantId + * @param pageLink + * @return List of LwM2mObject in PageData format + */ + public PageData findDeviceLwm2mObjects (TenantId tenantId, PageLink pageLink){ + log.trace("Executing findDeviceProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validatePageLink(pageLink); + return this.findLwm2mListObjects(pageLink); + } - /** - * @param pageLink - * @return List of LwM2mObject in PageData format, filter == TextSearch - * PageNumber = 1, PageSize = List.size() - */ - public PageData findLwm2mListObjects(PageLink pageLink) { - PageImpl page = new PageImpl(getLwm2mObjects(null, pageLink.getTextSearch())); - PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext()); - return pageData; - } + /** + * @param pageLink + * @return List of LwM2mObject in PageData format, filter == TextSearch + * PageNumber = 1, PageSize = List.size() + */ + public PageData findLwm2mListObjects (PageLink pageLink){ + PageImpl page = new PageImpl(getLwm2mObjects(null, pageLink.getTextSearch(), pageLink.getSortOrder().getProperty(), pageLink.getSortOrder().getDirection().name())); + PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext()); + return pageData; + } - /** - * - * @param securityMode - * @param bootstrapServerIs - * @return ServerSecurityConfig more value is default: Important - port, host, publicKey - */ - public ServerSecurityConfig getBootstrapSecurityInfo(String securityMode, boolean bootstrapServerIs) { - LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(securityMode.toLowerCase()); - return getBootstrapServer(bootstrapServerIs, lwM2MSecurityMode); - } + /** + * @param securityMode + * @param bootstrapServerIs + * @return ServerSecurityConfig more value is default: Important - port, host, publicKey + */ + public ServerSecurityConfig getBootstrapSecurityInfo (String securityMode,boolean bootstrapServerIs){ + LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(securityMode.toLowerCase()); + return getBootstrapServer(bootstrapServerIs, lwM2MSecurityMode); + } - /** - * - * @param bootstrapServerIs - * @param mode - * @return ServerSecurityConfig more value is default: Important - port, host, publicKey - */ - private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) { - ServerSecurityConfig bsServ = new ServerSecurityConfig(); - bsServ.setBootstrapServerIs(bootstrapServerIs); - if (bootstrapServerIs) { - bsServ.setServerId(contextBootStrap.getBootstrapServerId()); - switch (mode) { - case NO_SEC: - bsServ.setHost(contextBootStrap.getBootstrapHost()); - bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk()); - bsServ.setServerPublicKey(""); - break; - case PSK: - bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk()); - bsServ.setServerPublicKey(""); - break; - case RPK: - bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk()); - bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); - break; - case X509: - bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509()); - bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias())); - break; - default: - break; - } - } else { - bsServ.setServerId(contextServer.getServerId()); - switch (mode) { - case NO_SEC: - bsServ.setHost(contextServer.getServerHost()); - bsServ.setPort(contextServer.getServerPortNoSecPsk()); - bsServ.setServerPublicKey(""); - break; - case PSK: - bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerPortPsk()); - bsServ.setServerPublicKey(""); - break; - case RPK: - bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerPortRpk()); - bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY())); - break; - case X509: - bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerPortX509()); - bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias())); - break; - default: - break; + /** + * @param bootstrapServerIs + * @param mode + * @return ServerSecurityConfig more value is default: Important - port, host, publicKey + */ + private ServerSecurityConfig getBootstrapServer ( boolean bootstrapServerIs, LwM2MSecurityMode mode){ + ServerSecurityConfig bsServ = new ServerSecurityConfig(); + bsServ.setBootstrapServerIs(bootstrapServerIs); + if (bootstrapServerIs) { + bsServ.setServerId(contextBootStrap.getBootstrapServerId()); + switch (mode) { + case NO_SEC: + bsServ.setHost(contextBootStrap.getBootstrapHost()); + bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk()); + bsServ.setServerPublicKey(""); + break; + case PSK: + bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); + bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk()); + bsServ.setServerPublicKey(""); + break; + case RPK: + bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); + bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk()); + bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); + break; + case X509: + bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); + bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509()); + bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias())); + break; + default: + break; + } + } else { + bsServ.setServerId(contextServer.getServerId()); + switch (mode) { + case NO_SEC: + bsServ.setHost(contextServer.getServerHost()); + bsServ.setPort(contextServer.getServerPortNoSecPsk()); + bsServ.setServerPublicKey(""); + break; + case PSK: + bsServ.setHost(contextServer.getServerSecureHost()); + bsServ.setPort(contextServer.getServerPortPsk()); + bsServ.setServerPublicKey(""); + break; + case RPK: + bsServ.setHost(contextServer.getServerSecureHost()); + bsServ.setPort(contextServer.getServerPortRpk()); + bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY())); + break; + case X509: + bsServ.setHost(contextServer.getServerSecureHost()); + bsServ.setPort(contextServer.getServerPortX509()); + bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias())); + break; + default: + break; + } } + return bsServ; } - return bsServ; - } - /** - * - * @param alias - * @return PublicKey format HexString or null - */ - private String getServerPublicKeyX509 (String alias) { - try { - X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias); - return Hex.encodeHexString(serverCertificate.getEncoded()); - } catch (CertificateEncodingException | KeyStoreException e) { - e.printStackTrace(); + /** + * @param alias + * @return PublicKey format HexString or null + */ + private String getServerPublicKeyX509 (String alias){ + try { + X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias); + return Hex.encodeHexString(serverCertificate.getEncoded()); + } catch (CertificateEncodingException | KeyStoreException e) { + e.printStackTrace(); + } + return null; } - return null; - } - /** - * - * @param publicServerX - * @param publicServerY - * @return PublicKey format HexString or null - */ - private String getRPKPublicKey(String publicServerX, String publicServerY) { - try { - /** Get Elliptic Curve Parameter spec for secp256r1 */ - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - if (publicServerX != null && !publicServerX.isEmpty() && publicServerY != null && !publicServerY.isEmpty()) { - /** Get point values */ - byte[] publicX = Hex.decodeHex(publicServerX.toCharArray()); - byte[] publicY = Hex.decodeHex(publicServerY.toCharArray()); - /** Create key specs */ - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - /** Get keys */ - PublicKey publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - if (publicKey != null && publicKey.getEncoded().length > 0 ) { - return Hex.encodeHexString(publicKey.getEncoded()); + /** + * @param publicServerX + * @param publicServerY + * @return PublicKey format HexString or null + */ + private String getRPKPublicKey (String publicServerX, String publicServerY){ + try { + /** Get Elliptic Curve Parameter spec for secp256r1 */ + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); + algoParameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); + if (publicServerX != null && !publicServerX.isEmpty() && publicServerY != null && !publicServerY.isEmpty()) { + /** Get point values */ + byte[] publicX = Hex.decodeHex(publicServerX.toCharArray()); + byte[] publicY = Hex.decodeHex(publicServerY.toCharArray()); + /** Create key specs */ + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), + parameterSpec); + /** Get keys */ + PublicKey publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); + if (publicKey != null && publicKey.getEncoded().length > 0) { + return Hex.encodeHexString(publicKey.getEncoded()); + } } + } catch (GeneralSecurityException | IllegalArgumentException e) { + log.error("[{}] Failed generate Server RPK for profile", e.getMessage()); + throw new RuntimeException(e); } - } catch (GeneralSecurityException | IllegalArgumentException e) { - log.error("[{}] Failed generate Server RPK for profile", e.getMessage()); - throw new RuntimeException(e); + return null; } - return null; } -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java b/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java index 4bd700fdf9..5afb37c720 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java @@ -16,7 +16,11 @@ package org.thingsboard.server.common.data.lwm2m; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +@Setter +@Getter @Data public class LwM2mObject { int id; diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index 22ff52e99b..816dde3260 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -23,6 +23,7 @@ import { PageData } from '@shared/models/page/page-data'; import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; import { isDefinedAndNotNull } from '@core/utils'; import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/profile-config.models'; +import { SortOrder } from '@shared/models/page/sort-order'; @Injectable({ providedIn: 'root' @@ -31,7 +32,8 @@ export class DeviceProfileService { constructor( private http: HttpClient - ) { } + ) { + } public getDeviceProfiles(pageLink: PageLink, config?: RequestConfig): Observable> { return this.http.get>(`/api/deviceProfiles${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); @@ -41,8 +43,14 @@ export class DeviceProfileService { return this.http.get(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); } - public getLwm2mObjects(objectIds: number[], config?: RequestConfig): Observable> { - return this.http.get>(`/api/lwm2m/deviceProfile/${objectIds}`, defaultHttpOptionsFromConfig(config)); + public getLwm2mObjects(objectIds?: number[], searchText?: string, sortOrder?: SortOrder, config?: RequestConfig): + Observable> { + // tslint:disable-next-line:no-debugger + debugger; + return this.http.get> + (`/api/lwm2m/deviceProfile/?objectIds=${objectIds}&searchText=${searchText}&sortProperty=${sortOrder.property} + &sortOrder=${sortOrder.direction}`, + defaultHttpOptionsFromConfig(config)); } public getLwm2mBootstrapSecurityInfo(securityMode: string, bootstrapServerIs: boolean, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 1711f11ade..3dbe82287a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -37,6 +37,7 @@ import { deepClone, isUndefined } from '@core/utils'; import { WINDOW } from '@core/services/window.service'; import { JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; import { isNotNullOrUndefined } from 'codelyzer/util/isNotNullOrUndefined'; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; @Component({ selector: 'tb-profile-lwm2m-device-transport-configuration', @@ -128,7 +129,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const modelValue = {objectIds: null, objectsList: []}; modelValue.objectIds = this.getObjectsFromJsonAllConfig(); if (modelValue.objectIds !== null) { - this.deviceProfileService.getLwm2mObjects(modelValue.objectIds).subscribe( + const sortOrder = { + property: 'id', + direction: Direction.ASC + }; + this.deviceProfileService.getLwm2mObjects(modelValue.objectIds, null, sortOrder).subscribe( (objectsList) => { modelValue.objectsList = objectsList; this.updateWriteValue(modelValue); From de77c53fcc07b693d949d4beceb32ab192c1015f Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 22 Jan 2021 16:14:39 +0200 Subject: [PATCH 34/41] UI: Refactoring LwM2M --- .../app/core/http/device-profile.service.ts | 16 +- ...ile-transport-configuration.component.html | 62 +++--- ...ofile-transport-configuration.component.ts | 195 +++++++++--------- 3 files changed, 141 insertions(+), 132 deletions(-) diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index 816dde3260..df68af629e 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -43,14 +43,16 @@ export class DeviceProfileService { return this.http.get(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); } - public getLwm2mObjects(objectIds?: number[], searchText?: string, sortOrder?: SortOrder, config?: RequestConfig): + public getLwm2mObjects(objectIds: number[] = [], searchText?: string, sortOrder?: SortOrder, config?: RequestConfig): Observable> { - // tslint:disable-next-line:no-debugger - debugger; - return this.http.get> - (`/api/lwm2m/deviceProfile/?objectIds=${objectIds}&searchText=${searchText}&sortProperty=${sortOrder.property} - &sortOrder=${sortOrder.direction}`, - defaultHttpOptionsFromConfig(config)); + let url = `/api/lwm2m/deviceProfile/?objectIds=${objectIds}`; + if (isDefinedAndNotNull(searchText)) { + url += `&searchText=${searchText}`; + } + if (isDefinedAndNotNull(sortOrder)) { + url += `&sortProperty=${sortOrder.property}&sortOrder=${sortOrder.direction}`; + } + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } public getLwm2mBootstrapSecurityInfo(securityMode: string, bootstrapServerIs: boolean, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 7b49f8ffd6..57b767fb07 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -15,11 +15,11 @@ limitations under the License. --> - -
- - - +
+ + + +
- - - - +
+
+
+ + +
@@ -52,7 +54,7 @@ {{ 'device-profile.lwm2m.short-id' | translate }} - + {{ 'device-profile.lwm2m.short-id' | translate }} {{ 'device-profile.lwm2m.required' | translate }} @@ -61,7 +63,7 @@ {{ 'device-profile.lwm2m.lifetime' | translate }} + *ngIf="lwm2mDeviceProfileFormGroup.get('lifetime').hasError('required')"> {{ 'device-profile.lwm2m.lifetime' | translate }} {{ 'device-profile.lwm2m.required' | translate }} @@ -72,7 +74,7 @@ {{ 'device-profile.lwm2m.default-min-period' | translate }} + *ngIf="lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').hasError('required')"> {{ 'device-profile.lwm2m.default-min-period' | translate }} {{ 'device-profile.lwm2m.required' | translate }} @@ -80,7 +82,7 @@ {{ 'device-profile.lwm2m.binding' | translate }} - + {{ 'device-profile.lwm2m.binding' | translate }} {{ 'device-profile.lwm2m.required' | translate }} @@ -133,20 +135,20 @@
- - - - -
- - -
-
-
- -
- +
+ + + + +
+ + +
+
+
+ + diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 3dbe82287a..d5dc7e90b6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -33,11 +33,10 @@ import { TELEMETRY } from './profile-config.models'; import { DeviceProfileService } from '@core/http/device-profile.service'; -import { deepClone, isUndefined } from '@core/utils'; +import { deepClone, isDefinedAndNotNull, isUndefined } from '@core/utils'; import { WINDOW } from '@core/services/window.service'; import { JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; -import { isNotNullOrUndefined } from 'codelyzer/util/isNotNullOrUndefined'; -import { Direction, SortOrder } from '@shared/models/page/sort-order'; +import { Direction } from '@shared/models/page/sort-order'; @Component({ selector: 'tb-profile-lwm2m-device-transport-configuration', @@ -54,7 +53,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private requiredValue: boolean; private disabled = false; - lwm2mDeviceProfileTransportConfFormGroup: FormGroup; + lwm2mDeviceProfileFormGroup: FormGroup; + lwm2mDeviceConfigFormGroup: FormGroup; observeAttr = OBSERVE_ATTR; observe = OBSERVE; attribute = ATTR; @@ -80,7 +80,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private fb: FormBuilder, private deviceProfileService: DeviceProfileService, @Inject(WINDOW) private window: Window) { - this.lwm2mDeviceProfileTransportConfFormGroup = this.fb.group({ + this.lwm2mDeviceProfileFormGroup = this.fb.group({ objectIds: [{}, Validators.required], observeAttrTelemetry: [{clientLwM2M: []}, Validators.required], shortId: [null, Validators.required], @@ -90,9 +90,18 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro binding: ['U', Validators.required], bootstrapServer: [null, Validators.required], lwm2mServer: [null, Validators.required], - configurationJson: [null, Validators.required], }); - this.lwm2mDeviceProfileTransportConfFormGroup.valueChanges.subscribe(() => { + this.lwm2mDeviceConfigFormGroup = this.fb.group({ + configurationJson: [null, Validators.required] + }); + this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe(() => { + console.warn('main form'); + if (!this.disabled) { + this.updateModel(); + } + }); + this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => { + console.warn('config form'); if (!this.disabled) { this.updateModel(); } @@ -110,18 +119,19 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (isDisabled) { - this.lwm2mDeviceProfileTransportConfFormGroup.disable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.disable({emitEvent: false}); + this.lwm2mDeviceConfigFormGroup.disable({emitEvent: false}); } else { - this.lwm2mDeviceProfileTransportConfFormGroup.enable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.enable({emitEvent: false}); + this.lwm2mDeviceConfigFormGroup.enable({emitEvent: false}); } } writeValue(value: any | null): void { this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value; - this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ + this.lwm2mDeviceConfigFormGroup.patchValue({ configurationJson: this.configurationValue - }, - {emitEvent: false}); + }, {emitEvent: false}); this.initWriteValue(); } @@ -146,7 +156,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private updateWriteValue = (value: any): void => { const objectsList = value.objectsList; - this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ + this.lwm2mDeviceProfileFormGroup.patchValue({ objectIds: value, observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)}, shortId: this.configurationValue.bootstrap.servers.shortId, @@ -162,20 +172,20 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private updateModel = (): void => { let configuration: DeviceProfileTransportConfiguration = null; - if (this.lwm2mDeviceProfileTransportConfFormGroup.valid) { + if (this.lwm2mDeviceConfigFormGroup.valid) { this.upDateValueToJson(); - configuration = this.lwm2mDeviceProfileTransportConfFormGroup.getRawValue().configurationJson; + configuration = this.lwm2mDeviceConfigFormGroup.getRawValue().configurationJson; configuration.type = DeviceTransportType.LWM2M; } this.propagateChange(configuration); } private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => { - this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ + this.lwm2mDeviceProfileFormGroup.patchValue({ observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)} }, {emitEvent: false}); - this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').markAsPristine({ + this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').markAsPristine({ onlySelf: true }); } @@ -186,11 +196,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private upDateValueToJsonTab0 = (): void => { - if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').pristine) { + if (!this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').pristine) { this.upDateObserveAttrTelemetryFromGroupToJson( - this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M + this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M ); - this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').markAsPristine({ + this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); @@ -199,16 +209,16 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private upDateValueToJsonTab1 = (): void => { this.upDateValueServersToJson(); - if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('bootstrapServer').pristine) { - this.configurationValue.bootstrap.bootstrapServer = this.lwm2mDeviceProfileTransportConfFormGroup.get('bootstrapServer').value; - this.lwm2mDeviceProfileTransportConfFormGroup.get('bootstrapServer').markAsPristine({ + if (!this.lwm2mDeviceProfileFormGroup.get('bootstrapServer').pristine) { + this.configurationValue.bootstrap.bootstrapServer = this.lwm2mDeviceProfileFormGroup.get('bootstrapServer').value; + this.lwm2mDeviceProfileFormGroup.get('bootstrapServer').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } - if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('lwm2mServer').pristine) { - this.configurationValue.bootstrap.lwm2mServer = this.lwm2mDeviceProfileTransportConfFormGroup.get('lwm2mServer').value; - this.lwm2mDeviceProfileTransportConfFormGroup.get('lwm2mServer').markAsPristine({ + if (!this.lwm2mDeviceProfileFormGroup.get('lwm2mServer').pristine) { + this.configurationValue.bootstrap.lwm2mServer = this.lwm2mDeviceProfileFormGroup.get('lwm2mServer').value; + this.lwm2mDeviceProfileFormGroup.get('lwm2mServer').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); @@ -217,37 +227,37 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private upDateValueServersToJson = (): void => { const bootstrapServers = this.configurationValue.bootstrap.servers; - if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('shortId').pristine) { - bootstrapServers.shortId = this.lwm2mDeviceProfileTransportConfFormGroup.get('shortId').value; - this.lwm2mDeviceProfileTransportConfFormGroup.get('shortId').markAsPristine({ + if (!this.lwm2mDeviceProfileFormGroup.get('shortId').pristine) { + bootstrapServers.shortId = this.lwm2mDeviceProfileFormGroup.get('shortId').value; + this.lwm2mDeviceProfileFormGroup.get('shortId').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } - if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('lifetime').pristine) { - bootstrapServers.lifetime = this.lwm2mDeviceProfileTransportConfFormGroup.get('lifetime').value; - this.lwm2mDeviceProfileTransportConfFormGroup.get('lifetime').markAsPristine({ + if (!this.lwm2mDeviceProfileFormGroup.get('lifetime').pristine) { + bootstrapServers.lifetime = this.lwm2mDeviceProfileFormGroup.get('lifetime').value; + this.lwm2mDeviceProfileFormGroup.get('lifetime').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } - if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('defaultMinPeriod').pristine) { - bootstrapServers.defaultMinPeriod = this.lwm2mDeviceProfileTransportConfFormGroup.get('defaultMinPeriod').value; - this.lwm2mDeviceProfileTransportConfFormGroup.get('defaultMinPeriod').markAsPristine({ + if (!this.lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').pristine) { + bootstrapServers.defaultMinPeriod = this.lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').value; + this.lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } - if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('notifIfDisabled').pristine) { - bootstrapServers.notifIfDisabled = this.lwm2mDeviceProfileTransportConfFormGroup.get('notifIfDisabled').value; - this.lwm2mDeviceProfileTransportConfFormGroup.get('notifIfDisabled').markAsPristine({ + if (!this.lwm2mDeviceProfileFormGroup.get('notifIfDisabled').pristine) { + bootstrapServers.notifIfDisabled = this.lwm2mDeviceProfileFormGroup.get('notifIfDisabled').value; + this.lwm2mDeviceProfileFormGroup.get('notifIfDisabled').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); } - if (!this.lwm2mDeviceProfileTransportConfFormGroup.get('binding').pristine) { - bootstrapServers.binding = this.lwm2mDeviceProfileTransportConfFormGroup.get('binding').value; - this.lwm2mDeviceProfileTransportConfFormGroup.get('binding').markAsPristine({ + if (!this.lwm2mDeviceProfileFormGroup.get('binding').pristine) { + bootstrapServers.binding = this.lwm2mDeviceProfileFormGroup.get('binding').value; + this.lwm2mDeviceProfileFormGroup.get('binding').markAsPristine({ onlySelf: true }); this.upDateJsonAllConfig(); @@ -255,55 +265,50 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private getObserveAttrTelemetryObjects = (listObject: ObjectLwM2M[]): ObjectLwM2M [] => { - const clientObserveAttr = listObject; + const clientObserveAttrTelemetry = listObject; if (this.configurationValue[this.observeAttr]) { const observeArray = this.configurationValue[this.observeAttr][this.observe]; const attributeArray = this.configurationValue[this.observeAttr][this.attribute]; const telemetryArray = this.configurationValue[this.observeAttr][this.telemetry]; const keyNameJson = this.configurationValue[this.observeAttr][this.keyName]; - if (this.includesInstancesNo(attributeArray, telemetryArray)) { - this.addInstances(attributeArray, telemetryArray, clientObserveAttr); + if (this.includesNotZeroInstance(attributeArray, telemetryArray)) { + this.addInstances(attributeArray, telemetryArray, clientObserveAttrTelemetry); } - if (observeArray) { - this.updateObserveAttrTelemetryObjects(observeArray, clientObserveAttr, 'observe'); + if (isDefinedAndNotNull(observeArray)) { + this.updateObserveAttrTelemetryObjects(observeArray, clientObserveAttrTelemetry, 'observe'); } - if (attributeArray) { - this.updateObserveAttrTelemetryObjects(attributeArray, clientObserveAttr, 'attribute'); + if (isDefinedAndNotNull(attributeArray)) { + this.updateObserveAttrTelemetryObjects(attributeArray, clientObserveAttrTelemetry, 'attribute'); } - if (telemetryArray) { - this.updateObserveAttrTelemetryObjects(telemetryArray, clientObserveAttr, 'telemetry'); + if (isDefinedAndNotNull(telemetryArray)) { + this.updateObserveAttrTelemetryObjects(telemetryArray, clientObserveAttrTelemetry, 'telemetry'); } - if (keyNameJson) { - this.updateKeyNameObjects(deepClone(keyNameJson), clientObserveAttr); + if (isDefinedAndNotNull(keyNameJson)) { + this.updateKeyNameObjects(keyNameJson, clientObserveAttrTelemetry); } } - clientObserveAttr.forEach(obj => { + clientObserveAttrTelemetry.forEach(obj => { obj.instances.sort((a, b) => a.id - b.id); }); - return clientObserveAttr; + return clientObserveAttrTelemetry; } - private includesInstancesNo = (attributeArray: Array, telemetryArray: Array): boolean => { - const isIdIndex = (element) => !element.includes('/0/'); - return attributeArray.findIndex(isIdIndex) >= 0 || telemetryArray.findIndex(isIdIndex) >= 0; + private includesNotZeroInstance = (attribute: string[], telemetry: string[]): boolean => { + const isNotZeroInstanceId = (instance) => !instance.includes('/0/'); + return attribute.some(isNotZeroInstanceId) || telemetry.some(isNotZeroInstanceId); } - private addInstances = (attributeArray: Array, telemetryArray: Array, - clientObserveAttr: ObjectLwM2M[]): void => { - const attr = [] as Array; - [...attributeArray].filter(x => (!x.includes('/0/'))).forEach(x => { - attr.push(this.convertPathToInstance(x)); - }); - const telemetry = [] as Array; - [...telemetryArray].filter(x => (!x.includes('/0/'))).forEach(x => { - telemetry.push(this.convertPathToInstance(x)); - }); - const instancesNoZero = new Set(attr.concat(telemetry).sort()); - instancesNoZero.forEach(path => { + private addInstances = (attribute: string[], telemetry: string[], clientObserveAttrTelemetry: ObjectLwM2M[]): void => { + const instancesPath = attribute.concat(telemetry) + .filter(instance => !instance.includes('/0/')) + .map(instance => this.convertPathToInstance(instance)) + .sort(); + + new Set(instancesPath).forEach(path => { const pathParameter = Array.from(path.split('/'), Number); - const objectLwM2M = clientObserveAttr.find(x => (x.id === pathParameter[0])); + const objectLwM2M = clientObserveAttrTelemetry.find(x => x.id === pathParameter[0]); if (objectLwM2M) { - const instance = deepClone(objectLwM2M.instances[0]) as Instance; + const instance = deepClone(objectLwM2M.instances[0]); instance.id = pathParameter[1]; objectLwM2M.instances.push(instance); } @@ -311,36 +316,36 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private convertPathToInstance = (path: string): string => { - const newX = Array.from(path.substring(1).split('/'), Number); - return [newX[0], newX[1]].join('/'); + const [objectId, instanceId] = path.substring(1).split('/'); + return `${objectId}/${instanceId}`; } - private updateObserveAttrTelemetryObjects = (isParameter: Array, clientObserveAttr: ObjectLwM2M[], + private updateObserveAttrTelemetryObjects = (parameters: string[], clientObserveAttrTelemetry: ObjectLwM2M[], nameParameter: string): void => { - isParameter.forEach(attr => { - const idKeys = Array.from(attr.substring(1).split('/'), Number); - clientObserveAttr - .forEach(e => { - if (e.id === idKeys[0]) { - const instance = e.instances.find(itrInstance => itrInstance.id === idKeys[1]); - if (isNotNullOrUndefined(instance)) { - instance.resources.find(resource => resource.id === idKeys[2])[nameParameter] = true; + parameters.forEach(parameter => { + const [objectId, instanceId, resourceId] = Array.from(parameter.substring(1).split('/'), Number); + clientObserveAttrTelemetry + .forEach(key => { + if (key.id === objectId) { + const instance = key.instances.find(itrInstance => itrInstance.id === instanceId); + if (isDefinedAndNotNull(instance)) { + instance.resources.find(resource => resource.id === resourceId)[nameParameter] = true; } } }); }); } - private updateKeyNameObjects = (nameJson: JsonObject, clientObserveAttr: ObjectLwM2M[]): void => { + private updateKeyNameObjects = (nameJson: JsonObject, clientObserveAttrTelemetry: ObjectLwM2M[]): void => { const keyName = JSON.parse(JSON.stringify(nameJson)); Object.keys(keyName).forEach(key => { - const idKeys = Array.from(key.substring(1).split('/'), Number); - clientObserveAttr - .forEach(e => { - if (e.id === idKeys[0]) { - e.instances - .find(instance => instance.id === idKeys[1]).resources - .find(resource => resource.id === idKeys[2]).keyName = keyName[key]; + const [objectId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), Number); + clientObserveAttrTelemetry + .forEach(object => { + if (object.id === objectId) { + object.instances + .find(instance => instance.id === instanceId).resources + .find(resource => resource.id === resourceId).keyName = keyName[key]; } }); }); @@ -444,7 +449,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro paths.forEach(path => { const pathParameter = this.findIndexesForIds(path); if (pathParameter.length === 3) { - keyNameNew[path] = this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value + keyNameNew[path] = this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value .clientLwM2M[pathParameter[0]].instances[pathParameter[1]].resources[pathParameter[2]][this.keyName]; } }); @@ -455,7 +460,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const pathParameter = Array.from(path.substring(1).split('/'), Number); const pathParameterIndexes: number[] = []; const objectsOld = deepClone( - this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M) as ObjectLwM2M[]; + this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M) as ObjectLwM2M[]; let isIdIndex = (element) => element.id === pathParameter[0]; const objIndex = objectsOld.findIndex(isIdIndex); if (objIndex >= 0) { @@ -497,10 +502,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private upDateJsonAllConfig = (): void => { - this.lwm2mDeviceProfileTransportConfFormGroup.patchValue({ + this.lwm2mDeviceProfileFormGroup.patchValue({ configurationJson: this.configurationValue }, {emitEvent: false}); - this.lwm2mDeviceProfileTransportConfFormGroup.markAsPristine({ + this.lwm2mDeviceProfileFormGroup.markAsPristine({ onlySelf: true }); } @@ -510,7 +515,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } removeObjectsList = (value: ObjectLwM2M): void => { - const objectsOld = deepClone(this.lwm2mDeviceProfileTransportConfFormGroup.get('observeAttrTelemetry').value.clientLwM2M); + const objectsOld = deepClone(this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M); const isIdIndex = (element) => element.id === value.id; const index = objectsOld.findIndex(isIdIndex); if (index >= 0) { From 317dfdbc42a206808172b357f5b60f66eaa363f6 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 22 Jan 2021 19:11:00 +0200 Subject: [PATCH 35/41] Lwm2m: front&back: refactoring add on the back ObjectIdFromTextSearch for search list models with filter by ObjectId --- .../service/lwm2m/LwM2MModelsRepository.java | 299 +++++++++--------- .../server/common/data/lwm2m/LwM2mObject.java | 4 - 2 files changed, 157 insertions(+), 146 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java index 4057d613b9..1eb454c23a 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java @@ -35,7 +35,6 @@ import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer; import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; -import java.lang.reflect.Field; import java.math.BigInteger; import java.security.AlgorithmParameters; import java.security.GeneralSecurityException; @@ -52,7 +51,9 @@ import java.security.spec.KeySpec; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; +import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -80,11 +81,13 @@ public class LwM2MModelsRepository { * if textSearch is null then it uses AllList from List) */ public List getLwm2mObjects(int[] objectIds, String textSearch, String sortProperty, String sortOrder) { - return getLwm2mObjects((objectIds != null && objectIds.length > 0 && textSearch != null && !textSearch.isEmpty()) ? + return getLwm2mObjects((objectIds.length > 0 && textSearch != null && !textSearch.isEmpty()) ? (ObjectModel element) -> IntStream.of(objectIds).anyMatch(x -> x == element.id) || element.name.contains(textSearch) : - (objectIds != null && objectIds.length > 0) ? + (objectIds.length > 0) ? (ObjectModel element) -> IntStream.of(objectIds).anyMatch(x -> x == element.id) : - (textSearch != null && !textSearch.isEmpty()) ? (ObjectModel element) -> element.name.contains(textSearch) : null, + (textSearch != null && !textSearch.isEmpty()) ? + (ObjectModel element) -> element.name.contains(textSearch) : + null, sortProperty, sortOrder); } @@ -118,177 +121,189 @@ public class LwM2MModelsRepository { lwM2mObject.setInstances(new LwM2mInstance[]{instance}); lwM2mObjects.add(lwM2mObject); }); - try { - Field field = LwM2mObject.class.getField(sortProperty); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } + return lwM2mObjects.size() > 1 ? this.sortList (lwM2mObjects, sortProperty, sortOrder) : lwM2mObjects; + } + + private List sortList (List lwM2mObjects, String sortProperty, String sortOrder) { switch (sortProperty) { case "name": switch (sortOrder) { case "ASC": - // ASC lwM2mObjects.sort((o1, o2) -> o1.getName().compareTo(o2.getName())); break; case "DESC": - // DESC lwM2mObjects.stream().sorted(Comparator.comparing(LwM2mObject::getName).reversed()); break; } case "id": switch (sortOrder) { case "ASC": - // ASC lwM2mObjects.sort((o1, o2) -> Long.compare(o1.getId(), o2.getId())); break; case "DESC": - // DESC lwM2mObjects.sort((o1, o2) -> Long.compare(o2.getId(), o1.getId())); } } return lwM2mObjects; } - /** - * @param tenantId - * @param pageLink - * @return List of LwM2mObject in PageData format - */ - public PageData findDeviceLwm2mObjects (TenantId tenantId, PageLink pageLink){ - log.trace("Executing findDeviceProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validatePageLink(pageLink); - return this.findLwm2mListObjects(pageLink); - } + /** + * @param tenantId + * @param pageLink + * @return List of LwM2mObject in PageData format + */ + public PageData findDeviceLwm2mObjects(TenantId tenantId, PageLink pageLink) { + log.trace("Executing findDeviceProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validatePageLink(pageLink); + return this.findLwm2mListObjects(pageLink); + } - /** - * @param pageLink - * @return List of LwM2mObject in PageData format, filter == TextSearch - * PageNumber = 1, PageSize = List.size() - */ - public PageData findLwm2mListObjects (PageLink pageLink){ - PageImpl page = new PageImpl(getLwm2mObjects(null, pageLink.getTextSearch(), pageLink.getSortOrder().getProperty(), pageLink.getSortOrder().getDirection().name())); - PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext()); - return pageData; - } + /** + * @param pageLink + * @return List of LwM2mObject in PageData format, filter == TextSearch + * PageNumber = 1, PageSize = List.size() + */ + public PageData findLwm2mListObjects(PageLink pageLink) { + PageImpl page = new PageImpl(getLwm2mObjects(getObjectIdFromTextSearch(pageLink.getTextSearch()), pageLink.getTextSearch(), pageLink.getSortOrder().getProperty(), pageLink.getSortOrder().getDirection().name())); + PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext()); + return pageData; + } - /** - * @param securityMode - * @param bootstrapServerIs - * @return ServerSecurityConfig more value is default: Important - port, host, publicKey - */ - public ServerSecurityConfig getBootstrapSecurityInfo (String securityMode,boolean bootstrapServerIs){ - LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(securityMode.toLowerCase()); - return getBootstrapServer(bootstrapServerIs, lwM2MSecurityMode); + /** + * Filter for id Object + * @param textSearch - + * @return - return Object id only first chartAt in textSearch + */ + private int[] getObjectIdFromTextSearch(String textSearch) { + String filtered = null; + if (textSearch !=null && !textSearch.isEmpty()) { + AtomicInteger a = new AtomicInteger(); + filtered = textSearch.chars () + .mapToObj(chr -> (char) chr) + .filter(i -> Character.isDigit(i) && textSearch.charAt(a.getAndIncrement()) == i) + .collect(Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)); } + return (filtered != null && !filtered.isEmpty()) ? new int[]{Integer.parseInt(filtered)} : new int[0]; + } - /** - * @param bootstrapServerIs - * @param mode - * @return ServerSecurityConfig more value is default: Important - port, host, publicKey - */ - private ServerSecurityConfig getBootstrapServer ( boolean bootstrapServerIs, LwM2MSecurityMode mode){ - ServerSecurityConfig bsServ = new ServerSecurityConfig(); - bsServ.setBootstrapServerIs(bootstrapServerIs); - if (bootstrapServerIs) { - bsServ.setServerId(contextBootStrap.getBootstrapServerId()); - switch (mode) { - case NO_SEC: - bsServ.setHost(contextBootStrap.getBootstrapHost()); - bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk()); - bsServ.setServerPublicKey(""); - break; - case PSK: - bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk()); - bsServ.setServerPublicKey(""); - break; - case RPK: - bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk()); - bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); - break; - case X509: - bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509()); - bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias())); - break; - default: - break; - } - } else { - bsServ.setServerId(contextServer.getServerId()); - switch (mode) { - case NO_SEC: - bsServ.setHost(contextServer.getServerHost()); - bsServ.setPort(contextServer.getServerPortNoSecPsk()); - bsServ.setServerPublicKey(""); - break; - case PSK: - bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerPortPsk()); - bsServ.setServerPublicKey(""); - break; - case RPK: - bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerPortRpk()); - bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY())); - break; - case X509: - bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerPortX509()); - bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias())); - break; - default: - break; - } + /** + * @param securityMode + * @param bootstrapServerIs + * @return ServerSecurityConfig more value is default: Important - port, host, publicKey + */ + public ServerSecurityConfig getBootstrapSecurityInfo(String securityMode, boolean bootstrapServerIs) { + LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(securityMode.toLowerCase()); + return getBootstrapServer(bootstrapServerIs, lwM2MSecurityMode); + } + + /** + * @param bootstrapServerIs + * @param mode + * @return ServerSecurityConfig more value is default: Important - port, host, publicKey + */ + private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) { + ServerSecurityConfig bsServ = new ServerSecurityConfig(); + bsServ.setBootstrapServerIs(bootstrapServerIs); + if (bootstrapServerIs) { + bsServ.setServerId(contextBootStrap.getBootstrapServerId()); + switch (mode) { + case NO_SEC: + bsServ.setHost(contextBootStrap.getBootstrapHost()); + bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk()); + bsServ.setServerPublicKey(""); + break; + case PSK: + bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); + bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk()); + bsServ.setServerPublicKey(""); + break; + case RPK: + bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); + bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk()); + bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); + break; + case X509: + bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); + bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509()); + bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias())); + break; + default: + break; + } + } else { + bsServ.setServerId(contextServer.getServerId()); + switch (mode) { + case NO_SEC: + bsServ.setHost(contextServer.getServerHost()); + bsServ.setPort(contextServer.getServerPortNoSecPsk()); + bsServ.setServerPublicKey(""); + break; + case PSK: + bsServ.setHost(contextServer.getServerSecureHost()); + bsServ.setPort(contextServer.getServerPortPsk()); + bsServ.setServerPublicKey(""); + break; + case RPK: + bsServ.setHost(contextServer.getServerSecureHost()); + bsServ.setPort(contextServer.getServerPortRpk()); + bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY())); + break; + case X509: + bsServ.setHost(contextServer.getServerSecureHost()); + bsServ.setPort(contextServer.getServerPortX509()); + bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias())); + break; + default: + break; } - return bsServ; } + return bsServ; + } - /** - * @param alias - * @return PublicKey format HexString or null - */ - private String getServerPublicKeyX509 (String alias){ - try { - X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias); - return Hex.encodeHexString(serverCertificate.getEncoded()); - } catch (CertificateEncodingException | KeyStoreException e) { - e.printStackTrace(); - } - return null; + /** + * @param alias + * @return PublicKey format HexString or null + */ + private String getServerPublicKeyX509(String alias) { + try { + X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias); + return Hex.encodeHexString(serverCertificate.getEncoded()); + } catch (CertificateEncodingException | KeyStoreException e) { + e.printStackTrace(); } + return null; + } - /** - * @param publicServerX - * @param publicServerY - * @return PublicKey format HexString or null - */ - private String getRPKPublicKey (String publicServerX, String publicServerY){ - try { - /** Get Elliptic Curve Parameter spec for secp256r1 */ - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - if (publicServerX != null && !publicServerX.isEmpty() && publicServerY != null && !publicServerY.isEmpty()) { - /** Get point values */ - byte[] publicX = Hex.decodeHex(publicServerX.toCharArray()); - byte[] publicY = Hex.decodeHex(publicServerY.toCharArray()); - /** Create key specs */ - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - /** Get keys */ - PublicKey publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - if (publicKey != null && publicKey.getEncoded().length > 0) { - return Hex.encodeHexString(publicKey.getEncoded()); - } + /** + * @param publicServerX + * @param publicServerY + * @return PublicKey format HexString or null + */ + private String getRPKPublicKey(String publicServerX, String publicServerY) { + try { + /** Get Elliptic Curve Parameter spec for secp256r1 */ + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); + algoParameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); + if (publicServerX != null && !publicServerX.isEmpty() && publicServerY != null && !publicServerY.isEmpty()) { + /** Get point values */ + byte[] publicX = Hex.decodeHex(publicServerX.toCharArray()); + byte[] publicY = Hex.decodeHex(publicServerY.toCharArray()); + /** Create key specs */ + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), + parameterSpec); + /** Get keys */ + PublicKey publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); + if (publicKey != null && publicKey.getEncoded().length > 0) { + return Hex.encodeHexString(publicKey.getEncoded()); } - } catch (GeneralSecurityException | IllegalArgumentException e) { - log.error("[{}] Failed generate Server RPK for profile", e.getMessage()); - throw new RuntimeException(e); } - return null; + } catch (GeneralSecurityException | IllegalArgumentException e) { + log.error("[{}] Failed generate Server RPK for profile", e.getMessage()); + throw new RuntimeException(e); } + return null; } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java b/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java index 5afb37c720..4bd700fdf9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java @@ -16,11 +16,7 @@ package org.thingsboard.server.common.data.lwm2m; import lombok.Data; -import lombok.Getter; -import lombok.Setter; -@Setter -@Getter @Data public class LwM2mObject { int id; From 1ed44010e4dc957a30a1e37a3c6979dbc7a900f9 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 23 Jan 2021 11:20:35 +0200 Subject: [PATCH 36/41] Lwm2m: update serverKetStore.jks --- .../resources/credentials/serverKeyStore.jks | Bin 5708 -> 5708 bytes .../main/data/credentials/serverKeyStore.jks | Bin 5708 -> 5708 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks b/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks index e1d6deff4b3fd948655f802da4ad65f0ba568f4a..9336dd11684c6cd9b2993149b2144d2d1cb30155 100644 GIT binary patch delta 5401 zcmV+!73S*9EX*vBV`J^SL%qhDFiXf1u5&V+63hvww~7J-0K-rONOXpy$&>qPLKKTO zT2=zf1YP?~0e1|bu$|>}w*z3UV~AI`RHb$9e#NY|HSEZOSUV`~NUy4xH4ESDg+yO< zMugKf@~pwmh>^xQ3^+3}H#9diGB_}S&XGVPW3v|93_4T!@AhNe>Z7(R3JQH`VgdsI z!%zfBG(7$8uD>3}+2kIEaK(?G`QtdlaT>G2{<$X$!-gfd-Ko zJm&MIuW`_yIw>g^P4-yU4AR$;p=|XWEU_8GBQ}l#@KiFk^)Wy&8wLp~hDe6@4FL%h zF%BLK0CNCk0CE6!0F!4g6 zu1dqyBU>neDF!0xii=^iE(yx02@?56k7+7w_w+HNljCK7Zx;n&`V(o~f*G#8O`u*E z#XkIhL`k6}Urk9?OU@MFpFK^h1vXY)(c4RskPwF&%hlkgY#3r0on3xf<)-EyrQjLV z;fK9NpD;@KPI@Go>52h84gKW}+i7C5FlJ3|V1=o3y_OT${bp0KO5aab6d1s%lDmD& z9djOg&+(kmtUDGH(rVtp7qz21PWTP#G|jMolytc`S_qWZ9dl&j=$U?{lW%YTiR2Z| zey|uhvKG zVo*K=&;Vqp$kW=W*1tjj@pAWWA>vARqTDhU;k!#ITql0KyBUuW z1~q8zIaMr61pdrK83A8vosnE^GDvS_458=axxKYx7GFm+kBG8B8sQtB8K7xQCswsrRUX&-Eoz8X+&>#yT@@#jM0ssePS zey~J*eGOdP=VRQCWf&p(Vr`0**M~AzLGy_c3?J;iCOP|oYKbRcMn>@zYIos8x*WtO ztu*%tuhBopB@4S1yCI-_q>tKvpUJMY!f6jn3HxZtM;8ekmi=8y&dbLM86GwH{|*~k zKwStg8I;}Gsi~(=8g^WkzqJ{SC>G`FHje@L!_K`I;vu&%&Ucf9=0PxF{-?)QZe>Gt z2n=O9qS(h)gXU00p)bO3*oCQ~=-Zcp%I64~NVp`++so(k6d1d*oVXc(X>V$;1IQb% z!rD#mY;RrwZ0iaQd&s3W)+kx7wXssdeHtEUI})lVkZCc^&)^Yzc0>*zbUTMYS*tMP z+Li{N_P1_m8aglR#!obu_Y`I}7>kYRrL@Z`b2qFa>6GD#sU1W?R?{JA2#2X(M!!JEDgSa$Az)HMeYRbf+&{u2;;?vHX(!W0e$6u^ddHqdKladnJGMQOeP zjfJ0#lO|m6)bz9`D}3Y0U@sK~|AwHtfG>``{Y#@_;qSh?ww|rQrm1MHBd?1De?@WM$@)7|o71GxfriczzjidaT&{Uy6dl+$qEe!R$XB z1$vx)YL0yOk=CVvWJJtu{&&a8p0SwzVNOi^N@jSX2d<9l3i8A za4E8*Q|^T<=qQ+I4pOA{gM&2;q&W$LK?Vbg!CZ{vR3o+DSnGE+cA)w($4Lkxb%_+M7vdYuIs!=yR5Z93Pg=*1+|D%|M`5Ty!p8?K!vpvd;m& zB%j3>hoq#-pD|VcJib0>>?Wa=G;($ME?>|4k;$G`leE1^SFT!0`#4S~1iQp7ljt51>6lFko#fz_;kAm7FwjgVyky$KM$=&)Js0>}Z{ zN%pVA?7gac-xC$I|i!rC>vl*^eeyR;G8tYPt$XVYNwLRgVL1drC) zI_L<0_rOo3!=9(`SGiXU=SH3l@k8CA8abiS#L79GAZ`v9J#}9WjkD|1iCxcdk~9@~ zdM8ITKn{nDw)JzkRTlU$67g!h5t<#AujvUbZCUoj>Yw{ErLe9Xv190UmT0#lUICvW2 z7vna_cFbKMJUwVj+5NWdv?EON_c^g3OEFCl_EEEsBBMC==t}t&EY;i5&+D5x_HF+6 z(Xnmt9|m{i2QC`pW!#WaKR3Tu3y-qfo70Mdu5-urzydoZtLABGYRMN^2@$V<&gY!R zb@l;$d-)p4CKP_G`Rb}}vZ7R8;W})S;(81cJuuc3)zZvgn$YxGSLgp&pg?jg4z_2# zub3#hItE+4IBHAESKHPRI;VQCG816Ao$Oay;R4j9H_I3vpoHO4E#Vw?D5BnvxTg(f zO(oFYJ*N~z)O^Hqvu3fJLuA2!RW!_lFeYZp59owQC&2JmDJvezoNNspeh!O{CZl|c zi-E@+YjZHaRkZ5@UVD-J!29D-;3VgNzx}dSk91t@tRi78oD9~6@(BDRpUi^VI-t$K zX&LHbDm}zLNSLt7^lVCXD9?i-_jGV-83vbJI&${W|3#1+l+hY%wQweX#f!|&3eRFy z{`#_^3l99vVv=#u0(s98fz}z!vaX(6N64NSR8BW{?n)=Y=0JQu((x6`1!hoe$`Un1 zEA|R;_Bp$vUhp(Kc2}YgqYrgH2@`RDm=AA&8w{0ofD98qnbdF_p9Qnx`EsXe-Lu+v z&Kw-y8~Ev(z~on5Vg0SFKFpZ#jTvg0eFE- zrMYW^*Ny_`>r;e>&b8$x-{p~6pSVZbxA;QZlwY9_Ok(LqlPGY-vllW{jSfVwnKy(o zPv;s8YwYL43CxJ2N~tidiP@`e>_@g)+GvV#3_sfN1atv*wH^vAty72 zxb16<3up;5?#U!bNKx}Tg;`hm47rRG-dKdnCfR6;8362R+G>%2@h1&nN|r8drV!tM zlqsoVRe!5=0%J8m9BZ(Ifel7YVl{!jHuYQsB>^ks-l&In04S1IX zz13ZgLce-|e6vMDw{@<7M_;IOs}EfNqq3DE&*{A^;s>dJ)d{B5+vY)o>OI`RRLGNR zgj%T;-$$g|ywQ!9WRuI;$Jb%e8y%5YV44Ag1${K%GqjV-Od)cswj5oC%C#1=L*wQm zAj}&H&xtXQ09hE|h@R`dR-Xz@%N4W`gK(AOdvU9dq&i|naz+N#Ux;l&PCl}lXBIh? z^(R31%C%#EFqgi{A@FRq#KJe+Jc5Sy1e@d$d+^NgKkxVQ`?KKvGNv`nyB8+SI03f- z@A-quy*5KHtW|t}$jB6%T}H4zG;zEw>>|WO)Rr|8DqN8GeWc=;~!}s!{wn0>tE2eA(%43l92m8O zo8TYGFo&=2kS2!M!aWWy9LF@tCfH%D8MbKhK#2A%=6l{QXC>u!~Na%d>*&OC2LaWqPt0atky&#i)4`{cBqSCn!qY-oCim zHbm_h#rNR`h7Kwha|VY0n#lCXAE})AH<`sVm~|ITju-5ON6VK16#CHG|JMH-7gvh0 z^%BvC#UPF7S@;zpiX5iG`c-0{IUy%-g?o|S-L;ONa{x&vk5E4J1H zDLdasQk0?hBz9DfFAl*gByD9wH#VcaW;I)r)?cAvc>4v}E7-|KP(1{r#}x_t+UE*? zo>l&;*Rt`L1c6sU4>ef{UK!e9$8K_kj%2&0^>;^|92Aq2xn6aTwG7Y@^>Gh z!}8bX?)q>d(ZiA)Hpw=i1U6LuYaVe*P%7YyL`Y^g;|3*DG2kI8>JATYo8KbheGuFg zUombMQgbnF-6k1PQmca^hE(~_Zf;zE=jXM-w}I-ii^<>P&l4iF=`SkeVLo=PreIP} zp;O;&VGB6twU`_`vEfMP$lFwau$L|NG~u(M(L3W}`Up-x{BS9(6a+xJdf)X?$9AuE z^X(gl;OpvrnAR1Kt2BZQh*m<;>ir2$Tc@NWKfiTQErRLK)nU8R)mBL&4k>JZ4Lzr* zrhl7Qr+I#AyDeekf<7MnCgwO*_3QvBzIH#jhbzwrJkc9EIeU{?@uK;s1eo{Kq(hDa80z1c&emfin(DQNZi(y^W83a^7AJ26> z^hY(Pm5~|`KAvshLbNd)`O&q1r!X=>!axgDbv$sieOZRUST&()F#(5=4~0Q0{mt=@ zteaYbls&yZMtlukchKH!NL;u0i1>e?@c|@fz;*2#)9{B8uS)cnw*vCJR+10|Ghj1_?YWI{~ zB$D)vRBCDxR7W6Bggxm*CADiFc%%yKRwDP%<%5U>D{g4@j|W|?lvZ)eV#AnDMjs>j zYwrH`p&?25iCfw~u|Ok#{>=A@&%ZJfrlv{&I@3gB8XcBR&Iv|`pv)C(R&%TXpn4<6}9fGw1O>bXpk@4cKP@3FUO zufe$S9Z7Y&SRHVtA0f_o7blce6fTqX5m+U@AkHL&AGmYxJBS%C%izUrl#M`XjVRjz zc6qfNNF+7GWsW6(L{tzVGyFdcv8hh3$={Lj*3+NfewK%AQ^=W@HSV)jQ9h@(D(uI? z2@Tp6wlIfRmY6**NYh^b-ZX%`U8^h+=*+m$l~RXQW4qQ-hSVD0MXdmEdG|RM1@@E# zOBkzArRtI0ltTAqtom#rkb31wC*|tf0x7NF*H{n+`GN+2Q;Q0&!0VfYiq^n_U72{v ze?f=bf=5OoSQ3#RAc+3IalMg%Q0yewtvwmG@;tp6<9D3ElP~-@Wg_lp)xhw53xn%d zVk;#m@mpf72L-4~iEZi;u=`H{OJRPl6Ysu~JmO}>p#mq9$ezx!B!J;`wJ+0tIA);- zz3)k}Zv<4+t2f^)9h$4J}@CL2?hl#4g&%j z1povTM)BJ@{l0oWI{;gv*}H|XVlr4d1Qet3-^6VcvSQ{&3~73V6_0W`$PG6F0|ADh D0a;A9 delta 5401 zcmV+!73S*9EX*vBV`F^A$JgSfjw=w@^p~;BU{`=raLED#0K-rONSy9I@6U;BHkJ*> z_uD%3KZW3*U6CBO{3{WAk62B z!%zfB?PDq<`<~4qd+l%${3I)^5^;DzGvT_%?vt+ImzPQI1ST4WnqrA<$wa|;JIaI2 z3@*sl!woY>(59HZCVHc5)G;`SFqKcOlaT>G2{ko0FflPRF))!mCV%)$3DB8U9LglI zgHnvWnHl|YMco1e0K-rONJDN?1NA?hvp(P_ceP)X?e7`K3{;r*pWK_woOE>q6Jkzq z-M0*ILa3~X+FDO=9+tks70nZ?RIH$1y-K8wLp~hDe6@4FL%h zF%BLK0CNCk0CE6!0F!8|~!`Xp;(Fb!W$y-}JizivrMsottSq`{zptU+I=dk4V~ zxgz6#U5qn9S~ITLla19H+r9J6TwdwLxMr)Wu(sSfTAcWCL8_ShiobmZD5eYlS{~6{ zM@YaEo>nCAOVA2cnrkb76|BROcF5^Ev9)OE+K&4Cg(o1ePVlR<2-3XN7(DAr(Rk4G zpAWK2R)WIEf>)eF!@HYbY{n$cLzN38!DA_ZoNE58YdM&U+G@J11>#J`x#I8UPq_w5 z*`VjAA;Vg@8v2pm$cRk*VN-sm z6n3Wh!(#b%%s^_CR4RF{d2If6BUHVL8C}aKsp0Evx>sMds;y-YM5eYD4Jom zxJ)^aRg{3o*;ag3F?$g|k3IR;9mj_n2KIz5kHY)V_=Zu->b{d~VAIK_#B0ZY(NL#s zj~61LOq8Tse-7UMHrJ7emzg}SWzQb)t<2rI>AhvtYeXWMJaW(7caBl_tl)&5KPI%2 zcaB?IN;))k&JBK^yn_30*~#2hY{aEwP{q5yfV95o z;w*Hlq;*E!8&|9Q<&X5|*`JI~|179aIWuNW{kh8GZkTrZ>&3+y1jk^1tb4nnEG=D4 zS9-Q&OK;eI=z{6~EL|TwQiE{|1#|PA#KE?e+cR8;C^T$iSHKm&Ot5|7VR(wHf4d_I z&Koig6emb&L^$DFk$2^jERL?M%01aNq2{DRtr9+-;#9Bnw(E3U0=$CZn6y`=iqx|n zk7u4!%qq;-SGnk=_K#kFcj+d`53Ui+-miI)ifni0o2FQZ&uB~gQ$xj<;ShAo|RM#rAeL>z5!MaQ(;e|uYrMUR?b)*SN z(i`o;*m2dTTx097s(pg*!a%Xfd}hUKS#yd|0qG!e>S~L{5+k(8rzFjw1pT$@l?CYJ zsi*;adeGwDggF@Kqm?EEqrp1tsEQH{uQA{DKTUSY$Y*W1uDL&8EQ|G(XN0oz@14jc ziW6=T_66~Oj*f9aw2U3^a76PHj8@cyU!tnM17+{G{opz67JPfUmb}d2*IYadg|eM} zD}5-90&9ZWnBBIT#pw^o$0_^jvsanQ`4kXmR0ab=Y$Sw(HBmFuVSz$cnM&Vkx(VWk zV~zWkm~gW>@((00)UC>iDDZ1s)G)O4ThlS}P)7KF9E+foO(tBc>j_+lb>Uibe_z^V zNCdnWCDi>;Ld_f#VZIAF&QR5)JHb@R4tN1DjoWxFjakFU7ZD1W@O16`t(?D{5O(<^ zuqQ;uFY@!1e$u{Y8?ywJ(vwVGeQ4$f=)ToJP!UPn0xRe($$ngjgVcCYY8Gdl47}_o zHEeW$CSupk!$ycwv|W1Y`7POAjN6$!M>KL8Qr_$%GKwOhd#&m8Rr-q2Y9u2cxZj7TI`RFpElf`fC7uIxTY`YcBjX5VELF za&(sMhMROvKpx~1APkUNKsoe)Yl&E1pBnbP_zv6{vDVPj8tQ3W&v;hT8(bmPEOQ%U z{K}7FEc0qSIy4cZc$PMXau9hC1CztsCfK8(0dpP#?BRaOGiCsq7)f zmX-XMn(WxFq9X}GS7GypW-(Flflep--)h|p$%FKKm6#EnAV4!8HoLNX?rUFSWj$ic z`I`YC7)leSgA>gT2*v^pTh-PWgGRqFDE#G1|XdEO{|4UwvxjVjpHc|^Q zRAXaTQz3IL)Cqw9Qy&!yWY{WGX>LSg5ouCpeX zayt7c?OIL$DI8};(}LjStz(;ie>I~HY`N;i@iJOr77~}(^sSV39*S7G3ns=nJ_}^Q ztE@a##M-7mH&}gJ8VU^FF;r<52~XC4Bb>u>(Er&o^sZ_{Y{9F-z5t25Vn&)6qeQx~ zY{nITqxBPe{XH$@F`stO$#N%;f+kaTWf!BaC=P|!*tk~RqfQ$$XaHV+)nU8gOfn>M zw`!qNl$8=VLxj7CSgwa&djRykPwQE&5CH&|^A0ak2vxgbr!i(f)4Jm3NJe%r^vsn9 zzk{@-3zw%??&HuDsWcd5FzSQ0X`^qIjw4){_V?Azb|NWB?H0)(&qPs61=o_XDHzrl z=ZL<;ZUW{7){WB}eV9alQcafxjJBR{6tJW}3GC2OP%@)L!Lc(|+&P2c7vdpQGCb=Q24B-TS0Pe-iHScIHb2Uv# zLmci+$c)_R&8Bm`xx&l7MRkgDf~4>^BD0(n-z!`YBnCQ)Z>fKCL|Y6@OUi*9&qP}l z9)Jfepfuz@(B;*vSchol-*1$`^Edd_i2<2Zq}FV^dGMv9(HZH;Ksyoc8fEqv?Fm;( zk$$}UN9o#(-FSn48q(|+rIXqv5P-Mg?WY94qs6(fw^SwvwzHt}Y>Mtb86zPb^kn<* z{YvN#^enc5gV}KFuCK(?`b`Pj_~3osrI(~W#R)it!s1diQ1bR*wyI%pI>iXYw5%WR zfuae=f3k;Z^MpJT zXy{xh)yP5=q0D(8D$68xY-g|t}h_uYuQqT-79_4u_NvWhbhCjBazRj1dA_n7i8 z{*oT^LE_PWvH?hJGKdl5c3ug-<$&0Kw-Fhbt#Qnuj$x7~=AUg%!(~M_FS){+ z+nZnCN^s$1hscR5jI)M#Zuj>ovDoYTmN@A+9RVl1a#u-CX!zH4uQovQV@Qi8DM*>FM)Tc@F??t6friS#GOOq!K+15jZG$Q$GTjlr^fT_ zD4~beZ-#<8cV)lhp^^4rca`M{VQFok57PNn?Ku3_Z_Amg1Ys-iZMkE%x0|R@L7N!B z+FgmacRtK%{0|!6q26Eh;g4I5v)r5oxVyV58uSdX8J$`G^|{?>HRnN(=-C(? z!Vt&7T`~T$-cOjYMZqNW`E3B&k)qUI5(%7d3-@S;82jC=UOvC3AsoPu13VxibQy>3iv<3Mq`Z{=p9slF=c5*$U8m?G~`(vshJI{=XY|Z3~4}Vu;#q3Of4E4)B?sGp> zBIF2>=Si#iPI)=&2SYN4(5A$5ort29iVZ!hZ$kMfygj5~0({ArZb@WZ zAcPY~qOwl#o@KG94_h&<`@DmHsP00vw5%b~i+p~hxfHQDL|k9E1)kLg`R;@llgs{D z&sGO2-i{>9JP|15*Mj+&#HOnbiFn|$$}EltpUC^S(1E(aEAeq?r%&s=S80)Wn$*V( zHJp~Okb!2fJ6d$RnnB91);e!J66EH!WL&ZMVp{*C72@@lf^tF|FR&unsOc=KPa%?j zZpEF`U3dyuL{h|T`rSHGse6M4iH~qk9BzWi5T0lc$VI5JBDJ@1N57N$K!kx5nG@v* zU`6>4sX+c`@y7@Pr(SlXnGO~r_Wm`<0lESUb{))q9)Z34Xh z`5e$n@vaLVq?T3d~{wv9=DNPFiRZ7>_PntJqwGMpJiox((TU0LvpHLT+Gs(776@xo-%v&r0m zhUR?}Bfy!zbKk!p5Wa_6<)LzAQ?Yrvj+qLFej)N1cL~Xv)^210YctqsH{=8n$1jJu zA<-p);b=TAf8_aYY?E9m24Uz7qWr!v6<)%LJ+Ei!VGL7$dC-@shGhKnLDOr~pVRhY zS8TniCW+uh4&!flt6L{cJPP&cr6(JJuQ>^=XHS1g@GL2x+nuIzcS1&VFzqe=Zy&$> zdp6xCx-{!(a2+JR3n=6KvE|qkhOgvX^nFFm=Kns!L6;^4`$;bd+=7aKoJxW7<$@Ia zJuNOnTjzX#LT5PDBAIjz4AM7x^t?bb{&>{ARA2D!hUUkf_R!rqPLKKTO zT2=zf1YP?~0e1|bu$|>}w*z3UV~AI`RHb$9e#NY|HSEZOSUV`~NUy4xH4ESDg+yO< zMugKf@~pwmh>^xQ3^+3}H#9diGB_}S&XGVPW3v|93_4T!@AhNe>Z7(R3JQH`VgdsI z!%zfBG(7$8uD>3}+2kIEaK(?G`QtdlaT>G2{<$X$!-gfd-Ko zJm&MIuW`_yIw>g^P4-yU4AR$;p=|XWEU_8GBQ}l#@KiFk^)Wy&8wLp~hDe6@4FL%h zF%BLK0CNCk0CE6!0F!4g6 zu1dqyBU>neDF!0xii=^iE(yx02@?56k7+7w_w+HNljCK7Zx;n&`V(o~f*G#8O`u*E z#XkIhL`k6}Urk9?OU@MFpFK^h1vXY)(c4RskPwF&%hlkgY#3r0on3xf<)-EyrQjLV z;fK9NpD;@KPI@Go>52h84gKW}+i7C5FlJ3|V1=o3y_OT${bp0KO5aab6d1s%lDmD& z9djOg&+(kmtUDGH(rVtp7qz21PWTP#G|jMolytc`S_qWZ9dl&j=$U?{lW%YTiR2Z| zey|uhvKG zVo*K=&;Vqp$kW=W*1tjj@pAWWA>vARqTDhU;k!#ITql0KyBUuW z1~q8zIaMr61pdrK83A8vosnE^GDvS_458=axxKYx7GFm+kBG8B8sQtB8K7xQCswsrRUX&-Eoz8X+&>#yT@@#jM0ssePS zey~J*eGOdP=VRQCWf&p(Vr`0**M~AzLGy_c3?J;iCOP|oYKbRcMn>@zYIos8x*WtO ztu*%tuhBopB@4S1yCI-_q>tKvpUJMY!f6jn3HxZtM;8ekmi=8y&dbLM86GwH{|*~k zKwStg8I;}Gsi~(=8g^WkzqJ{SC>G`FHje@L!_K`I;vu&%&Ucf9=0PxF{-?)QZe>Gt z2n=O9qS(h)gXU00p)bO3*oCQ~=-Zcp%I64~NVp`++so(k6d1d*oVXc(X>V$;1IQb% z!rD#mY;RrwZ0iaQd&s3W)+kx7wXssdeHtEUI})lVkZCc^&)^Yzc0>*zbUTMYS*tMP z+Li{N_P1_m8aglR#!obu_Y`I}7>kYRrL@Z`b2qFa>6GD#sU1W?R?{JA2#2X(M!!JEDgSa$Az)HMeYRbf+&{u2;;?vHX(!W0e$6u^ddHqdKladnJGMQOeP zjfJ0#lO|m6)bz9`D}3Y0U@sK~|AwHtfG>``{Y#@_;qSh?ww|rQrm1MHBd?1De?@WM$@)7|o71GxfriczzjidaT&{Uy6dl+$qEe!R$XB z1$vx)YL0yOk=CVvWJJtu{&&a8p0SwzVNOi^N@jSX2d<9l3i8A za4E8*Q|^T<=qQ+I4pOA{gM&2;q&W$LK?Vbg!CZ{vR3o+DSnGE+cA)w($4Lkxb%_+M7vdYuIs!=yR5Z93Pg=*1+|D%|M`5Ty!p8?K!vpvd;m& zB%j3>hoq#-pD|VcJib0>>?Wa=G;($ME?>|4k;$G`leE1^SFT!0`#4S~1iQp7ljt51>6lFko#fz_;kAm7FwjgVyky$KM$=&)Js0>}Z{ zN%pVA?7gac-xC$I|i!rC>vl*^eeyR;G8tYPt$XVYNwLRgVL1drC) zI_L<0_rOo3!=9(`SGiXU=SH3l@k8CA8abiS#L79GAZ`v9J#}9WjkD|1iCxcdk~9@~ zdM8ITKn{nDw)JzkRTlU$67g!h5t<#AujvUbZCUoj>Yw{ErLe9Xv190UmT0#lUICvW2 z7vna_cFbKMJUwVj+5NWdv?EON_c^g3OEFCl_EEEsBBMC==t}t&EY;i5&+D5x_HF+6 z(Xnmt9|m{i2QC`pW!#WaKR3Tu3y-qfo70Mdu5-urzydoZtLABGYRMN^2@$V<&gY!R zb@l;$d-)p4CKP_G`Rb}}vZ7R8;W})S;(81cJuuc3)zZvgn$YxGSLgp&pg?jg4z_2# zub3#hItE+4IBHAESKHPRI;VQCG816Ao$Oay;R4j9H_I3vpoHO4E#Vw?D5BnvxTg(f zO(oFYJ*N~z)O^Hqvu3fJLuA2!RW!_lFeYZp59owQC&2JmDJvezoNNspeh!O{CZl|c zi-E@+YjZHaRkZ5@UVD-J!29D-;3VgNzx}dSk91t@tRi78oD9~6@(BDRpUi^VI-t$K zX&LHbDm}zLNSLt7^lVCXD9?i-_jGV-83vbJI&${W|3#1+l+hY%wQweX#f!|&3eRFy z{`#_^3l99vVv=#u0(s98fz}z!vaX(6N64NSR8BW{?n)=Y=0JQu((x6`1!hoe$`Un1 zEA|R;_Bp$vUhp(Kc2}YgqYrgH2@`RDm=AA&8w{0ofD98qnbdF_p9Qnx`EsXe-Lu+v z&Kw-y8~Ev(z~on5Vg0SFKFpZ#jTvg0eFE- zrMYW^*Ny_`>r;e>&b8$x-{p~6pSVZbxA;QZlwY9_Ok(LqlPGY-vllW{jSfVwnKy(o zPv;s8YwYL43CxJ2N~tidiP@`e>_@g)+GvV#3_sfN1atv*wH^vAty72 zxb16<3up;5?#U!bNKx}Tg;`hm47rRG-dKdnCfR6;8362R+G>%2@h1&nN|r8drV!tM zlqsoVRe!5=0%J8m9BZ(Ifel7YVl{!jHuYQsB>^ks-l&In04S1IX zz13ZgLce-|e6vMDw{@<7M_;IOs}EfNqq3DE&*{A^;s>dJ)d{B5+vY)o>OI`RRLGNR zgj%T;-$$g|ywQ!9WRuI;$Jb%e8y%5YV44Ag1${K%GqjV-Od)cswj5oC%C#1=L*wQm zAj}&H&xtXQ09hE|h@R`dR-Xz@%N4W`gK(AOdvU9dq&i|naz+N#Ux;l&PCl}lXBIh? z^(R31%C%#EFqgi{A@FRq#KJe+Jc5Sy1e@d$d+^NgKkxVQ`?KKvGNv`nyB8+SI03f- z@A-quy*5KHtW|t}$jB6%T}H4zG;zEw>>|WO)Rr|8DqN8GeWc=;~!}s!{wn0>tE2eA(%43l92m8O zo8TYGFo&=2kS2!M!aWWy9LF@tCfH%D8MbKhK#2A%=6l{QXC>u!~Na%d>*&OC2LaWqPt0atky&#i)4`{cBqSCn!qY-oCim zHbm_h#rNR`h7Kwha|VY0n#lCXAE})AH<`sVm~|ITju-5ON6VK16#CHG|JMH-7gvh0 z^%BvC#UPF7S@;zpiX5iG`c-0{IUy%-g?o|S-L;ONa{x&vk5E4J1H zDLdasQk0?hBz9DfFAl*gByD9wH#VcaW;I)r)?cAvc>4v}E7-|KP(1{r#}x_t+UE*? zo>l&;*Rt`L1c6sU4>ef{UK!e9$8K_kj%2&0^>;^|92Aq2xn6aTwG7Y@^>Gh z!}8bX?)q>d(ZiA)Hpw=i1U6LuYaVe*P%7YyL`Y^g;|3*DG2kI8>JATYo8KbheGuFg zUombMQgbnF-6k1PQmca^hE(~_Zf;zE=jXM-w}I-ii^<>P&l4iF=`SkeVLo=PreIP} zp;O;&VGB6twU`_`vEfMP$lFwau$L|NG~u(M(L3W}`Up-x{BS9(6a+xJdf)X?$9AuE z^X(gl;OpvrnAR1Kt2BZQh*m<;>ir2$Tc@NWKfiTQErRLK)nU8R)mBL&4k>JZ4Lzr* zrhl7Qr+I#AyDeekf<7MnCgwO*_3QvBzIH#jhbzwrJkc9EIeU{?@uK;s1eo{Kq(hDa80z1c&emfin(DQNZi(y^W83a^7AJ26> z^hY(Pm5~|`KAvshLbNd)`O&q1r!X=>!axgDbv$sieOZRUST&()F#(5=4~0Q0{mt=@ zteaYbls&yZMtlukchKH!NL;u0i1>e?@c|@fz;*2#)9{B8uS)cnw*vCJR+10|Ghj1_?YWI{~ zB$D)vRBCDxR7W6Bggxm*CADiFc%%yKRwDP%<%5U>D{g4@j|W|?lvZ)eV#AnDMjs>j zYwrH`p&?25iCfw~u|Ok#{>=A@&%ZJfrlv{&I@3gB8XcBR&Iv|`pv)C(R&%TXpn4<6}9fGw1O>bXpk@4cKP@3FUO zufe$S9Z7Y&SRHVtA0f_o7blce6fTqX5m+U@AkHL&AGmYxJBS%C%izUrl#M`XjVRjz zc6qfNNF+7GWsW6(L{tzVGyFdcv8hh3$={Lj*3+NfewK%AQ^=W@HSV)jQ9h@(D(uI? z2@Tp6wlIfRmY6**NYh^b-ZX%`U8^h+=*+m$l~RXQW4qQ-hSVD0MXdmEdG|RM1@@E# zOBkzArRtI0ltTAqtom#rkb31wC*|tf0x7NF*H{n+`GN+2Q;Q0&!0VfYiq^n_U72{v ze?f=bf=5OoSQ3#RAc+3IalMg%Q0yewtvwmG@;tp6<9D3ElP~-@Wg_lp)xhw53xn%d zVk;#m@mpf72L-4~iEZi;u=`H{OJRPl6Ysu~JmO}>p#mq9$ezx!B!J;`wJ+0tIA);- zz3)k}Zv<4+t2f^)9h$4J}@CL2?hl#4g&%j z1povTM)BJ@{l0oWI{;gv*}H|XVlr4d1Qet3-^6VcvSQ{&3~73V6_0W`$PG6F0|ADh D0a;A9 delta 5401 zcmV+!73S*9EX*vBV`F^A$JgSfjw=w@^p~;BU{`=raLED#0K-rONSy9I@6U;BHkJ*> z_uD%3KZW3*U6CBO{3{WAk62B z!%zfB?PDq<`<~4qd+l%${3I)^5^;DzGvT_%?vt+ImzPQI1ST4WnqrA<$wa|;JIaI2 z3@*sl!woY>(59HZCVHc5)G;`SFqKcOlaT>G2{ko0FflPRF))!mCV%)$3DB8U9LglI zgHnvWnHl|YMco1e0K-rONJDN?1NA?hvp(P_ceP)X?e7`K3{;r*pWK_woOE>q6Jkzq z-M0*ILa3~X+FDO=9+tks70nZ?RIH$1y-K8wLp~hDe6@4FL%h zF%BLK0CNCk0CE6!0F!8|~!`Xp;(Fb!W$y-}JizivrMsottSq`{zptU+I=dk4V~ zxgz6#U5qn9S~ITLla19H+r9J6TwdwLxMr)Wu(sSfTAcWCL8_ShiobmZD5eYlS{~6{ zM@YaEo>nCAOVA2cnrkb76|BROcF5^Ev9)OE+K&4Cg(o1ePVlR<2-3XN7(DAr(Rk4G zpAWK2R)WIEf>)eF!@HYbY{n$cLzN38!DA_ZoNE58YdM&U+G@J11>#J`x#I8UPq_w5 z*`VjAA;Vg@8v2pm$cRk*VN-sm z6n3Wh!(#b%%s^_CR4RF{d2If6BUHVL8C}aKsp0Evx>sMds;y-YM5eYD4Jom zxJ)^aRg{3o*;ag3F?$g|k3IR;9mj_n2KIz5kHY)V_=Zu->b{d~VAIK_#B0ZY(NL#s zj~61LOq8Tse-7UMHrJ7emzg}SWzQb)t<2rI>AhvtYeXWMJaW(7caBl_tl)&5KPI%2 zcaB?IN;))k&JBK^yn_30*~#2hY{aEwP{q5yfV95o z;w*Hlq;*E!8&|9Q<&X5|*`JI~|179aIWuNW{kh8GZkTrZ>&3+y1jk^1tb4nnEG=D4 zS9-Q&OK;eI=z{6~EL|TwQiE{|1#|PA#KE?e+cR8;C^T$iSHKm&Ot5|7VR(wHf4d_I z&Koig6emb&L^$DFk$2^jERL?M%01aNq2{DRtr9+-;#9Bnw(E3U0=$CZn6y`=iqx|n zk7u4!%qq;-SGnk=_K#kFcj+d`53Ui+-miI)ifni0o2FQZ&uB~gQ$xj<;ShAo|RM#rAeL>z5!MaQ(;e|uYrMUR?b)*SN z(i`o;*m2dTTx097s(pg*!a%Xfd}hUKS#yd|0qG!e>S~L{5+k(8rzFjw1pT$@l?CYJ zsi*;adeGwDggF@Kqm?EEqrp1tsEQH{uQA{DKTUSY$Y*W1uDL&8EQ|G(XN0oz@14jc ziW6=T_66~Oj*f9aw2U3^a76PHj8@cyU!tnM17+{G{opz67JPfUmb}d2*IYadg|eM} zD}5-90&9ZWnBBIT#pw^o$0_^jvsanQ`4kXmR0ab=Y$Sw(HBmFuVSz$cnM&Vkx(VWk zV~zWkm~gW>@((00)UC>iDDZ1s)G)O4ThlS}P)7KF9E+foO(tBc>j_+lb>Uibe_z^V zNCdnWCDi>;Ld_f#VZIAF&QR5)JHb@R4tN1DjoWxFjakFU7ZD1W@O16`t(?D{5O(<^ zuqQ;uFY@!1e$u{Y8?ywJ(vwVGeQ4$f=)ToJP!UPn0xRe($$ngjgVcCYY8Gdl47}_o zHEeW$CSupk!$ycwv|W1Y`7POAjN6$!M>KL8Qr_$%GKwOhd#&m8Rr-q2Y9u2cxZj7TI`RFpElf`fC7uIxTY`YcBjX5VELF za&(sMhMROvKpx~1APkUNKsoe)Yl&E1pBnbP_zv6{vDVPj8tQ3W&v;hT8(bmPEOQ%U z{K}7FEc0qSIy4cZc$PMXau9hC1CztsCfK8(0dpP#?BRaOGiCsq7)f zmX-XMn(WxFq9X}GS7GypW-(Flflep--)h|p$%FKKm6#EnAV4!8HoLNX?rUFSWj$ic z`I`YC7)leSgA>gT2*v^pTh-PWgGRqFDE#G1|XdEO{|4UwvxjVjpHc|^Q zRAXaTQz3IL)Cqw9Qy&!yWY{WGX>LSg5ouCpeX zayt7c?OIL$DI8};(}LjStz(;ie>I~HY`N;i@iJOr77~}(^sSV39*S7G3ns=nJ_}^Q ztE@a##M-7mH&}gJ8VU^FF;r<52~XC4Bb>u>(Er&o^sZ_{Y{9F-z5t25Vn&)6qeQx~ zY{nITqxBPe{XH$@F`stO$#N%;f+kaTWf!BaC=P|!*tk~RqfQ$$XaHV+)nU8gOfn>M zw`!qNl$8=VLxj7CSgwa&djRykPwQE&5CH&|^A0ak2vxgbr!i(f)4Jm3NJe%r^vsn9 zzk{@-3zw%??&HuDsWcd5FzSQ0X`^qIjw4){_V?Azb|NWB?H0)(&qPs61=o_XDHzrl z=ZL<;ZUW{7){WB}eV9alQcafxjJBR{6tJW}3GC2OP%@)L!Lc(|+&P2c7vdpQGCb=Q24B-TS0Pe-iHScIHb2Uv# zLmci+$c)_R&8Bm`xx&l7MRkgDf~4>^BD0(n-z!`YBnCQ)Z>fKCL|Y6@OUi*9&qP}l z9)Jfepfuz@(B;*vSchol-*1$`^Edd_i2<2Zq}FV^dGMv9(HZH;Ksyoc8fEqv?Fm;( zk$$}UN9o#(-FSn48q(|+rIXqv5P-Mg?WY94qs6(fw^SwvwzHt}Y>Mtb86zPb^kn<* z{YvN#^enc5gV}KFuCK(?`b`Pj_~3osrI(~W#R)it!s1diQ1bR*wyI%pI>iXYw5%WR zfuae=f3k;Z^MpJT zXy{xh)yP5=q0D(8D$68xY-g|t}h_uYuQqT-79_4u_NvWhbhCjBazRj1dA_n7i8 z{*oT^LE_PWvH?hJGKdl5c3ug-<$&0Kw-Fhbt#Qnuj$x7~=AUg%!(~M_FS){+ z+nZnCN^s$1hscR5jI)M#Zuj>ovDoYTmN@A+9RVl1a#u-CX!zH4uQovQV@Qi8DM*>FM)Tc@F??t6friS#GOOq!K+15jZG$Q$GTjlr^fT_ zD4~beZ-#<8cV)lhp^^4rca`M{VQFok57PNn?Ku3_Z_Amg1Ys-iZMkE%x0|R@L7N!B z+FgmacRtK%{0|!6q26Eh;g4I5v)r5oxVyV58uSdX8J$`G^|{?>HRnN(=-C(? z!Vt&7T`~T$-cOjYMZqNW`E3B&k)qUI5(%7d3-@S;82jC=UOvC3AsoPu13VxibQy>3iv<3Mq`Z{=p9slF=c5*$U8m?G~`(vshJI{=XY|Z3~4}Vu;#q3Of4E4)B?sGp> zBIF2>=Si#iPI)=&2SYN4(5A$5ort29iVZ!hZ$kMfygj5~0({ArZb@WZ zAcPY~qOwl#o@KG94_h&<`@DmHsP00vw5%b~i+p~hxfHQDL|k9E1)kLg`R;@llgs{D z&sGO2-i{>9JP|15*Mj+&#HOnbiFn|$$}EltpUC^S(1E(aEAeq?r%&s=S80)Wn$*V( zHJp~Okb!2fJ6d$RnnB91);e!J66EH!WL&ZMVp{*C72@@lf^tF|FR&unsOc=KPa%?j zZpEF`U3dyuL{h|T`rSHGse6M4iH~qk9BzWi5T0lc$VI5JBDJ@1N57N$K!kx5nG@v* zU`6>4sX+c`@y7@Pr(SlXnGO~r_Wm`<0lESUb{))q9)Z34Xh z`5e$n@vaLVq?T3d~{wv9=DNPFiRZ7>_PntJqwGMpJiox((TU0LvpHLT+Gs(776@xo-%v&r0m zhUR?}Bfy!zbKk!p5Wa_6<)LzAQ?Yrvj+qLFej)N1cL~Xv)^210YctqsH{=8n$1jJu zA<-p);b=TAf8_aYY?E9m24Uz7qWr!v6<)%LJ+Ei!VGL7$dC-@shGhKnLDOr~pVRhY zS8TniCW+uh4&!flt6L{cJPP&cr6(JJuQ>^=XHS1g@GL2x+nuIzcS1&VFzqe=Zy&$> zdp6xCx-{!(a2+JR3n=6KvE|qkhOgvX^nFFm=Kns!L6;^4`$;bd+=7aKoJxW7<$@Ia zJuNOnTjzX#LT5PDBAIjz4AM7x^t?bb{&>{ARA2D!hUUkf_R!r Date: Mon, 25 Jan 2021 15:57:09 +0200 Subject: [PATCH 37/41] Lwm2m: back&front: update filter for objectIds --- .../controller/DeviceLwm2mController.java | 17 +++++++++-------- .../service/lwm2m/LwM2MModelsRepository.java | 17 ++++++++++++----- .../src/app/core/http/device-profile.service.ts | 14 +++++++------- ...profile-transport-configuration.component.ts | 15 ++++++++------- .../device/lwm2m/lwm2m-object-list.component.ts | 16 +++++++--------- 5 files changed, 43 insertions(+), 36 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java index a12bfb8900..4ddeff5af6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceLwm2mController.java @@ -49,14 +49,15 @@ import java.util.Map; public class DeviceLwm2mController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/lwm2m/deviceProfile", params = {"objectIds"}, method = RequestMethod.GET) + @RequestMapping(value = "/lwm2m/deviceProfile", params = {"sortOrder", "sortProperty"}, method = RequestMethod.GET) @ResponseBody - public List getLwm2mListObjects(@RequestParam int[] objectIds, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String sortProperty, - @RequestParam(required = false) String sortOrder) throws ThingsboardException { + public List getLwm2mListObjects(@RequestParam String sortOrder, + @RequestParam String sortProperty, + @RequestParam(required = false) int[] objectIds, + @RequestParam(required = false) String searchText) + throws ThingsboardException { try { - return lwM2MModelsRepository.getLwm2mObjects(objectIds, textSearch, sortProperty, sortOrder); + return lwM2MModelsRepository.getLwm2mObjects(objectIds, searchText, sortProperty, sortOrder); } catch (Exception e) { throw handleException(e); } @@ -67,11 +68,11 @@ public class DeviceLwm2mController extends BaseController { @ResponseBody public PageData getLwm2mListObjects(@RequestParam int pageSize, @RequestParam int page, - @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String searchText, @RequestParam(required = false) String sortProperty, @RequestParam(required = false) String sortOrder) throws ThingsboardException { try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageLink pageLink = createPageLink(pageSize, page, searchText, sortProperty, sortOrder); return checkNotNull(lwM2MModelsRepository.findDeviceLwm2mObjects(getTenantId(), pageLink)); } catch (Exception e) { throw handleException(e); diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java index 1eb454c23a..e909dd8741 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java @@ -81,10 +81,14 @@ public class LwM2MModelsRepository { * if textSearch is null then it uses AllList from List) */ public List getLwm2mObjects(int[] objectIds, String textSearch, String sortProperty, String sortOrder) { - return getLwm2mObjects((objectIds.length > 0 && textSearch != null && !textSearch.isEmpty()) ? - (ObjectModel element) -> IntStream.of(objectIds).anyMatch(x -> x == element.id) || element.name.contains(textSearch) : - (objectIds.length > 0) ? - (ObjectModel element) -> IntStream.of(objectIds).anyMatch(x -> x == element.id) : + if (objectIds == null && textSearch != null && !textSearch.isEmpty()) { + objectIds = getObjectIdFromTextSearch(textSearch); + } + int[] finalObjectIds = objectIds; + return getLwm2mObjects((objectIds != null && objectIds.length > 0 && textSearch != null && !textSearch.isEmpty()) ? + (ObjectModel element) -> IntStream.of(finalObjectIds).anyMatch(x -> x == element.id) || element.name.toLowerCase().contains(textSearch.toLowerCase()) : + (objectIds != null && objectIds.length > 0) ? + (ObjectModel element) -> IntStream.of(finalObjectIds).anyMatch(x -> x == element.id) : (textSearch != null && !textSearch.isEmpty()) ? (ObjectModel element) -> element.name.contains(textSearch) : null, @@ -165,7 +169,10 @@ public class LwM2MModelsRepository { * PageNumber = 1, PageSize = List.size() */ public PageData findLwm2mListObjects(PageLink pageLink) { - PageImpl page = new PageImpl(getLwm2mObjects(getObjectIdFromTextSearch(pageLink.getTextSearch()), pageLink.getTextSearch(), pageLink.getSortOrder().getProperty(), pageLink.getSortOrder().getDirection().name())); + PageImpl page = new PageImpl(getLwm2mObjects(getObjectIdFromTextSearch(pageLink.getTextSearch()), + pageLink.getTextSearch(), + pageLink.getSortOrder().getProperty(), + pageLink.getSortOrder().getDirection().name())); PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext()); return pageData; } diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index df68af629e..26c642c4bc 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -21,7 +21,7 @@ import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { Observable } from 'rxjs'; import { PageData } from '@shared/models/page/page-data'; import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; -import { isDefinedAndNotNull } from '@core/utils'; +import { isDefinedAndNotNull, isEmptyStr } from '@core/utils'; import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/profile-config.models'; import { SortOrder } from '@shared/models/page/sort-order'; @@ -43,14 +43,14 @@ export class DeviceProfileService { return this.http.get(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); } - public getLwm2mObjects(objectIds: number[] = [], searchText?: string, sortOrder?: SortOrder, config?: RequestConfig): + public getLwm2mObjects(sortOrder: SortOrder, objectIds?: number[], searchText?: string, config?: RequestConfig): Observable> { - let url = `/api/lwm2m/deviceProfile/?objectIds=${objectIds}`; - if (isDefinedAndNotNull(searchText)) { - url += `&searchText=${searchText}`; + let url = `/api/lwm2m/deviceProfile/?sortProperty=${sortOrder.property}&sortOrder=${sortOrder.direction}`; + if (isDefinedAndNotNull(objectIds) && objectIds.length > 0) { + url += `&objectIds=${objectIds}`; } - if (isDefinedAndNotNull(sortOrder)) { - url += `&sortProperty=${sortOrder.property}&sortOrder=${sortOrder.direction}`; + if (isDefinedAndNotNull(searchText) && !isEmptyStr(searchText)) { + url += `&searchText=${searchText}`; } return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index d5dc7e90b6..f66a45a178 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -74,7 +74,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.requiredValue = coerceBooleanProperty(value); } - private propagateChange = (v: any) => { }; + private propagateChange = (v: any) => { + }; constructor(private store: Store, private fb: FormBuilder, @@ -130,8 +131,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro writeValue(value: any | null): void { this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value; this.lwm2mDeviceConfigFormGroup.patchValue({ - configurationJson: this.configurationValue - }, {emitEvent: false}); + configurationJson: this.configurationValue + }, {emitEvent: false}); this.initWriteValue(); } @@ -143,7 +144,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro property: 'id', direction: Direction.ASC }; - this.deviceProfileService.getLwm2mObjects(modelValue.objectIds, null, sortOrder).subscribe( + this.deviceProfileService.getLwm2mObjects(sortOrder, modelValue.objectIds, null).subscribe( (objectsList) => { modelValue.objectsList = objectsList; this.updateWriteValue(modelValue); @@ -300,9 +301,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private addInstances = (attribute: string[], telemetry: string[], clientObserveAttrTelemetry: ObjectLwM2M[]): void => { const instancesPath = attribute.concat(telemetry) - .filter(instance => !instance.includes('/0/')) - .map(instance => this.convertPathToInstance(instance)) - .sort(); + .filter(instance => !instance.includes('/0/')) + .map(instance => this.convertPathToInstance(instance)) + .sort(); new Set(instancesPath).forEach(path => { const pathParameter = Array.from(path.split('/'), Number); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index d0f6c3cc47..0426f1d032 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -19,12 +19,11 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Valida import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { Observable } from 'rxjs'; -import { filter, map, mergeMap, share, tap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { filter, mergeMap, share, tap } from 'rxjs/operators'; import { ObjectLwM2M } from './profile-config.models'; import { TranslateService } from '@ngx-translate/core'; import { DeviceProfileService } from '@core/http/device-profile.service'; -import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; @Component({ @@ -115,6 +114,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V this.disabled = isDisabled; if (isDisabled) { this.lwm2mListFormGroup.disable({emitEvent: false}); + this.clear(); } else { this.lwm2mListFormGroup.enable({emitEvent: false}); } @@ -168,16 +168,14 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V return object ? object.name : undefined; } - // @TODO: add support page size - // @TODO: filter for id + name private fetchListObjects = (searchText?: string): Observable> => { this.searchText = searchText; - const pageLink = new PageLink(50, 0, searchText, { + const sortOrder = { property: 'name', direction: Direction.ASC - }); - return this.deviceProfileService.getLwm2mObjectsPage(pageLink, {ignoreLoading: true}).pipe( - map(pageData => pageData.data) + }; + return this.deviceProfileService.getLwm2mObjects(sortOrder, null, searchText).pipe( + mergeMap(objectsList => of(objectsList)) ); } From ca940ba1b6619802b9c1b6abcf26e0a1e3416588 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 25 Jan 2021 19:54:01 +0200 Subject: [PATCH 38/41] UI: Refactoring LwM2M --- ...ofile-transport-configuration.component.ts | 134 +++++------------- .../lwm2m/lwm2m-object-list.component.ts | 9 +- .../lwm2m-observe-attr-telemetry.component.ts | 4 +- 3 files changed, 44 insertions(+), 103 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index f66a45a178..86743074f9 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -75,15 +75,15 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private propagateChange = (v: any) => { - }; + } constructor(private store: Store, private fb: FormBuilder, private deviceProfileService: DeviceProfileService, @Inject(WINDOW) private window: Window) { this.lwm2mDeviceProfileFormGroup = this.fb.group({ - objectIds: [{}, Validators.required], - observeAttrTelemetry: [{clientLwM2M: []}, Validators.required], + objectIds: [[], Validators.required], + observeAttrTelemetry: [null, Validators.required], shortId: [null, Validators.required], lifetime: [null, Validators.required], defaultMinPeriod: [null, Validators.required], @@ -95,10 +95,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.lwm2mDeviceConfigFormGroup = this.fb.group({ configurationJson: [null, Validators.required] }); - this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe(() => { + this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => { console.warn('main form'); if (!this.disabled) { - this.updateModel(); + this.updateDeviceProfileValue(value); } }); this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => { @@ -159,7 +159,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const objectsList = value.objectsList; this.lwm2mDeviceProfileFormGroup.patchValue({ objectIds: value, - observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)}, + observeAttrTelemetry: this.getObserveAttrTelemetryObjects(objectsList), shortId: this.configurationValue.bootstrap.servers.shortId, lifetime: this.configurationValue.bootstrap.servers.lifetime, defaultMinPeriod: this.configurationValue.bootstrap.servers.defaultMinPeriod, @@ -173,9 +173,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private updateModel = (): void => { let configuration: DeviceProfileTransportConfiguration = null; - if (this.lwm2mDeviceConfigFormGroup.valid) { - this.upDateValueToJson(); - configuration = this.lwm2mDeviceConfigFormGroup.getRawValue().configurationJson; + if (this.lwm2mDeviceConfigFormGroup.valid && this.lwm2mDeviceProfileFormGroup.valid) { + configuration = this.lwm2mDeviceConfigFormGroup.value.configurationJson; configuration.type = DeviceTransportType.LWM2M; } this.propagateChange(configuration); @@ -183,89 +182,31 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => { this.lwm2mDeviceProfileFormGroup.patchValue({ - observeAttrTelemetry: {clientLwM2M: this.getObserveAttrTelemetryObjects(objectsList)} + observeAttrTelemetry: this.getObserveAttrTelemetryObjects(objectsList) }, {emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').markAsPristine({ - onlySelf: true - }); - } - - private upDateValueToJson = (): void => { - this.upDateValueToJsonTab0(); - this.upDateValueToJsonTab1(); - } - - private upDateValueToJsonTab0 = (): void => { - if (!this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').pristine) { - this.upDateObserveAttrTelemetryFromGroupToJson( - this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M - ); - this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').markAsPristine({ - onlySelf: true - }); - this.upDateJsonAllConfig(); - } - } - - private upDateValueToJsonTab1 = (): void => { - this.upDateValueServersToJson(); - if (!this.lwm2mDeviceProfileFormGroup.get('bootstrapServer').pristine) { - this.configurationValue.bootstrap.bootstrapServer = this.lwm2mDeviceProfileFormGroup.get('bootstrapServer').value; - this.lwm2mDeviceProfileFormGroup.get('bootstrapServer').markAsPristine({ - onlySelf: true - }); - this.upDateJsonAllConfig(); - } - if (!this.lwm2mDeviceProfileFormGroup.get('lwm2mServer').pristine) { - this.configurationValue.bootstrap.lwm2mServer = this.lwm2mDeviceProfileFormGroup.get('lwm2mServer').value; - this.lwm2mDeviceProfileFormGroup.get('lwm2mServer').markAsPristine({ - onlySelf: true - }); - this.upDateJsonAllConfig(); - } - } - - private upDateValueServersToJson = (): void => { - const bootstrapServers = this.configurationValue.bootstrap.servers; - if (!this.lwm2mDeviceProfileFormGroup.get('shortId').pristine) { - bootstrapServers.shortId = this.lwm2mDeviceProfileFormGroup.get('shortId').value; - this.lwm2mDeviceProfileFormGroup.get('shortId').markAsPristine({ - onlySelf: true - }); - this.upDateJsonAllConfig(); - } - if (!this.lwm2mDeviceProfileFormGroup.get('lifetime').pristine) { - bootstrapServers.lifetime = this.lwm2mDeviceProfileFormGroup.get('lifetime').value; - this.lwm2mDeviceProfileFormGroup.get('lifetime').markAsPristine({ - onlySelf: true - }); - this.upDateJsonAllConfig(); - } - if (!this.lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').pristine) { - bootstrapServers.defaultMinPeriod = this.lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').value; - this.lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').markAsPristine({ - onlySelf: true - }); - this.upDateJsonAllConfig(); - } - if (!this.lwm2mDeviceProfileFormGroup.get('notifIfDisabled').pristine) { - bootstrapServers.notifIfDisabled = this.lwm2mDeviceProfileFormGroup.get('notifIfDisabled').value; - this.lwm2mDeviceProfileFormGroup.get('notifIfDisabled').markAsPristine({ - onlySelf: true - }); - this.upDateJsonAllConfig(); - } - if (!this.lwm2mDeviceProfileFormGroup.get('binding').pristine) { - bootstrapServers.binding = this.lwm2mDeviceProfileFormGroup.get('binding').value; - this.lwm2mDeviceProfileFormGroup.get('binding').markAsPristine({ - onlySelf: true - }); + // this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').markAsPristine({ + // onlySelf: true + // }); + } + + private updateDeviceProfileValue(config): void { + if (this.lwm2mDeviceProfileFormGroup.valid) { + this.upDateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M); + this.configurationValue.bootstrap.bootstrapServer = config.bootstrapServer; + this.configurationValue.bootstrap.lwm2mServer = config.lwm2mServer; + const bootstrapServers = this.configurationValue.bootstrap.servers; + bootstrapServers.shortId = config.shortId; + bootstrapServers.lifetime = config.lifetime; + bootstrapServers.defaultMinPeriod = config.defaultMinPeriod; + bootstrapServers.notifIfDisabled = config.notifIfDisabled; + bootstrapServers.binding = config.binding; this.upDateJsonAllConfig(); + this.updateModel(); } } - private getObserveAttrTelemetryObjects = (listObject: ObjectLwM2M[]): ObjectLwM2M [] => { + private getObserveAttrTelemetryObjects = (listObject: ObjectLwM2M[]): object => { const clientObserveAttrTelemetry = listObject; if (this.configurationValue[this.observeAttr]) { const observeArray = this.configurationValue[this.observeAttr][this.observe]; @@ -291,7 +232,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro clientObserveAttrTelemetry.forEach(obj => { obj.instances.sort((a, b) => a.id - b.id); }); - return clientObserveAttrTelemetry; + return {clientLwM2M: clientObserveAttrTelemetry}; } private includesNotZeroInstance = (attribute: string[], telemetry: string[]): boolean => { @@ -480,7 +421,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro return pathParameterIndexes; } - private getObjectsFromJsonAllConfig = (): number [] => { + private getObjectsFromJsonAllConfig = (): number[] => { const objectsIds = new Set(); if (this.configurationValue[this.observeAttr]) { if (this.configurationValue[this.observeAttr][this.observe]) { @@ -503,20 +444,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private upDateJsonAllConfig = (): void => { - this.lwm2mDeviceProfileFormGroup.patchValue({ + this.lwm2mDeviceConfigFormGroup.patchValue({ configurationJson: this.configurationValue }, {emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.markAsPristine({ - onlySelf: true - }); } addObjectsList = (value: ObjectLwM2M[]): void => { - this.updateObserveAttrTelemetryObjectFormGroup(deepClone(value)); + this.updateObserveAttrTelemetryObjectFormGroup(value); } removeObjectsList = (value: ObjectLwM2M): void => { - const objectsOld = deepClone(this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M); + const objectsOld = this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M; const isIdIndex = (element) => element.id === value.id; const index = objectsOld.findIndex(isIdIndex); if (index >= 0) { @@ -526,25 +464,23 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.removeObserveAttrTelemetryFromJson(this.observe, value.id); this.removeObserveAttrTelemetryFromJson(this.telemetry, value.id); this.removeObserveAttrTelemetryFromJson(this.attribute, value.id); - this.removeObserveAttrTelemetryFromJson(this.attribute, value.id); this.removeKeyNameFromJson(value.id); this.upDateJsonAllConfig(); } private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, id: number): void => { - const isIdIndex = (element) => Array.from(element.substring(1).split('/'), Number)[0] === id; + const isIdIndex = (element: string) => element.startsWith(`/${id}`); let index = this.configurationValue[this.observeAttr][observeAttrTel].findIndex(isIdIndex); while (index >= 0) { this.configurationValue[this.observeAttr][observeAttrTel].splice(index, 1); - index = this.configurationValue[this.observeAttr][observeAttrTel].findIndex(isIdIndex); + index = this.configurationValue[this.observeAttr][observeAttrTel].findIndex(isIdIndex, index); } } private removeKeyNameFromJson = (id: number): void => { const keyNameJson = this.configurationValue[this.observeAttr][this.keyName]; Object.keys(keyNameJson).forEach(key => { - const idKey = Array.from(key.substring(1).split('/'), Number)[0]; - if (idKey === id) { + if (key.startsWith(`/${id}`)) { delete keyNameJson[key]; } }); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index 0426f1d032..d868d6b04f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -25,6 +25,7 @@ import { ObjectLwM2M } from './profile-config.models'; import { TranslateService } from '@ngx-translate/core'; import { DeviceProfileService } from '@core/http/device-profile.service'; import { Direction } from '@shared/models/page/sort-order'; +import { isDefined } from '@core/utils'; @Component({ selector: 'tb-profile-lwm2m-object-list', @@ -114,7 +115,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V this.disabled = isDisabled; if (isDisabled) { this.lwm2mListFormGroup.disable({emitEvent: false}); - this.clear(); + if (isDefined(this.objectInput)) { + this.clear(); + } } else { this.lwm2mListFormGroup.enable({emitEvent: false}); } @@ -144,7 +147,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); this.addList.next(this.objectsList); } - this.propagateChange(this.modelValue); + // this.propagateChange(this.modelValue); this.clear(); } @@ -159,7 +162,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V if (!this.modelValue.length) { this.modelValue = null; } - this.propagateChange(this.modelValue); + // this.propagateChange(this.modelValue); this.clear(); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index 4122971e50..4eeeb4b3e5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -124,7 +124,9 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor } writeValue(value: any): void { - this.buildClientObjectsLwM2M(value.clientLwM2M); + if (isDefinedAndNotNull(value)) { + this.buildClientObjectsLwM2M(value.clientLwM2M); + } } private buildClientObjectsLwM2M = (objectsLwM2M: ObjectLwM2M []): void => { From f770f407971c6d99e39ce488fe29cf08c9a76442 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 26 Jan 2021 14:40:32 +0200 Subject: [PATCH 39/41] Lwm2m: front: update filter for models --- ...ofile-transport-configuration.component.ts | 4 -- .../lwm2m/lwm2m-object-list.component.ts | 45 +++++++++++++------ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 86743074f9..f7d01db377 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -96,7 +96,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro configurationJson: [null, Validators.required] }); this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => { - console.warn('main form'); if (!this.disabled) { this.updateDeviceProfileValue(value); } @@ -185,9 +184,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro observeAttrTelemetry: this.getObserveAttrTelemetryObjects(objectsList) }, {emitEvent: false}); - // this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').markAsPristine({ - // onlySelf: true - // }); } private updateDeviceProfileValue(config): void { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index d868d6b04f..625f9c77d9 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -20,12 +20,12 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { Observable, of } from 'rxjs'; -import { filter, mergeMap, share, tap } from 'rxjs/operators'; +import { filter, map, mergeMap, tap } from 'rxjs/operators'; import { ObjectLwM2M } from './profile-config.models'; import { TranslateService } from '@ngx-translate/core'; import { DeviceProfileService } from '@core/http/device-profile.service'; import { Direction } from '@shared/models/page/sort-order'; -import { isDefined } from '@core/utils'; +import { isDefined, isDefinedAndNotNull, isEmptyStr } from '@core/utils'; @Component({ selector: 'tb-profile-lwm2m-object-list', @@ -42,6 +42,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V private requiredValue: boolean; private dirty = false; + private allObjectsList: Observable>; lwm2mListFormGroup: FormGroup; modelValue: Array | null; @@ -71,7 +72,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V @ViewChild('objectInput') objectInput: ElementRef; - private propagateChange = (v: any) => { }; + private propagateChange = (v: any) => { + }; constructor(private store: Store, public translate: TranslateService, @@ -106,8 +108,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } }), filter((value) => typeof value === 'string'), - mergeMap(name => this.fetchListObjects(name)), - share() + // map(value => value ? value : ''), + mergeMap(searchText => this.fetchListObjects(searchText)) ); } @@ -147,7 +149,6 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); this.addList.next(this.objectsList); } - // this.propagateChange(this.modelValue); this.clear(); } @@ -162,7 +163,6 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V if (!this.modelValue.length) { this.modelValue = null; } - // this.propagateChange(this.modelValue); this.clear(); } } @@ -173,15 +173,34 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V private fetchListObjects = (searchText?: string): Observable> => { this.searchText = searchText; - const sortOrder = { - property: 'name', - direction: Direction.ASC - }; - return this.deviceProfileService.getLwm2mObjects(sortOrder, null, searchText).pipe( - mergeMap(objectsList => of(objectsList)) + const filters = {names: [], ids: []}; + if (isDefinedAndNotNull(searchText) && !isEmptyStr(searchText)) { + const ids = searchText.match(/\d+/g); + filters.ids = isDefinedAndNotNull(ids) ? ids.map(Number) as [] : filters.ids; + const names = searchText.trim().split(" ") as []; + filters.names = names; + } + const predicate = objectLwM2M => filters.names.filter(word => objectLwM2M.name.toUpperCase().includes(word.toUpperCase())).length>0 + || filters.ids.includes(objectLwM2M.id); + return this.getListModels().pipe( + map(objectLwM2Ms => searchText ? objectLwM2Ms.filter(predicate) : objectLwM2Ms) ); } + private getListModels(): Observable> { + if (!this.allObjectsList) { + const sortOrder = { + property: 'name', + direction: Direction.ASC + }; + this.allObjectsList = this.deviceProfileService.getLwm2mObjects(sortOrder, null, null).pipe( + mergeMap(objectsList => of(objectsList)) + ); + } + return this.allObjectsList; + } + + onFocus = (): void => { if (this.dirty) { this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity({onlySelf: true, emitEvent: true}); From c14e2c81b1840a9160496b0baa431fb1e13d66be Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 26 Jan 2021 19:20:36 +0200 Subject: [PATCH 40/41] UI: Refactoring LwM2M --- ...ofile-transport-configuration.component.ts | 118 +++++++----------- .../lwm2m/lwm2m-object-list.component.ts | 78 ++++++------ ...wm2m-observe-attr-telemetry.component.html | 25 ++-- .../lwm2m-observe-attr-telemetry.component.ts | 2 +- 4 files changed, 94 insertions(+), 129 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index f7d01db377..e9b6b17edc 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -82,7 +82,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private deviceProfileService: DeviceProfileService, @Inject(WINDOW) private window: Window) { this.lwm2mDeviceProfileFormGroup = this.fb.group({ - objectIds: [[], Validators.required], + objectIds: [null, Validators.required], observeAttrTelemetry: [null, Validators.required], shortId: [null, Validators.required], lifetime: [null, Validators.required], @@ -96,15 +96,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro configurationJson: [null, Validators.required] }); this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => { - if (!this.disabled) { - this.updateDeviceProfileValue(value); - } + this.updateDeviceProfileValue(value); }); this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => { - console.warn('config form'); - if (!this.disabled) { - this.updateModel(); - } + this.updateModel(); }); this.sortFunction = this.sortObjectKeyPathJson; } @@ -188,7 +183,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private updateDeviceProfileValue(config): void { if (this.lwm2mDeviceProfileFormGroup.valid) { - this.upDateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M); + this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M); this.configurationValue.bootstrap.bootstrapServer = config.bootstrapServer; this.configurationValue.bootstrap.lwm2mServer = config.lwm2mServer; const bootstrapServers = this.configurationValue.bootstrap.servers; @@ -225,9 +220,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.updateKeyNameObjects(keyNameJson, clientObserveAttrTelemetry); } } - clientObserveAttrTelemetry.forEach(obj => { - obj.instances.sort((a, b) => a.id - b.id); - }); return {clientLwM2M: clientObserveAttrTelemetry}; } @@ -239,8 +231,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private addInstances = (attribute: string[], telemetry: string[], clientObserveAttrTelemetry: ObjectLwM2M[]): void => { const instancesPath = attribute.concat(telemetry) .filter(instance => !instance.includes('/0/')) - .map(instance => this.convertPathToInstance(instance)) - .sort(); + .map(instance => instance.slice(1, instance.lastIndexOf('/'))) + .sort(this.sortPath); new Set(instancesPath).forEach(path => { const pathParameter = Array.from(path.split('/'), Number); @@ -253,24 +245,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro }); } - private convertPathToInstance = (path: string): string => { - const [objectId, instanceId] = path.substring(1).split('/'); - return `${objectId}/${instanceId}`; - } - private updateObserveAttrTelemetryObjects = (parameters: string[], clientObserveAttrTelemetry: ObjectLwM2M[], nameParameter: string): void => { parameters.forEach(parameter => { const [objectId, instanceId, resourceId] = Array.from(parameter.substring(1).split('/'), Number); - clientObserveAttrTelemetry - .forEach(key => { - if (key.id === objectId) { - const instance = key.instances.find(itrInstance => itrInstance.id === instanceId); - if (isDefinedAndNotNull(instance)) { - instance.resources.find(resource => resource.id === resourceId)[nameParameter] = true; - } - } - }); + clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId) + .instances.find(itrInstance => itrInstance.id === instanceId) + .resources.find(resource => resource.id === resourceId) + [nameParameter] = true; }); } @@ -278,62 +260,60 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const keyName = JSON.parse(JSON.stringify(nameJson)); Object.keys(keyName).forEach(key => { const [objectId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), Number); - clientObserveAttrTelemetry - .forEach(object => { - if (object.id === objectId) { - object.instances - .find(instance => instance.id === instanceId).resources - .find(resource => resource.id === resourceId).keyName = keyName[key]; - } - }); + clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId) + .instances.find(instance => instance.id === instanceId) + .resources.find(resource => resource.id === resourceId) + .keyName = keyName[key]; }); } - private upDateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => { + private updateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => { const observeArray: Array = []; const attributeArray: Array = []; const telemetryArray: Array = []; const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); + const paths = new Set(); let pathObj; let pathInst; let pathRes; observeJson.forEach(obj => { - Object.entries(obj).forEach(([key, value]) => { + for (const [key, value] of Object.entries(obj)) { if (key === 'id') { pathObj = value; } if (key === 'instances') { - const instancesJson = JSON.parse(JSON.stringify(value)) as Instance[]; + const instancesJson = value as Instance[]; if (instancesJson.length > 0) { instancesJson.forEach(instance => { - Object.entries(instance).forEach(([instanceKey, instanceValue]) => { + for (const [instanceKey, instanceValue] of Object.entries(instance)) { if (instanceKey === 'id') { pathInst = instanceValue; } if (instanceKey === 'resources') { - const resourcesJson = JSON.parse(JSON.stringify(instanceValue)) as ResourceLwM2M[]; + const resourcesJson = instanceValue as ResourceLwM2M[]; if (resourcesJson.length > 0) { resourcesJson.forEach(res => { - Object.entries(res).forEach(([resourceKey, resourceValue]) => { + for (const [resourceKey, idResource] of Object.entries(res)) { if (resourceKey === 'id') { - // pathRes = resourceValue - pathRes = '/' + pathObj + '/' + pathInst + '/' + resourceValue; - } else if (resourceKey === 'observe' && resourceValue) { + pathRes = `/${pathObj}/${pathInst}/${idResource}`; + } else if (resourceKey === 'observe' && idResource) { observeArray.push(pathRes); - } else if (resourceKey === 'attribute' && resourceValue) { + } else if (resourceKey === 'attribute' && idResource) { attributeArray.push(pathRes); - } else if (resourceKey === 'telemetry' && resourceValue) { + paths.add(pathRes); + } else if (resourceKey === 'telemetry' && idResource) { telemetryArray.push(pathRes); + paths.add(pathRes); } - }); + } }); } } - }); + } }); } } - }); + } }); if (isUndefined(this.configurationValue[this.observeAttr])) { this.configurationValue[this.observeAttr] = { @@ -346,7 +326,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.configurationValue[this.observeAttr][this.attribute] = attributeArray; this.configurationValue[this.observeAttr][this.telemetry] = telemetryArray; } - this.updateKeyName(); + this.updateKeyName(paths); } sortObjectKeyPathJson = (key: string, value: object): object => { @@ -366,23 +346,13 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private sortPath = (a, b): number => { - const aLC = Array.from(a.substring(1).split('/'), Number); - const bLC = Array.from(b.substring(1).split('/'), Number); - return aLC[0] === bLC[0] ? aLC[1] - bLC[1] : aLC[0] - bLC[0]; + return a.localeCompare(b, undefined, { + numeric: true, + sensitivity: 'base' + }); } - private updateKeyName = (): void => { - const paths = new Set(); - if (this.configurationValue[this.observeAttr][this.attribute]) { - this.configurationValue[this.observeAttr][this.attribute].forEach(path => { - paths.add(path); - }); - } - if (this.configurationValue[this.observeAttr][this.telemetry]) { - this.configurationValue[this.observeAttr][this.telemetry].forEach(path => { - paths.add(path); - }); - } + private updateKeyName = (paths: Set): void => { const keyNameNew = {}; paths.forEach(path => { const pathParameter = this.findIndexesForIds(path); @@ -395,19 +365,19 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private findIndexesForIds = (path: string): number[] => { - const pathParameter = Array.from(path.substring(1).split('/'), Number); + const [objectId, instanceId, resourceId] = Array.from(path.substring(1).split('/'), Number); + // TODO: All paths to map const pathParameterIndexes: number[] = []; - const objectsOld = deepClone( - this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M) as ObjectLwM2M[]; - let isIdIndex = (element) => element.id === pathParameter[0]; + const objectsOld = this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M as ObjectLwM2M[]; + let isIdIndex = (element) => element.id === objectId; const objIndex = objectsOld.findIndex(isIdIndex); if (objIndex >= 0) { pathParameterIndexes.push(objIndex); - isIdIndex = (element) => element.id === pathParameter[1]; + isIdIndex = (element) => element.id === instanceId; const instIndex = objectsOld[objIndex].instances.findIndex(isIdIndex); if (instIndex >= 0) { pathParameterIndexes.push(instIndex); - isIdIndex = (element) => element.id === pathParameter[2]; + isIdIndex = (element) => element.id === resourceId; const resIndex = objectsOld[objIndex].instances[instIndex].resources.findIndex(isIdIndex); if (resIndex >= 0) { pathParameterIndexes.push(resIndex); @@ -456,16 +426,16 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro if (index >= 0) { objectsOld.splice(index, 1); } - this.updateObserveAttrTelemetryObjectFormGroup(objectsOld); this.removeObserveAttrTelemetryFromJson(this.observe, value.id); this.removeObserveAttrTelemetryFromJson(this.telemetry, value.id); this.removeObserveAttrTelemetryFromJson(this.attribute, value.id); this.removeKeyNameFromJson(value.id); + this.updateObserveAttrTelemetryObjectFormGroup(objectsOld); this.upDateJsonAllConfig(); } private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, id: number): void => { - const isIdIndex = (element: string) => element.startsWith(`/${id}`); + const isIdIndex = (element) => element.startsWith(`/${id}`); let index = this.configurationValue[this.observeAttr][observeAttrTel].findIndex(isIdIndex); while (index >= 0) { this.configurationValue[this.observeAttr][observeAttrTel].splice(index, 1); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index 625f9c77d9..3062c52ed3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -19,18 +19,17 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Valida import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { Observable, of } from 'rxjs'; -import { filter, map, mergeMap, tap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; import { ObjectLwM2M } from './profile-config.models'; import { TranslateService } from '@ngx-translate/core'; import { DeviceProfileService } from '@core/http/device-profile.service'; import { Direction } from '@shared/models/page/sort-order'; -import { isDefined, isDefinedAndNotNull, isEmptyStr } from '@core/utils'; +import { isDefined, isDefinedAndNotNull, isEmptyStr, isString } from '@core/utils'; @Component({ selector: 'tb-profile-lwm2m-object-list', templateUrl: './lwm2m-object-list.component.html', - styleUrls: [], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -42,10 +41,10 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V private requiredValue: boolean; private dirty = false; - private allObjectsList: Observable>; + private lw2mModels: Observable>; + private modelValue: Array = []; lwm2mListFormGroup: FormGroup; - modelValue: Array | null; objectsList: Array = []; filteredObjectsList: Observable>; disabled = false; @@ -57,11 +56,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V @Input() set required(value: boolean) { - const newVal = coerceBooleanProperty(value); - if (this.requiredValue !== newVal) { - this.requiredValue = newVal; - this.updateValidators(); - } + this.requiredValue = coerceBooleanProperty(value); + this.updateValidators(); } @Output() @@ -73,7 +69,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V @ViewChild('objectInput') objectInput: ElementRef; private propagateChange = (v: any) => { - }; + } constructor(private store: Store, public translate: TranslateService, @@ -81,7 +77,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V private fb: FormBuilder) { this.lwm2mListFormGroup = this.fb.group({ objectsList: [this.objectsList], - objectLwm2m: [null, this.required ? [Validators.required] : []] + objectLwm2m: [''] }); } @@ -104,11 +100,10 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V if (value && typeof value !== 'string') { this.add(value); } else if (value === null) { - this.clear(this.objectInput.nativeElement.value); + this.clear(); } }), - filter((value) => typeof value === 'string'), - // map(value => value ? value : ''), + filter(searchText => isString(searchText)), mergeMap(searchText => this.fetchListObjects(searchText)) ); } @@ -127,23 +122,21 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V writeValue(value: any): void { this.searchText = ''; - const objectIds = 'objectIds'; - if (value.hasOwnProperty(objectIds) && value[objectIds] != null && value[objectIds].length > 0) { - this.modelValue = [...value[objectIds]]; - this.objectsList = value.objectsList; - } else { - this.objectsList = []; - this.modelValue = null; + if (isDefinedAndNotNull(value)) { + if (Array.isArray(value.objectIds)) { + this.modelValue = value.objectIds; + this.objectsList = value.objectsList; + } else { + this.objectsList = []; + this.modelValue = []; + } + this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList, {emitEvents: false}); + this.dirty = false; } - this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); - this.dirty = true; } private add(object: ObjectLwM2M): void { - if (!this.modelValue || this.modelValue.indexOf(object.id) === -1) { - if (!this.modelValue) { - this.modelValue = []; - } + if (this.modelValue.indexOf(object.id) === -1) { this.modelValue.push(object.id); this.objectsList.push(object); this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); @@ -176,41 +169,42 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V const filters = {names: [], ids: []}; if (isDefinedAndNotNull(searchText) && !isEmptyStr(searchText)) { const ids = searchText.match(/\d+/g); - filters.ids = isDefinedAndNotNull(ids) ? ids.map(Number) as [] : filters.ids; - const names = searchText.trim().split(" ") as []; - filters.names = names; + filters.ids = ids !== null ? ids.map(Number) : filters.ids; + filters.names = searchText.trim().toUpperCase().split(' '); } - const predicate = objectLwM2M => filters.names.filter(word => objectLwM2M.name.toUpperCase().includes(word.toUpperCase())).length>0 + const predicate = objectLwM2M => filters.names.find(word => objectLwM2M.name.toUpperCase().includes(word)) || filters.ids.includes(objectLwM2M.id); - return this.getListModels().pipe( + return this.getLwM2mModels().pipe( map(objectLwM2Ms => searchText ? objectLwM2Ms.filter(predicate) : objectLwM2Ms) ); } - private getListModels(): Observable> { - if (!this.allObjectsList) { + private getLwM2mModels(): Observable> { + if (!this.lw2mModels) { const sortOrder = { property: 'name', direction: Direction.ASC }; - this.allObjectsList = this.deviceProfileService.getLwm2mObjects(sortOrder, null, null).pipe( - mergeMap(objectsList => of(objectsList)) + this.lw2mModels = this.deviceProfileService.getLwm2mObjects(sortOrder).pipe( + publishReplay(1), + refCount() ); } - return this.allObjectsList; + return this.lw2mModels; } onFocus = (): void => { - if (this.dirty) { + if (!this.dirty) { this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity({onlySelf: true, emitEvent: true}); - this.dirty = false; + this.dirty = true; } } private clear = (value: string = ''): void => { this.objectInput.nativeElement.value = value; - this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value, {emitEvent: true}); + this.searchText = ''; + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value); setTimeout(() => { this.objectInput.nativeElement.blur(); this.objectInput.nativeElement.focus(); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html index eb0e4bc88d..7b33264ba6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html @@ -16,23 +16,24 @@ --> -
+
- -
{{ objectLwM2M.get('name').value}} (object [{{ objectLwM2M.get('id').value}}])
- + + {{ objectLwM2M.get('name').value}} <id: {{ objectLwM2M.get('id').value}}> + + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index 4eeeb4b3e5..b208ed7e14 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -176,7 +176,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor this.observeAttrTelemetryFormGroup.get('clientLwM2M').updateValueAndValidity(); } - trackByParams = (index: number): number => { + trackByParams = (index: number, element: any): number => { return index; } From 5f3cf1e3a2a77a13cde03d3309540824d43cf059 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 26 Jan 2021 22:10:26 +0200 Subject: [PATCH 41/41] Lwm2m: front: update filter for keyName --- .../server/LwM2MTransportServiceImpl.java | 2 +- .../lwm2m/server/client/LwM2MClient.java | 4 +- ...ofile-transport-configuration.component.ts | 52 ++++--------------- 3 files changed, 14 insertions(+), 44 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java index 3255d24760..10df996b79 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java @@ -154,7 +154,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { lwM2MClient.setLwM2MTransportServiceImpl(this); lwM2MClient.setSessionUuid(UUID.randomUUID()); this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration); -// this.setLwM2mFromClientValue(lwServer, registration, lwM2MClient); + this.setLwM2mFromClientValue(lwServer, registration, lwM2MClient); SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); if (sessionInfo != null) { lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); 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 f20853299d..3c1a886454 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 @@ -93,7 +93,7 @@ public class LwM2MClient implements Cloneable { this.pendingRequests.remove(path); if (this.pendingRequests.size() == 0) { this.initValue(); -// this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this); + this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this); } } @@ -106,7 +106,7 @@ public class LwM2MClient implements Cloneable { ((LwM2mObjectInstance)((ReadResponse)resp).getContent()).getResources().forEach((k, v) -> { String rez = pathIds.toString() + "/" + k; if (((LwM2mObjectInstance) ((ReadResponse) resp).getContent()).getResource(k) instanceof LwM2mMultipleResource){ -// this.resources.put(rez, new ResourceValue(v.getInstances().values(), null, true)); + this.resources.put(rez, new ResourceValue(v.getValues(), null, true)); } else { this.resources.put(rez, new ResourceValue(null, v.getValue(), false)); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index e9b6b17edc..6451bbb0b8 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -271,6 +271,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const observeArray: Array = []; const attributeArray: Array = []; const telemetryArray: Array = []; + const keyNameNew = {}; const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); const paths = new Set(); let pathObj; @@ -293,18 +294,22 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const resourcesJson = instanceValue as ResourceLwM2M[]; if (resourcesJson.length > 0) { resourcesJson.forEach(res => { - for (const [resourceKey, idResource] of Object.entries(res)) { + for (const [resourceKey, value] of Object.entries(res)) { if (resourceKey === 'id') { - pathRes = `/${pathObj}/${pathInst}/${idResource}`; - } else if (resourceKey === 'observe' && idResource) { + pathRes = `/${pathObj}/${pathInst}/${value}`; + } else if (resourceKey === 'observe' && value) { observeArray.push(pathRes); - } else if (resourceKey === 'attribute' && idResource) { + } else if (resourceKey === 'attribute' && value) { attributeArray.push(pathRes); paths.add(pathRes); - } else if (resourceKey === 'telemetry' && idResource) { + } else if (resourceKey === 'telemetry' && value) { telemetryArray.push(pathRes); paths.add(pathRes); } + else if (resourceKey === this.keyName && paths.has(pathRes)) { + console.warn(pathRes, value); + keyNameNew[pathRes] = value; + } } }); } @@ -326,7 +331,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.configurationValue[this.observeAttr][this.attribute] = attributeArray; this.configurationValue[this.observeAttr][this.telemetry] = telemetryArray; } - this.updateKeyName(paths); + this.configurationValue[this.observeAttr][this.keyName] = this.sortObjectKeyPathJson('keyName', keyNameNew); } sortObjectKeyPathJson = (key: string, value: object): object => { @@ -352,41 +357,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro }); } - private updateKeyName = (paths: Set): void => { - const keyNameNew = {}; - paths.forEach(path => { - const pathParameter = this.findIndexesForIds(path); - if (pathParameter.length === 3) { - keyNameNew[path] = this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value - .clientLwM2M[pathParameter[0]].instances[pathParameter[1]].resources[pathParameter[2]][this.keyName]; - } - }); - this.configurationValue[this.observeAttr][this.keyName] = this.sortObjectKeyPathJson('keyName', keyNameNew); - } - - private findIndexesForIds = (path: string): number[] => { - const [objectId, instanceId, resourceId] = Array.from(path.substring(1).split('/'), Number); - // TODO: All paths to map - const pathParameterIndexes: number[] = []; - const objectsOld = this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M as ObjectLwM2M[]; - let isIdIndex = (element) => element.id === objectId; - const objIndex = objectsOld.findIndex(isIdIndex); - if (objIndex >= 0) { - pathParameterIndexes.push(objIndex); - isIdIndex = (element) => element.id === instanceId; - const instIndex = objectsOld[objIndex].instances.findIndex(isIdIndex); - if (instIndex >= 0) { - pathParameterIndexes.push(instIndex); - isIdIndex = (element) => element.id === resourceId; - const resIndex = objectsOld[objIndex].instances[instIndex].resources.findIndex(isIdIndex); - if (resIndex >= 0) { - pathParameterIndexes.push(resIndex); - } - } - } - return pathParameterIndexes; - } - private getObjectsFromJsonAllConfig = (): number[] => { const objectsIds = new Set(); if (this.configurationValue[this.observeAttr]) {