Commit bc4684c907ecebf581e8c6b4dd9763c6fa0af1fa

Authored by nickAS21
1 parent 58e544c3

Lwm2m: backEnd: refactoring security

Showing 19 changed files with 1750 additions and 1702 deletions
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 20 import org.eclipse.leshan.core.model.StaticModel;
  21 +import org.eclipse.leshan.core.util.Hex;
21 22 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
22 23 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
23 24 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
... ... @@ -28,18 +29,40 @@ import org.springframework.context.annotation.Primary;
28 29 import org.springframework.stereotype.Component;
29 30 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore;
30 31 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore;
31   -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MSetSecurityStoreBootstrap;
32 32 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager;
33 33 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
34 34 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
35   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
  35 +
  36 +import java.math.BigInteger;
  37 +import java.security.AlgorithmParameters;
  38 +import java.security.GeneralSecurityException;
  39 +import java.security.KeyFactory;
  40 +import java.security.KeyStore;
  41 +import java.security.KeyStoreException;
  42 +import java.security.PrivateKey;
  43 +import java.security.PublicKey;
  44 +import java.security.cert.CertificateEncodingException;
  45 +import java.security.cert.X509Certificate;
  46 +import java.security.interfaces.ECPublicKey;
  47 +import java.security.spec.ECGenParameterSpec;
  48 +import java.security.spec.ECParameterSpec;
  49 +import java.security.spec.ECPoint;
  50 +import java.security.spec.ECPrivateKeySpec;
  51 +import java.security.spec.ECPublicKeySpec;
  52 +import java.security.spec.KeySpec;
  53 +import java.util.Arrays;
  54 +
  55 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
36 56 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
  57 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
37 58 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig;
38 59
39 60 @Slf4j
40 61 @Component
41 62 @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')")
42 63 public class LwM2MTransportBootstrapServerConfiguration {
  64 + private PublicKey publicKey;
  65 + private PrivateKey privateKey;
43 66
44 67 @Autowired
45 68 private LwM2MTransportContextBootstrap contextBs;
... ... @@ -90,7 +113,7 @@ public class LwM2MTransportBootstrapServerConfiguration {
90 113 builder.setDtlsConfig(dtlsConfig);
91 114
92 115 /** Create credentials */
93   - new LwM2MSetSecurityStoreBootstrap(builder, contextBs, contextS, dtlsMode);
  116 + LwM2MSetSecurityStoreBootstrap(builder, dtlsMode);
94 117
95 118 BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore);
96 119 builder.setSessionManager(sessionManager);
... ... @@ -98,4 +121,151 @@ public class LwM2MTransportBootstrapServerConfiguration {
98 121 /** Create BootstrapServer */
99 122 return builder.build();
100 123 }
  124 +
  125 + public void LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MSecurityMode dtlsMode) {
  126 +
  127 + /** Set securityStore with new registrationStore */
  128 +
  129 + switch (dtlsMode) {
  130 + /** Use No_Sec only */
  131 + case NO_SEC:
  132 + setServerWithX509Cert(builder, NO_SEC.code);
  133 + break;
  134 + /** Use PSK/RPK */
  135 + case PSK:
  136 + case RPK:
  137 + setRPK(builder);
  138 + break;
  139 + case X509:
  140 + setServerWithX509Cert(builder, X509.code);
  141 + break;
  142 + /** Use X509_EST only */
  143 + case X509_EST:
  144 + // TODO support sentinel pool and make pool configurable
  145 + break;
  146 + /** Use ather X509, PSK, No_Sec ?? */
  147 + default:
  148 + break;
  149 + }
  150 + }
  151 +
  152 + private void setRPK(LeshanBootstrapServerBuilder builder) {
  153 + try {
  154 + /** Get Elliptic Curve Parameter spec for secp256r1 */
  155 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
  156 + algoParameters.init(new ECGenParameterSpec("secp256r1"));
  157 + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
  158 + if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) {
  159 + /** Get point values */
  160 + byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray());
  161 + byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray());
  162 + /** Create key specs */
  163 + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
  164 + parameterSpec);
  165 + /** Get keys */
  166 + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  167 + }
  168 + if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) {
  169 + /** Get point values */
  170 + byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray());
  171 + /** Create key specs */
  172 + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
  173 + /** Get keys */
  174 + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
  175 + }
  176 + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
  177 + this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  178 + builder.setPublicKey(this.publicKey);
  179 + builder.setPrivateKey(this.privateKey);
  180 + this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey);
  181 + getParamsRPK();
  182 + }
  183 + } catch (GeneralSecurityException | IllegalArgumentException e) {
  184 + log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
  185 + throw new RuntimeException(e);
  186 + }
  187 + }
  188 +
  189 + private void setServerWithX509Cert(LeshanBootstrapServerBuilder builder, int securityModeCode) {
  190 + try {
  191 + if (this.contextS.getCtxServer().getKeyStoreValue() != null) {
  192 + KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue();
  193 + setBuilderX509(builder);
  194 + X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias());
  195 + if (rootCAX509Cert != null && securityModeCode == X509.code) {
  196 + X509Certificate[] trustedCertificates = new X509Certificate[1];
  197 + trustedCertificates[0] = rootCAX509Cert;
  198 + builder.setTrustedCertificates(trustedCertificates);
  199 + } else {
  200 + /** by default trust all */
  201 + builder.setTrustedCertificates(new X509Certificate[0]);
  202 + }
  203 + }
  204 + else {
  205 + /** by default trust all */
  206 + builder.setTrustedCertificates(new X509Certificate[0]);
  207 + log.error("Unable to load X509 files for BootStrapServer");
  208 + }
  209 + } catch (KeyStoreException ex) {
  210 + log.error("[{}] Unable to load X509 files server", ex.getMessage());
  211 + }
  212 +
  213 + }
  214 +
  215 + private void setBuilderX509(LeshanBootstrapServerBuilder builder) {
  216 + /**
  217 + * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
  218 + * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
  219 + */
  220 + try {
  221 + X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias());
  222 + this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray());
  223 + if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  224 + builder.setPrivateKey(this.privateKey);
  225 + }
  226 + if (serverCertificate != null) {
  227 + builder.setCertificateChain(new X509Certificate[]{serverCertificate});
  228 + this.contextBs.getCtxBootStrap().setBootstrapCertificate(serverCertificate);
  229 + infoParamsX509(serverCertificate);
  230 + }
  231 + } catch (Exception ex) {
  232 + log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
  233 + }
  234 + }
  235 +
  236 + private void getParamsRPK() {
  237 + if (this.publicKey instanceof ECPublicKey) {
  238 + /** Get x coordinate */
  239 + byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
  240 + if (x[0] == 0)
  241 + x = Arrays.copyOfRange(x, 1, x.length);
  242 +
  243 + /** Get Y coordinate */
  244 + byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
  245 + if (y[0] == 0)
  246 + y = Arrays.copyOfRange(y, 1, y.length);
  247 +
  248 + /** Get Curves params */
  249 + String params = ((ECPublicKey) this.publicKey).getParams().toString();
  250 + log.info(
  251 + " \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
  252 + params, Hex.encodeHexString(x), Hex.encodeHexString(y),
  253 + Hex.encodeHexString(this.publicKey.getEncoded()),
  254 + Hex.encodeHexString(this.privateKey.getEncoded()));
  255 + } else {
  256 + throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
  257 + }
  258 + }
  259 +
  260 + private void infoParamsX509(X509Certificate certificate) {
  261 + try {
  262 + log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",
  263 + Hex.encodeHexString(certificate.getEncoded()),
  264 + Hex.encodeHexString(this.privateKey.getEncoded()));
  265 + } catch (CertificateEncodingException e) {
  266 + log.error("", e);
  267 + }
  268 + }
  269 +
  270 +
101 271 }
... ...
... ... @@ -29,10 +29,10 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore;
29 29 import org.eclipse.leshan.server.security.SecurityInfo;
30 30 import org.springframework.beans.factory.annotation.Autowired;
31 31 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
32   -import org.springframework.stereotype.Component;
  32 +import org.springframework.stereotype.Service;
33 33 import org.thingsboard.server.gen.transport.TransportProtos;
34   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo;
35 34 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  35 +import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo;
36 36 import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
37 37 import org.thingsboard.server.transport.lwm2m.server.LwM2MSessionMsgListener;
38 38 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
... ... @@ -53,14 +53,14 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
53 53 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getBootstrapParametersFromThingsboard;
54 54
55 55 @Slf4j
56   -@Component("LwM2MBootstrapSecurityStore")
  56 +@Service("LwM2MBootstrapSecurityStore")
57 57 @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')")
58 58 public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
59 59
60 60 private final EditableBootstrapConfigStore bootstrapConfigStore;
61 61
62 62 @Autowired
63   - LwM2MGetSecurityInfo lwM2MGetSecurityInfo;
  63 + LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo;
64 64
65 65 @Autowired
66 66 public LwM2MTransportContextServer context;
... ... @@ -72,8 +72,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
72 72 @Override
73 73 public List<SecurityInfo> getAllByEndpoint(String endPoint) {
74 74 String endPointKey = endPoint;
75   - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(endPointKey, TypeServer.BOOTSTRAP);
76   - if (store.getBootstrapJsonCredential() != null) {
  75 + ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(endPointKey, TypeServer.BOOTSTRAP);
  76 + if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
77 77 /** add value to store from BootstrapJson */
78 78 this.setBootstrapConfigScurityInfo(store);
79 79 BootstrapConfig bsConfigNew = store.getBootstrapConfig();
... ... @@ -86,7 +86,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
86 86 }
87 87 bootstrapConfigStore.add(endPoint, bsConfigNew);
88 88 } catch (InvalidConfigurationException e) {
89   - e.printStackTrace();
  89 + log.error("", e);
90 90 }
91 91 return store.getSecurityInfo() == null ? null : Arrays.asList(store.getSecurityInfo());
92 92 }
... ... @@ -96,17 +96,16 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
96 96
97 97 @Override
98 98 public SecurityInfo getByIdentity(String identity) {
99   - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.BOOTSTRAP);
100   - /** add value to store from BootstrapJson */
101   - this.setBootstrapConfigScurityInfo(store);
102   -
103   - if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
  99 + ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.BOOTSTRAP);
  100 + if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
  101 + /** add value to store from BootstrapJson */
  102 + this.setBootstrapConfigScurityInfo(store);
104 103 BootstrapConfig bsConfig = store.getBootstrapConfig();
105 104 if (bsConfig.security != null) {
106 105 try {
107 106 bootstrapConfigStore.add(store.getEndPoint(), bsConfig);
108 107 } catch (InvalidConfigurationException e) {
109   - e.printStackTrace();
  108 + log.error("", e);
110 109 }
111 110 return store.getSecurityInfo();
112 111 }
... ... @@ -154,33 +153,36 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
154 153 private LwM2MBootstrapConfig getParametersBootstrap(ReadResultSecurityStore store) {
155 154 try {
156 155 JsonObject bootstrapJsonCredential = store.getBootstrapJsonCredential();
157   - ObjectMapper mapper = new ObjectMapper();
158   - LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class);
159   - JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
160   - lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class);
161   - LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);
162   - LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class);
163   - UUID sessionUUiD = UUID.randomUUID();
164   - TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
165   - context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo));
166   - if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
167   - lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
168   - lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
169   - String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint());
170   - context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
171   - return lwM2MBootstrapConfig;
172   - }
173   - else {
174   - log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
175   - log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
176   - String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint());
177   - context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
178   - return null;
  156 + if (bootstrapJsonCredential != null) {
  157 + ObjectMapper mapper = new ObjectMapper();
  158 + LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class);
  159 + JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
  160 + lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class);
  161 + LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);
  162 + LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class);
  163 + UUID sessionUUiD = UUID.randomUUID();
  164 + TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
  165 + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo));
  166 + if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
  167 + lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
  168 + lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
  169 + String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint());
  170 + context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
  171 + return lwM2MBootstrapConfig;
  172 + } else {
  173 + log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
  174 + log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
  175 + String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint());
  176 + context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
  177 + return null;
  178 + }
179 179 }
180 180 } catch (JsonProcessingException e) {
181 181 log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndPoint(), e.getMessage());
182 182 return null;
183 183 }
  184 + log.error("Unable to decode Json or Certificate for [{}]", store.getEndPoint());
  185 + return null;
184 186 }
185 187
186 188 /**
... ... @@ -193,7 +195,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
193 195 * @return false if not sync between SecurityMode of Bootstrap credential and profile
194 196 */
195 197 private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap profileServerBootstrap, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
196   - return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) &&
  198 + return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) &&
197 199 lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode()));
198 200 }
199 201 }
... ...
1   -/**
2   - * Copyright © 2016-2020 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
17   -
18   -import lombok.Data;
19   -import lombok.extern.slf4j.Slf4j;
20   -import org.eclipse.leshan.core.util.Hex;
21   -import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
22   -import org.eclipse.leshan.server.security.EditableSecurityStore;
23   -import org.thingsboard.server.transport.lwm2m.bootstrap.LwM2MTransportContextBootstrap;
24   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
25   -import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
26   -
27   -import java.math.BigInteger;
28   -import java.security.AlgorithmParameters;
29   -import java.security.KeyStore;
30   -import java.security.PublicKey;
31   -import java.security.PrivateKey;
32   -import java.security.KeyFactory;
33   -import java.security.GeneralSecurityException;
34   -import java.security.KeyStoreException;
35   -import java.security.cert.X509Certificate;
36   -import java.security.interfaces.ECPublicKey;
37   -import java.security.spec.ECGenParameterSpec;
38   -import java.security.spec.ECParameterSpec;
39   -import java.security.spec.ECPublicKeySpec;
40   -import java.security.spec.ECPoint;
41   -import java.security.spec.KeySpec;
42   -import java.security.spec.ECPrivateKeySpec;
43   -import java.util.Arrays;
44   -
45   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
46   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
47   -
48   -@Slf4j
49   -@Data
50   -public class LwM2MSetSecurityStoreBootstrap {
51   -
52   - private KeyStore keyStore;
53   - private PublicKey publicKey;
54   - private PrivateKey privateKey;
55   - private LwM2MTransportContextBootstrap contextBs;
56   - private LwM2MTransportContextServer contextS;
57   - private LeshanBootstrapServerBuilder builder;
58   - EditableSecurityStore securityStore;
59   -
60   - public LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MTransportContextBootstrap contextBs, LwM2MTransportContextServer contextS, LwM2MSecurityMode dtlsMode) {
61   - this.builder = builder;
62   - this.contextBs = contextBs;
63   - this.contextS = contextS;
64   - /** Set securityStore with new registrationStore */
65   -
66   - switch (dtlsMode) {
67   - /** Use No_Sec only */
68   - case NO_SEC:
69   - setServerWithX509Cert(NO_SEC.code);
70   - break;
71   - /** Use PSK/RPK */
72   - case PSK:
73   - case RPK:
74   - setRPK();
75   - break;
76   - case X509:
77   - setServerWithX509Cert(X509.code);
78   - break;
79   - /** Use X509_EST only */
80   - case X509_EST:
81   - // TODO support sentinel pool and make pool configurable
82   - break;
83   - /** Use ather X509, PSK, No_Sec ?? */
84   - default:
85   - break;
86   - }
87   - }
88   -
89   - private void setRPK() {
90   - try {
91   - /** Get Elliptic Curve Parameter spec for secp256r1 */
92   - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
93   - algoParameters.init(new ECGenParameterSpec("secp256r1"));
94   - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
95   - if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) {
96   - /** Get point values */
97   - byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray());
98   - byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray());
99   - /** Create key specs */
100   - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
101   - parameterSpec);
102   - /** Get keys */
103   - this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
104   - }
105   - if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) {
106   - /** Get point values */
107   - byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray());
108   - /** Create key specs */
109   - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
110   - /** Get keys */
111   - this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
112   - }
113   - if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
114   - this.privateKey != null && this.privateKey.getEncoded().length > 0) {
115   - this.builder.setPublicKey(this.publicKey);
116   - this.builder.setPrivateKey(this.privateKey);
117   - this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey);
118   - getParamsRPK();
119   - }
120   - } catch (GeneralSecurityException | IllegalArgumentException e) {
121   - log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
122   - throw new RuntimeException(e);
123   - }
124   - }
125   -
126   - private void setServerWithX509Cert(int securityModeCode) {
127   - try {
128   - if (this.contextS.getCtxServer().getKeyStoreValue() != null) {
129   - KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue();
130   - setBuilderX509();
131   - X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias());
132   - if (rootCAX509Cert != null && securityModeCode == X509.code) {
133   - X509Certificate[] trustedCertificates = new X509Certificate[1];
134   - trustedCertificates[0] = rootCAX509Cert;
135   - this.builder.setTrustedCertificates(trustedCertificates);
136   - } else {
137   - /** by default trust all */
138   - this.builder.setTrustedCertificates(new X509Certificate[0]);
139   - }
140   - }
141   - else {
142   - /** by default trust all */
143   - this.builder.setTrustedCertificates(new X509Certificate[0]);
144   - log.error("Unable to load X509 files for BootStrapServer");
145   - }
146   - } catch (KeyStoreException ex) {
147   - log.error("[{}] Unable to load X509 files server", ex.getMessage());
148   - }
149   -
150   - }
151   -
152   - private void setBuilderX509() {
153   - /**
154   - * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
155   - * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
156   - */
157   - try {
158   - X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias());
159   - this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray());
160   - if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
161   - this.builder.setPrivateKey(this.privateKey);
162   - }
163   - if (serverCertificate != null) {
164   - this.builder.setCertificateChain(new X509Certificate[]{serverCertificate});
165   - this.contextBs.getCtxBootStrap().setBootstrapCertificate(serverCertificate);
166   - }
167   - } catch (Exception ex) {
168   - log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
169   - }
170   - }
171   -
172   - private void getParamsRPK() {
173   - if (this.publicKey instanceof ECPublicKey) {
174   - /** Get x coordinate */
175   - byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
176   - if (x[0] == 0)
177   - x = Arrays.copyOfRange(x, 1, x.length);
178   -
179   - /** Get Y coordinate */
180   - byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
181   - if (y[0] == 0)
182   - y = Arrays.copyOfRange(y, 1, y.length);
183   -
184   - /** Get Curves params */
185   - String params = ((ECPublicKey) this.publicKey).getParams().toString();
186   - log.info(
187   - " \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
188   - params, Hex.encodeHexString(x), Hex.encodeHexString(y),
189   - Hex.encodeHexString(this.publicKey.getEncoded()),
190   - Hex.encodeHexString(this.privateKey.getEncoded()));
191   - } else {
192   - throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
193   - }
194   - }
195   -
196   -// private void getParamsX509() {
197   -// try {
198   -// log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",
199   -// Hex.encodeHexString(this.certificate.getEncoded()),
200   -// Hex.encodeHexString(this.privateKey.getEncoded()));
201   -// } catch (CertificateEncodingException e) {
202   -// e.printStackTrace();
203   -// }
204   -// }
205   -}
... ... @@ -73,15 +73,15 @@ public class LWM2MGenerationPSkRPkECC {
73 73 try {
74 74 kpg = KeyPairGenerator.getInstance(algorithm, provider);
75 75 } catch (NoSuchAlgorithmException e) {
76   - e.printStackTrace();
  76 + log.error("", e);
77 77 } catch (NoSuchProviderException e) {
78   - e.printStackTrace();
  78 + log.error("", e);
79 79 }
80 80 ECGenParameterSpec ecsp = new ECGenParameterSpec(nameParameterSpec);
81 81 try {
82 82 kpg.initialize(ecsp);
83 83 } catch (InvalidAlgorithmParameterException e) {
84   - e.printStackTrace();
  84 + log.error("", e);
85 85 }
86 86
87 87 KeyPair kp = kpg.genKeyPair();
... ...
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java
... ... @@ -44,11 +44,10 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PS
44 44 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
45 45 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
46 46
47   -
48 47 @Slf4j
49 48 @Component("LwM2MGetSecurityInfo")
50 49 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
51   -public class LwM2MGetSecurityInfo {
  50 +public class LwM2mValidateCredentialsSecurityInfo {
52 51
53 52 @Autowired
54 53 public LwM2MTransportContextServer contextS;
... ... @@ -57,7 +56,13 @@ public class LwM2MGetSecurityInfo {
57 56 public LwM2MTransportContextBootstrap contextBS;
58 57
59 58
60   - public ReadResultSecurityStore getSecurityInfo(String endPoint, TypeServer keyValue) {
  59 + /**
  60 + * Request to thingsboard Response from thingsboard ValidateDeviceLwM2MCredentials
  61 + * @param endPoint -
  62 + * @param keyValue -
  63 + * @return ValidateDeviceCredentialsResponseMsg and SecurityInfo
  64 + */
  65 + public ReadResultSecurityStore validateCredentialsSecurityInfo(String endPoint, TypeServer keyValue) {
61 66 CountDownLatch latch = new CountDownLatch(1);
62 67 final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1];
63 68 contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endPoint).build(),
... ... @@ -65,7 +70,7 @@ public class LwM2MGetSecurityInfo {
65 70 @Override
66 71 public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
67 72 String credentialsBody = msg.getCredentialsBody();
68   - resultSecurityStore[0] = putSecurityInfo(endPoint, msg.getDeviceInfo().getDeviceName(), credentialsBody, keyValue);
  73 + resultSecurityStore[0] = createSecurityInfo(endPoint, credentialsBody, keyValue);
69 74 resultSecurityStore[0].setMsg(msg);
70 75 Optional<DeviceProfile> deviceProfileOpt = LwM2MTransportHandler.decode(msg.getProfileBody().toByteArray());
71 76 deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile));
... ... @@ -74,20 +79,27 @@ public class LwM2MGetSecurityInfo {
74 79
75 80 @Override
76 81 public void onError(Throwable e) {
77   - log.trace("[{}] Failed to process credentials PSK: {}", endPoint, e);
78   - resultSecurityStore[0] = putSecurityInfo(endPoint, null, null, null);
  82 + log.trace("[{}] [{}] Failed to process credentials PSK ", endPoint, e.toString());
  83 + resultSecurityStore[0] = createSecurityInfo(endPoint, null, null);
79 84 latch.countDown();
80 85 }
81 86 });
82 87 try {
83 88 latch.await(contextS.getCtxServer().getTimeout(), TimeUnit.MILLISECONDS);
84 89 } catch (InterruptedException e) {
85   - e.printStackTrace();
  90 + log.error("", e);
86 91 }
87 92 return resultSecurityStore[0];
88 93 }
89 94
90   - private ReadResultSecurityStore putSecurityInfo(String endPoint, String deviceName, String jsonStr, TypeServer keyValue) {
  95 + /**
  96 + * Create new SecurityInfo
  97 + * @param endPoint -
  98 + * @param jsonStr -
  99 + * @param keyValue -
  100 + * @return SecurityInfo
  101 + */
  102 + private ReadResultSecurityStore createSecurityInfo(String endPoint, String jsonStr, TypeServer keyValue) {
91 103 ReadResultSecurityStore result = new ReadResultSecurityStore();
92 104 JsonObject objectMsg = LwM2MTransportHandler.validateJson(jsonStr);
93 105 if (objectMsg != null && !objectMsg.isJsonNull()) {
... ... @@ -103,20 +115,21 @@ public class LwM2MGetSecurityInfo {
103 115 if (keyValue.equals(TypeServer.BOOTSTRAP)) {
104 116 result.setBootstrapJsonCredential(object);
105 117 result.setEndPoint(endPoint);
  118 + result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code);
106 119 } else {
107 120 LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(object.get("securityConfigClientMode").getAsString().toLowerCase());
108 121 switch (lwM2MSecurityMode) {
109 122 case NO_SEC:
110   - getClientSecurityInfoNoSec(result);
  123 + createClientSecurityInfoNoSec(result);
111 124 break;
112 125 case PSK:
113   - getClientSecurityInfoPSK(result, endPoint, object);
  126 + createClientSecurityInfoPSK(result, endPoint, object);
114 127 break;
115 128 case RPK:
116   - getClientSecurityInfoRPK(result, endPoint, object);
  129 + createClientSecurityInfoRPK(result, endPoint, object);
117 130 break;
118 131 case X509:
119   - getClientSecurityInfoX509(result, endPoint);
  132 + createClientSecurityInfoX509(result, endPoint);
120 133 break;
121 134 default:
122 135 break;
... ... @@ -127,12 +140,12 @@ public class LwM2MGetSecurityInfo {
127 140 return result;
128 141 }
129 142
130   - private void getClientSecurityInfoNoSec(ReadResultSecurityStore result) {
  143 + private void createClientSecurityInfoNoSec(ReadResultSecurityStore result) {
131 144 result.setSecurityInfo(null);
132 145 result.setSecurityMode(NO_SEC.code);
133 146 }
134 147
135   - private void getClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) {
  148 + private void createClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) {
136 149 /** PSK Deserialization */
137 150 String identity = (object.has("identity") && object.get("identity").isJsonPrimitive()) ? object.get("identity").getAsString() : null;
138 151 if (identity != null && !identity.isEmpty()) {
... ... @@ -152,7 +165,7 @@ public class LwM2MGetSecurityInfo {
152 165 }
153 166 }
154 167
155   - private void getClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) {
  168 + private void createClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) {
156 169 try {
157 170 if (object.has("key") && object.get("key").isJsonPrimitive()) {
158 171 byte[] rpkkey = Hex.decodeHex(object.get("key").getAsString().toLowerCase().toCharArray());
... ... @@ -167,7 +180,7 @@ public class LwM2MGetSecurityInfo {
167 180 }
168 181 }
169 182
170   - private void getClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) {
  183 + private void createClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) {
171 184 result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
172 185 result.setSecurityMode(X509.code);
173 186 }
... ...
... ... @@ -33,10 +33,10 @@ import java.util.Optional;
33 33
34 34 @Slf4j
35 35 public class LwM2MSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
36   - private LwM2MTransportService service;
  36 + private LwM2MTransportServiceImpl service;
37 37 private TransportProtos.SessionInfoProto sessionInfo;
38 38
39   - public LwM2MSessionMsgListener(LwM2MTransportService service, TransportProtos.SessionInfoProto sessionInfo) {
  39 + public LwM2MSessionMsgListener(LwM2MTransportServiceImpl service, TransportProtos.SessionInfoProto sessionInfo) {
40 40 this.service = service;
41 41 this.sessionInfo = sessionInfo;
42 42 }
... ...
... ... @@ -31,20 +31,14 @@ import org.eclipse.leshan.core.node.LwM2mPath;
31 31 import org.eclipse.leshan.core.node.LwM2mSingleResource;
32 32 import org.eclipse.leshan.core.node.codec.CodecException;
33 33 import org.eclipse.leshan.core.util.Hex;
34   -import org.eclipse.leshan.server.californium.LeshanServer;
35 34 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
36 35 import org.nustaq.serialization.FSTConfiguration;
37   -import org.springframework.beans.factory.annotation.Autowired;
38   -import org.springframework.beans.factory.annotation.Qualifier;
39   -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
40   -import org.springframework.stereotype.Component;
41 36 import org.thingsboard.server.common.data.DeviceProfile;
42 37 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
43 38 import org.thingsboard.server.common.transport.TransportServiceCallback;
44 39 import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
45 40 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
46 41
47   -import javax.annotation.PostConstruct;
48 42 import java.io.File;
49 43 import java.io.IOException;
50 44 import java.util.Arrays;
... ... @@ -53,8 +47,8 @@ import java.util.LinkedList;
53 47 import java.util.Optional;
54 48
55 49 @Slf4j
56   -@Component("LwM2MTransportHandler")
57   -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
  50 +//@Component("LwM2MTransportHandler")
  51 +//@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
58 52 public class LwM2MTransportHandler {
59 53
60 54 // 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 {
114 108 public static final String SERVICE_CHANNEL = "SERVICE";
115 109 public static final String RESPONSE_CHANNEL = "RESP";
116 110
117   - @Autowired
118   - @Qualifier("LeshanServerCert")
119   - private LeshanServer lhServerCert;
  111 +// @Autowired
  112 +// @Qualifier("LeshanServerCert")
  113 +// private LeshanServer lhServerCert;
  114 +//
  115 +// @Autowired
  116 +// @Qualifier("LeshanServerNoSecPskRpk")
  117 +// private LeshanServer lhServerNoSecPskRpk;
120 118
121   - @Autowired
122   - @Qualifier("leshanServerNoSecPskRpk")
123   - private LeshanServer lhServerNoSecPskRpk;
  119 +// @Autowired
  120 +// @Qualifier("ServerListenerCert")
  121 +// private LwM2mServerListener serverListenerCert;
  122 +//
  123 +// @Autowired
  124 +// @Qualifier("ServerListenerNoSecPskRpk")
  125 +// private LwM2mServerListener serverListenerNoSecPskRpk;
124 126
125   - @Autowired
126   - private LwM2MTransportService service;
127 127
128   - @PostConstruct
129   - public void init() {
130   - try {
131   - LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service);
132   - this.lhServerCert.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
133   - this.lhServerCert.getPresenceService().addListener(lwM2mServerListener.presenceListener);
134   - this.lhServerCert.getObservationService().addListener(lwM2mServerListener.observationListener);
135   - lwM2mServerListener = new LwM2mServerListener(lhServerNoSecPskRpk, service);
136   - this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
137   - this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener);
138   - this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener);
139   - } catch (java.lang.NullPointerException e) {
140   - log.error("init [{}]", e.toString());
141   - }
142   - }
  128 +// @PostConstruct
  129 +// public void init() {
  130 +// try {
  131 +// serverListenerCert.init(lhServerCert);
  132 +// this.lhServerCert.getRegistrationService().addListener(serverListenerCert.registrationListener);
  133 +// this.lhServerCert.getPresenceService().addListener(serverListenerCert.presenceListener);
  134 +// this.lhServerCert.getObservationService().addListener(serverListenerCert.observationListener);
  135 +// serverListenerNoSecPskRpk.init(lhServerNoSecPskRpk);
  136 +// this.lhServerNoSecPskRpk.getRegistrationService().addListener(serverListenerNoSecPskRpk.registrationListener);
  137 +// this.lhServerNoSecPskRpk.getPresenceService().addListener(serverListenerNoSecPskRpk.presenceListener);
  138 +// this.lhServerNoSecPskRpk.getObservationService().addListener(serverListenerNoSecPskRpk.observationListener);
  139 +// } catch (Exception e) {
  140 +// log.error("init [{}]", e.toString());
  141 +// }
  142 +// }
143 143
144 144 public static NetworkConfig getCoapConfig() {
145 145 NetworkConfig coapConfig;
... ... @@ -217,7 +217,7 @@ public class LwM2MTransportHandler {
217 217 JsonObject objectMsg = (observeAttrStr != null) ? validateJson(observeAttrStr) : null;
218 218 return (getValidateCredentialsBodyFromThingsboard(objectMsg)) ? objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject() : null;
219 219 } catch (IOException e) {
220   - e.printStackTrace();
  220 + log.error("", e);
221 221 }
222 222 }
223 223 return null;
... ... @@ -232,7 +232,7 @@ public class LwM2MTransportHandler {
232 232 JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null;
233 233 return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null;
234 234 } catch (IOException e) {
235   - e.printStackTrace();
  235 + log.error("", e);
236 236 }
237 237 }
238 238 return null;
... ...
... ... @@ -86,7 +86,7 @@ public class LwM2MTransportRequest {
86 86 private LwM2mValueConverterImpl converter;
87 87
88 88 @Autowired
89   - LwM2MTransportService service;
  89 + LwM2MTransportServiceImpl service;
90 90
91 91
92 92 @PostConstruct
... ...
... ... @@ -20,10 +20,16 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 20 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
21 21 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
22 22 import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
  23 +import org.eclipse.leshan.core.util.Hex;
23 24 import org.eclipse.leshan.server.californium.LeshanServer;
24 25 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
25 26 import org.eclipse.leshan.server.model.LwM2mModelProvider;
26 27 import org.eclipse.leshan.server.model.VersionedModelProvider;
  28 +import org.eclipse.leshan.server.redis.RedisRegistrationStore;
  29 +import org.eclipse.leshan.server.redis.RedisSecurityStore;
  30 +import org.eclipse.leshan.server.security.DefaultAuthorizer;
  31 +import org.eclipse.leshan.server.security.EditableSecurityStore;
  32 +import org.eclipse.leshan.server.security.SecurityChecker;
27 33 import org.springframework.beans.factory.annotation.Autowired;
28 34 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
29 35 import org.springframework.context.annotation.Bean;
... ... @@ -31,11 +37,33 @@ import org.springframework.context.annotation.ComponentScan;
31 37 import org.springframework.context.annotation.Configuration;
32 38 import org.springframework.context.annotation.Primary;
33 39 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
34   -import org.thingsboard.server.transport.lwm2m.server.secure.LwM2MSetSecurityStoreServer;
35 40 import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
36 41 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
37   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
  42 +import redis.clients.jedis.Jedis;
  43 +import redis.clients.jedis.JedisPool;
  44 +import redis.clients.jedis.util.Pool;
  45 +
  46 +import java.math.BigInteger;
  47 +import java.net.URI;
  48 +import java.net.URISyntaxException;
  49 +import java.security.AlgorithmParameters;
  50 +import java.security.GeneralSecurityException;
  51 +import java.security.KeyFactory;
  52 +import java.security.KeyStoreException;
  53 +import java.security.PrivateKey;
  54 +import java.security.PublicKey;
  55 +import java.security.cert.X509Certificate;
  56 +import java.security.interfaces.ECPublicKey;
  57 +import java.security.spec.ECGenParameterSpec;
  58 +import java.security.spec.ECParameterSpec;
  59 +import java.security.spec.ECPoint;
  60 +import java.security.spec.ECPrivateKeySpec;
  61 +import java.security.spec.ECPublicKeySpec;
  62 +import java.security.spec.KeySpec;
  63 +import java.util.Arrays;
  64 +
38 65 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
  66 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
39 67 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig;
40 68
41 69
... ... @@ -45,6 +73,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
45 73 @Configuration("LwM2MTransportServerConfiguration")
46 74 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
47 75 public class LwM2MTransportServerConfiguration {
  76 + private PublicKey publicKey;
  77 + private PrivateKey privateKey;
48 78
49 79 @Autowired
50 80 private LwM2MTransportContextServer context;
... ... @@ -52,14 +82,26 @@ public class LwM2MTransportServerConfiguration {
52 82 @Autowired
53 83 private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
54 84
  85 + @Bean
  86 + public LwM2mServerListener lwM2mServerListenerCert() {
  87 + return new LwM2mServerListener();
  88 + }
  89 +
  90 + @Bean
  91 + public LwM2mServerListener lwM2mServerListenerNoSecPskRpk() {
  92 + return new LwM2mServerListener();
  93 + }
  94 +
55 95 @Primary
56 96 @Bean(name = "LeshanServerCert")
57 97 public LeshanServer getLeshanServerCert() {
58 98 log.info("Starting LwM2M transport ServerCert... PostConstruct");
59   - return getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509);
  99 + LeshanServer leshanServerCert = getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509);
  100 +
  101 + return leshanServerCert;
60 102 }
61 103
62   - @Bean(name = "leshanServerNoSecPskRpk")
  104 + @Bean(name = "LeshanServerNoSecPskRpk")
63 105 public LeshanServer getLeshanServerNoSecPskRpk() {
64 106 log.info("Starting LwM2M transport ServerNoSecPskRpk... PostConstruct");
65 107 return getLeshanServer(this.context.getCtxServer().getServerPort(), this.context.getCtxServer().getServerSecurePort(), RPK);
... ... @@ -94,9 +136,178 @@ public class LwM2MTransportServerConfiguration {
94 136 /** Create DTLS security mode
95 137 * There can be only one DTLS security mode
96 138 */
97   - new LwM2MSetSecurityStoreServer(builder, context, lwM2mInMemorySecurityStore, dtlsMode);
  139 + this.LwM2MSetSecurityStoreServer(builder, dtlsMode);
98 140
99 141 /** Create LWM2M server */
100 142 return builder.build();
101 143 }
  144 +
  145 + private void LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MSecurityMode dtlsMode) {
  146 + /** Set securityStore with new registrationStore */
  147 + EditableSecurityStore securityStore = lwM2mInMemorySecurityStore;
  148 +
  149 + switch (dtlsMode) {
  150 + /** Use PSK only */
  151 + case PSK:
  152 + generatePSK_RPK();
  153 + if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  154 + builder.setPrivateKey(this.privateKey);
  155 + builder.setPublicKey(null);
  156 + infoParamsPSK();
  157 + }
  158 + break;
  159 + /** Use RPK only */
  160 + case RPK:
  161 + generatePSK_RPK();
  162 + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
  163 + this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  164 + builder.setPublicKey(this.publicKey);
  165 + builder.setPrivateKey(this.privateKey);
  166 + infoParamsRPK();
  167 + }
  168 + break;
  169 + /** Use x509 only */
  170 + case X509:
  171 + setServerWithX509Cert(builder);
  172 + break;
  173 + /** No security */
  174 + case NO_SEC:
  175 + builder.setTrustedCertificates(new X509Certificate[0]);
  176 + break;
  177 + /** Use x509 with EST */
  178 + case X509_EST:
  179 + // TODO support sentinel pool and make pool configurable
  180 + break;
  181 + case REDIS:
  182 + /**
  183 + * Set securityStore with new registrationStore (if use redis store)
  184 + * Connect to redis
  185 + */
  186 + Pool<Jedis> jedis = null;
  187 + try {
  188 + jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl()));
  189 + securityStore = new RedisSecurityStore(jedis);
  190 + builder.setRegistrationStore(new RedisRegistrationStore(jedis));
  191 + } catch (URISyntaxException e) {
  192 + log.error("", e);
  193 + }
  194 + break;
  195 + default:
  196 + }
  197 +
  198 + /** Set securityStore with registrationStore (if x509)*/
  199 + if (dtlsMode == X509) {
  200 + builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
  201 + @Override
  202 + protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
  203 + String expectedX509CommonName) {
  204 + return endpoint.startsWith(expectedX509CommonName);
  205 + }
  206 + }));
  207 + }
  208 +
  209 + /** Set securityStore with new registrationStore */
  210 + builder.setSecurityStore(securityStore);
  211 + }
  212 +
  213 + private void generatePSK_RPK() {
  214 + try {
  215 + /** Get Elliptic Curve Parameter spec for secp256r1 */
  216 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
  217 + algoParameters.init(new ECGenParameterSpec("secp256r1"));
  218 + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
  219 + if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) {
  220 + /** Get point values */
  221 + byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray());
  222 + byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray());
  223 + /** Create key specs */
  224 + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
  225 + parameterSpec);
  226 + /** Get keys */
  227 + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  228 + }
  229 + if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) {
  230 + /** Get point values */
  231 + byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray());
  232 + /** Create key specs */
  233 + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
  234 + /** Get keys */
  235 + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
  236 + }
  237 + } catch (GeneralSecurityException | IllegalArgumentException e) {
  238 + log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
  239 + throw new RuntimeException(e);
  240 + }
  241 + }
  242 +
  243 + private void infoParamsPSK() {
  244 + log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]",
  245 + Hex.encodeHexString(this.privateKey.getEncoded()),
  246 + this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerSecurePort()));
  247 + }
  248 +
  249 + private void infoParamsRPK() {
  250 + if (this.publicKey instanceof ECPublicKey) {
  251 + /** Get x coordinate */
  252 + byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
  253 + if (x[0] == 0)
  254 + x = Arrays.copyOfRange(x, 1, x.length);
  255 +
  256 + /** Get Y coordinate */
  257 + byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
  258 + if (y[0] == 0)
  259 + y = Arrays.copyOfRange(y, 1, y.length);
  260 +
  261 + /** Get Curves params */
  262 + String params = ((ECPublicKey) this.publicKey).getParams().toString();
  263 + log.info(
  264 + " \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
  265 + params, Hex.encodeHexString(x), Hex.encodeHexString(y),
  266 + Hex.encodeHexString(this.publicKey.getEncoded()),
  267 + Hex.encodeHexString(this.privateKey.getEncoded()));
  268 + } else {
  269 + throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
  270 + }
  271 + }
  272 +
  273 +
  274 + private void setServerWithX509Cert(LeshanServerBuilder builder) {
  275 + try {
  276 + if (this.context.getCtxServer().getKeyStoreValue() != null) {
  277 + setBuilderX509(builder);
  278 + X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias());
  279 + if (rootCAX509Cert != null) {
  280 + X509Certificate[] trustedCertificates = new X509Certificate[1];
  281 + trustedCertificates[0] = rootCAX509Cert;
  282 + builder.setTrustedCertificates(trustedCertificates);
  283 + } else {
  284 + /** by default trust all */
  285 + builder.setTrustedCertificates(new X509Certificate[0]);
  286 + }
  287 + } else {
  288 + /** by default trust all */
  289 + builder.setTrustedCertificates(new X509Certificate[0]);
  290 + log.error("Unable to load X509 files for LWM2MServer");
  291 + }
  292 + } catch (KeyStoreException ex) {
  293 + log.error("[{}] Unable to load X509 files server", ex.getMessage());
  294 + }
  295 + }
  296 +
  297 + private void setBuilderX509(LeshanServerBuilder builder) {
  298 + /**
  299 + * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
  300 + * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
  301 + */
  302 + try {
  303 + X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias());
  304 + PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray());
  305 + builder.setPrivateKey(privateKey);
  306 + builder.setCertificateChain(new X509Certificate[]{serverCertificate});
  307 + } catch (Exception ex) {
  308 + log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
  309 + }
  310 + }
  311 +
  312 +
102 313 }
... ...
... ... @@ -20,14 +20,15 @@ import org.eclipse.leshan.server.californium.LeshanServer;
20 20 import org.springframework.beans.factory.annotation.Autowired;
21 21 import org.springframework.beans.factory.annotation.Qualifier;
22 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
23   -import org.springframework.stereotype.Service;
  23 +import org.springframework.stereotype.Component;
24 24 import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
25 25 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  26 +
26 27 import javax.annotation.PostConstruct;
27 28 import javax.annotation.PreDestroy;
28 29
29 30 @Slf4j
30   -@Service("LwM2MTransportServerInitializer")
  31 +@Component("LwM2MTransportServerInitializer")
31 32 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
32 33 public class LwM2MTransportServerInitializer {
33 34
... ... @@ -36,8 +37,18 @@ public class LwM2MTransportServerInitializer {
36 37 private LeshanServer lhServerCert;
37 38
38 39 @Autowired
39   - @Qualifier("leshanServerNoSecPskRpk")
  40 + @Qualifier("LeshanServerNoSecPskRpk")
40 41 private LeshanServer lhServerNoSecPskRpk;
  42 +//
  43 +// @Autowired
  44 +// @Qualifier("LeshanServerListener")
  45 +// private LwM2mServerListener lwM2mServerListener;
  46 +
  47 + @Autowired
  48 + private LwM2mServerListener lwM2mServerListenerNoSecPskRpk;
  49 +
  50 + @Autowired
  51 + private LwM2mServerListener lwM2mServerListenerCert;
41 52
42 53 @Autowired
43 54 private LwM2MTransportContextServer context;
... ... @@ -46,19 +57,33 @@ public class LwM2MTransportServerInitializer {
46 57 public void init() {
47 58 if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC();
48 59 if (this.context.getCtxServer().isServerStartAll()) {
49   - this.lhServerCert.start();
50   - this.lhServerNoSecPskRpk.start();
51   - }
52   - else {
  60 + this.startLhServerCert();
  61 + this.startLhServerNoSecPskRpk();
  62 + } else {
53 63 if (this.context.getCtxServer().getServerDtlsMode() == LwM2MSecurityMode.X509.code) {
54   - this.lhServerCert.start();
55   - }
56   - else {
57   - this.lhServerNoSecPskRpk.start();
  64 + this.startLhServerCert();
  65 + } else {
  66 + this.startLhServerNoSecPskRpk();
58 67 }
59 68 }
60 69 }
61 70
  71 + private void startLhServerCert() {
  72 + this.lhServerCert.start();
  73 + LwM2mServerListener serverListenerCert = this.lwM2mServerListenerCert.init(this.lhServerCert);
  74 + this.lhServerCert.getRegistrationService().addListener(serverListenerCert.registrationListener);
  75 + this.lhServerCert.getPresenceService().addListener(serverListenerCert.presenceListener);
  76 + this.lhServerCert.getObservationService().addListener(serverListenerCert.observationListener);
  77 + }
  78 +
  79 + private void startLhServerNoSecPskRpk() {
  80 + this.lhServerNoSecPskRpk.start();
  81 + LwM2mServerListener serverListenerNoSecPskRpk = this.lwM2mServerListenerNoSecPskRpk.init(this.lhServerNoSecPskRpk);
  82 + this.lhServerNoSecPskRpk.getRegistrationService().addListener(serverListenerNoSecPskRpk.registrationListener);
  83 + this.lhServerNoSecPskRpk.getPresenceService().addListener(serverListenerNoSecPskRpk.presenceListener);
  84 + this.lhServerNoSecPskRpk.getObservationService().addListener(serverListenerNoSecPskRpk.observationListener);
  85 + }
  86 +
62 87 @PreDestroy
63 88 public void shutdown() {
64 89 log.info("Stopping LwM2M transport Server!");
... ...
... ... @@ -15,1114 +15,42 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
18   -import com.google.gson.Gson;
19   -import com.google.gson.JsonArray;
20   -import com.google.gson.JsonElement;
21   -import com.google.gson.JsonObject;
22   -import lombok.extern.slf4j.Slf4j;
23   -import org.eclipse.leshan.core.model.ResourceModel;
24   -import org.eclipse.leshan.core.node.LwM2mMultipleResource;
25   -import org.eclipse.leshan.core.node.LwM2mObject;
26   -import org.eclipse.leshan.core.node.LwM2mObjectInstance;
27   -import org.eclipse.leshan.core.node.LwM2mPath;
28   -import org.eclipse.leshan.core.node.LwM2mSingleResource;
29 18 import org.eclipse.leshan.core.observation.Observation;
30   -import org.eclipse.leshan.core.request.ContentFormat;
31   -import org.eclipse.leshan.core.request.WriteRequest;
32 19 import org.eclipse.leshan.core.response.ReadResponse;
33   -import org.eclipse.leshan.core.util.NamedThreadFactory;
34 20 import org.eclipse.leshan.server.californium.LeshanServer;
35 21 import org.eclipse.leshan.server.registration.Registration;
36   -import org.springframework.beans.factory.annotation.Autowired;
37   -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
38   -import org.springframework.stereotype.Service;
39 22 import org.thingsboard.server.common.data.Device;
40 23 import org.thingsboard.server.common.data.DeviceProfile;
41   -import org.thingsboard.server.common.transport.TransportService;
42   -import org.thingsboard.server.common.transport.adaptor.AdaptorException;
43   -import org.thingsboard.server.common.transport.adaptor.JsonConverter;
44   -import org.thingsboard.server.common.transport.service.DefaultTransportService;
45 24 import org.thingsboard.server.gen.transport.TransportProtos;
46   -import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
47   -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
48   -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
49   -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
50   -import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
51   -import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
52   -import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
53   -import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
54   -import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
55   -import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
56 25
57   -import javax.annotation.PostConstruct;
58   -import java.util.ArrayList;
59   -import java.util.Arrays;
60 26 import java.util.Collection;
61   -import java.util.HashSet;
62   -import java.util.LinkedHashSet;
63   -import java.util.List;
64   -import java.util.Map;
65 27 import java.util.Optional;
66   -import java.util.Random;
67   -import java.util.Set;
68   -import java.util.UUID;
69   -import java.util.concurrent.ConcurrentHashMap;
70   -import java.util.concurrent.ConcurrentMap;
71   -import java.util.concurrent.CountDownLatch;
72   -import java.util.concurrent.ExecutorService;
73   -import java.util.concurrent.Executors;
74   -import java.util.concurrent.TimeUnit;
75   -import java.util.concurrent.atomic.AtomicBoolean;
76   -import java.util.stream.Collectors;
77 28
78   -import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject;
79   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.CLIENT_NOT_AUTHORIZED;
80   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEFAULT_TIMEOUT;
81   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_REQUEST;
82   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC;
83   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC;
84   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE;
85   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ;
86   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR;
87   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO;
88   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY;
89   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE;
90   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE;
91   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVICE_CHANNEL;
92   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getAckCallback;
  29 +public interface LwM2MTransportService {
93 30
94   -@Slf4j
95   -@Service("LwM2MTransportService")
96   -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
97   -public class LwM2MTransportService {
  31 + void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations);
98 32
99   - private ExecutorService executorRegistered;
100   - private ExecutorService executorUpdateRegistered;
101   - private ExecutorService executorUnRegistered;
102   - private LwM2mValueConverterImpl converter;
  33 + void updatedReg(LeshanServer lwServer, Registration registration);
103 34
  35 + void unReg(Registration registration, Collection<Observation> observations);
104 36
105   - @Autowired
106   - private TransportService transportService;
  37 + void onSleepingDev(Registration registration);
107 38
108   - @Autowired
109   - public LwM2MTransportContextServer context;
  39 + void setCancelObservations(LeshanServer lwServer, Registration registration);
110 40
111   - @Autowired
112   - private LwM2MTransportRequest lwM2MTransportRequest;
  41 + void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path);
113 42
114   - @Autowired
115   - LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
  43 + void onObservationResponse(Registration registration, String path, ReadResponse response);
116 44
117   - @PostConstruct
118   - public void init() {
119   - this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS);
120   - this.executorRegistered = Executors.newCachedThreadPool(
121   - new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL)));
122   - this.executorUpdateRegistered = Executors.newCachedThreadPool(
123   - new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL)));
124   - this.executorUnRegistered = Executors.newCachedThreadPool(
125   - new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL)));
126   - this.converter = LwM2mValueConverterImpl.getInstance();
127   - }
  45 + void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
128 46
129   - /**
130   - * Start registration device
131   - * Create session: Map<String <registrationId >, LwM2MClient>
132   - * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint)
133   - * 1.1 When we initialize the registration, we register the session by endpoint.
134   - * 1.2 If the server has incomplete requests (canceling the registration of the previous session),
135   - * delete the previous session only by the previous registration.getId
136   - * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId
137   - * 1.2 Remove from sessions Model by enpPoint
138   - * Next -> Create new LwM2MClient for current session -> setModelClient...
139   - *
140   - * @param lwServer - LeshanServer
141   - * @param registration - Registration LwM2M Client
142   - * @param previousObsersations - may be null
143   - */
144   - public void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations) {
145   - executorRegistered.submit(() -> {
146   - try {
147   - log.info("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
148   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration);
149   - if (lwM2MClient != null) {
150   - lwM2MClient.setLwM2MTransportService(this);
151   - lwM2MClient.setSessionUuid(UUID.randomUUID());
152   - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration);
153   - this.setLwM2MClient(lwServer, registration, lwM2MClient);
154   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
155   - if (sessionInfo != null) {
156   - lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
157   - lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
158   - lwM2MClient.setDeviceName(sessionInfo.getDeviceName());
159   - lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType());
160   - transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));
161   - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
162   - transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
163   - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);
164   - } else {
165   - log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
166   - }
167   - } else {
168   - log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);
169   - }
170   - } catch (Throwable t) {
171   - log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t);
172   - }
173   - });
174   - }
  47 + void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile);
175 48
176   - /**
177   - * if sessionInfo removed from sessions, then new registerAsyncSession
178   - * @param lwServer - LeshanServer
179   - * @param registration - Registration LwM2M Client
180   - */
181   - public void updatedReg(LeshanServer lwServer, Registration registration) {
182   - executorUpdateRegistered.submit(() -> {
183   - try {
184   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
185   - if (sessionInfo != null) {
186   - this.checkInactivity(sessionInfo);
187   - log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
188   - } else {
189   - log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
190   - }
191   - } catch (Throwable t) {
192   - log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);
193   - }
194   - });
195   - }
  49 + void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt);
196 50
  51 + void doTrigger(LeshanServer lwServer, Registration registration, String path);
197 52
198   - /**
199   - * @param registration - Registration LwM2M Client
200   - * @param observations - All paths observations before unReg
201   - * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
202   - */
203   - public void unReg(Registration registration, Collection<Observation> observations) {
204   - executorUnRegistered.submit(() -> {
205   - try {
206   - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
207   - this.closeClientSession(registration);
208   - } catch (Throwable t) {
209   - log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
210   - }
211   - });
212   - }
  53 + void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
213 54
214   - private void closeClientSession(Registration registration) {
215   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
216   - if (sessionInfo != null) {
217   - transportService.deregisterSession(sessionInfo);
218   - this.doCloseSession(sessionInfo);
219   - lwM2mInMemorySecurityStore.delRemoveSessionAndListener(registration.getId());
220   - if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) {
221   - this.syncSessionsAndProfiles();
222   - }
223   - log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
224   - } else {
225   - log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
226   - }
227   - }
228 55
229   - public void onSleepingDev(Registration registration) {
230   - log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
231   - //TODO: associate endpointId with device information.
232   - }
233   -
234   - /**
235   - * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,
236   - * * if you need to do long time processing use a dedicated thread pool.
237   - *
238   - * @param registration -
239   - */
240   - protected void onAwakeDev(Registration registration) {
241   - log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
242   - //TODO: associate endpointId with device information.
243   - }
244   -
245   - /**
246   - * This method is used to sync with sessions
247   - * Removes a profile if not used in sessions
248   - */
249   - private void syncSessionsAndProfiles() {
250   - Map<UUID, AttrTelemetryObserveValue> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()
251   - .stream()
252   - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
253   - profilesClone.forEach((k, v) -> {
254   - String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet()
255   - .stream()
256   - .filter(e -> e.getValue().getProfileUuid().equals(k))
257   - .findFirst()
258   - .map(Map.Entry::getKey) // return the key of the matching entry if found
259   - .orElse("");
260   - if (registrationId.isEmpty()) {
261   - lwM2mInMemorySecurityStore.getProfiles().remove(k);
262   - }
263   - });
264   - }
265   -
266   - /**
267   - * #0 Add new ObjectModel to context
268   - * Create new LwM2MClient for current session -> setModelClient...
269   - * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client
270   - * to get the client model with current values
271   - * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse
272   - *
273   - * @param lwServer - LeshanServer
274   - * @param registration - Registration LwM2M Client
275   - * @param lwM2MClient - object with All parameters off client
276   - */
277   - private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) {
278   - Arrays.stream(registration.getObjectLinks()).forEach(url -> {
279   - LwM2mPath pathIds = new LwM2mPath(url.getUrl());
280   - if (pathIds.isObjectInstance() && !pathIds.isResource()) {
281   - lwM2MClient.getPendingRequests().add(url.getUrl());
282   - }
283   - });
284   - // #2
285   - Arrays.stream(registration.getObjectLinks()).forEach(url -> {
286   - LwM2mPath pathIds = new LwM2mPath(url.getUrl());
287   - if (pathIds.isObjectInstance() && !pathIds.isResource()) {
288   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
289   - lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
290   - }
291   - });
292   - }
293   -
294   - /**
295   - * @param registration - Registration LwM2M Client
296   - * @return - sessionInfo after access connect client
297   - */
298   - private SessionInfoProto getValidateSessionInfo(Registration registration) {
299   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
300   - return getNewSessionInfoProto(lwM2MClient);
301   -
302   - }
303   -
304   - /**
305   - *
306   - * @param registrationId -
307   - * @return -
308   - */
309   - private SessionInfoProto getValidateSessionInfo(String registrationId) {
310   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
311   - return getNewSessionInfoProto(lwM2MClient);
312   - }
313   -
314   - private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) {
315   - if (lwM2MClient != null) {
316   - ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();
317   - if (msg == null || msg.getDeviceInfo() == null) {
318   - log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);
319   - this.closeClientSession(lwM2MClient.getRegistration());
320   - return null;
321   - } else {
322   - return SessionInfoProto.newBuilder()
323   - .setNodeId(this.context.getNodeId())
324   - .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())
325   - .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())
326   - .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
327   - .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
328   - .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
329   - .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
330   - .setDeviceName(msg.getDeviceInfo().getDeviceName())
331   - .setDeviceType(msg.getDeviceInfo().getDeviceType())
332   - .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
333   - .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
334   - .build();
335   - }
336   - }
337   - return null;
338   - }
339   -
340   - /**
341   - * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe
342   - * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use
343   - * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection
344   - * #2 Start observe
345   - *
346   - * @param lwM2MClient - LwM2M Client
347   - */
348   -
349   - public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) {
350   - // #1
351   - this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null);
352   - // #2
353   - this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration());
354   -
355   - }
356   -
357   - /**
358   - * If there is a difference in values between the current resource values and the shared attribute values
359   - * when the client connects to the server
360   - * #1 get attributes name from profile include name resources in ModelObject if resource isWritable
361   - * #2.1 #1 size > 0 => send Request getAttributes to thingsboard
362   - * #2.2 #1 size == 0 => continue normal process
363   - *
364   - * @param lwM2MClient - LwM2M Client
365   - */
366   - public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) {
367   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
368   - if (sessionInfo != null) {
369   - //#1.1 + #1.2
370   - List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);
371   - if (attrSharedNames.size() > 0) {
372   - //#2.1
373   - try {
374   - TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames);
375   - lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId());
376   - transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
377   - } catch (AdaptorException e) {
378   - log.warn("Failed to decode get attributes request", e);
379   - }
380   - }
381   - // #2.2
382   - else {
383   - lwM2MClient.onSuccessOrErrorDelayedRequests(null);
384   - }
385   - }
386   - }
387   -
388   - /**
389   - * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
390   - * #1 Get path resource by result attributesResponse
391   - * #1.1 If two names have equal path => last time attribute
392   - * #2.1 if there is a difference in values between the current resource values and the shared attribute values
393   - * => sent to client Request Update of value (new value from shared attribute)
394   - * and LwM2MClient.delayedRequests.add(path)
395   - * #2.1 if there is not a difference in values between the current resource values and the shared attribute values
396   - *
397   - * @param attributesResponse -
398   - * @param sessionInfo -
399   - */
400   - public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
401   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo);
402   - if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) {
403   - attributesResponse.getSharedAttributeListList().forEach(attr -> {
404   - String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey());
405   - // #1.1
406   - if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) {
407   - lwM2MClient.getDelayedRequests().put(path, attr);
408   - } else {
409   - lwM2MClient.getDelayedRequests().put(path, attr);
410   - }
411   - });
412   - // #2.1
413   - lwM2MClient.getDelayedRequests().forEach((k, v) -> {
414   - ArrayList<TransportProtos.KeyValueProto> listV = new ArrayList<>();
415   - listV.add(v.getKv());
416   - this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k);
417   - });
418   - lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId());
419   - if (lwM2MClient.getDelayedRequests().size() == 0) {
420   - lwM2MClient.onSuccessOrErrorDelayedRequests(null);
421   - }
422   - }
423   - }
424   -
425   - private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {
426   - if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) {
427   - lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
428   - ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(),
429   - true);
430   - }
431   - }
432   -
433   - /**
434   - * Get names and keyNames from profile shared!!!! attr resources IsWritable
435   - * @param lwM2MClient -
436   - * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable
437   - */
438   - private List<String> getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) {
439   - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid());
440   - Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
441   - ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class);
442   -
443   - ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
444   - .stream()
445   - .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&
446   - context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))
447   - .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
448   -
449   - Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
450   - namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
451   - return new ArrayList<>(namesIsWritable);
452   - }
453   -
454   -
455   - /**
456   - * Sent Attribute and Telemetry to Thingsboard
457   - * #1 - get AttrName/TelemetryName with value:
458   - * #1.1 from Client
459   - * #1.2 from LwM2MClient:
460   - * -- resourceId == path from AttrTelemetryObserveValue.postAttributeProfile/postTelemetryProfile/postObserveProfile
461   - * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
462   - * #2 - set Attribute/Telemetry
463   - *
464   - * @param registration - Registration LwM2M Client
465   - */
466   - private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {
467   - JsonObject attributes = new JsonObject();
468   - JsonObject telemetries = new JsonObject();
469   - if (start) {
470   - // #1.1
471   - JsonObject attributeClient = this.getAttributeClient(registration);
472   - if (attributeClient != null) {
473   - attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));
474   - }
475   - }
476   - // #1.2
477   - CountDownLatch cancelLatch = new CountDownLatch(1);
478   - this.getParametersFromProfile(attributes, telemetries, registration, paths);
479   - cancelLatch.countDown();
480   - try {
481   - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
482   - } catch (InterruptedException e) {
483   - log.error("[{}] updateAttrTelemetry", e.toString());
484   - }
485   - if (attributes.getAsJsonObject().entrySet().size() > 0)
486   - this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);
487   - if (telemetries.getAsJsonObject().entrySet().size() > 0)
488   - this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
489   - }
490   -
491   - /**
492   - * get AttrName/TelemetryName with value from Client
493   - *
494   - * @param registration -
495   - * @return - JsonObject, format: {name: value}}
496   - */
497   - private JsonObject getAttributeClient(Registration registration) {
498   - if (registration.getAdditionalRegistrationAttributes().size() > 0) {
499   - JsonObject resNameValues = new JsonObject();
500   - registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty);
501   - return resNameValues;
502   - }
503   - return null;
504   - }
505   -
506   - /**
507   - * @param attributes - new JsonObject
508   - * @param telemetry - new JsonObject
509   - * @param registration - Registration LwM2M Client
510   - * result: add to JsonObject those resources to which the user is subscribed and they have a value
511   - * if path==null add All resources else only one
512   - * (attributes/telemetry): new {name(Attr/Telemetry):value}
513   - */
514   - private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
515   - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid());
516   - attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> {
517   - LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
518   - if (pathIds.isResource()) {
519   - if (path == null || path.contains(p.getAsString())) {
520   - this.addParameters(p.getAsString().toString(), attributes, registration);
521   - }
522   - }
523   - });
524   - attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> {
525   - LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
526   - if (pathIds.isResource()) {
527   - if (path == null || path.contains(p.getAsString())) {
528   - this.addParameters(p.getAsString().toString(), telemetry, registration);
529   - }
530   - }
531   - });
532   - }
533   -
534   - /**
535   - * @param parameters - JsonObject attributes/telemetry
536   - * @param registration - Registration LwM2M Client
537   - */
538   - private void addParameters(String path, JsonObject parameters, Registration registration) {
539   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId());
540   - JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile();
541   - String resName = String.valueOf(names.get(path));
542   - if (resName != null && !resName.isEmpty()) {
543   - try {
544   - String resValue = this.getResourceValueToString(lwM2MClient, path);
545   - if (resValue != null) {
546   - parameters.addProperty(resName, resValue);
547   - }
548   - } catch (Exception e) {
549   - log.error(e.getStackTrace().toString());
550   - }
551   - }
552   - }
553   -
554   - /**
555   - * Prepare Sent to Thigsboard callback - Attribute or Telemetry
556   - *
557   - * @param msg - JsonArray: [{name: value}]
558   - * @param topicName - Api Attribute or Telemetry
559   - * @param registration - Id of Registration LwM2M Client
560   - */
561   - public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
562   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
563   - if (sessionInfo != null) {
564   - context.sentParametersOnThingsboard(msg, topicName, sessionInfo);
565   - } else {
566   - log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
567   - }
568   - }
569   -
570   - /**
571   - * Start observe
572   - * #1 - Analyze:
573   - * #1.1 path in observe == (attribute or telemetry)
574   - * #2 Analyze after sent request (response):
575   - * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation
576   - * #2.2 Next: ObservationListener.onResponse *
577   - *
578   - * @param lwServer - LeshanServer
579   - * @param registration - Registration LwM2M Client
580   - */
581   - private void onSentObserveToClient(LeshanServer lwServer, Registration registration) {
582   - if (lwServer.getObservationService().getObservations(registration).size() > 0) {
583   - this.setCancelObservations(lwServer, registration);
584   - }
585   - UUID profileUUid = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid();
586   - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(profileUUid);
587   - attrTelemetryObserveValue.getPostObserveProfile().forEach(p -> {
588   - // #1.1
589   - String target = (getValidateObserve(attrTelemetryObserveValue.getPostAttributeProfile(), p.getAsString().toString())) ?
590   - p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ?
591   - p.getAsString().toString() : null;
592   - if (target != null) {
593   - // #2
594   - if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) {
595   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE,
596   - null, null, null, null, this.context.getCtxServer().getTimeout(),
597   - false);
598   - }
599   - }
600   - });
601   - }
602   -
603   - public void setCancelObservations(LeshanServer lwServer, Registration registration) {
604   - if (registration != null) {
605   - Set<Observation> observations = lwServer.getObservationService().getObservations(registration);
606   - observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()));
607   - }
608   - }
609   -
610   - /**
611   - * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
612   - * At server side this will not remove the observation from the observation store, to do it you need to use
613   - * {@code ObservationService#cancelObservation()}
614   - */
615   - public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {
616   - CountDownLatch cancelLatch = new CountDownLatch(1);
617   - lwServer.getObservationService().cancelObservations(registration, path);
618   - cancelLatch.countDown();
619   - try {
620   - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
621   - } catch (InterruptedException e) {
622   - e.printStackTrace();
623   - }
624   - }
625   -
626   - /**
627   - * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile
628   - * @param path - recourse from postObserveProfile
629   - * @return rez - true if path observe is in attribute/telemetry
630   - */
631   - private boolean getValidateObserve(JsonElement parameters, String path) {
632   - AtomicBoolean rez = new AtomicBoolean(false);
633   - if (parameters.isJsonArray()) {
634   - parameters.getAsJsonArray().forEach(p -> {
635   - if (p.getAsString().toString().equals(path)) rez.set(true);
636   - }
637   - );
638   - } else if (parameters.isJsonObject()) {
639   - rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString())
640   - .filter(path::equals).findAny().orElse(null) != null);
641   - }
642   - return rez.get();
643   - }
644   -
645   - /**
646   - * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
647   - *
648   - * @param registration - Registration LwM2M Client
649   - * @param path - observe
650   - * @param response - observe
651   - */
652   -
653   - public void onObservationResponse(Registration registration, String path, ReadResponse response) {
654   - if (response.getContent() != null) {
655   - if (response.getContent() instanceof LwM2mObject) {
656   -// LwM2mObject content = (LwM2mObject) response.getContent();
657   - } else if (response.getContent() instanceof LwM2mObjectInstance) {
658   -// LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent();
659   - } else if (response.getContent() instanceof LwM2mSingleResource) {
660   - LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();
661   - this.onObservationSetResourcesValue(registration, content.getValue(), null, path);
662   - } else if (response.getContent() instanceof LwM2mMultipleResource) {
663   - LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();
664   - this.onObservationSetResourcesValue(registration, null, content.getValues(), path);
665   - }
666   - }
667   - }
668   -
669   - /**
670   - * Sending observe value of resources to thingsboard
671   - * #1 Return old Value Resource from LwM2MClient
672   - * #2 Update new Resources (replace old Resource Value on new Resource Value)
673   - *
674   - * @param registration - Registration LwM2M Client
675   - * @param value - LwM2mSingleResource response.getContent()
676   - * @param values - LwM2mSingleResource response.getContent()
677   - * @param path - resource
678   - */
679   - private void onObservationSetResourcesValue(Registration registration, Object value, Map<Integer, ?> values, String path) {
680   - CountDownLatch respLatch = new CountDownLatch(1);
681   - boolean isChange = false;
682   - try {
683   - // #1
684   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
685   - LwM2mPath pathIds = new LwM2mPath(path);
686   - log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values);
687   - ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds);
688   - ResourceValue resValueOld = lwM2MClient.getResources().get(path);
689   - // #2
690   - if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) {
691   - ResourceValue resourceValue = new ResourceValue(values, null, true);
692   - lwM2MClient.getResources().put(path, resourceValue);
693   - isChange = true;
694   - } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) {
695   - ResourceValue resourceValue = new ResourceValue(null, value, false);
696   - lwM2MClient.getResources().put(path, resourceValue);
697   - isChange = true;
698   - }
699   - } finally {
700   - respLatch.countDown();
701   - }
702   - try {
703   - respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
704   - } catch (InterruptedException ex) {
705   - ex.printStackTrace();
706   - log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value);
707   - }
708   - if (isChange) {
709   - Set<String> paths = new HashSet<>();
710   - paths.add(path);
711   - this.updateAttrTelemetry(registration, false, paths);
712   - }
713   - }
714   -
715   - /**
716   - * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
717   - * config attr/telemetry... in profile
718   - */
719   - public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) {
720   - log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
721   - }
722   -
723   - /**
724   - * Update - sent request in change value resources in Client
725   - * Path to resources from profile equal keyName or from ModelObject equal name
726   - * Only for resources: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
727   - * Delete - nothing *
728   - *
729   - * @param msg -
730   - */
731   - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
732   - if (msg.getSharedUpdatedCount() > 0) {
733   - JsonElement el = JsonConverter.toJson(msg);
734   - el.getAsJsonObject().entrySet().forEach(de -> {
735   - String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());
736   - String value = de.getValue().getAsString();
737   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
738   - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
739   - ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));
740   - if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) {
741   - if (resourceModel != null && resourceModel.operations.isWritable()) {
742   - lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
743   - ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(),
744   - false);
745   - } else {
746   - log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
747   - String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);
748   - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
749   - }
750   - } else {
751   - log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);
752   - 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);
753   - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
754   - }
755   - });
756   - } else if (msg.getSharedDeletedCount() > 0) {
757   - log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
758   - }
759   - }
760   -
761   - /**
762   - * Get path to resource from profile equal keyName or from ModelObject equal name
763   - * Only for resource: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
764   - *
765   - * @param sessionInfo -
766   - * @param name -
767   - * @return path if path isPresent in postProfile
768   - */
769   - private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) {
770   - String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name);
771   -// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name);
772   - return !profilePath.isEmpty() ? profilePath : null;
773   - }
774   -
775   - /**
776   - * @param profile -
777   - * @param path -
778   - * @return true if path isPresent in postAttributeProfile
779   - */
780   - private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) {
781   - Set<String> attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
782   - return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
783   - }
784   -
785   - /**
786   - * @param profile -
787   - * @param path -
788   - * @return true if path isPresent in postAttributeProfile
789   - */
790   - private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) {
791   - Set<String> telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), Set.class);
792   - return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
793   - }
794   -
795   -
796   - /**
797   - * Get path to resource from profile equal keyName
798   - *
799   - * @param sessionInfo -
800   - * @param name -
801   - * @return -
802   - */
803   - private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
804   - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
805   - return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
806   - .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey)
807   - .orElse("");
808   - }
809   -
810   - /**
811   - * Update resource (attribute) value on thingsboard after update value in client
812   - *
813   - * @param registration -
814   - * @param path -
815   - * @param request -
816   - */
817   - public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) {
818   - ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path));
819   - if (resource.multiple) {
820   - this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path);
821   - } else {
822   - this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path);
823   - }
824   - if (isDelayedUpdate) lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null)
825   - .onSuccessOrErrorDelayedRequests(request.getPath().toString());
826   - }
827   -
828   - /**
829   - * @param sessionInfo -
830   - * @param deviceProfile -
831   - */
832   - public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
833   - Set<String> registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet()
834   - .stream()
835   - .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId()))
836   - .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
837   - if (registrationIds.size() > 0) {
838   - this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);
839   - }
840   - }
841   -
842   - /**
843   - * @param sessionInfo -
844   - * @param device -
845   - * @param deviceProfileOpt -
846   - */
847   - public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
848   - Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()
849   - .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))
850   - .map(Map.Entry::getKey)
851   - .findFirst();
852   - registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
853   - }
854   -
855   - /**
856   - * Update parameters device in LwM2MClient
857   - * If new deviceProfile != old deviceProfile => update deviceProfile
858   - *
859   - * @param registrationId -
860   - * @param device -
861   - */
862   - private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {
863   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);
864   - lwM2MClient.setDeviceName(device.getName());
865   - if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {
866   - Set<String> registrationIds = new HashSet<>();
867   - registrationIds.add(registrationId);
868   - deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile));
869   - }
870   -
871   - lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId());
872   - }
873   -
874   - /**
875   - * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName)
876   - * #2 Update in lwM2MClient: ...Profile if changes from update device
877   - * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName)
878   - * #3.1 Attribute isChange (add&del)
879   - * #3.2 Telemetry isChange (add&del)
880   - * #3.3 KeyName isChange (add)
881   - * #4 update
882   - * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value to thingsboard
883   - * #4.2 del
884   - * -- if add attributes includes del telemetry - result del for observe
885   - * #5
886   - * #5.1 Observe isChange (add&del)
887   - * #5.2 Observe.add
888   - * -- path Attr/Telemetry includes newObserve and does not include oldObserve: sent Request observe to Client
889   - * #5.3 Observe.del
890   - * -- different between newObserve and oldObserve: sent Request cancel observe to client
891   - *
892   - * @param registrationIds -
893   - * @param deviceProfile -
894   - */
895   - public void onDeviceUpdateChangeProfile(Set<String> registrationIds, DeviceProfile deviceProfile) {
896   -
897   - AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
898   - if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) {
899   -
900   - // #1
901   - JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile();
902   - Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class);
903   - JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile();
904   - Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class);
905   - JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile();
906   - JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile();
907   -
908   - AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
909   - JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile();
910   - Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class);
911   - JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile();
912   - Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class);
913   - JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile();
914   - JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile();
915   -
916   - // #3
917   - ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters();
918   - // #3.1
919   - if (!attributeOld.equals(attributeNew)) {
920   - ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, Set.class), attributeSetNew);
921   - sentAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd());
922   - sentAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel());
923   - }
924   - // #3.2
925   - if (!attributeOld.equals(attributeNew)) {
926   - ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, Set.class), telemetrySetNew);
927   - sentAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd());
928   - sentAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel());
929   - }
930   - // #3.3
931   - if (!keyNameOld.equals(keyNameNew)) {
932   - ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), ConcurrentHashMap.class),
933   - new Gson().fromJson(keyNameNew.toString(), ConcurrentHashMap.class));
934   - sentAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd());
935   - }
936   -
937   - // #4.1 add
938   - if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
939   - // update value in Resources
940   - registrationIds.forEach(registrationId -> {
941   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
942   - LeshanServer lwServer = lwM2MClient.getLwServer();
943   - Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
944   - log.warn("[{}] # 4.1", registration.getEndpoint());
945   - this.updateResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ);
946   - // sent attr/telemetry to tingsboard for new path
947   - this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd());
948   - });
949   - }
950   - // #4.2 del
951   - if (sentAttrToThingsboard.getPathPostParametersDel().size() > 0) {
952   - ResultsAnalyzerParameters sentAttrToThingsboardDel = this.getAnalyzerParameters(sentAttrToThingsboard.getPathPostParametersAdd(), sentAttrToThingsboard.getPathPostParametersDel());
953   - sentAttrToThingsboard.setPathPostParametersDel(sentAttrToThingsboardDel.getPathPostParametersDel());
954   - }
955   -
956   - // #5.1
957   - if (!observeOld.equals(observeNew)) {
958   - Set observeSetOld = new Gson().fromJson(observeOld, Set.class);
959   - Set observeSetNew = new Gson().fromJson(observeNew, Set.class);
960   - //#5.2 add
961   - // path Attr/Telemetry includes newObserve
962   - attributeSetOld.addAll(telemetrySetOld);
963   - ResultsAnalyzerParameters sentObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe
964   - attributeSetNew.addAll(telemetrySetNew);
965   - ResultsAnalyzerParameters sentObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe
966   - // does not include oldObserve
967   - ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd());
968   - // sent Request observe to Client
969   - registrationIds.forEach(registrationId -> {
970   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(null, registrationId);
971   - LeshanServer lwServer = lwM2MClient.getLwServer();
972   - Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
973   - log.warn("[{}] # 5.1", registration.getEndpoint());
974   - this.updateResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE);
975   - // 5.3 del
976   - // sent Request cancel observe to Client
977   - this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel());
978   - });
979   - }
980   - }
981   - }
982   -
983   - /**
984   - * Compare old list with new list after change AttrTelemetryObserve in config Profile
985   - *
986   - * @param parametersOld -
987   - * @param parametersNew -
988   - * @return ResultsAnalyzerParameters: add && new
989   - */
990   - private ResultsAnalyzerParameters getAnalyzerParameters(Set<String> parametersOld, Set<String> parametersNew) {
991   - ResultsAnalyzerParameters analyzerParameters = null;
992   - if (!parametersOld.equals(parametersNew)) {
993   - analyzerParameters = new ResultsAnalyzerParameters();
994   - analyzerParameters.setPathPostParametersAdd(parametersNew
995   - .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet()));
996   - analyzerParameters.setPathPostParametersDel(parametersOld
997   - .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet()));
998   - }
999   - return analyzerParameters;
1000   - }
1001   -
1002   - private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {
1003   - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
1004   - Set<String> paths = keyNameNew.entrySet()
1005   - .stream()
1006   - .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
1007   - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();
1008   - analyzerParameters.setPathPostParametersAdd(paths);
1009   - return analyzerParameters;
1010   - }
1011   -
1012   - private ResultsAnalyzerParameters getAnalyzerParametersIn(Set<String> parametersObserve, Set<String> parameters) {
1013   - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
1014   - analyzerParameters.setPathPostParametersAdd(parametersObserve
1015   - .stream().filter(parameters::contains).collect(Collectors.toSet()));
1016   - return analyzerParameters;
1017   - }
1018   -
1019   - /**
1020   - * Update Resource value after change RezAttrTelemetry in config Profile
1021   - * sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
1022   - *
1023   - * @param lwServer - LeshanServer
1024   - * @param registration - Registration LwM2M Client
1025   - * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
1026   - */
1027   - private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, Set<String> targets, String typeOper) {
1028   - targets.forEach(target -> {
1029   - LwM2mPath pathIds = new LwM2mPath(target);
1030   - if (pathIds.isResource()) {
1031   - if (GET_TYPE_OPER_READ.equals(typeOper)) {
1032   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
1033   - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
1034   - false);
1035   - } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
1036   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
1037   - null, null, null, null, this.context.getCtxServer().getTimeout(),
1038   - false);
1039   - }
1040   - }
1041   - });
1042   - }
1043   -
1044   - private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set<String> paramAnallyzer) {
1045   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
1046   - paramAnallyzer.forEach(p -> {
1047   - if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) {
1048   - this.setCancelObservationRecourse(lwServer, registration, p);
1049   - }
1050   - }
1051   - );
1052   - }
1053   -
1054   - private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) {
1055   - ResourceValue resourceValue = null;
1056   - if (pathIds.isResource()) {
1057   - resourceValue = lwM2MClient.getResources().get(pathIds.toString());
1058   - }
1059   - return resourceValue;
1060   - }
1061   -
1062   - /**
1063   - * Trigger Server path = "/1/0/8"
1064   - *
1065   - * Trigger bootStrap path = "/1/0/9" - have to implemented on client
1066   - */
1067   - public void doTrigger(LeshanServer lwServer, Registration registration, String path) {
1068   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,
1069   - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
1070   - false);
1071   - }
1072   -
1073   - /**
1074   - * Session device in thingsboard is closed
1075   - *
1076   - * @param sessionInfo - lwm2m client
1077   - */
1078   - private void doCloseSession(SessionInfoProto sessionInfo) {
1079   - TransportProtos.SessionEvent event = SessionEvent.CLOSED;
1080   - TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
1081   - .setSessionType(TransportProtos.SessionType.ASYNC)
1082   - .setEvent(event).build();
1083   - transportService.process(sessionInfo, msg, null);
1084   - }
1085   -
1086   - /**
1087   - * Deregister session in transport
1088   - *
1089   - * @param sessionInfo - lwm2m client
1090   - */
1091   - private void doDisconnect(SessionInfoProto sessionInfo) {
1092   - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
1093   - transportService.deregisterSession(sessionInfo);
1094   - }
1095   -
1096   - private void checkInactivityAndReportActivity() {
1097   - lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key)));
1098   - }
1099   -
1100   - /**
1101   - * if sessionInfo removed from sessions, then new registerAsyncSession
1102   - * @param sessionInfo -
1103   - */
1104   - private void checkInactivity(SessionInfoProto sessionInfo) {
1105   - if (transportService.reportActivity(sessionInfo) == null) {
1106   - transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));
1107   - }
1108   - }
1109   -
1110   - public void sentLogsToThingsboard(String msg, Registration registration) {
1111   - if (msg != null) {
1112   - JsonObject telemetries = new JsonObject();
1113   - telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);
1114   - this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration);
1115   - }
1116   - }
1117   -
1118   - /**
1119   - * @param path - path resource
1120   - * @return - value of Resource or null
1121   - */
1122   - public String getResourceValueToString(LwM2MClient lwM2MClient, String path) {
1123   - LwM2mPath pathIds = new LwM2mPath(path);
1124   - ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds);
1125   - return (resourceValue == null) ? null :
1126   - (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds);
1127   - }
1128 56 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server;
  17 +
  18 +import com.google.gson.Gson;
  19 +import com.google.gson.JsonArray;
  20 +import com.google.gson.JsonElement;
  21 +import com.google.gson.JsonObject;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.eclipse.leshan.core.model.ResourceModel;
  24 +import org.eclipse.leshan.core.node.LwM2mMultipleResource;
  25 +import org.eclipse.leshan.core.node.LwM2mObject;
  26 +import org.eclipse.leshan.core.node.LwM2mObjectInstance;
  27 +import org.eclipse.leshan.core.node.LwM2mPath;
  28 +import org.eclipse.leshan.core.node.LwM2mSingleResource;
  29 +import org.eclipse.leshan.core.observation.Observation;
  30 +import org.eclipse.leshan.core.request.ContentFormat;
  31 +import org.eclipse.leshan.core.request.WriteRequest;
  32 +import org.eclipse.leshan.core.response.ReadResponse;
  33 +import org.eclipse.leshan.core.util.NamedThreadFactory;
  34 +import org.eclipse.leshan.server.californium.LeshanServer;
  35 +import org.eclipse.leshan.server.registration.Registration;
  36 +import org.springframework.beans.factory.annotation.Autowired;
  37 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  38 +import org.springframework.stereotype.Service;
  39 +import org.thingsboard.server.common.data.Device;
  40 +import org.thingsboard.server.common.data.DeviceProfile;
  41 +import org.thingsboard.server.common.transport.TransportService;
  42 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  43 +import org.thingsboard.server.common.transport.adaptor.JsonConverter;
  44 +import org.thingsboard.server.common.transport.service.DefaultTransportService;
  45 +import org.thingsboard.server.gen.transport.TransportProtos;
  46 +import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
  47 +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
  48 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
  49 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
  50 +import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
  51 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
  52 +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
  53 +import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
  54 +import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
  55 +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
  56 +
  57 +import javax.annotation.PostConstruct;
  58 +import java.util.ArrayList;
  59 +import java.util.Arrays;
  60 +import java.util.Collection;
  61 +import java.util.HashSet;
  62 +import java.util.LinkedHashSet;
  63 +import java.util.List;
  64 +import java.util.Map;
  65 +import java.util.Optional;
  66 +import java.util.Random;
  67 +import java.util.Set;
  68 +import java.util.UUID;
  69 +import java.util.concurrent.ConcurrentHashMap;
  70 +import java.util.concurrent.ConcurrentMap;
  71 +import java.util.concurrent.CountDownLatch;
  72 +import java.util.concurrent.ExecutorService;
  73 +import java.util.concurrent.Executors;
  74 +import java.util.concurrent.TimeUnit;
  75 +import java.util.concurrent.atomic.AtomicBoolean;
  76 +import java.util.concurrent.locks.Lock;
  77 +import java.util.concurrent.locks.ReadWriteLock;
  78 +import java.util.concurrent.locks.ReentrantReadWriteLock;
  79 +import java.util.stream.Collectors;
  80 +
  81 +import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject;
  82 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.CLIENT_NOT_AUTHORIZED;
  83 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEFAULT_TIMEOUT;
  84 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_REQUEST;
  85 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC;
  86 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC;
  87 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE;
  88 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ;
  89 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR;
  90 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO;
  91 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY;
  92 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE;
  93 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE;
  94 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVICE_CHANNEL;
  95 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getAckCallback;
  96 +
  97 +@Slf4j
  98 +@Service("LwM2MTransportService")
  99 +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
  100 +public class LwM2MTransportServiceImpl implements LwM2MTransportService {
  101 +
  102 + private ExecutorService executorRegistered;
  103 + private ExecutorService executorUpdateRegistered;
  104 + private ExecutorService executorUnRegistered;
  105 + private LwM2mValueConverterImpl converter;
  106 + protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  107 + protected final Lock writeLock = readWriteLock.writeLock();
  108 +
  109 +
  110 + @Autowired
  111 + private TransportService transportService;
  112 +
  113 + @Autowired
  114 + public LwM2MTransportContextServer context;
  115 +
  116 + @Autowired
  117 + private LwM2MTransportRequest lwM2MTransportRequest;
  118 +
  119 + @Autowired
  120 + LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
  121 +
  122 + @PostConstruct
  123 + public void init() {
  124 + this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS);
  125 + this.executorRegistered = Executors.newCachedThreadPool(
  126 + new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL)));
  127 + this.executorUpdateRegistered = Executors.newCachedThreadPool(
  128 + new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL)));
  129 + this.executorUnRegistered = Executors.newCachedThreadPool(
  130 + new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL)));
  131 + this.converter = LwM2mValueConverterImpl.getInstance();
  132 + }
  133 +
  134 + /**
  135 + * Start registration device
  136 + * Create session: Map<String <registrationId >, LwM2MClient>
  137 + * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint)
  138 + * 1.1 When we initialize the registration, we register the session by endpoint.
  139 + * 1.2 If the server has incomplete requests (canceling the registration of the previous session),
  140 + * delete the previous session only by the previous registration.getId
  141 + * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId
  142 + * 1.2 Remove from sessions Model by enpPoint
  143 + * Next -> Create new LwM2MClient for current session -> setModelClient...
  144 + *
  145 + * @param lwServer - LeshanServer
  146 + * @param registration - Registration LwM2M Client
  147 + * @param previousObsersations - may be null
  148 + */
  149 + public void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations) {
  150 + executorRegistered.submit(() -> {
  151 + try {
  152 + log.info("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
  153 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration);
  154 + if (lwM2MClient != null) {
  155 + lwM2MClient.setLwM2MTransportServiceImpl(this);
  156 + lwM2MClient.setSessionUuid(UUID.randomUUID());
  157 + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration);
  158 + this.setLwM2MClient(lwServer, registration, lwM2MClient);
  159 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  160 + if (sessionInfo != null) {
  161 + lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
  162 + lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  163 + lwM2MClient.setDeviceName(sessionInfo.getDeviceName());
  164 + lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType());
  165 + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));
  166 + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
  167 + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
  168 + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);
  169 + } else {
  170 + log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
  171 + }
  172 + } else {
  173 + log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);
  174 + }
  175 + } catch (Throwable t) {
  176 + log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t);
  177 + }
  178 + });
  179 + }
  180 +
  181 + /**
  182 + * if sessionInfo removed from sessions, then new registerAsyncSession
  183 + * @param lwServer - LeshanServer
  184 + * @param registration - Registration LwM2M Client
  185 + */
  186 + public void updatedReg(LeshanServer lwServer, Registration registration) {
  187 + executorUpdateRegistered.submit(() -> {
  188 + try {
  189 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  190 + if (sessionInfo != null) {
  191 + this.checkInactivity(sessionInfo);
  192 + log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
  193 + } else {
  194 + log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
  195 + }
  196 + } catch (Throwable t) {
  197 + log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);
  198 + }
  199 + });
  200 + }
  201 +
  202 +
  203 + /**
  204 + * @param registration - Registration LwM2M Client
  205 + * @param observations - All paths observations before unReg
  206 + * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
  207 + */
  208 + public void unReg(Registration registration, Collection<Observation> observations) {
  209 + executorUnRegistered.submit(() -> {
  210 + try {
  211 + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
  212 + this.closeClientSession(registration);
  213 + } catch (Throwable t) {
  214 + log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
  215 + }
  216 + });
  217 + }
  218 +
  219 + private void closeClientSession(Registration registration) {
  220 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  221 + if (sessionInfo != null) {
  222 + transportService.deregisterSession(sessionInfo);
  223 + this.doCloseSession(sessionInfo);
  224 + lwM2mInMemorySecurityStore.delRemoveSessionAndListener(registration.getId());
  225 + if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) {
  226 + this.syncSessionsAndProfiles();
  227 + }
  228 + log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
  229 + } else {
  230 + log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
  231 + }
  232 + }
  233 +
  234 + public void onSleepingDev(Registration registration) {
  235 + log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
  236 + //TODO: associate endpointId with device information.
  237 + }
  238 +
  239 + /**
  240 + * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,
  241 + * * if you need to do long time processing use a dedicated thread pool.
  242 + *
  243 + * @param registration -
  244 + */
  245 + protected void onAwakeDev(Registration registration) {
  246 + log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
  247 + //TODO: associate endpointId with device information.
  248 + }
  249 +
  250 + /**
  251 + * This method is used to sync with sessions
  252 + * Removes a profile if not used in sessions
  253 + */
  254 + private void syncSessionsAndProfiles() {
  255 + Map<UUID, AttrTelemetryObserveValue> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()
  256 + .stream()
  257 + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
  258 + profilesClone.forEach((k, v) -> {
  259 + String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet()
  260 + .stream()
  261 + .filter(e -> e.getValue().getProfileUuid().equals(k))
  262 + .findFirst()
  263 + .map(Map.Entry::getKey) // return the key of the matching entry if found
  264 + .orElse("");
  265 + if (registrationId.isEmpty()) {
  266 + lwM2mInMemorySecurityStore.getProfiles().remove(k);
  267 + }
  268 + });
  269 + }
  270 +
  271 + /**
  272 + * #0 Add new ObjectModel to context
  273 + * Create new LwM2MClient for current session -> setModelClient...
  274 + * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client
  275 + * to get the client model with current values
  276 + * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse
  277 + *
  278 + * @param lwServer - LeshanServer
  279 + * @param registration - Registration LwM2M Client
  280 + * @param lwM2MClient - object with All parameters off client
  281 + */
  282 + private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) {
  283 + Arrays.stream(registration.getObjectLinks()).forEach(url -> {
  284 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  285 + if (pathIds.isObjectInstance() && !pathIds.isResource()) {
  286 + lwM2MClient.getPendingRequests().add(url.getUrl());
  287 + }
  288 + });
  289 + // #2
  290 + Arrays.stream(registration.getObjectLinks()).forEach(url -> {
  291 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  292 + if (pathIds.isObjectInstance() && !pathIds.isResource()) {
  293 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
  294 + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
  295 + }
  296 + });
  297 + }
  298 +
  299 + /**
  300 + * @param registration - Registration LwM2M Client
  301 + * @return - sessionInfo after access connect client
  302 + */
  303 + private SessionInfoProto getValidateSessionInfo(Registration registration) {
  304 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  305 + return getNewSessionInfoProto(lwM2MClient);
  306 +
  307 + }
  308 +
  309 + /**
  310 + *
  311 + * @param registrationId -
  312 + * @return -
  313 + */
  314 + private SessionInfoProto getValidateSessionInfo(String registrationId) {
  315 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
  316 + return getNewSessionInfoProto(lwM2MClient);
  317 + }
  318 +
  319 + private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) {
  320 + if (lwM2MClient != null) {
  321 + ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();
  322 + if (msg == null || msg.getDeviceInfo() == null) {
  323 + log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);
  324 + this.closeClientSession(lwM2MClient.getRegistration());
  325 + return null;
  326 + } else {
  327 + return SessionInfoProto.newBuilder()
  328 + .setNodeId(this.context.getNodeId())
  329 + .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())
  330 + .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())
  331 + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
  332 + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
  333 + .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
  334 + .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
  335 + .setDeviceName(msg.getDeviceInfo().getDeviceName())
  336 + .setDeviceType(msg.getDeviceInfo().getDeviceType())
  337 + .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
  338 + .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
  339 + .build();
  340 + }
  341 + }
  342 + return null;
  343 + }
  344 +
  345 + /**
  346 + * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe
  347 + * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use
  348 + * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection
  349 + * #2 Start observe
  350 + *
  351 + * @param lwM2MClient - LwM2M Client
  352 + */
  353 +
  354 + public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) {
  355 + // #1
  356 + this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null);
  357 + // #2
  358 + this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration());
  359 +
  360 + }
  361 +
  362 + /**
  363 + * If there is a difference in values between the current resource values and the shared attribute values
  364 + * when the client connects to the server
  365 + * #1 get attributes name from profile include name resources in ModelObject if resource isWritable
  366 + * #2.1 #1 size > 0 => send Request getAttributes to thingsboard
  367 + * #2.2 #1 size == 0 => continue normal process
  368 + *
  369 + * @param lwM2MClient - LwM2M Client
  370 + */
  371 + public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) {
  372 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
  373 + if (sessionInfo != null) {
  374 + //#1.1 + #1.2
  375 + List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);
  376 + if (attrSharedNames.size() > 0) {
  377 + //#2.1
  378 + try {
  379 + TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames);
  380 + lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId());
  381 + transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
  382 + } catch (AdaptorException e) {
  383 + log.warn("Failed to decode get attributes request", e);
  384 + }
  385 + }
  386 + // #2.2
  387 + else {
  388 + lwM2MClient.onSuccessOrErrorDelayedRequests(null);
  389 + }
  390 + }
  391 + }
  392 +
  393 + /**
  394 + * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
  395 + * #1 Get path resource by result attributesResponse
  396 + * #1.1 If two names have equal path => last time attribute
  397 + * #2.1 if there is a difference in values between the current resource values and the shared attribute values
  398 + * => sent to client Request Update of value (new value from shared attribute)
  399 + * and LwM2MClient.delayedRequests.add(path)
  400 + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values
  401 + *
  402 + * @param attributesResponse -
  403 + * @param sessionInfo -
  404 + */
  405 + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
  406 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo);
  407 + if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) {
  408 + attributesResponse.getSharedAttributeListList().forEach(attr -> {
  409 + String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey());
  410 + // #1.1
  411 + if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) {
  412 + lwM2MClient.getDelayedRequests().put(path, attr);
  413 + } else {
  414 + lwM2MClient.getDelayedRequests().put(path, attr);
  415 + }
  416 + });
  417 + // #2.1
  418 + lwM2MClient.getDelayedRequests().forEach((k, v) -> {
  419 + ArrayList<TransportProtos.KeyValueProto> listV = new ArrayList<>();
  420 + listV.add(v.getKv());
  421 + this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k);
  422 + });
  423 + lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId());
  424 + if (lwM2MClient.getDelayedRequests().size() == 0) {
  425 + lwM2MClient.onSuccessOrErrorDelayedRequests(null);
  426 + }
  427 + }
  428 + }
  429 +
  430 + private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {
  431 + if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) {
  432 + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
  433 + ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(),
  434 + true);
  435 + }
  436 + }
  437 +
  438 + /**
  439 + * Get names and keyNames from profile shared!!!! attr resources IsWritable
  440 + * @param lwM2MClient -
  441 + * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable
  442 + */
  443 + private List<String> getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) {
  444 + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid());
  445 + Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
  446 + ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class);
  447 +
  448 + ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
  449 + .stream()
  450 + .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&
  451 + context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))
  452 + .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
  453 +
  454 + Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
  455 + namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
  456 + return new ArrayList<>(namesIsWritable);
  457 + }
  458 +
  459 +
  460 + /**
  461 + * Sent Attribute and Telemetry to Thingsboard
  462 + * #1 - get AttrName/TelemetryName with value:
  463 + * #1.1 from Client
  464 + * #1.2 from LwM2MClient:
  465 + * -- resourceId == path from AttrTelemetryObserveValue.postAttributeProfile/postTelemetryProfile/postObserveProfile
  466 + * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
  467 + * #2 - set Attribute/Telemetry
  468 + *
  469 + * @param registration - Registration LwM2M Client
  470 + */
  471 + private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {
  472 + JsonObject attributes = new JsonObject();
  473 + JsonObject telemetries = new JsonObject();
  474 + if (start) {
  475 + // #1.1
  476 + JsonObject attributeClient = this.getAttributeClient(registration);
  477 + if (attributeClient != null) {
  478 + attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));
  479 + }
  480 + }
  481 + // #1.2
  482 + CountDownLatch cancelLatch = new CountDownLatch(1);
  483 + this.getParametersFromProfile(attributes, telemetries, registration, paths);
  484 + cancelLatch.countDown();
  485 + try {
  486 + cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
  487 + } catch (InterruptedException e) {
  488 + log.error("[{}] updateAttrTelemetry", e.toString());
  489 + }
  490 + if (attributes.getAsJsonObject().entrySet().size() > 0)
  491 + this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);
  492 + if (telemetries.getAsJsonObject().entrySet().size() > 0)
  493 + this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
  494 + }
  495 +
  496 + /**
  497 + * get AttrName/TelemetryName with value from Client
  498 + *
  499 + * @param registration -
  500 + * @return - JsonObject, format: {name: value}}
  501 + */
  502 + private JsonObject getAttributeClient(Registration registration) {
  503 + if (registration.getAdditionalRegistrationAttributes().size() > 0) {
  504 + JsonObject resNameValues = new JsonObject();
  505 + registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty);
  506 + return resNameValues;
  507 + }
  508 + return null;
  509 + }
  510 +
  511 + /**
  512 + * @param attributes - new JsonObject
  513 + * @param telemetry - new JsonObject
  514 + * @param registration - Registration LwM2M Client
  515 + * result: add to JsonObject those resources to which the user is subscribed and they have a value
  516 + * if path==null add All resources else only one
  517 + * (attributes/telemetry): new {name(Attr/Telemetry):value}
  518 + */
  519 + private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
  520 + AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid());
  521 + attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> {
  522 + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
  523 + if (pathIds.isResource()) {
  524 + if (path == null || path.contains(p.getAsString())) {
  525 + this.addParameters(p.getAsString().toString(), attributes, registration);
  526 + }
  527 + }
  528 + });
  529 + attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> {
  530 + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
  531 + if (pathIds.isResource()) {
  532 + if (path == null || path.contains(p.getAsString())) {
  533 + this.addParameters(p.getAsString().toString(), telemetry, registration);
  534 + }
  535 + }
  536 + });
  537 + }
  538 +
  539 + /**
  540 + * @param parameters - JsonObject attributes/telemetry
  541 + * @param registration - Registration LwM2M Client
  542 + */
  543 + private void addParameters(String path, JsonObject parameters, Registration registration) {
  544 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId());
  545 + JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile();
  546 + String resName = String.valueOf(names.get(path));
  547 + if (resName != null && !resName.isEmpty()) {
  548 + try {
  549 + String resValue = this.getResourceValueToString(lwM2MClient, path);
  550 + if (resValue != null) {
  551 + parameters.addProperty(resName, resValue);
  552 + }
  553 + } catch (Exception e) {
  554 + log.error(e.getStackTrace().toString());
  555 + }
  556 + }
  557 + }
  558 +
  559 + /**
  560 + * Prepare Sent to Thigsboard callback - Attribute or Telemetry
  561 + *
  562 + * @param msg - JsonArray: [{name: value}]
  563 + * @param topicName - Api Attribute or Telemetry
  564 + * @param registration - Id of Registration LwM2M Client
  565 + */
  566 + public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
  567 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  568 + if (sessionInfo != null) {
  569 + context.sentParametersOnThingsboard(msg, topicName, sessionInfo);
  570 + } else {
  571 + log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
  572 + }
  573 + }
  574 +
  575 + /**
  576 + * Start observe
  577 + * #1 - Analyze:
  578 + * #1.1 path in observe == (attribute or telemetry)
  579 + * #2 Analyze after sent request (response):
  580 + * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation
  581 + * #2.2 Next: ObservationListener.onResponse *
  582 + *
  583 + * @param lwServer - LeshanServer
  584 + * @param registration - Registration LwM2M Client
  585 + */
  586 + private void onSentObserveToClient(LeshanServer lwServer, Registration registration) {
  587 + if (lwServer.getObservationService().getObservations(registration).size() > 0) {
  588 + this.setCancelObservations(lwServer, registration);
  589 + }
  590 + UUID profileUUid = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid();
  591 + AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(profileUUid);
  592 + attrTelemetryObserveValue.getPostObserveProfile().forEach(p -> {
  593 + // #1.1
  594 + String target = (getValidateObserve(attrTelemetryObserveValue.getPostAttributeProfile(), p.getAsString().toString())) ?
  595 + p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ?
  596 + p.getAsString().toString() : null;
  597 + if (target != null) {
  598 + // #2
  599 + if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) {
  600 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE,
  601 + null, null, null, null, this.context.getCtxServer().getTimeout(),
  602 + false);
  603 + }
  604 + }
  605 + });
  606 + }
  607 +
  608 + public void setCancelObservations(LeshanServer lwServer, Registration registration) {
  609 + if (registration != null) {
  610 + Set<Observation> observations = lwServer.getObservationService().getObservations(registration);
  611 + observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()));
  612 + }
  613 + }
  614 +
  615 + /**
  616 + * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
  617 + * At server side this will not remove the observation from the observation store, to do it you need to use
  618 + * {@code ObservationService#cancelObservation()}
  619 + */
  620 + public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {
  621 + CountDownLatch cancelLatch = new CountDownLatch(1);
  622 + lwServer.getObservationService().cancelObservations(registration, path);
  623 + cancelLatch.countDown();
  624 + try {
  625 + cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
  626 + } catch (InterruptedException e) {
  627 + log.error("", e);
  628 + }
  629 + }
  630 +
  631 + /**
  632 + * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile
  633 + * @param path - recourse from postObserveProfile
  634 + * @return rez - true if path observe is in attribute/telemetry
  635 + */
  636 + private boolean getValidateObserve(JsonElement parameters, String path) {
  637 + AtomicBoolean rez = new AtomicBoolean(false);
  638 + if (parameters.isJsonArray()) {
  639 + parameters.getAsJsonArray().forEach(p -> {
  640 + if (p.getAsString().toString().equals(path)) rez.set(true);
  641 + }
  642 + );
  643 + } else if (parameters.isJsonObject()) {
  644 + rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString())
  645 + .filter(path::equals).findAny().orElse(null) != null);
  646 + }
  647 + return rez.get();
  648 + }
  649 +
  650 + /**
  651 + * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
  652 + *
  653 + * @param registration - Registration LwM2M Client
  654 + * @param path - observe
  655 + * @param response - observe
  656 + */
  657 +
  658 + public void onObservationResponse(Registration registration, String path, ReadResponse response) {
  659 + if (response.getContent() != null) {
  660 + if (response.getContent() instanceof LwM2mObject) {
  661 +// LwM2mObject content = (LwM2mObject) response.getContent();
  662 + } else if (response.getContent() instanceof LwM2mObjectInstance) {
  663 +// LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent();
  664 + } else if (response.getContent() instanceof LwM2mSingleResource) {
  665 + LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();
  666 + this.onObservationSetResourcesValue(registration, content.getValue(), null, path);
  667 + } else if (response.getContent() instanceof LwM2mMultipleResource) {
  668 + LwM2mMultipleResource content = (LwM2mMultipleResource) response.getContent();
  669 + this.onObservationSetResourcesValue(registration, null, content.getValues(), path);
  670 + }
  671 + }
  672 + }
  673 +
  674 + /**
  675 + * Sending observe value of resources to thingsboard
  676 + * #1 Return old Value Resource from LwM2MClient
  677 + * #2 Update new Resources (replace old Resource Value on new Resource Value)
  678 + *
  679 + * @param registration - Registration LwM2M Client
  680 + * @param value - LwM2mSingleResource response.getContent()
  681 + * @param values - LwM2mSingleResource response.getContent()
  682 + * @param path - resource
  683 + */
  684 + private void onObservationSetResourcesValue(Registration registration, Object value, Map<Integer, ?> values, String path) {
  685 + boolean isChange = false;
  686 + try {
  687 + writeLock.lock();
  688 + try {
  689 + // #1
  690 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  691 + LwM2mPath pathIds = new LwM2mPath(path);
  692 + log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values);
  693 + ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds);
  694 + ResourceValue resValueOld = lwM2MClient.getResources().get(path);
  695 + // #2
  696 + if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) {
  697 + ResourceValue resourceValue = new ResourceValue(values, null, true);
  698 + lwM2MClient.getResources().put(path, resourceValue);
  699 + isChange = true;
  700 + } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) {
  701 + ResourceValue resourceValue = new ResourceValue(null, value, false);
  702 + lwM2MClient.getResources().put(path, resourceValue);
  703 + isChange = true;
  704 + }
  705 + } finally {
  706 + writeLock.unlock();
  707 + }
  708 + }
  709 + catch (Exception e) {
  710 + log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}] [{}]", path, value, e.toString());
  711 + }
  712 + if (isChange) {
  713 + Set<String> paths = new HashSet<>();
  714 + paths.add(path);
  715 + this.updateAttrTelemetry(registration, false, paths);
  716 + }
  717 + }
  718 +
  719 + /**
  720 + * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
  721 + * config attr/telemetry... in profile
  722 + */
  723 + public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) {
  724 + log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
  725 + }
  726 +
  727 + /**
  728 + * Update - sent request in change value resources in Client
  729 + * Path to resources from profile equal keyName or from ModelObject equal name
  730 + * Only for resources: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
  731 + * Delete - nothing *
  732 + *
  733 + * @param msg -
  734 + */
  735 + public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
  736 + if (msg.getSharedUpdatedCount() > 0) {
  737 + JsonElement el = JsonConverter.toJson(msg);
  738 + el.getAsJsonObject().entrySet().forEach(de -> {
  739 + String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());
  740 + String value = de.getValue().getAsString();
  741 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
  742 + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  743 + ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));
  744 + if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) {
  745 + if (resourceModel != null && resourceModel.operations.isWritable()) {
  746 + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
  747 + ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(),
  748 + false);
  749 + } else {
  750 + log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
  751 + String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);
  752 + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  753 + }
  754 + } else {
  755 + log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);
  756 + 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);
  757 + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  758 + }
  759 + });
  760 + } else if (msg.getSharedDeletedCount() > 0) {
  761 + log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
  762 + }
  763 + }
  764 +
  765 + /**
  766 + * Get path to resource from profile equal keyName or from ModelObject equal name
  767 + * Only for resource: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
  768 + *
  769 + * @param sessionInfo -
  770 + * @param name -
  771 + * @return path if path isPresent in postProfile
  772 + */
  773 + private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) {
  774 + String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name);
  775 +// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name);
  776 + return !profilePath.isEmpty() ? profilePath : null;
  777 + }
  778 +
  779 + /**
  780 + * @param profile -
  781 + * @param path -
  782 + * @return true if path isPresent in postAttributeProfile
  783 + */
  784 + private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) {
  785 + Set<String> attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
  786 + return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
  787 + }
  788 +
  789 + /**
  790 + * @param profile -
  791 + * @param path -
  792 + * @return true if path isPresent in postAttributeProfile
  793 + */
  794 + private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) {
  795 + Set<String> telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), Set.class);
  796 + return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
  797 + }
  798 +
  799 +
  800 + /**
  801 + * Get path to resource from profile equal keyName
  802 + *
  803 + * @param sessionInfo -
  804 + * @param name -
  805 + * @return -
  806 + */
  807 + private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
  808 + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  809 + return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
  810 + .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey)
  811 + .orElse("");
  812 + }
  813 +
  814 + /**
  815 + * Update resource (attribute) value on thingsboard after update value in client
  816 + *
  817 + * @param registration -
  818 + * @param path -
  819 + * @param request -
  820 + */
  821 + public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) {
  822 + ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path));
  823 + if (resource.multiple) {
  824 + this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path);
  825 + } else {
  826 + this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path);
  827 + }
  828 + if (isDelayedUpdate) lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null)
  829 + .onSuccessOrErrorDelayedRequests(request.getPath().toString());
  830 + }
  831 +
  832 + /**
  833 + * @param sessionInfo -
  834 + * @param deviceProfile -
  835 + */
  836 + public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
  837 + Set<String> registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet()
  838 + .stream()
  839 + .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId()))
  840 + .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
  841 + if (registrationIds.size() > 0) {
  842 + this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);
  843 + }
  844 + }
  845 +
  846 + /**
  847 + * @param sessionInfo -
  848 + * @param device -
  849 + * @param deviceProfileOpt -
  850 + */
  851 + public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
  852 + Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()
  853 + .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))
  854 + .map(Map.Entry::getKey)
  855 + .findFirst();
  856 + registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
  857 + }
  858 +
  859 + /**
  860 + * Update parameters device in LwM2MClient
  861 + * If new deviceProfile != old deviceProfile => update deviceProfile
  862 + *
  863 + * @param registrationId -
  864 + * @param device -
  865 + */
  866 + private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {
  867 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);
  868 + lwM2MClient.setDeviceName(device.getName());
  869 + if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {
  870 + Set<String> registrationIds = new HashSet<>();
  871 + registrationIds.add(registrationId);
  872 + deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile));
  873 + }
  874 +
  875 + lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId());
  876 + }
  877 +
  878 + /**
  879 + * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName)
  880 + * #2 Update in lwM2MClient: ...Profile if changes from update device
  881 + * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName)
  882 + * #3.1 Attribute isChange (add&del)
  883 + * #3.2 Telemetry isChange (add&del)
  884 + * #3.3 KeyName isChange (add)
  885 + * #4 update
  886 + * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value to thingsboard
  887 + * #4.2 del
  888 + * -- if add attributes includes del telemetry - result del for observe
  889 + * #5
  890 + * #5.1 Observe isChange (add&del)
  891 + * #5.2 Observe.add
  892 + * -- path Attr/Telemetry includes newObserve and does not include oldObserve: sent Request observe to Client
  893 + * #5.3 Observe.del
  894 + * -- different between newObserve and oldObserve: sent Request cancel observe to client
  895 + *
  896 + * @param registrationIds -
  897 + * @param deviceProfile -
  898 + */
  899 + private void onDeviceUpdateChangeProfile(Set<String> registrationIds, DeviceProfile deviceProfile) {
  900 +
  901 + AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
  902 + if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) {
  903 +
  904 + // #1
  905 + JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile();
  906 + Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class);
  907 + JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile();
  908 + Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class);
  909 + JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile();
  910 + JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile();
  911 +
  912 + AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
  913 + JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile();
  914 + Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class);
  915 + JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile();
  916 + Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class);
  917 + JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile();
  918 + JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile();
  919 +
  920 + // #3
  921 + ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters();
  922 + // #3.1
  923 + if (!attributeOld.equals(attributeNew)) {
  924 + ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, Set.class), attributeSetNew);
  925 + sentAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd());
  926 + sentAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel());
  927 + }
  928 + // #3.2
  929 + if (!attributeOld.equals(attributeNew)) {
  930 + ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, Set.class), telemetrySetNew);
  931 + sentAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd());
  932 + sentAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel());
  933 + }
  934 + // #3.3
  935 + if (!keyNameOld.equals(keyNameNew)) {
  936 + ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), ConcurrentHashMap.class),
  937 + new Gson().fromJson(keyNameNew.toString(), ConcurrentHashMap.class));
  938 + sentAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd());
  939 + }
  940 +
  941 + // #4.1 add
  942 + if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
  943 + // update value in Resources
  944 + registrationIds.forEach(registrationId -> {
  945 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
  946 + LeshanServer lwServer = lwM2MClient.getLwServer();
  947 + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
  948 + log.warn("[{}] # 4.1", registration.getEndpoint());
  949 + this.updateResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ);
  950 + // sent attr/telemetry to tingsboard for new path
  951 + this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd());
  952 + });
  953 + }
  954 + // #4.2 del
  955 + if (sentAttrToThingsboard.getPathPostParametersDel().size() > 0) {
  956 + ResultsAnalyzerParameters sentAttrToThingsboardDel = this.getAnalyzerParameters(sentAttrToThingsboard.getPathPostParametersAdd(), sentAttrToThingsboard.getPathPostParametersDel());
  957 + sentAttrToThingsboard.setPathPostParametersDel(sentAttrToThingsboardDel.getPathPostParametersDel());
  958 + }
  959 +
  960 + // #5.1
  961 + if (!observeOld.equals(observeNew)) {
  962 + Set observeSetOld = new Gson().fromJson(observeOld, Set.class);
  963 + Set observeSetNew = new Gson().fromJson(observeNew, Set.class);
  964 + //#5.2 add
  965 + // path Attr/Telemetry includes newObserve
  966 + attributeSetOld.addAll(telemetrySetOld);
  967 + ResultsAnalyzerParameters sentObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe
  968 + attributeSetNew.addAll(telemetrySetNew);
  969 + ResultsAnalyzerParameters sentObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe
  970 + // does not include oldObserve
  971 + ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd());
  972 + // sent Request observe to Client
  973 + registrationIds.forEach(registrationId -> {
  974 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(null, registrationId);
  975 + LeshanServer lwServer = lwM2MClient.getLwServer();
  976 + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
  977 + log.warn("[{}] # 5.1", registration.getEndpoint());
  978 + this.updateResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE);
  979 + // 5.3 del
  980 + // sent Request cancel observe to Client
  981 + this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel());
  982 + });
  983 + }
  984 + }
  985 + }
  986 +
  987 + /**
  988 + * Compare old list with new list after change AttrTelemetryObserve in config Profile
  989 + *
  990 + * @param parametersOld -
  991 + * @param parametersNew -
  992 + * @return ResultsAnalyzerParameters: add && new
  993 + */
  994 + private ResultsAnalyzerParameters getAnalyzerParameters(Set<String> parametersOld, Set<String> parametersNew) {
  995 + ResultsAnalyzerParameters analyzerParameters = null;
  996 + if (!parametersOld.equals(parametersNew)) {
  997 + analyzerParameters = new ResultsAnalyzerParameters();
  998 + analyzerParameters.setPathPostParametersAdd(parametersNew
  999 + .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet()));
  1000 + analyzerParameters.setPathPostParametersDel(parametersOld
  1001 + .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet()));
  1002 + }
  1003 + return analyzerParameters;
  1004 + }
  1005 +
  1006 + private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {
  1007 + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
  1008 + Set<String> paths = keyNameNew.entrySet()
  1009 + .stream()
  1010 + .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
  1011 + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();
  1012 + analyzerParameters.setPathPostParametersAdd(paths);
  1013 + return analyzerParameters;
  1014 + }
  1015 +
  1016 + private ResultsAnalyzerParameters getAnalyzerParametersIn(Set<String> parametersObserve, Set<String> parameters) {
  1017 + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
  1018 + analyzerParameters.setPathPostParametersAdd(parametersObserve
  1019 + .stream().filter(parameters::contains).collect(Collectors.toSet()));
  1020 + return analyzerParameters;
  1021 + }
  1022 +
  1023 + /**
  1024 + * Update Resource value after change RezAttrTelemetry in config Profile
  1025 + * sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
  1026 + *
  1027 + * @param lwServer - LeshanServer
  1028 + * @param registration - Registration LwM2M Client
  1029 + * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
  1030 + */
  1031 + private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, Set<String> targets, String typeOper) {
  1032 + targets.forEach(target -> {
  1033 + LwM2mPath pathIds = new LwM2mPath(target);
  1034 + if (pathIds.isResource()) {
  1035 + if (GET_TYPE_OPER_READ.equals(typeOper)) {
  1036 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
  1037 + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
  1038 + false);
  1039 + } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
  1040 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
  1041 + null, null, null, null, this.context.getCtxServer().getTimeout(),
  1042 + false);
  1043 + }
  1044 + }
  1045 + });
  1046 + }
  1047 +
  1048 + private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set<String> paramAnallyzer) {
  1049 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  1050 + paramAnallyzer.forEach(p -> {
  1051 + if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) {
  1052 + this.setCancelObservationRecourse(lwServer, registration, p);
  1053 + }
  1054 + }
  1055 + );
  1056 + }
  1057 +
  1058 + private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) {
  1059 + ResourceValue resourceValue = null;
  1060 + if (pathIds.isResource()) {
  1061 + resourceValue = lwM2MClient.getResources().get(pathIds.toString());
  1062 + }
  1063 + return resourceValue;
  1064 + }
  1065 +
  1066 + /**
  1067 + * Trigger Server path = "/1/0/8"
  1068 + *
  1069 + * Trigger bootStrap path = "/1/0/9" - have to implemented on client
  1070 + */
  1071 + public void doTrigger(LeshanServer lwServer, Registration registration, String path) {
  1072 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,
  1073 + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
  1074 + false);
  1075 + }
  1076 +
  1077 + /**
  1078 + * Session device in thingsboard is closed
  1079 + *
  1080 + * @param sessionInfo - lwm2m client
  1081 + */
  1082 + private void doCloseSession(SessionInfoProto sessionInfo) {
  1083 + TransportProtos.SessionEvent event = SessionEvent.CLOSED;
  1084 + TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
  1085 + .setSessionType(TransportProtos.SessionType.ASYNC)
  1086 + .setEvent(event).build();
  1087 + transportService.process(sessionInfo, msg, null);
  1088 + }
  1089 +
  1090 + /**
  1091 + * Deregister session in transport
  1092 + *
  1093 + * @param sessionInfo - lwm2m client
  1094 + */
  1095 + public void doDisconnect(SessionInfoProto sessionInfo) {
  1096 + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
  1097 + transportService.deregisterSession(sessionInfo);
  1098 + }
  1099 +
  1100 + private void checkInactivityAndReportActivity() {
  1101 + lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key)));
  1102 + }
  1103 +
  1104 + /**
  1105 + * if sessionInfo removed from sessions, then new registerAsyncSession
  1106 + * @param sessionInfo -
  1107 + */
  1108 + private void checkInactivity(SessionInfoProto sessionInfo) {
  1109 + if (transportService.reportActivity(sessionInfo) == null) {
  1110 + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));
  1111 + }
  1112 + }
  1113 +
  1114 + public void sentLogsToThingsboard(String msg, Registration registration) {
  1115 + if (msg != null) {
  1116 + JsonObject telemetries = new JsonObject();
  1117 + telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);
  1118 + this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration);
  1119 + }
  1120 + }
  1121 +
  1122 + /**
  1123 + * @param path - path resource
  1124 + * @return - value of Resource or null
  1125 + */
  1126 + private String getResourceValueToString(LwM2MClient lwM2MClient, String path) {
  1127 + LwM2mPath pathIds = new LwM2mPath(path);
  1128 + ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds);
  1129 + return (resourceValue == null) ? null :
  1130 + (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds);
  1131 + }
  1132 +}
... ...
... ... @@ -24,17 +24,25 @@ import org.eclipse.leshan.server.queue.PresenceListener;
24 24 import org.eclipse.leshan.server.registration.Registration;
25 25 import org.eclipse.leshan.server.registration.RegistrationListener;
26 26 import org.eclipse.leshan.server.registration.RegistrationUpdate;
  27 +import org.springframework.beans.factory.annotation.Autowired;
  28 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  29 +import org.springframework.stereotype.Component;
27 30
28 31 import java.util.Collection;
29 32
30 33 @Slf4j
  34 +@Component()
  35 +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
31 36 public class LwM2mServerListener {
  37 +
32 38 private LeshanServer lhServer;
33   - private LwM2MTransportService service;
34 39
35   - public LwM2mServerListener(LeshanServer lhServer, LwM2MTransportService service) {
  40 + @Autowired
  41 + private LwM2MTransportServiceImpl service;
  42 +
  43 + public LwM2mServerListener init(LeshanServer lhServer) {
36 44 this.lhServer = lhServer;
37   - this.service = service;
  45 + return this;
38 46 }
39 47
40 48 public final RegistrationListener registrationListener = new RegistrationListener() {
... ... @@ -96,8 +104,7 @@ public class LwM2mServerListener {
96 104 try {
97 105 service.onObservationResponse(registration, observation.getPath().toString(), response);
98 106 } catch (Exception e) {
99   - e.printStackTrace();
100   - log.error("onResponse");
  107 + log.error("[{}] onResponse", e.toString());
101 108
102 109 }
103 110 }
... ...
... ... @@ -27,7 +27,7 @@ import org.eclipse.leshan.server.registration.Registration;
27 27 import org.eclipse.leshan.server.security.SecurityInfo;
28 28 import org.thingsboard.server.gen.transport.TransportProtos;
29 29 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
30   -import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportService;
  30 +import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportServiceImpl;
31 31 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
32 32
33 33 import java.util.Map;
... ... @@ -48,7 +48,7 @@ public class LwM2MClient implements Cloneable {
48 48 private UUID sessionUuid;
49 49 private UUID profileUuid;
50 50 private LeshanServer lwServer;
51   - private LwM2MTransportService lwM2MTransportService;
  51 + private LwM2MTransportServiceImpl lwM2MTransportServiceImpl;
52 52 private Registration registration;
53 53 private ValidateDeviceCredentialsResponseMsg credentialsResponse;
54 54 private Map<String, String> attributes;
... ... @@ -92,7 +92,7 @@ public class LwM2MClient implements Cloneable {
92 92 this.pendingRequests.remove(path);
93 93 if (this.pendingRequests.size() == 0) {
94 94 this.initValue();
95   - this.lwM2MTransportService.putDelayedUpdateResourcesThingsboard(this);
  95 + this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this);
96 96 }
97 97 }
98 98
... ... @@ -123,7 +123,7 @@ public class LwM2MClient implements Cloneable {
123 123 public void onSuccessOrErrorDelayedRequests(String path) {
124 124 if (path != null) this.delayedRequests.remove(path);
125 125 if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) {
126   - this.lwM2MTransportService.updatesAndSentModelParameter(this);
  126 + this.lwM2MTransportServiceImpl.updatesAndSentModelParameter(this);
127 127 }
128 128 }
129 129
... ...
1   -/**
2   - * Copyright © 2016-2020 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -package org.thingsboard.server.transport.lwm2m.server.secure;
17   -
18   -import lombok.Data;
19   -import lombok.extern.slf4j.Slf4j;
20   -import org.eclipse.leshan.core.util.Hex;
21   -import org.eclipse.leshan.server.californium.LeshanServerBuilder;
22   -import org.eclipse.leshan.server.redis.RedisRegistrationStore;
23   -import org.eclipse.leshan.server.redis.RedisSecurityStore;
24   -import org.eclipse.leshan.server.security.DefaultAuthorizer;
25   -import org.eclipse.leshan.server.security.EditableSecurityStore;
26   -import org.eclipse.leshan.server.security.SecurityChecker;
27   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
28   -import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
29   -import redis.clients.jedis.Jedis;
30   -import redis.clients.jedis.JedisPool;
31   -import redis.clients.jedis.util.Pool;
32   -
33   -import java.math.BigInteger;
34   -import java.net.URI;
35   -import java.net.URISyntaxException;
36   -import java.security.KeyStore;
37   -import java.security.PublicKey;
38   -import java.security.PrivateKey;
39   -import java.security.AlgorithmParameters;
40   -import java.security.KeyFactory;
41   -import java.security.GeneralSecurityException;
42   -import java.security.KeyStoreException;
43   -import java.security.cert.X509Certificate;
44   -import java.security.interfaces.ECPublicKey;
45   -import java.security.spec.ECGenParameterSpec;
46   -import java.security.spec.ECParameterSpec;
47   -import java.security.spec.ECPoint;
48   -import java.security.spec.ECPrivateKeySpec;
49   -import java.security.spec.ECPublicKeySpec;
50   -import java.security.spec.KeySpec;
51   -import java.util.Arrays;
52   -
53   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.REDIS;
54   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
55   -
56   -
57   -@Slf4j
58   -@Data
59   -public class LwM2MSetSecurityStoreServer {
60   -
61   - private KeyStore keyStore;
62   - private X509Certificate certificate;
63   - private PublicKey publicKey;
64   - private PrivateKey privateKey;
65   - private LwM2MTransportContextServer context;
66   - private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
67   -
68   - private LeshanServerBuilder builder;
69   - EditableSecurityStore securityStore;
70   -
71   - public LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MTransportContextServer context, LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore, LwM2MSecurityMode dtlsMode) {
72   - this.builder = builder;
73   - this.context = context;
74   - this.lwM2mInMemorySecurityStore = lwM2mInMemorySecurityStore;
75   - /** Set securityStore with new registrationStore */
76   - switch (dtlsMode) {
77   - /** Use PSK only */
78   - case PSK:
79   - generatePSK_RPK();
80   - if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
81   - builder.setPrivateKey(this.privateKey);
82   - builder.setPublicKey(null);
83   - getParamsPSK();
84   - }
85   - break;
86   - /** Use RPK only */
87   - case RPK:
88   - generatePSK_RPK();
89   - if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
90   - this.privateKey != null && this.privateKey.getEncoded().length > 0) {
91   - builder.setPublicKey(this.publicKey);
92   - builder.setPrivateKey(this.privateKey);
93   - getParamsRPK();
94   - }
95   - break;
96   - /** Use x509 only */
97   - case X509:
98   - setServerWithX509Cert();
99   - break;
100   - /** No security */
101   - case NO_SEC:
102   - builder.setTrustedCertificates(new X509Certificate[0]);
103   - break;
104   - /** Use x509 with EST */
105   - case X509_EST:
106   - // TODO support sentinel pool and make pool configurable
107   - break;
108   - case REDIS:
109   - /**
110   - * Set securityStore with new registrationStore (if use redis store)
111   - * Connect to redis
112   - */
113   - Pool<Jedis> jedis = null;
114   - try {
115   - jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl()));
116   - securityStore = new RedisSecurityStore(jedis);
117   - builder.setRegistrationStore(new RedisRegistrationStore(jedis));
118   - } catch (URISyntaxException e) {
119   - e.printStackTrace();
120   - }
121   - break;
122   - default:
123   - }
124   -
125   - /** Set securityStore with new registrationStore (if not redis)*/
126   - if (dtlsMode.code < REDIS.code) {
127   - securityStore = lwM2mInMemorySecurityStore;
128   - if (dtlsMode == X509) {
129   - builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
130   - @Override
131   - protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
132   - String expectedX509CommonName) {
133   - return endpoint.startsWith(expectedX509CommonName);
134   - }
135   - }));
136   - }
137   - }
138   -
139   - /** Set securityStore with new registrationStore */
140   - builder.setSecurityStore(securityStore);
141   - }
142   -
143   - private void generatePSK_RPK() {
144   - try {
145   - /** Get Elliptic Curve Parameter spec for secp256r1 */
146   - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
147   - algoParameters.init(new ECGenParameterSpec("secp256r1"));
148   - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
149   - if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) {
150   - /** Get point values */
151   - byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray());
152   - byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray());
153   - /** Create key specs */
154   - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
155   - parameterSpec);
156   - /** Get keys */
157   - this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
158   - }
159   - if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) {
160   - /** Get point values */
161   - byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray());
162   - /** Create key specs */
163   - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
164   - /** Get keys */
165   - this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
166   - }
167   - } catch (GeneralSecurityException | IllegalArgumentException e) {
168   - log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
169   - throw new RuntimeException(e);
170   - }
171   - }
172   -
173   - private void setServerWithX509Cert() {
174   - try {
175   - if (this.context.getCtxServer().getKeyStoreValue() != null) {
176   - setBuilderX509();
177   - X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias());
178   - if (rootCAX509Cert != null) {
179   - X509Certificate[] trustedCertificates = new X509Certificate[1];
180   - trustedCertificates[0] = rootCAX509Cert;
181   - builder.setTrustedCertificates(trustedCertificates);
182   - } else {
183   - /** by default trust all */
184   - builder.setTrustedCertificates(new X509Certificate[0]);
185   - }
186   - }
187   - else {
188   - /** by default trust all */
189   - this.builder.setTrustedCertificates(new X509Certificate[0]);
190   - log.error("Unable to load X509 files for LWM2MServer");
191   - }
192   - } catch (KeyStoreException ex) {
193   - log.error("[{}] Unable to load X509 files server", ex.getMessage());
194   - }
195   - }
196   -
197   - private void setBuilderX509() {
198   - /**
199   - * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
200   - * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
201   - */
202   - try {
203   - X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias());
204   - PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray());
205   - this.builder.setPrivateKey(privateKey);
206   - this.builder.setCertificateChain(new X509Certificate[]{serverCertificate});
207   - } catch (Exception ex) {
208   - log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
209   - }
210   - }
211   -
212   - private void getParamsPSK() {
213   - log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]",
214   - Hex.encodeHexString(this.privateKey.getEncoded()),
215   - this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerSecurePort()));
216   - }
217   -
218   - private void getParamsRPK() {
219   - if (this.publicKey instanceof ECPublicKey) {
220   - /** Get x coordinate */
221   - byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
222   - if (x[0] == 0)
223   - x = Arrays.copyOfRange(x, 1, x.length);
224   -
225   - /** Get Y coordinate */
226   - byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
227   - if (y[0] == 0)
228   - y = Arrays.copyOfRange(y, 1, y.length);
229   -
230   - /** Get Curves params */
231   - String params = ((ECPublicKey) this.publicKey).getParams().toString();
232   - log.info(
233   - " \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
234   - params, Hex.encodeHexString(x), Hex.encodeHexString(y),
235   - Hex.encodeHexString(this.publicKey.getEncoded()),
236   - Hex.encodeHexString(this.privateKey.getEncoded()));
237   - } else {
238   - throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
239   - }
240   - }
241   -}
... ... @@ -25,32 +25,32 @@ import org.eclipse.leshan.server.security.SecurityInfo;
25 25 import org.eclipse.leshan.server.security.SecurityStoreListener;
26 26 import org.springframework.beans.factory.annotation.Autowired;
27 27 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
28   -import org.springframework.stereotype.Component;
  28 +import org.springframework.stereotype.Service;
29 29 import org.thingsboard.server.common.data.DeviceProfile;
30 30 import org.thingsboard.server.gen.transport.TransportProtos;
31   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo;
  31 +import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  32 +import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo;
32 33 import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
33 34 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler;
34 35 import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
35 36 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
36 37 import org.thingsboard.server.transport.lwm2m.utils.TypeServer;
37 38
38   -import java.util.Map;
39   -import java.util.UUID;
40 39 import java.util.Collection;
41 40 import java.util.Collections;
42 41 import java.util.List;
  42 +import java.util.Map;
  43 +import java.util.UUID;
43 44 import java.util.concurrent.ConcurrentHashMap;
44 45 import java.util.concurrent.locks.Lock;
45 46 import java.util.concurrent.locks.ReadWriteLock;
46 47 import java.util.concurrent.locks.ReentrantReadWriteLock;
47 48 import java.util.stream.Collectors;
48 49
49   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.DEFAULT_MODE;
50 50 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
51 51
52 52 @Slf4j
53   -@Component("LwM2mInMemorySecurityStore")
  53 +@Service("LwM2mInMemorySecurityStore")
54 54 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
55 55 public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
56 56 // lock for the two maps
... ... @@ -63,26 +63,36 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
63 63 private SecurityStoreListener listener;
64 64
65 65 @Autowired
66   - LwM2MGetSecurityInfo lwM2MGetSecurityInfo;
  66 + LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo;
67 67
  68 + /**
  69 + * Start after DefaultAuthorizer or LwM2mPskStore
  70 + * @param endPoint -
  71 + * @return SecurityInfo
  72 + */
68 73 @Override
69 74 public SecurityInfo getByEndpoint(String endPoint) {
70 75 readLock.lock();
71 76 try {
72 77 String registrationId = this.getByRegistrationId(endPoint, null);
73   - SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.add(endPoint);
  78 + SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.addLwM2MClientToSession(endPoint);
74 79 return info;
75 80 } finally {
76 81 readLock.unlock();
77 82 }
78 83 }
79 84
  85 + /**
  86 + * Start after LwM2mPskStore
  87 + * @param identity -
  88 + * @return SecurityInfo
  89 + */
80 90 @Override
81 91 public SecurityInfo getByIdentity(String identity) {
82 92 readLock.lock();
83 93 try {
84 94 String integrationId = this.getByRegistrationId(null, identity);
85   - return (integrationId != null) ? sessions.get(integrationId).getInfo() : add(identity);
  95 + return (integrationId != null) ? sessions.get(integrationId).getInfo() : this.addLwM2MClientToSession(identity);
86 96 } finally {
87 97 readLock.unlock();
88 98 }
... ... @@ -141,11 +151,19 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
141 151 return this.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
142 152
143 153 }
  154 +
  155 + /**
  156 + * Update in sessions (LwM2MClient for key registration_Id) after starting registration LwM2MClient in LwM2MTransportServiceImpl
  157 + * Remove from sessions LwM2MClient with key registration_Endpoint
  158 + * @param lwServer -
  159 + * @param registration -
  160 + * @return LwM2MClient after adding it to session
  161 + */
144 162 public LwM2MClient updateInSessionsLwM2MClient(LeshanServer lwServer, Registration registration) {
145 163 writeLock.lock();
146 164 try {
147 165 if (this.sessions.get(registration.getEndpoint()) == null) {
148   - this.add(registration.getEndpoint());
  166 + this.addLwM2MClientToSession(registration.getEndpoint());
149 167 }
150 168 LwM2MClient lwM2MClient = this.sessions.get(registration.getEndpoint());
151 169 lwM2MClient.setLwServer(lwServer);
... ... @@ -166,45 +184,43 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
166 184 return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null;
167 185 }
168 186
169   - public String getByRegistrationId(String credentialsId) {
170   - List<String> registrationIds = (this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getEndPoint())).map(model -> model.getKey()).collect(Collectors.toList()).size() > 0) ?
171   - this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getEndPoint())).map(model -> model.getKey()).collect(Collectors.toList()) :
172   - this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getIdentity())).map(model -> model.getKey()).collect(Collectors.toList());
173   - return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null;
174   - }
175   -
176 187 public Registration getByRegistration(String registrationId) {
177 188 return this.sessions.get(registrationId).getRegistration();
178 189 }
179 190
180   - private SecurityInfo add(String identity) {
181   - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.CLIENT);
182   - UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null;
183   - if (store.getSecurityInfo() != null) {
184   - if (store.getSecurityMode() < DEFAULT_MODE.code) {
  191 + /**
  192 + * Add new LwM2MClient to session
  193 + * @param identity-
  194 + * @return SecurityInfo. If error - SecurityInfoError
  195 + * and log:
  196 + * - FORBIDDEN - if there is no authorization
  197 + * - profileUuid - if the device does not have a profile
  198 + * - device - if the thingsboard does not have a device with a name equal to the identity
  199 + */
  200 + private SecurityInfo addLwM2MClientToSession(String identity) {
  201 + ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.CLIENT);
  202 + if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
  203 + UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null;
  204 + if (store.getSecurityInfo() != null && profileUuid != null) {
185 205 String endpoint = store.getSecurityInfo().getEndpoint();
186   -// sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, null, profileUuid));
187 206 sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, profileUuid));
188   - }
189   - } else {
190   - if (store.getSecurityMode() == NO_SEC.code && profileUuid != null)
191   -// sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, null, profileUuid));
  207 + } else if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) {
192 208 sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, profileUuid));
193   - else {
194   - log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity);
195   - /**
196   - * Return Error securityInfo
197   - */
198   - byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray());
199   - SecurityInfo info = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey);
200   - return info;
  209 + } else {
  210 + log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity);
  211 + /**
  212 + * Return Error securityInfo
  213 + */
  214 + byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray());
  215 + SecurityInfo infoError = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey);
  216 + return infoError;
  217 + }
201 218 }
202   - }
203   - return store.getSecurityInfo();
  219 + return store.getSecurityInfo();
204 220 }
205 221
206   - public Map<String, LwM2MClient> getSession (UUID sessionUuId){
207   - return this.sessions.entrySet().stream().filter(e -> e.getValue().getSessionUuid().equals(sessionUuId)).collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
  222 + public Map<String, LwM2MClient> getSession(UUID sessionUuId) {
  223 + return this.sessions.entrySet().stream().filter(e -> e.getValue().getSessionUuid().equals(sessionUuId)).collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
208 224 }
209 225
210 226 public Map<String, LwM2MClient> getSessions() {
... ... @@ -219,13 +235,10 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
219 235 return this.profiles.get(profileUuId);
220 236 }
221 237
222   - public Map<UUID, AttrTelemetryObserveValue>setProfiles(Map<UUID, AttrTelemetryObserveValue> profiles) {
  238 + public Map<UUID, AttrTelemetryObserveValue> setProfiles(Map<UUID, AttrTelemetryObserveValue> profiles) {
223 239 return this.profiles = profiles;
224 240 }
225 241
226   - /**
227   - * @param deviceProfile
228   - */
229 242 public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) {
230 243 JsonObject profilesConfigData = LwM2MTransportHandler.getObserveAttrTelemetryFromThingsboard(deviceProfile);
231 244 if (profilesConfigData != null) {
... ... @@ -233,5 +246,4 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
233 246 }
234 247 return (profilesConfigData != null);
235 248 }
236   -
237 249 }
... ...
... ... @@ -129,7 +129,6 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter {
129 129 case FLOAT:
130 130 return String.valueOf(value);
131 131 case TIME:
132   -// return Long.toString(((Date) value).getTime());
133 132 String DATE_FORMAT = "MMM d, yyyy HH:mm a";
134 133 Long timeValue = ((Date) value).getTime();
135 134 DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
... ...
... ... @@ -67,11 +67,6 @@
67 67 <plugin>
68 68 <groupId>org.apache.maven.plugins</groupId>
69 69 <artifactId>maven-compiler-plugin</artifactId>
70   - <version>3.1</version>
71   - <configuration>
72   - <source>1.8</source>
73   - <target>1.8</target>
74   - </configuration>
75 70 </plugin>
76 71 <plugin>
77 72 <groupId>org.apache.maven.plugins</groupId>
... ... @@ -87,4 +82,4 @@
87 82 </plugin>
88 83 </plugins>
89 84 </build>
90   -</project>
\ No newline at end of file
  85 +</project>
... ...
... ... @@ -579,7 +579,7 @@
579 579 <plugin>
580 580 <groupId>org.apache.maven.plugins</groupId>
581 581 <artifactId>maven-compiler-plugin</artifactId>
582   - <version>2.5.1</version>
  582 + <version>3.8.1</version>
583 583 <configuration>
584 584 <source>1.8</source>
585 585 <target>1.8</target>
... ...