Commit b9d3904171a772ad4a1263718cb49c8f581533f0

Authored by Andrew Shvayka
Committed by GitHub
2 parents c877609a 5f3cf1e3

Merge pull request #3840 from thingsboard/lwm2m_tests

[3.3] Lwm2m tests
Showing 25 changed files with 2322 additions and 1576 deletions

Too many changes to show.

To preserve performance only 25 of 91 files are displayed.

... ... @@ -278,7 +278,6 @@ public class DeviceController extends BaseController {
278 278 try {
279 279 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
280 280 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
281   - //log.info("0 LwM2M CredentialsUpdate start)
282 281 tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null);
283 282 logEntityAction(device.getId(), device,
284 283 device.getCustomerId(),
... ...
... ... @@ -15,17 +15,32 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
18 19 import lombok.extern.slf4j.Slf4j;
19 20 import org.springframework.security.access.prepost.PreAuthorize;
20   -import org.springframework.web.bind.annotation.*;
  21 +import org.springframework.web.bind.annotation.PathVariable;
  22 +import org.springframework.web.bind.annotation.RequestBody;
  23 +import org.springframework.web.bind.annotation.RequestMapping;
  24 +import org.springframework.web.bind.annotation.RequestMethod;
  25 +import org.springframework.web.bind.annotation.RequestParam;
  26 +import org.springframework.web.bind.annotation.ResponseBody;
  27 +import org.springframework.web.bind.annotation.RestController;
  28 +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
  29 +import org.thingsboard.server.common.data.Device;
  30 +import org.thingsboard.server.common.data.EntityType;
  31 +import org.thingsboard.server.common.data.audit.ActionType;
21 32 import org.thingsboard.server.common.data.exception.ThingsboardException;
22 33 import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
23 34 import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
24 35 import org.thingsboard.server.common.data.page.PageData;
25 36 import org.thingsboard.server.common.data.page.PageLink;
  37 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  38 +import org.thingsboard.server.common.data.security.DeviceCredentials;
26 39 import org.thingsboard.server.queue.util.TbCoreComponent;
  40 +import org.thingsboard.server.service.security.permission.Resource;
27 41
28 42 import java.util.List;
  43 +import java.util.Map;
29 44
30 45 @Slf4j
31 46 @RestController
... ... @@ -33,13 +48,16 @@ import java.util.List;
33 48 @RequestMapping("/api")
34 49 public class DeviceLwm2mController extends BaseController {
35 50
36   -
37 51 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
38   - @RequestMapping(value = "/lwm2m/deviceProfile/{objectIds}", method = RequestMethod.GET)
  52 + @RequestMapping(value = "/lwm2m/deviceProfile", params = {"sortOrder", "sortProperty"}, method = RequestMethod.GET)
39 53 @ResponseBody
40   - public List<LwM2mObject> getLwm2mListObjects(@PathVariable("objectIds") int[] objectIds) throws ThingsboardException {
  54 + public List<LwM2mObject> getLwm2mListObjects(@RequestParam String sortOrder,
  55 + @RequestParam String sortProperty,
  56 + @RequestParam(required = false) int[] objectIds,
  57 + @RequestParam(required = false) String searchText)
  58 + throws ThingsboardException {
41 59 try {
42   - return lwM2MModelsRepository.getLwm2mObjects(objectIds, null);
  60 + return lwM2MModelsRepository.getLwm2mObjects(objectIds, searchText, sortProperty, sortOrder);
43 61 } catch (Exception e) {
44 62 throw handleException(e);
45 63 }
... ... @@ -50,11 +68,11 @@ public class DeviceLwm2mController extends BaseController {
50 68 @ResponseBody
51 69 public PageData<LwM2mObject> getLwm2mListObjects(@RequestParam int pageSize,
52 70 @RequestParam int page,
53   - @RequestParam(required = false) String textSearch,
  71 + @RequestParam(required = false) String searchText,
54 72 @RequestParam(required = false) String sortProperty,
55 73 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
56 74 try {
57   - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  75 + PageLink pageLink = createPageLink(pageSize, page, searchText, sortProperty, sortOrder);
58 76 return checkNotNull(lwM2MModelsRepository.findDeviceLwm2mObjects(getTenantId(), pageLink));
59 77 } catch (Exception e) {
60 78 throw handleException(e);
... ... @@ -72,4 +90,40 @@ public class DeviceLwm2mController extends BaseController {
72 90 throw handleException(e);
73 91 }
74 92 }
  93 +
  94 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  95 + @RequestMapping(value = "/lwm2m/device-credentials", method = RequestMethod.POST)
  96 + @ResponseBody
  97 + public Device saveDeviceWithCredentials(@RequestBody (required=false) Map<Class<?>, Object> deviceWithDeviceCredentials) throws ThingsboardException {
  98 + ObjectMapper mapper = new ObjectMapper();
  99 + Device device = checkNotNull(mapper.convertValue(deviceWithDeviceCredentials.get(Device.class), Device.class));
  100 + DeviceCredentials credentials = checkNotNull(mapper.convertValue( deviceWithDeviceCredentials.get(DeviceCredentials.class), DeviceCredentials.class));
  101 + try {
  102 + device.setTenantId(getCurrentUser().getTenantId());
  103 + checkEntity(device.getId(), device, Resource.DEVICE);
  104 + Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials);
  105 + checkNotNull(savedDevice);
  106 +
  107 + tbClusterService.onDeviceChange(savedDevice, null);
  108 + tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(),
  109 + savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
  110 + tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(),
  111 + device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  112 +
  113 + logEntityAction(savedDevice.getId(), savedDevice,
  114 + savedDevice.getCustomerId(),
  115 + device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
  116 +
  117 + if (device.getId() == null) {
  118 + deviceStateService.onDeviceAdded(savedDevice);
  119 + } else {
  120 + deviceStateService.onDeviceUpdated(savedDevice);
  121 + }
  122 + return savedDevice;
  123 + } catch (Exception e) {
  124 + logEntityAction(emptyId(EntityType.DEVICE), device,
  125 + null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
  126 + throw handleException(e);
  127 + }
  128 + }
75 129 }
... ...
... ... @@ -23,8 +23,11 @@ import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
24 24 import org.springframework.data.domain.PageImpl;
25 25 import org.springframework.stereotype.Service;
26   -import org.thingsboard.server.common.data.lwm2m.*;
27 26 import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.lwm2m.LwM2mInstance;
  28 +import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
  29 +import org.thingsboard.server.common.data.lwm2m.LwM2mResource;
  30 +import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
28 31 import org.thingsboard.server.common.data.page.PageData;
29 32 import org.thingsboard.server.common.data.page.PageLink;
30 33 import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap;
... ... @@ -33,17 +36,24 @@ import org.thingsboard.server.dao.service.Validator;
33 36 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
34 37
35 38 import java.math.BigInteger;
36   -import java.security.*;
  39 +import java.security.AlgorithmParameters;
  40 +import java.security.GeneralSecurityException;
  41 +import java.security.KeyFactory;
  42 +import java.security.KeyStoreException;
  43 +import java.security.PublicKey;
37 44 import java.security.cert.CertificateEncodingException;
38 45 import java.security.cert.X509Certificate;
39 46 import java.security.spec.ECGenParameterSpec;
40 47 import java.security.spec.ECParameterSpec;
41   -import java.security.spec.ECPublicKeySpec;
42 48 import java.security.spec.ECPoint;
  49 +import java.security.spec.ECPublicKeySpec;
43 50 import java.security.spec.KeySpec;
44   -import java.util.List;
45 51 import java.util.ArrayList;
  52 +import java.util.Comparator;
  53 +import java.util.List;
  54 +import java.util.concurrent.atomic.AtomicInteger;
46 55 import java.util.function.Predicate;
  56 +import java.util.stream.Collector;
47 57 import java.util.stream.Collectors;
48 58 import java.util.stream.IntStream;
49 59
... ... @@ -70,22 +80,32 @@ public class LwM2MModelsRepository {
70 80 * Filter by Predicate (uses objectIds, if objectIds is null then it uses textSearch,
71 81 * if textSearch is null then it uses AllList from List<ObjectModel>)
72 82 */
73   - public List<LwM2mObject> getLwm2mObjects(int[] objectIds, String textSearch) {
74   - return getLwm2mObjects((objectIds != null && objectIds.length > 0) ?
75   - (ObjectModel element) -> IntStream.of(objectIds).anyMatch(x -> x == element.id) :
76   - (textSearch != null && !textSearch.isEmpty()) ? (ObjectModel element) -> element.name.contains(textSearch) : null);
  83 + public List<LwM2mObject> getLwm2mObjects(int[] objectIds, String textSearch, String sortProperty, String sortOrder) {
  84 + if (objectIds == null && textSearch != null && !textSearch.isEmpty()) {
  85 + objectIds = getObjectIdFromTextSearch(textSearch);
  86 + }
  87 + int[] finalObjectIds = objectIds;
  88 + return getLwm2mObjects((objectIds != null && objectIds.length > 0 && textSearch != null && !textSearch.isEmpty()) ?
  89 + (ObjectModel element) -> IntStream.of(finalObjectIds).anyMatch(x -> x == element.id) || element.name.toLowerCase().contains(textSearch.toLowerCase()) :
  90 + (objectIds != null && objectIds.length > 0) ?
  91 + (ObjectModel element) -> IntStream.of(finalObjectIds).anyMatch(x -> x == element.id) :
  92 + (textSearch != null && !textSearch.isEmpty()) ?
  93 + (ObjectModel element) -> element.name.contains(textSearch) :
  94 + null,
  95 + sortProperty, sortOrder);
77 96 }
78 97
79 98 /**
80 99 * @param predicate
81 100 * @return list of LwM2mObject
82 101 */
83   - private List<LwM2mObject> getLwm2mObjects(Predicate<? super ObjectModel> predicate) {
  102 + private List<LwM2mObject> getLwm2mObjects(Predicate<? super ObjectModel> predicate, String sortProperty, String sortOrder) {
84 103 List<LwM2mObject> lwM2mObjects = new ArrayList<>();
85 104 List<ObjectModel> listObjects = (predicate == null) ? this.contextServer.getModelsValue() :
86 105 contextServer.getModelsValue().stream()
87 106 .filter(predicate)
88 107 .collect(Collectors.toList());
  108 +
89 109 listObjects.forEach(obj -> {
90 110 LwM2mObject lwM2mObject = new LwM2mObject();
91 111 lwM2mObject.setId(obj.id);
... ... @@ -105,6 +125,29 @@ public class LwM2MModelsRepository {
105 125 lwM2mObject.setInstances(new LwM2mInstance[]{instance});
106 126 lwM2mObjects.add(lwM2mObject);
107 127 });
  128 + return lwM2mObjects.size() > 1 ? this.sortList (lwM2mObjects, sortProperty, sortOrder) : lwM2mObjects;
  129 + }
  130 +
  131 + private List<LwM2mObject> sortList (List<LwM2mObject> lwM2mObjects, String sortProperty, String sortOrder) {
  132 + switch (sortProperty) {
  133 + case "name":
  134 + switch (sortOrder) {
  135 + case "ASC":
  136 + lwM2mObjects.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
  137 + break;
  138 + case "DESC":
  139 + lwM2mObjects.stream().sorted(Comparator.comparing(LwM2mObject::getName).reversed());
  140 + break;
  141 + }
  142 + case "id":
  143 + switch (sortOrder) {
  144 + case "ASC":
  145 + lwM2mObjects.sort((o1, o2) -> Long.compare(o1.getId(), o2.getId()));
  146 + break;
  147 + case "DESC":
  148 + lwM2mObjects.sort((o1, o2) -> Long.compare(o2.getId(), o1.getId()));
  149 + }
  150 + }
108 151 return lwM2mObjects;
109 152 }
110 153
... ... @@ -126,13 +169,32 @@ public class LwM2MModelsRepository {
126 169 * PageNumber = 1, PageSize = List<LwM2mObject>.size()
127 170 */
128 171 public PageData<LwM2mObject> findLwm2mListObjects(PageLink pageLink) {
129   - PageImpl page = new PageImpl(getLwm2mObjects(null, pageLink.getTextSearch()));
  172 + PageImpl page = new PageImpl(getLwm2mObjects(getObjectIdFromTextSearch(pageLink.getTextSearch()),
  173 + pageLink.getTextSearch(),
  174 + pageLink.getSortOrder().getProperty(),
  175 + pageLink.getSortOrder().getDirection().name()));
130 176 PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext());
131 177 return pageData;
132 178 }
133 179
134 180 /**
135   - *
  181 + * Filter for id Object
  182 + * @param textSearch -
  183 + * @return - return Object id only first chartAt in textSearch
  184 + */
  185 + private int[] getObjectIdFromTextSearch(String textSearch) {
  186 + String filtered = null;
  187 + if (textSearch !=null && !textSearch.isEmpty()) {
  188 + AtomicInteger a = new AtomicInteger();
  189 + filtered = textSearch.chars ()
  190 + .mapToObj(chr -> (char) chr)
  191 + .filter(i -> Character.isDigit(i) && textSearch.charAt(a.getAndIncrement()) == i)
  192 + .collect(Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString));
  193 + }
  194 + return (filtered != null && !filtered.isEmpty()) ? new int[]{Integer.parseInt(filtered)} : new int[0];
  195 + }
  196 +
  197 + /**
136 198 * @param securityMode
137 199 * @param bootstrapServerIs
138 200 * @return ServerSecurityConfig more value is default: Important - port, host, publicKey
... ... @@ -143,60 +205,60 @@ public class LwM2MModelsRepository {
143 205 }
144 206
145 207 /**
146   - *
147 208 * @param bootstrapServerIs
148 209 * @param mode
149 210 * @return ServerSecurityConfig more value is default: Important - port, host, publicKey
150 211 */
151 212 private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) {
152 213 ServerSecurityConfig bsServ = new ServerSecurityConfig();
  214 + bsServ.setBootstrapServerIs(bootstrapServerIs);
153 215 if (bootstrapServerIs) {
  216 + bsServ.setServerId(contextBootStrap.getBootstrapServerId());
154 217 switch (mode) {
155 218 case NO_SEC:
156 219 bsServ.setHost(contextBootStrap.getBootstrapHost());
157   - bsServ.setPort(contextBootStrap.getBootstrapPort());
  220 + bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk());
158 221 bsServ.setServerPublicKey("");
159 222 break;
160 223 case PSK:
161 224 bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
162   - bsServ.setPort(contextBootStrap.getBootstrapSecurePort());
  225 + bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk());
163 226 bsServ.setServerPublicKey("");
164 227 break;
165 228 case RPK:
166 229 bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
167   - bsServ.setPort(contextBootStrap.getBootstrapSecurePort());
  230 + bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk());
168 231 bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));
169 232 break;
170 233 case X509:
171 234 bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
172   - bsServ.setPort(contextBootStrap.getBootstrapSecurePortCert());
  235 + bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509());
173 236 bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias()));
174 237 break;
175 238 default:
176 239 break;
177 240 }
178 241 } else {
179   - bsServ.setBootstrapServerIs(bootstrapServerIs);
180   - bsServ.setServerId(123);
  242 + bsServ.setServerId(contextServer.getServerId());
181 243 switch (mode) {
182 244 case NO_SEC:
183 245 bsServ.setHost(contextServer.getServerHost());
184   - bsServ.setPort(contextServer.getServerPort());
  246 + bsServ.setPort(contextServer.getServerPortNoSecPsk());
185 247 bsServ.setServerPublicKey("");
186 248 break;
187 249 case PSK:
188 250 bsServ.setHost(contextServer.getServerSecureHost());
189   - bsServ.setPort(contextServer.getServerSecurePort());
  251 + bsServ.setPort(contextServer.getServerPortPsk());
190 252 bsServ.setServerPublicKey("");
191 253 break;
192 254 case RPK:
193 255 bsServ.setHost(contextServer.getServerSecureHost());
194   - bsServ.setPort(contextServer.getServerSecurePort());
  256 + bsServ.setPort(contextServer.getServerPortRpk());
195 257 bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
196 258 break;
197 259 case X509:
198 260 bsServ.setHost(contextServer.getServerSecureHost());
199   - bsServ.setPort(contextServer.getServerSecurePortCert());
  261 + bsServ.setPort(contextServer.getServerPortX509());
200 262 bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias()));
201 263 break;
202 264 default:
... ... @@ -207,25 +269,23 @@ public class LwM2MModelsRepository {
207 269 }
208 270
209 271 /**
210   - *
211 272 * @param alias
212 273 * @return PublicKey format HexString or null
213 274 */
214   - private String getServerPublicKeyX509 (String alias) {
  275 + private String getServerPublicKeyX509(String alias) {
215 276 try {
216   - X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias);
217   - return Hex.encodeHexString(serverCertificate.getEncoded());
  277 + X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias);
  278 + return Hex.encodeHexString(serverCertificate.getEncoded());
218 279 } catch (CertificateEncodingException | KeyStoreException e) {
219 280 e.printStackTrace();
220 281 }
221   - return null;
  282 + return null;
222 283 }
223 284
224 285 /**
225   - *
226 286 * @param publicServerX
227 287 * @param publicServerY
228   - * @return PublicKey format HexString or null
  288 + * @return PublicKey format HexString or null
229 289 */
230 290 private String getRPKPublicKey(String publicServerX, String publicServerY) {
231 291 try {
... ... @@ -241,9 +301,9 @@ public class LwM2MModelsRepository {
241 301 KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
242 302 parameterSpec);
243 303 /** Get keys */
244   - PublicKey publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
245   - if (publicKey != null && publicKey.getEncoded().length > 0 ) {
246   - return Hex.encodeHexString(publicKey.getEncoded());
  304 + PublicKey publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  305 + if (publicKey != null && publicKey.getEncoded().length > 0) {
  306 + return Hex.encodeHexString(publicKey.getEncoded());
247 307 }
248 308 }
249 309 } catch (GeneralSecurityException | IllegalArgumentException e) {
... ...
... ... @@ -49,34 +49,31 @@ import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
49 49 import org.thingsboard.server.dao.device.DeviceCredentialsService;
50 50 import org.thingsboard.server.dao.device.DeviceProvisionService;
51 51 import org.thingsboard.server.dao.device.DeviceService;
  52 +import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
52 53 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
53 54 import org.thingsboard.server.dao.device.provision.ProvisionResponse;
54 55 import org.thingsboard.server.dao.relation.RelationService;
  56 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
55 57 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
56 58 import org.thingsboard.server.gen.transport.TransportProtos;
57   -import org.thingsboard.server.gen.transport.TransportProtos.DeviceCredentialsProto;
58 59 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
59   -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
60   -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
61 60 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
62 61 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
  62 +import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
  63 +import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
63 64 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
64   -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
65   -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsgOrBuilder;
66 65 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus;
67 66 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
68 67 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
69 68 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
  69 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
70 70 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
71 71 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
72   -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
73 72 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
74 73 import org.thingsboard.server.queue.util.TbCoreComponent;
75   -import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
76 74 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
77 75 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
78 76 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
79   -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
80 77 import org.thingsboard.server.service.queue.TbClusterService;
81 78 import org.thingsboard.server.service.state.DeviceStateService;
82 79
... ... @@ -326,6 +323,7 @@ public class DefaultTransportApiService implements TransportApiService {
326 323 break;
327 324 case MQTT_BASIC:
328 325 case X509_CERTIFICATE:
  326 + case LWM2M_CREDENTIALS:
329 327 provisionResponse.setCredentialsValue(deviceCredentials.getCredentialsValue());
330 328 break;
331 329 }
... ...
... ... @@ -575,7 +575,13 @@ transport:
575 575 timeout: "${LWM2M_TIMEOUT:120000}"
576 576 # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}"
577 577 model_path_file: "${LWM2M_MODEL_PATH_FILE:}"
578   - support_deprecated_ciphers_enable: "${LWM2M_SUPPORT_DEPRECATED_CIPHERS_ENABLED:true}"
  578 + recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
  579 + recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:false}"
  580 + request_pool_size: "${LWM2M_REQUEST_POOL_SIZE:100}"
  581 + request_error_pool_size: "${LWM2M_REQUEST_ERROR_POOL_SIZE:10}"
  582 + registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}"
  583 + update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
  584 + un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
579 585 secure:
580 586 # Only Certificate_x509:
581 587 # To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
... ... @@ -588,24 +594,19 @@ transport:
588 594 root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}"
589 595 enable_gen_psk_rpk: "${ENABLE_GEN_PSK_RPK:true}"
590 596 server:
  597 + id: "${LWM2M_SERVER_ID:123}"
591 598 bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
592   - bind_port: "${LWM2M_BIND_PORT:5685}"
593   - bind_port_cert: "${LWM2M_BIND_PORT_CERT:5687}"
  599 + bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_PSK:5685}"
  600 + bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_RPK:5687}"
  601 + bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_X509:5689}"
594 602 secure:
595   - start_all: "${START_SERVER_ALL:true}"
596   - #leshan.core (V1_1)
597   - #DTLS security modes:
598   - #0: Pre-Shared Key mode
599   - #1: Raw Public Key mode
600   - #2: Certificate mode X509
601   - #3: NoSec mode *
602   - #OMA-TS-LightweightM2M_Core-V1_1_1-20190617-A (add)
603   - #4: Certificate mode X509 with EST
604   - # If only startAll == false
605   - dtls_mode: "${LWM2M_SECURITY_MODE:1}"
606 603 bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
607   - bind_port: "${LWM2M_BIND_PORT_SEC:5686}"
608   - bind_port_cert: "${LWM2M_BIND_PORT_SEC_CERT:5688}"
  604 + start_psk: "${START_SERVER_PSK:true}"
  605 + start_rpk: "${START_SERVER_RPK:true}"
  606 + start_x509: "${START_SERVER_X509:true}"
  607 + bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK:5686}"
  608 + bind_port_rpk: "${LWM2M_BIND_PORT_SEC_RPK:5688}"
  609 + bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509:5690}"
609 610 # Only RPK: Public & Private Key
610 611 # create_rpk: "${CREATE_RPK:}"
611 612 public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}"
... ... @@ -615,23 +616,26 @@ transport:
615 616 alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}"
616 617 bootstrap:
617 618 enable: "${BOOTSTRAP:true}"
  619 + id: "${LWM2M_SERVER_ID:111}"
618 620 bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
619   - bind_port: "${LWM2M_BIND_PORT_BS:5689}"
620   - bind_port_cert: "${LWM2M_BIND_PORT_SER_BS:5691}"
  621 + bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_BS:5691}"
  622 + bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_BS:5693}"
  623 + bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_BS:5695}"
621 624 secure:
622   - start_all: "${START_BOOTSTRAP_ALL:true}"
623   - # If only startAll == false
624   - dtls_mode: "${LWM2M_SECURITY_MODE_BS:1}"
625 625 bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
626   - bind_port: "${LWM2M_BIND_PORT_SEC_BS:5690}"
627   - bind_port_cert: "${LWM2M_BIND_PORT_SEC_CERT_BS:5692}"
  626 + start_psk: "${START_SERVER_PSK_BS:true}"
  627 + start_rpk: "${START_SERVER_RPK_BS:true}"
  628 + start_x509: "${START_SERVER_X509_BS:true}"
  629 + bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK_BS:5692}"
  630 + bind_port_rpk: "${LWM2M_BIND_PORT_SER_RPK_BS:5694}"
  631 + bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509_BS:5696}"
628 632 # Only RPK: Public & Private Key
629 633 public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}"
630 634 public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}"
631 635 private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}"
632 636 # Only Certificate_x509:
633 637 alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}"
634   - # Redis
  638 + # Redis
635 639 redis_url: "${LWM2M_REDIS_URL:''}"
636 640
637 641 swagger:
... ...
... ... @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
27 27 import org.thingsboard.server.common.data.id.TenantId;
28 28 import org.thingsboard.server.common.data.page.PageData;
29 29 import org.thingsboard.server.common.data.page.PageLink;
  30 +import org.thingsboard.server.common.data.security.DeviceCredentials;
30 31 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
31 32
32 33 import java.util.List;
... ... @@ -45,6 +46,8 @@ public interface DeviceService {
45 46
46 47 Device saveDeviceWithAccessToken(Device device, String accessToken);
47 48
  49 + Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials);
  50 +
48 51 Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId);
49 52
50 53 Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId);
... ...
... ... @@ -20,7 +20,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
20 20 import lombok.EqualsAndHashCode;
21 21 import lombok.extern.slf4j.Slf4j;
22 22 import org.thingsboard.server.common.data.device.data.DeviceData;
23   -import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
24 23 import org.thingsboard.server.common.data.id.CustomerId;
25 24 import org.thingsboard.server.common.data.id.DeviceId;
26 25 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -64,6 +63,17 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
64 63 this.setDeviceData(device.getDeviceData());
65 64 }
66 65
  66 + public Device updateDevice(Device device) {
  67 + this.tenantId = device.getTenantId();
  68 + this.customerId = device.getCustomerId();
  69 + this.name = device.getName();
  70 + this.type = device.getType();
  71 + this.label = device.getLabel();
  72 + this.deviceProfileId = device.getDeviceProfileId();
  73 + this.setDeviceData(device.getDeviceData());
  74 + return this;
  75 + }
  76 +
67 77 public TenantId getTenantId() {
68 78 return tenantId;
69 79 }
... ...
... ... @@ -21,6 +21,4 @@ public enum DeviceCredentialsType {
21 21 X509_CERTIFICATE,
22 22 MQTT_BASIC,
23 23 LWM2M_CREDENTIALS
24   -
25   -
26 24 }
... ...
... ... @@ -77,6 +77,7 @@ enum CredentialsType {
77 77 ACCESS_TOKEN = 0;
78 78 X509_CERTIFICATE = 1;
79 79 MQTT_BASIC = 2;
  80 + LWM2M_CREDENTIALS = 3;
80 81 }
81 82
82 83 message KeyValueProto {
... ...
... ... @@ -65,15 +65,10 @@
65 65 <groupId>ch.qos.logback</groupId>
66 66 <artifactId>logback-classic</artifactId>
67 67 </dependency>
68   - <!-- lechan start -->
69 68 <dependency>
70 69 <groupId>org.eclipse.leshan</groupId>
71 70 <artifactId>leshan-server-cf</artifactId>
72 71 </dependency>
73   -<!-- <dependency>-->
74   -<!-- <groupId>org.eclipse.leshan</groupId>-->
75   -<!-- <artifactId>leshan-server-cf</artifactId>-->
76   -<!-- </dependency> -->
77 72 <dependency>
78 73 <groupId>org.eclipse.leshan</groupId>
79 74 <artifactId>leshan-client-cf</artifactId>
... ... @@ -83,12 +78,6 @@
83 78 <groupId>org.eclipse.leshan</groupId>
84 79 <artifactId>leshan-server-redis</artifactId>
85 80 </dependency>
86   -<!-- <dependency>-->
87   -<!-- <groupId>org.eclipse.californium</groupId>-->
88   -<!-- <artifactId>californium-core</artifactId>-->
89   -<!-- </dependency>-->
90   - <!-- leshan finish -->
91   -
92 81 <dependency>
93 82 <groupId>org.springframework.boot</groupId>
94 83 <artifactId>spring-boot-starter-test</artifactId>
... ... @@ -107,14 +96,20 @@
107 96 <dependency>
108 97 <groupId>org.eclipse.californium</groupId>
109 98 <artifactId>californium-core</artifactId>
110   - <version>${californium.version}</version>
111   - <type>test-jar</type>
  99 + <type>test-jar</type>
112 100 <scope>test</scope>
113 101 </dependency>
114 102 <dependency>
115 103 <groupId>org.eclipse.californium</groupId>
  104 + <artifactId>californium-core</artifactId>
  105 + </dependency>
  106 +<!-- <dependency>-->
  107 +<!-- <groupId>org.eclipse.californium</groupId>-->
  108 +<!-- <artifactId>scandium</artifactId>-->
  109 +<!-- </dependency>-->
  110 + <dependency>
  111 + <groupId>org.eclipse.californium</groupId>
116 112 <artifactId>element-connector</artifactId>
117   - <version>${californium.version}</version>
118 113 <type>test-jar</type>
119 114 <scope>test</scope>
120 115 </dependency>
... ...
... ... @@ -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,42 @@ 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.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
  56 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
  57 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK;
36 58 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
  59 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
37 60 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig;
38 61
39 62 @Slf4j
40 63 @Component
41   -@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')")
  64 +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true')")
42 65 public class LwM2MTransportBootstrapServerConfiguration {
  66 + private PublicKey publicKey;
  67 + private PrivateKey privateKey;
43 68
44 69 @Autowired
45 70 private LwM2MTransportContextBootstrap contextBs;
... ... @@ -53,27 +78,35 @@ public class LwM2MTransportBootstrapServerConfiguration {
53 78 @Autowired
54 79 private LwM2MInMemoryBootstrapConfigStore lwM2MInMemoryBootstrapConfigStore;
55 80
56   -
57 81 @Primary
58   - @Bean(name = "leshanBootstrapCert")
59   - public LeshanBootstrapServer getLeshanBootstrapServerCert() {
60   - log.info("Prepare and start BootstrapServerCert... PostConstruct");
61   - return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortCert(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortCert(), X509);
  82 + @Bean(name = "leshanBootstrapX509")
  83 + @ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_x509:false}'=='true')")
  84 + public LeshanBootstrapServer getLeshanBootstrapServerX509() {
  85 + log.info("Prepare and start BootstrapServerX509... PostConstruct");
  86 + return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecX509(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortX509(), X509);
  87 + }
  88 +
  89 + @Bean(name = "leshanBootstrapPsk")
  90 + @ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_psk:false}'=='true')")
  91 + public LeshanBootstrapServer getLeshanBootstrapServerPsk() {
  92 + log.info("Prepare and start BootstrapServerRsk... PostConstruct");
  93 + return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecPsk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortPsk(), PSK);
62 94 }
63 95
64   - @Bean(name = "leshanBootstrapRPK")
65   - public LeshanBootstrapServer getLeshanBootstrapServerRPK() {
66   - log.info("Prepare and start BootstrapServerRPK... PostConstruct");
67   - return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPort(), this.contextBs.getCtxBootStrap().getBootstrapSecurePort(), RPK);
  96 + @Bean(name = "leshanBootstrapRpk")
  97 + @ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_rpk:false}'=='true')")
  98 + public LeshanBootstrapServer getLeshanBootstrapServerRpk() {
  99 + log.info("Prepare and start BootstrapServerRpk... PostConstruct");
  100 + return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecRpk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortRpk(), RPK);
68 101 }
69 102
70   - public LeshanBootstrapServer getLeshanBootstrapServer(Integer bootstrapPort, Integer bootstrapSecurePort, LwM2MSecurityMode dtlsMode) {
  103 + public LeshanBootstrapServer getLeshanBootstrapServer(Integer bootstrapPortNoSec, Integer bootstrapSecurePort, LwM2MSecurityMode dtlsMode) {
71 104 LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder();
72   - builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPort);
  105 + builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPortNoSec);
73 106 builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapSecureHost(), bootstrapSecurePort);
74 107
75 108 /** Create CoAP Config */
76   - builder.setCoapConfig(getCoapConfig ());
  109 + builder.setCoapConfig(getCoapConfig (bootstrapPortNoSec, bootstrapSecurePort));
77 110
78 111 /** ConfigStore */
79 112 builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
... ... @@ -84,13 +117,22 @@ public class LwM2MTransportBootstrapServerConfiguration {
84 117 /** Define model provider (Create Models )*/
85 118 builder.setModel(new StaticModel(contextS.getCtxServer().getModelsValue()));
86 119
  120 + /** Create credentials */
  121 + LwM2MSetSecurityStoreBootstrap(builder, dtlsMode);
  122 +
87 123 /** Create and Set DTLS Config */
88 124 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
89   - dtlsConfig.setRecommendedCipherSuitesOnly(contextS.getCtxServer().isSupportDeprecatedCiphersEnable());
  125 + if (dtlsMode==PSK) {
  126 + dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers());
  127 + dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getCtxServer().isRecommendedSupportedGroups());
  128 + dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256);
  129 + }
  130 + else {
  131 + dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers());
  132 +// dtlsConfig.setRecommendedSupportedGroupsOnly(false);
  133 + }
90 134 builder.setDtlsConfig(dtlsConfig);
91 135
92   - /** Create credentials */
93   - new LwM2MSetSecurityStoreBootstrap(builder, contextBs, contextS, dtlsMode);
94 136
95 137 BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore);
96 138 builder.setSessionManager(sessionManager);
... ... @@ -98,4 +140,151 @@ public class LwM2MTransportBootstrapServerConfiguration {
98 140 /** Create BootstrapServer */
99 141 return builder.build();
100 142 }
  143 +
  144 + public void LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MSecurityMode dtlsMode) {
  145 +
  146 + /** Set securityStore with new registrationStore */
  147 +
  148 + switch (dtlsMode) {
  149 + /** Use No_Sec only */
  150 + case NO_SEC:
  151 + setServerWithX509Cert(builder, NO_SEC.code);
  152 + break;
  153 + /** Use PSK/RPK */
  154 + case PSK:
  155 + break;
  156 + case RPK:
  157 + setRPK(builder);
  158 + break;
  159 + case X509:
  160 + setServerWithX509Cert(builder, X509.code);
  161 + break;
  162 + /** Use X509_EST only */
  163 + case X509_EST:
  164 + // TODO support sentinel pool and make pool configurable
  165 + break;
  166 + /** Use ather X509, PSK, No_Sec ?? */
  167 + default:
  168 + break;
  169 + }
  170 + }
  171 +
  172 + private void setRPK(LeshanBootstrapServerBuilder builder) {
  173 + try {
  174 + /** Get Elliptic Curve Parameter spec for secp256r1 */
  175 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
  176 + algoParameters.init(new ECGenParameterSpec("secp256r1"));
  177 + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
  178 + if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) {
  179 + /** Get point values */
  180 + byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray());
  181 + byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray());
  182 + /** Create key specs */
  183 + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
  184 + parameterSpec);
  185 + /** Get keys */
  186 + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  187 + }
  188 + if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) {
  189 + /** Get point values */
  190 + byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray());
  191 + /** Create key specs */
  192 + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
  193 + /** Get keys */
  194 + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
  195 + }
  196 + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
  197 + this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  198 + builder.setPublicKey(this.publicKey);
  199 + builder.setPrivateKey(this.privateKey);
  200 + this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey);
  201 + getParamsRPK();
  202 + }
  203 + } catch (GeneralSecurityException | IllegalArgumentException e) {
  204 + log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
  205 + throw new RuntimeException(e);
  206 + }
  207 + }
  208 +
  209 + private void setServerWithX509Cert(LeshanBootstrapServerBuilder builder, int securityModeCode) {
  210 + try {
  211 + if (this.contextS.getCtxServer().getKeyStoreValue() != null) {
  212 + KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue();
  213 + setBuilderX509(builder);
  214 + X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias());
  215 + if (rootCAX509Cert != null && securityModeCode == X509.code) {
  216 + X509Certificate[] trustedCertificates = new X509Certificate[1];
  217 + trustedCertificates[0] = rootCAX509Cert;
  218 + builder.setTrustedCertificates(trustedCertificates);
  219 + } else {
  220 + /** by default trust all */
  221 + builder.setTrustedCertificates(new X509Certificate[0]);
  222 + }
  223 + }
  224 + else {
  225 + /** by default trust all */
  226 + builder.setTrustedCertificates(new X509Certificate[0]);
  227 + log.error("Unable to load X509 files for BootStrapServer");
  228 + }
  229 + } catch (KeyStoreException ex) {
  230 + log.error("[{}] Unable to load X509 files server", ex.getMessage());
  231 + }
  232 +
  233 + }
  234 +
  235 + private void setBuilderX509(LeshanBootstrapServerBuilder builder) {
  236 + /**
  237 + * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
  238 + * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
  239 + */
  240 + try {
  241 + X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias());
  242 + this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray());
  243 + if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  244 + builder.setPrivateKey(this.privateKey);
  245 + }
  246 + if (serverCertificate != null) {
  247 + builder.setCertificateChain(new X509Certificate[]{serverCertificate});
  248 + this.infoParamsX509(serverCertificate);
  249 + }
  250 + } catch (Exception ex) {
  251 + log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
  252 + }
  253 + }
  254 +
  255 + private void getParamsRPK() {
  256 + if (this.publicKey instanceof ECPublicKey) {
  257 + /** Get x coordinate */
  258 + byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
  259 + if (x[0] == 0)
  260 + x = Arrays.copyOfRange(x, 1, x.length);
  261 +
  262 + /** Get Y coordinate */
  263 + byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
  264 + if (y[0] == 0)
  265 + y = Arrays.copyOfRange(y, 1, y.length);
  266 +
  267 + /** Get Curves params */
  268 + String params = ((ECPublicKey) this.publicKey).getParams().toString();
  269 + log.info(
  270 + " \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
  271 + params, Hex.encodeHexString(x), Hex.encodeHexString(y),
  272 + Hex.encodeHexString(this.publicKey.getEncoded()),
  273 + Hex.encodeHexString(this.privateKey.getEncoded()));
  274 + } else {
  275 + throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
  276 + }
  277 + }
  278 +
  279 + private void infoParamsX509(X509Certificate certificate) {
  280 + try {
  281 + log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",
  282 + Hex.encodeHexString(certificate.getEncoded()),
  283 + Hex.encodeHexString(this.privateKey.getEncoded()));
  284 + } catch (CertificateEncodingException e) {
  285 + log.error("", e);
  286 + }
  287 + }
  288 +
  289 +
101 290 }
... ...
... ... @@ -14,57 +14,56 @@
14 14 * limitations under the License.
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.bootstrap;
  17 +
17 18 import lombok.extern.slf4j.Slf4j;
18 19 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
19 20 import org.springframework.beans.factory.annotation.Autowired;
20 21 import org.springframework.beans.factory.annotation.Qualifier;
21 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
22 23 import org.springframework.stereotype.Service;
23   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
24 24
25 25 import javax.annotation.PostConstruct;
26 26 import javax.annotation.PreDestroy;
27 27
28 28 @Slf4j
29 29 @Service
30   -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled}'=='true'&& '${transport.lwm2m.bootstrap.enable}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true'&& '${transport.lwm2m.bootstrap.enable}'=='true')")
  30 +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true')")
31 31 public class LwM2MTransportBootstrapServerInitializer {
32 32
33   - @Autowired
34   - @Qualifier("leshanBootstrapCert")
  33 + @Autowired(required = false)
  34 + @Qualifier("leshanBootstrapX509")
35 35 private LeshanBootstrapServer lhBServerCert;
36 36
37   - @Autowired
38   - @Qualifier("leshanBootstrapRPK")
39   - private LeshanBootstrapServer lhBServerRPK;
  37 + @Autowired(required = false)
  38 + @Qualifier("leshanBootstrapPsk")
  39 + private LeshanBootstrapServer lhBServerPsk;
  40 +
  41 + @Autowired(required = false)
  42 + @Qualifier("leshanBootstrapRpk")
  43 + private LeshanBootstrapServer lhBServerRpk;
40 44
41 45 @Autowired
42 46 private LwM2MTransportContextBootstrap contextBS;
43 47
44 48 @PostConstruct
45 49 public void init() {
46   - if (this.contextBS.getCtxBootStrap().isBootstrapStartAll()) {
47   - this.lhBServerCert.start();
48   - this.lhBServerRPK.start();
  50 + if (this.contextBS.getCtxBootStrap().getBootstrapStartPsk()) {
  51 + this.lhBServerPsk.start();
  52 + }
  53 + if (this.contextBS.getCtxBootStrap().getBootstrapStartRpk()) {
  54 + this.lhBServerRpk.start();
49 55 }
50   - else {
51   - if (this.contextBS.getCtxBootStrap().getBootStrapDtlsMode() == LwM2MSecurityMode.X509.code) {
52   - this.lhBServerCert.start();
53   - }
54   - else {
55   - this.lhBServerRPK.start();
56   - }
  56 + if (this.contextBS.getCtxBootStrap().getBootstrapStartX509()) {
  57 + this.lhBServerCert.start();
57 58 }
58 59 }
59 60
60 61 @PreDestroy
61 62 public void shutdown() throws InterruptedException {
62 63 log.info("Stopping LwM2M transport Bootstrap Server!");
63   - try {
64   - lhBServerCert.destroy();
65   - lhBServerRPK.destroy();
66   - } finally {
67   - }
  64 + lhBServerPsk.destroy();
  65 + lhBServerRpk.destroy();
  66 + lhBServerCert.destroy();
68 67 log.info("LwM2M transport Bootstrap Server stopped!");
69 68 }
70 69 }
... ...
... ... @@ -29,28 +29,41 @@ 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;
33   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo;
  32 +import org.springframework.stereotype.Service;
  33 +import org.thingsboard.server.gen.transport.TransportProtos;
34 34 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  35 +import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
35 36 import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
  37 +import org.thingsboard.server.transport.lwm2m.server.LwM2MSessionMsgListener;
  38 +import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
  39 +import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler;
36 40 import org.thingsboard.server.transport.lwm2m.utils.TypeServer;
37 41
38 42 import java.io.IOException;
39 43 import java.security.GeneralSecurityException;
40 44 import java.util.Arrays;
41 45 import java.util.List;
  46 +import java.util.UUID;
42 47
43   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.*;
  48 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.BOOTSTRAP_SERVER;
  49 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR;
  50 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO;
  51 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LWM2M_SERVER;
  52 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVERS;
  53 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getBootstrapParametersFromThingsboard;
44 54
45 55 @Slf4j
46   -@Component("LwM2MBootstrapSecurityStore")
  56 +@Service("LwM2MBootstrapSecurityStore")
47 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')")
48 58 public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
49 59
50 60 private final EditableBootstrapConfigStore bootstrapConfigStore;
51 61
52 62 @Autowired
53   - LwM2MGetSecurityInfo lwM2MGetSecurityInfo;
  63 + LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator;
  64 +
  65 + @Autowired
  66 + public LwM2MTransportContextServer context;
54 67
55 68 public LwM2MBootstrapSecurityStore(EditableBootstrapConfigStore bootstrapConfigStore) {
56 69 this.bootstrapConfigStore = bootstrapConfigStore;
... ... @@ -59,8 +72,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
59 72 @Override
60 73 public List<SecurityInfo> getAllByEndpoint(String endPoint) {
61 74 String endPointKey = endPoint;
62   - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(endPointKey, TypeServer.BOOTSTRAP);
63   - if (store.getBootstrapJsonCredential() != null) {
  75 + ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(endPointKey, TypeServer.BOOTSTRAP);
  76 + if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
64 77 /** add value to store from BootstrapJson */
65 78 this.setBootstrapConfigScurityInfo(store);
66 79 BootstrapConfig bsConfigNew = store.getBootstrapConfig();
... ... @@ -73,7 +86,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
73 86 }
74 87 bootstrapConfigStore.add(endPoint, bsConfigNew);
75 88 } catch (InvalidConfigurationException e) {
76   - e.printStackTrace();
  89 + log.error("", e);
77 90 }
78 91 return store.getSecurityInfo() == null ? null : Arrays.asList(store.getSecurityInfo());
79 92 }
... ... @@ -83,17 +96,16 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
83 96
84 97 @Override
85 98 public SecurityInfo getByIdentity(String identity) {
86   - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.BOOTSTRAP);
87   - /** add value to store from BootstrapJson */
88   - this.setBootstrapConfigScurityInfo(store);
89   -
90   - if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
  99 + ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(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);
91 103 BootstrapConfig bsConfig = store.getBootstrapConfig();
92 104 if (bsConfig.security != null) {
93 105 try {
94 106 bootstrapConfigStore.add(store.getEndPoint(), bsConfig);
95 107 } catch (InvalidConfigurationException e) {
96   - e.printStackTrace();
  108 + log.error("", e);
97 109 }
98 110 return store.getSecurityInfo();
99 111 }
... ... @@ -141,28 +153,36 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
141 153 private LwM2MBootstrapConfig getParametersBootstrap(ReadResultSecurityStore store) {
142 154 try {
143 155 JsonObject bootstrapJsonCredential = store.getBootstrapJsonCredential();
144   - ObjectMapper mapper = new ObjectMapper();
145   - LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class);
146   - JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
147   - lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class);
148   - LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);
149   - LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class);
150   - if (getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
151   - lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
152   - lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
153   - return lwM2MBootstrapConfig;
154   - }
155   - else {
156   - log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
157   - log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
158   - String logMsg = String.format(LOG_LW2M_ERROR + " getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint());
159   -// sentLogsToThingsboard(logMsg, store.getEndPoint());
160   - 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 + }
161 179 }
162 180 } catch (JsonProcessingException e) {
163 181 log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndPoint(), e.getMessage());
164 182 return null;
165 183 }
  184 + log.error("Unable to decode Json or Certificate for [{}]", store.getEndPoint());
  185 + return null;
166 186 }
167 187
168 188 /**
... ... @@ -175,7 +195,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
175 195 * @return false if not sync between SecurityMode of Bootstrap credential and profile
176 196 */
177 197 private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap profileServerBootstrap, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
178   - return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) &&
  198 + return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) &&
179 199 lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode()));
180 200 }
181 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   -}
... ... @@ -49,7 +49,6 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
49 49 this.securityChecker = securityChecker;
50 50 }
51 51
52   - @Override
53 52 public BootstrapSession begin(String endpoint, Identity clientIdentity) {
54 53 boolean authorized;
55 54 if (bsSecurityStore != null) {
... ...
... ... @@ -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/LwM2mCredentialsSecurityInfoValidator.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java
... ... @@ -39,12 +39,15 @@ import java.util.Optional;
39 39 import java.util.concurrent.CountDownLatch;
40 40 import java.util.concurrent.TimeUnit;
41 41
42   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.*;
  42 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
  43 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK;
  44 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
  45 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
43 46
44 47 @Slf4j
45 48 @Component("LwM2MGetSecurityInfo")
46 49 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
47   -public class LwM2MGetSecurityInfo {
  50 +public class LwM2mCredentialsSecurityInfoValidator {
48 51
49 52 @Autowired
50 53 public LwM2MTransportContextServer contextS;
... ... @@ -53,7 +56,13 @@ public class LwM2MGetSecurityInfo {
53 56 public LwM2MTransportContextBootstrap contextBS;
54 57
55 58
56   - 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 createAndValidateCredentialsSecurityInfo(String endPoint, TypeServer keyValue) {
57 66 CountDownLatch latch = new CountDownLatch(1);
58 67 final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1];
59 68 contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endPoint).build(),
... ... @@ -61,7 +70,7 @@ public class LwM2MGetSecurityInfo {
61 70 @Override
62 71 public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
63 72 String credentialsBody = msg.getCredentialsBody();
64   - resultSecurityStore[0] = putSecurityInfo(endPoint, msg.getDeviceInfo().getDeviceName(), credentialsBody, keyValue);
  73 + resultSecurityStore[0] = createSecurityInfo(endPoint, credentialsBody, keyValue);
65 74 resultSecurityStore[0].setMsg(msg);
66 75 Optional<DeviceProfile> deviceProfileOpt = LwM2MTransportHandler.decode(msg.getProfileBody().toByteArray());
67 76 deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile));
... ... @@ -70,20 +79,27 @@ public class LwM2MGetSecurityInfo {
70 79
71 80 @Override
72 81 public void onError(Throwable e) {
73   - log.trace("[{}] Failed to process credentials PSK: {}", endPoint, e);
74   - resultSecurityStore[0] = putSecurityInfo(endPoint, null, null, null);
  82 + log.trace("[{}] [{}] Failed to process credentials ", endPoint, e);
  83 + resultSecurityStore[0] = createSecurityInfo(endPoint, null, null);
75 84 latch.countDown();
76 85 }
77 86 });
78 87 try {
79 88 latch.await(contextS.getCtxServer().getTimeout(), TimeUnit.MILLISECONDS);
80 89 } catch (InterruptedException e) {
81   - e.printStackTrace();
  90 + log.error("Failed to await credentials!", e);
82 91 }
83 92 return resultSecurityStore[0];
84 93 }
85 94
86   - 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) {
87 103 ReadResultSecurityStore result = new ReadResultSecurityStore();
88 104 JsonObject objectMsg = LwM2MTransportHandler.validateJson(jsonStr);
89 105 if (objectMsg != null && !objectMsg.isJsonNull()) {
... ... @@ -99,20 +115,21 @@ public class LwM2MGetSecurityInfo {
99 115 if (keyValue.equals(TypeServer.BOOTSTRAP)) {
100 116 result.setBootstrapJsonCredential(object);
101 117 result.setEndPoint(endPoint);
  118 + result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code);
102 119 } else {
103 120 LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(object.get("securityConfigClientMode").getAsString().toLowerCase());
104 121 switch (lwM2MSecurityMode) {
105 122 case NO_SEC:
106   - getClientSecurityInfoNoSec(result);
  123 + createClientSecurityInfoNoSec(result);
107 124 break;
108 125 case PSK:
109   - getClientSecurityInfoPSK(result, endPoint, object);
  126 + createClientSecurityInfoPSK(result, endPoint, object);
110 127 break;
111 128 case RPK:
112   - getClientSecurityInfoRPK(result, endPoint, object);
  129 + createClientSecurityInfoRPK(result, endPoint, object);
113 130 break;
114 131 case X509:
115   - getClientSecurityInfoX509(result, endPoint);
  132 + createClientSecurityInfoX509(result, endPoint);
116 133 break;
117 134 default:
118 135 break;
... ... @@ -123,12 +140,12 @@ public class LwM2MGetSecurityInfo {
123 140 return result;
124 141 }
125 142
126   - private void getClientSecurityInfoNoSec(ReadResultSecurityStore result) {
  143 + private void createClientSecurityInfoNoSec(ReadResultSecurityStore result) {
127 144 result.setSecurityInfo(null);
128 145 result.setSecurityMode(NO_SEC.code);
129 146 }
130 147
131   - private void getClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) {
  148 + private void createClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) {
132 149 /** PSK Deserialization */
133 150 String identity = (object.has("identity") && object.get("identity").isJsonPrimitive()) ? object.get("identity").getAsString() : null;
134 151 if (identity != null && !identity.isEmpty()) {
... ... @@ -148,7 +165,7 @@ public class LwM2MGetSecurityInfo {
148 165 }
149 166 }
150 167
151   - private void getClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) {
  168 + private void createClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) {
152 169 try {
153 170 if (object.has("key") && object.get("key").isJsonPrimitive()) {
154 171 byte[] rpkkey = Hex.decodeHex(object.get("key").getAsString().toLowerCase().toCharArray());
... ... @@ -163,7 +180,7 @@ public class LwM2MGetSecurityInfo {
163 180 }
164 181 }
165 182
166   - private void getClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) {
  183 + private void createClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) {
167 184 result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
168 185 result.setSecurityMode(X509.code);
169 186 }
... ...
... ... @@ -33,17 +33,17 @@ 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   - 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 }
43 43
44 44 @Override
45 45 public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
46   - log.info("[{}] attributesResponse", getAttributesResponse);
  46 + this.service.onGetAttributesResponse(getAttributesResponse, this.sessionInfo);
47 47 }
48 48
49 49 @Override
... ...
... ... @@ -30,15 +30,26 @@ package org.thingsboard.server.transport.lwm2m.server;
30 30 * limitations under the License.
31 31 */
32 32
  33 +import com.google.gson.JsonElement;
  34 +import com.google.gson.JsonObject;
  35 +import lombok.Getter;
33 36 import lombok.extern.slf4j.Slf4j;
34 37 import org.springframework.beans.factory.annotation.Autowired;
35 38 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
36 39 import org.springframework.stereotype.Component;
37 40 import org.thingsboard.server.common.transport.TransportContext;
  41 +import org.thingsboard.server.common.transport.TransportService;
  42 +import org.thingsboard.server.common.transport.TransportServiceCallback;
  43 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
38 44 import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer;
  45 +import org.thingsboard.server.gen.transport.TransportProtos;
  46 +import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
  47 +import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
39 48
40 49 import javax.annotation.PostConstruct;
41 50
  51 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY;
  52 +
42 53 @Slf4j
43 54 @Component
44 55 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
... ... @@ -47,14 +58,87 @@ public class LwM2MTransportContextServer extends TransportContext {
47 58 private LwM2MTransportConfigServer ctxServer;
48 59
49 60 @Autowired
50   - LwM2MTransportConfigServer lwM2MTransportConfigServer;
  61 + protected LwM2MTransportConfigServer lwM2MTransportConfigServer;
  62 +
  63 + @Autowired
  64 + private TransportService transportService;
  65 +
  66 + @Getter
  67 + @Autowired
  68 + private LwM2MJsonAdaptor adaptor;
  69 +
  70 + @Autowired
  71 + LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
51 72
52 73 @PostConstruct
53 74 public void init() {
54 75 this.ctxServer = lwM2MTransportConfigServer;
55 76 }
56 77
57   - public LwM2MTransportConfigServer getCtxServer () {
  78 + public LwM2MTransportConfigServer getCtxServer() {
58 79 return this.ctxServer;
59 80 }
  81 +
  82 + /**
  83 + * Sent to Thingsboard Attribute || Telemetry
  84 + *
  85 + * @param msg - JsonObject: [{name: value}]
  86 + * @return - dummy
  87 + */
  88 + private <T> TransportServiceCallback<Void> getPubAckCallbackSentAttrTelemetry(final T msg) {
  89 + return new TransportServiceCallback<Void>() {
  90 + @Override
  91 + public void onSuccess(Void dummy) {
  92 + log.trace("Success to publish msg: {}, dummy: {}", msg, dummy);
  93 + }
  94 +
  95 + @Override
  96 + public void onError(Throwable e) {
  97 + log.trace("[{}] Failed to publish msg: {}", msg, e);
  98 + }
  99 + };
  100 + }
  101 +
  102 + public void sentParametersOnThingsboard(JsonElement msg, String topicName, TransportProtos.SessionInfoProto sessionInfo) {
  103 + try {
  104 + if (topicName.equals(LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC)) {
  105 + TransportProtos.PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(msg);
  106 + TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(postAttributeMsg);
  107 + transportService.process(sessionInfo, postAttributeMsg, this.getPubAckCallbackSentAttrTelemetry(call));
  108 + } else if (topicName.equals(LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC)) {
  109 + TransportProtos.PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(msg);
  110 + TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(postTelemetryMsg);
  111 + transportService.process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSentAttrTelemetry(call));
  112 + }
  113 + } catch (AdaptorException e) {
  114 + log.error("[{}] Failed to process publish msg [{}]", topicName, e);
  115 + log.info("[{}] Closing current session due to invalid publish", topicName);
  116 + }
  117 + }
  118 +
  119 + public JsonObject getTelemetryMsgObject(String logMsg) {
  120 + JsonObject telemetries = new JsonObject();
  121 + telemetries.addProperty(LOG_LW2M_TELEMETRY, logMsg);
  122 + return telemetries;
  123 + }
  124 +
  125 + /**
  126 + * @return - sessionInfo after access connect client
  127 + */
  128 + public TransportProtos.SessionInfoProto getValidateSessionInfo(TransportProtos.ValidateDeviceCredentialsResponseMsg msg, long mostSignificantBits, long leastSignificantBits) {
  129 + return TransportProtos.SessionInfoProto.newBuilder()
  130 + .setNodeId(this.getNodeId())
  131 + .setSessionIdMSB(mostSignificantBits)
  132 + .setSessionIdLSB(leastSignificantBits)
  133 + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
  134 + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
  135 + .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
  136 + .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
  137 + .setDeviceName(msg.getDeviceInfo().getDeviceName())
  138 + .setDeviceType(msg.getDeviceInfo().getDeviceType())
  139 + .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
  140 + .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
  141 + .build();
  142 + }
  143 +
60 144 }
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
18 18 import com.fasterxml.jackson.databind.ObjectMapper;
19   -import com.google.gson.Gson;
20 19 import com.google.gson.JsonObject;
21 20 import com.google.gson.JsonParser;
22 21 import com.google.gson.JsonSyntaxException;
... ... @@ -24,57 +23,60 @@ import lombok.extern.slf4j.Slf4j;
24 23 import org.apache.commons.lang3.StringUtils;
25 24 import org.eclipse.californium.core.network.config.NetworkConfig;
26 25 import org.eclipse.leshan.core.model.ResourceModel;
  26 +import org.eclipse.leshan.core.node.LwM2mMultipleResource;
27 27 import org.eclipse.leshan.core.node.LwM2mNode;
28 28 import org.eclipse.leshan.core.node.LwM2mObject;
29 29 import org.eclipse.leshan.core.node.LwM2mObjectInstance;
  30 +import org.eclipse.leshan.core.node.LwM2mPath;
30 31 import org.eclipse.leshan.core.node.LwM2mSingleResource;
31   -import org.eclipse.leshan.core.node.LwM2mMultipleResource;
  32 +import org.eclipse.leshan.core.node.codec.CodecException;
32 33 import org.eclipse.leshan.core.util.Hex;
33   -import org.eclipse.leshan.server.californium.LeshanServer;
34 34 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
35 35 import org.nustaq.serialization.FSTConfiguration;
36   -import org.springframework.beans.factory.annotation.Autowired;
37   -import org.springframework.beans.factory.annotation.Qualifier;
38   -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
39   -import org.springframework.stereotype.Component;
40 36 import org.thingsboard.server.common.data.DeviceProfile;
41 37 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
  38 +import org.thingsboard.server.common.transport.TransportServiceCallback;
42 39 import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
  40 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
43 41
44   -import javax.annotation.PostConstruct;
45 42 import java.io.File;
46 43 import java.io.IOException;
47   -import java.text.DateFormat;
48   -import java.text.SimpleDateFormat;
49   -import java.util.Date;
50   -import java.util.Optional;
51   -import java.util.Map;
52 44 import java.util.Arrays;
53   -import java.util.List;
54   -import java.util.ArrayList;
  45 +import java.util.Date;
55 46 import java.util.LinkedList;
56   -import java.util.Collections;
  47 +import java.util.Optional;
57 48
58 49 @Slf4j
59   -@Component("LwM2MTransportHandler")
60   -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
61   -public class LwM2MTransportHandler{
  50 +//@Component("LwM2MTransportHandler")
  51 +//@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
  52 +public class LwM2MTransportHandler {
62 53
63 54 // We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to
64 55 // send a Confirmable message to the time when an acknowledgement is no longer expected.
65   - public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000l; // 2min in ms
66   - public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr";
67   - public static final String KEYNAME = "keyName";
  56 +
  57 + public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";
68 58 public static final String ATTRIBUTE = "attribute";
69 59 public static final String TELEMETRY = "telemetry";
  60 + private static final String REQUEST = "/request";
  61 + private static final String RESPONSE = "/response";
  62 + private static final String ATTRIBUTES = "/" + ATTRIBUTE;
  63 + public static final String TELEMETRIES = "/" + TELEMETRY;
  64 + public static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE;
  65 + public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
  66 + public static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/";
  67 + public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/";
  68 + public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES;
  69 + public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES;
  70 +
  71 + public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
  72 + public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr";
  73 + public static final String KEYNAME = "keyName";
70 74 public static final String OBSERVE = "observe";
71 75 public static final String BOOTSTRAP = "bootstrap";
72 76 public static final String SERVERS = "servers";
73 77 public static final String LWM2M_SERVER = "lwm2mServer";
74 78 public static final String BOOTSTRAP_SERVER = "bootstrapServer";
75   - public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";
76   - public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + "/attributes";
77   - public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + "/telemetry";
  79 +
78 80 public static final String LOG_LW2M_TELEMETRY = "logLwm2m";
79 81 public static final String LOG_LW2M_INFO = "info";
80 82 public static final String LOG_LW2M_ERROR = "error";
... ... @@ -91,44 +93,55 @@ public class LwM2MTransportHandler{
91 93 /**
92 94 * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see
93 95 * section 5.3.3 of the LW M2M spec).
  96 + * if all resources are to be replaced
94 97 */
95 98 public static final String POST_TYPE_OPER_WRITE_REPLACE = "replace";
96 99 /**
97 100 * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section
98 101 * 5.3.3 of the LW M2M spec).
  102 + * if this is a partial update request
99 103 */
100 104 public static final String PUT_TYPE_OPER_WRITE_UPDATE = "update";
101 105 public static final String PUT_TYPE_OPER_WRITE_ATTRIBUTES = "wright-attributes";
102 106
103 107 public static final String EVENT_AWAKE = "AWAKE";
  108 + public static final String SERVICE_CHANNEL = "SERVICE";
  109 + public static final String RESPONSE_CHANNEL = "RESP";
104 110
105   - private static Gson gson = null;
  111 +// @Autowired
  112 +// @Qualifier("LeshanServerCert")
  113 +// private LeshanServer lhServerCert;
  114 +//
  115 +// @Autowired
  116 +// @Qualifier("LeshanServerNoSecPskRpk")
  117 +// private LeshanServer lhServerNoSecPskRpk;
106 118
107   - @Autowired
108   - @Qualifier("LeshanServerCert")
109   - private LeshanServer lhServerCert;
  119 +// @Autowired
  120 +// @Qualifier("ServerListenerCert")
  121 +// private LwM2mServerListener serverListenerCert;
  122 +//
  123 +// @Autowired
  124 +// @Qualifier("ServerListenerNoSecPskRpk")
  125 +// private LwM2mServerListener serverListenerNoSecPskRpk;
110 126
111   - @Autowired
112   - @Qualifier("leshanServerNoSecPskRpk")
113   - private LeshanServer lhServerNoSecPskRpk;
114 127
115   - @Autowired
116   - private LwM2MTransportService service;
  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 +// }
117 143
118   -
119   - @PostConstruct
120   - public void init() {
121   - LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service);
122   - this.lhServerCert.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
123   - this.lhServerCert.getPresenceService().addListener(lwM2mServerListener.presenceListener);
124   - this.lhServerCert.getObservationService().addListener(lwM2mServerListener.observationListener);
125   - lwM2mServerListener = new LwM2mServerListener(lhServerNoSecPskRpk, service);
126   - this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
127   - this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener);
128   - this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener);
129   - }
130   -
131   - public static NetworkConfig getCoapConfig() {
  144 + public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {
132 145 NetworkConfig coapConfig;
133 146 File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME);
134 147 if (configFile.isFile()) {
... ... @@ -138,28 +151,26 @@ public class LwM2MTransportHandler{
138 151 coapConfig = LeshanServerBuilder.createDefaultNetworkConfig();
139 152 coapConfig.store(configFile);
140 153 }
  154 + coapConfig.setString("COAP_PORT", Integer.toString(serverPortNoSec));
  155 + coapConfig.setString("COAP_SECURE_PORT", Integer.toString(serverSecurePort));
141 156 return coapConfig;
142 157 }
143 158
144   - public static String getValueTypeToString (Object value, ResourceModel.Type type) {
  159 + public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException {
145 160 switch (type) {
146   - case STRING: // String
147   - case OBJLNK: // ObjectLink
148   - return value.toString();
149   - case INTEGER: // Long
150   - return Long.toString((long) value);
151   - case BOOLEAN: // Boolean
152   - return Boolean.toString((Boolean) value);
153   - case FLOAT: // Double
154   - return Double.toString((Float)value);
155   - case TIME: // Date
156   - String DATE_FORMAT = "MMM d, yyyy HH:mm a";
157   - DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
158   - return formatter.format(new Date((Long) Integer.toUnsignedLong(Integer.valueOf((Integer) value))));
159   - case OPAQUE: // byte[] value, base64
160   - return Hex.encodeHexString((byte[])value);
  161 + case BOOLEAN:
  162 + case INTEGER:
  163 + case FLOAT:
  164 + return String.valueOf(valueOld).equals(String.valueOf(valueNew));
  165 + case TIME:
  166 + return ((Date) valueOld).getTime() == ((Date) valueNew).getTime();
  167 + case STRING:
  168 + case OBJLNK:
  169 + return valueOld.equals(valueNew);
  170 + case OPAQUE:
  171 + return Hex.decodeHex(((String) valueOld).toCharArray()).equals(Hex.decodeHex(((String) valueNew).toCharArray()));
161 172 default:
162   - return null;
  173 + throw new CodecException("Invalid value type for resource %s, type %s", resourcePath, type);
163 174 }
164 175 }
165 176
... ... @@ -182,26 +193,25 @@ public class LwM2MTransportHandler{
182 193 attrTelemetryObserveValue.setPostAttributeProfile(profilesConfigData.get(ATTRIBUTE).getAsJsonArray());
183 194 attrTelemetryObserveValue.setPostTelemetryProfile(profilesConfigData.get(TELEMETRY).getAsJsonArray());
184 195 attrTelemetryObserveValue.setPostObserveProfile(profilesConfigData.get(OBSERVE).getAsJsonArray());
185   - return attrTelemetryObserveValue;
  196 + return attrTelemetryObserveValue;
186 197 }
187 198
188 199 /**
189   -
190 200 * @return deviceProfileBody with Observe&Attribute&Telemetry From Thingsboard
191 201 * Example: with pathResource (use only pathResource)
192 202 * property: "observeAttr"
193 203 * {"keyName": {
194   - * "/3/0/1": "modelNumber",
195   - * "/3/0/0": "manufacturer",
196   - * "/3/0/2": "serialNumber"
197   - * },
  204 + * "/3/0/1": "modelNumber",
  205 + * "/3/0/0": "manufacturer",
  206 + * "/3/0/2": "serialNumber"
  207 + * },
198 208 * "attribute":["/2/0/1","/3/0/9"],
199 209 * "telemetry":["/1/0/1","/2/0/1","/6/0/1"],
200 210 * "observe":["/2/0","/2/0/0","/4/0/2"]}
201 211 */
202 212 public static JsonObject getObserveAttrTelemetryFromThingsboard(DeviceProfile deviceProfile) {
203 213 if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) {
204   - Lwm2mDeviceProfileTransportConfiguration lwm2mDeviceProfileTransportConfiguration = (Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
  214 +// Lwm2mDeviceProfileTransportConfiguration lwm2mDeviceProfileTransportConfiguration = (Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
205 215 Object observeAttr = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties();
206 216 try {
207 217 ObjectMapper mapper = new ObjectMapper();
... ... @@ -209,7 +219,7 @@ public class LwM2MTransportHandler{
209 219 JsonObject objectMsg = (observeAttrStr != null) ? validateJson(observeAttrStr) : null;
210 220 return (getValidateCredentialsBodyFromThingsboard(objectMsg)) ? objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject() : null;
211 221 } catch (IOException e) {
212   - e.printStackTrace();
  222 + log.error("", e);
213 223 }
214 224 }
215 225 return null;
... ... @@ -217,7 +227,6 @@ public class LwM2MTransportHandler{
217 227
218 228 public static JsonObject getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) {
219 229 if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) {
220   - Lwm2mDeviceProfileTransportConfiguration lwm2mDeviceProfileTransportConfiguration = (Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
221 230 Object bootstrap = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties();
222 231 try {
223 232 ObjectMapper mapper = new ObjectMapper();
... ... @@ -225,7 +234,7 @@ public class LwM2MTransportHandler{
225 234 JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null;
226 235 return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null;
227 236 } catch (IOException e) {
228   - e.printStackTrace();
  237 + log.error("", e);
229 238 }
230 239 }
231 240 return null;
... ... @@ -276,7 +285,7 @@ public class LwM2MTransportHandler{
276 285 jsonValidFlesh = jsonValidFlesh.replaceAll("\n", "");
277 286 jsonValidFlesh = jsonValidFlesh.replaceAll("\t", "");
278 287 jsonValidFlesh = jsonValidFlesh.replaceAll(" ", "");
279   - String jsonValid = (jsonValidFlesh.substring(0, 1).equals("\"") && jsonValidFlesh.substring(jsonValidFlesh.length() - 1).equals("\"")) ? jsonValidFlesh.substring(1, jsonValidFlesh.length() - 1) : jsonValidFlesh;
  288 + String jsonValid = (jsonValidFlesh.charAt(0) == '"' && jsonValidFlesh.charAt(jsonValidFlesh.length() - 1) == '"') ? jsonValidFlesh.substring(1, jsonValidFlesh.length() - 1) : jsonValidFlesh;
280 289 try {
281 290 object = new JsonParser().parse(jsonValid).getAsJsonObject();
282 291 } catch (JsonSyntaxException e) {
... ... @@ -288,7 +297,7 @@ public class LwM2MTransportHandler{
288 297
289 298 public static <T> Optional<T> decode(byte[] byteArray) {
290 299 try {
291   - FSTConfiguration config = FSTConfiguration.createDefaultConfiguration();;
  300 + FSTConfiguration config = FSTConfiguration.createDefaultConfiguration();
292 301 T msg = (T) config.asObject(byteArray);
293 302 return Optional.ofNullable(msg);
294 303 } catch (IllegalArgumentException e) {
... ... @@ -297,37 +306,29 @@ public class LwM2MTransportHandler{
297 306 }
298 307 }
299 308
300   - /**
301   - * Equals to Map for values
302   - * @param map1
303   - * @param map2
304   - * @param <V>
305   - * @return
306   - */
307   - public static <V extends Comparable<V>> boolean mapsEquals(Map<?,V> map1, Map<?,V> map2) {
308   - List<V> values1 = new ArrayList<V>(map1.values());
309   - List<V> values2 = new ArrayList<V>(map2.values());
310   - Collections.sort(values1);
311   - Collections.sort(values2);
312   - return values1.equals(values2);
313   - }
314   -
315   - public static String convertCamelCase (String str) {
316   - str = str.toLowerCase().replace("/[^a-z ]+/g", " ");
317   - str = str.replace("/^(.)|\\s(.)/g", "$1");
318   - str = str.replace("/[^a-zA-Z]+/g", "");
319   - return str;
320   - }
321   -
322   - public static String splitCamelCaseString(String s){
323   - LinkedList<String> linkedListOut = new LinkedList<String>();
  309 + public static String splitCamelCaseString(String s) {
  310 + LinkedList<String> linkedListOut = new LinkedList<>();
324 311 LinkedList<String> linkedList = new LinkedList<String>((Arrays.asList(s.split(" "))));
325   - linkedList.stream().forEach(str-> {
  312 + linkedList.forEach(str -> {
326 313 String strOut = str.replaceAll("\\W", "").replaceAll("_", "").toUpperCase();
327   - if (strOut.length()>1) linkedListOut.add(strOut.substring(0, 1) + strOut.substring(1).toLowerCase());
  314 + if (strOut.length() > 1) linkedListOut.add(strOut.charAt(0) + strOut.substring(1).toLowerCase());
328 315 else linkedListOut.add(strOut);
329 316 });
330 317 linkedListOut.set(0, (linkedListOut.get(0).substring(0, 1).toLowerCase() + linkedListOut.get(0).substring(1)));
331 318 return StringUtils.join(linkedListOut, "");
332 319 }
  320 +
  321 + public static <T> TransportServiceCallback<Void> getAckCallback(LwM2MClient lwM2MClient, int requestId, String typeTopic) {
  322 + return new TransportServiceCallback<Void>() {
  323 + @Override
  324 + public void onSuccess(Void dummy) {
  325 + log.trace("[{}] [{}] - requestId [{}] - EndPoint , Access AckCallback", typeTopic, requestId, lwM2MClient.getEndPoint());
  326 + }
  327 +
  328 + @Override
  329 + public void onError(Throwable e) {
  330 + log.trace("[{}] Failed to publish msg", e.toString());
  331 + }
  332 + };
  333 + }
333 334 }
... ...
... ... @@ -20,26 +20,28 @@ import org.eclipse.californium.core.coap.Response;
20 20 import org.eclipse.leshan.core.attributes.Attribute;
21 21 import org.eclipse.leshan.core.attributes.AttributeSet;
22 22 import org.eclipse.leshan.core.model.ResourceModel;
  23 +import org.eclipse.leshan.core.node.LwM2mNode;
  24 +import org.eclipse.leshan.core.node.LwM2mPath;
23 25 import org.eclipse.leshan.core.node.LwM2mSingleResource;
24 26 import org.eclipse.leshan.core.node.ObjectLink;
25 27 import org.eclipse.leshan.core.observation.Observation;
  28 +import org.eclipse.leshan.core.request.CancelObservationRequest;
26 29 import org.eclipse.leshan.core.request.ContentFormat;
27   -import org.eclipse.leshan.core.request.WriteRequest;
28 30 import org.eclipse.leshan.core.request.DiscoverRequest;
29 31 import org.eclipse.leshan.core.request.DownlinkRequest;
  32 +import org.eclipse.leshan.core.request.ExecuteRequest;
30 33 import org.eclipse.leshan.core.request.ObserveRequest;
31   -import org.eclipse.leshan.core.request.CancelObservationRequest;
32 34 import org.eclipse.leshan.core.request.ReadRequest;
33   -import org.eclipse.leshan.core.request.ExecuteRequest;
34 35 import org.eclipse.leshan.core.request.WriteAttributesRequest;
35   -import org.eclipse.leshan.core.response.ResponseCallback;
36   -import org.eclipse.leshan.core.response.LwM2mResponse;
37   -import org.eclipse.leshan.core.response.ObserveResponse;
38   -import org.eclipse.leshan.core.response.ReadResponse;
  36 +import org.eclipse.leshan.core.request.WriteRequest;
39 37 import org.eclipse.leshan.core.response.CancelObservationResponse;
40 38 import org.eclipse.leshan.core.response.DeleteResponse;
41   -import org.eclipse.leshan.core.response.ExecuteResponse;
42 39 import org.eclipse.leshan.core.response.DiscoverResponse;
  40 +import org.eclipse.leshan.core.response.ExecuteResponse;
  41 +import org.eclipse.leshan.core.response.LwM2mResponse;
  42 +import org.eclipse.leshan.core.response.ObserveResponse;
  43 +import org.eclipse.leshan.core.response.ReadResponse;
  44 +import org.eclipse.leshan.core.response.ResponseCallback;
43 45 import org.eclipse.leshan.core.response.WriteAttributesResponse;
44 46 import org.eclipse.leshan.core.response.WriteResponse;
45 47 import org.eclipse.leshan.core.util.Hex;
... ... @@ -50,6 +52,7 @@ import org.springframework.beans.factory.annotation.Autowired;
50 52 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
51 53 import org.springframework.stereotype.Service;
52 54 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
  55 +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
53 56
54 57 import javax.annotation.PostConstruct;
55 58 import java.util.ArrayList;
... ... @@ -65,32 +68,36 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
65 68 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_DISCOVER;
66 69 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE;
67 70 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ;
68   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_ATTRIBUTES;
69   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_UPDATE;
  71 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR;
  72 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO;
70 73 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE;
71 74 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_OBSERVE_CANCEL;
72 75 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE;
73   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR;
74   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO;
  76 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_ATTRIBUTES;
  77 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.PUT_TYPE_OPER_WRITE_UPDATE;
  78 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.RESPONSE_CHANNEL;
75 79
76 80 @Slf4j
77 81 @Service("LwM2MTransportRequest")
78 82 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
79 83 public class LwM2MTransportRequest {
80   - private final ExecutorService executorService;
81   - private static final String RESPONSE_CHANNEL = "THINGSBOARD_RESP";
  84 + private ExecutorService executorResponse;
  85 + private ExecutorService executorResponseError;
  86 + private LwM2mValueConverterImpl converter;
82 87
83 88 @Autowired
84   - LwM2MTransportService service;
85   -
86   - public LwM2MTransportRequest() {
87   - executorService = Executors.newCachedThreadPool(
88   - new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL)));
89   - }
  89 + LwM2MTransportServiceImpl service;
90 90
  91 + @Autowired
  92 + public LwM2MTransportContextServer context;
91 93
92 94 @PostConstruct
93 95 public void init() {
  96 + this.converter = LwM2mValueConverterImpl.getInstance();
  97 + executorResponse = Executors.newFixedThreadPool(this.context.getCtxServer().getRequestPoolSize(),
  98 + new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL)));
  99 + executorResponseError = Executors.newFixedThreadPool(this.context.getCtxServer().getRequestErrorPoolSize(),
  100 + new NamedThreadFactory(String.format("LwM2M %s channel response Error", RESPONSE_CHANNEL)));
94 101 }
95 102
96 103 public Collection<Registration> doGetRegistrations(LeshanServer lwServer) {
... ... @@ -113,16 +120,13 @@ public class LwM2MTransportRequest {
113 120 * @param lwM2MClient
114 121 * @param observation
115 122 */
116   - public void sendAllRequest(LeshanServer lwServer, Registration registration, String target, String typeOper,
117   - String contentFormatParam, LwM2MClient lwM2MClient, Observation observation, Object params, long timeoutInMs) {
118   - ResultIds resultIds = new ResultIds(target);
  123 + public void sendAllRequest(LeshanServer lwServer, Registration registration, String target, String typeOper, String contentFormatParam,
  124 + LwM2MClient lwM2MClient, Observation observation, Object params, long timeoutInMs, boolean isDelayedUpdate) {
  125 + LwM2mPath resultIds = new LwM2mPath(target);
119 126 if (registration != null && resultIds.getObjectId() >= 0) {
120 127 DownlinkRequest request = null;
121 128 ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null;
122   - ResourceModel resource = (resultIds.resourceId >= 0) ? (lwM2MClient != null) ?
123   - lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.resourceId) : null : null;
124   - ResourceModel.Type resType = (resource == null) ? null : resource.type;
125   - boolean resMultiple = (resource == null) ? false : resource.multiple;
  129 + ResourceModel resource = service.context.getCtxServer().getResourceModel(registration, resultIds);
126 130 timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
127 131 switch (typeOper) {
128 132 case GET_TYPE_OPER_READ:
... ... @@ -132,10 +136,10 @@ public class LwM2MTransportRequest {
132 136 request = new DiscoverRequest(target);
133 137 break;
134 138 case GET_TYPE_OPER_OBSERVE:
135   - if (resultIds.getResourceId() >= 0) {
136   - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId());
137   - } else if (resultIds.getInstanceId() >= 0) {
138   - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getInstanceId());
  139 + if (resultIds.isResource()) {
  140 + request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
  141 + } else if (resultIds.isObjectInstance()) {
  142 + request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId());
139 143 } else if (resultIds.getObjectId() >= 0) {
140 144 request = new ObserveRequest(resultIds.getObjectId());
141 145 }
... ... @@ -144,27 +148,30 @@ public class LwM2MTransportRequest {
144 148 request = new CancelObservationRequest(observation);
145 149 break;
146 150 case POST_TYPE_OPER_EXECUTE:
147   - if (params != null && !resMultiple) {
148   - request = new ExecuteRequest(target, LwM2MTransportHandler.getValueTypeToString(params, resType));
  151 + if (params != null && resource != null && !resource.multiple) {
  152 + request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resource.type, ResourceModel.Type.STRING, resultIds));
149 153 } else {
150 154 request = new ExecuteRequest(target);
151 155 }
152 156 break;
153 157 case POST_TYPE_OPER_WRITE_REPLACE:
154 158 // Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
155   - if (contentFormat.equals(ContentFormat.TLV) && !resMultiple) {
156   - request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType);
157   - }
158   - // Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON)
159   - else if (!contentFormat.equals(ContentFormat.TLV) && !resMultiple) {
160   - request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType);
  159 + if (resource != null) {
  160 + if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {
  161 + request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resource.type, registration);
  162 + }
  163 + // Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON)
  164 + else if (!contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {
  165 + request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resource.type, registration);
  166 + }
161 167 }
162 168 break;
163 169 case PUT_TYPE_OPER_WRITE_UPDATE:
164 170 if (resultIds.getResourceId() >= 0) {
165 171 ResourceModel resourceModel = lwServer.getModelProvider().getObjectModel(registration).getObjectModel(resultIds.getObjectId()).resources.get(resultIds.getResourceId());
166 172 ResourceModel.Type typeRes = resourceModel.type;
167   -// request = getWriteRequestResource(resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, typeRes);
  173 + LwM2mNode node = LwM2mSingleResource.newStringResource(resultIds.getResourceId(), (String) this.converter.convertValue(params, resource.type, ResourceModel.Type.STRING, resultIds));
  174 + request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, target, node);
168 175 }
169 176 break;
170 177 case PUT_TYPE_OPER_WRITE_ATTRIBUTES:
... ... @@ -202,55 +209,76 @@ public class LwM2MTransportRequest {
202 209 Attribute pmin = new Attribute(MINIMUM_PERIOD, Integer.toUnsignedLong(Integer.valueOf("1")));
203 210 Attribute[] attrs = {pmin};
204 211 AttributeSet attrSet = new AttributeSet(attrs);
205   - if (resultIds.getResourceId() >= 0) {
206   - request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), attrSet);
207   - } else if (resultIds.getInstanceId() >= 0) {
208   - request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getInstanceId(), attrSet);
  212 + if (resultIds.isResource()) {
  213 + request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), attrSet);
  214 + } else if (resultIds.isObjectInstance()) {
  215 + request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), attrSet);
209 216 } else if (resultIds.getObjectId() >= 0) {
210 217 request = new WriteAttributesRequest(resultIds.getObjectId(), attrSet);
211 218 }
212 219 break;
213 220 default:
214 221 }
215   - if (request != null) sendRequest(lwServer, registration, request, lwM2MClient, timeoutInMs);
  222 +
  223 + if (request != null) {
  224 + this.sendRequest(lwServer, registration, request, lwM2MClient, timeoutInMs, isDelayedUpdate);
  225 + } else if (isDelayedUpdate) {
  226 + String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg No SendRequest to Client", target);
  227 + service.sentLogsToThingsboard(msg, registration);
  228 + log.error("[{}] - [{}] No SendRequest", target);
  229 + this.handleResponseError(registration, target, lwM2MClient, isDelayedUpdate);
  230 +
  231 + }
216 232 }
217 233 }
218 234
219 235 /**
220 236 *
221   - * @param lwServer
222   - * @param registration
223   - * @param request
224   - * @param lwM2MClient
225   - * @param timeoutInMs
  237 + * @param lwServer -
  238 + * @param registration -
  239 + * @param request -
  240 + * @param lwM2MClient -
  241 + * @param timeoutInMs -
226 242 */
227   - private void sendRequest(LeshanServer lwServer, Registration registration, DownlinkRequest request, LwM2MClient lwM2MClient, long timeoutInMs) {
  243 +
  244 + private void sendRequest(LeshanServer lwServer, Registration registration, DownlinkRequest request, LwM2MClient lwM2MClient, long timeoutInMs, boolean isDelayedUpdate) {
228 245 lwServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
229   - if (isSuccess(((Response)response.getCoapResponse()).getCode())) {
230   - this.handleResponse(registration, request.getPath().toString(), response, request, lwM2MClient);
  246 + if (isSuccess(((Response) response.getCoapResponse()).getCode())) {
  247 + this.handleResponse(registration, request.getPath().toString(), response, request, lwM2MClient, isDelayedUpdate);
231 248 if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) {
232   - String msg = String.format(LOG_LW2M_INFO + " sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",
233   - ((Response)response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(),
234   - ((LwM2mSingleResource)((WriteRequest) request).getNode()).getValue().toString());
235   - service.sentLogsToThingsboard(msg, registration.getId());
236   - log.info("[{}] - [{}] [{}] [{}] Update SendRequest", ((Response)response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), ((LwM2mSingleResource)((WriteRequest) request).getNode()).getValue());
  249 + String delayedUpdateStr = "";
  250 + if (isDelayedUpdate) {
  251 + delayedUpdateStr = " (delayedUpdate) ";
  252 + }
  253 + String msg = String.format(LOG_LW2M_INFO + ": sendRequest Replace%s: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",
  254 + delayedUpdateStr, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(),
  255 + ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue().toString());
  256 + service.sentLogsToThingsboard(msg, registration);
  257 + log.info("[{}] - [{}] [{}] [{}] Update SendRequest[{}]", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(),
  258 + ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue(), delayedUpdateStr);
  259 + }
  260 + } else {
  261 + String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client",
  262 + ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
  263 + service.sentLogsToThingsboard(msg, registration);
  264 + log.error("[{}] - [{}] [{}] error SendRequest", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
  265 + if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) {
  266 + this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate);
237 267 }
238 268 }
239   - else {
240   - String msg = String.format(LOG_LW2M_ERROR + " sendRequest: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client",
241   - ((Response)response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
242   - service.sentLogsToThingsboard(msg, registration.getId());
243   - log.error("[{}] - [{}] [{}] error SendRequest", ((Response)response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
244   - }
  269 +
245 270 }, e -> {
246   - String msg = String.format(LOG_LW2M_ERROR + " sendRequest: Resource path - %s msg error - %s SendRequest to Client",
  271 + String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg error - %s SendRequest to Client",
247 272 request.getPath().toString(), e.toString());
248   - service.sentLogsToThingsboard(msg, registration.getId());
249   - log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString());
  273 + service.sentLogsToThingsboard(msg, registration);
  274 + log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString());
  275 + if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) {
  276 + this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate);
  277 + }
250 278 });
251 279 }
252 280
253   - private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, Integer resourceId, Object value, ResourceModel.Type type) {
  281 + private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, Integer resourceId, Object value, ResourceModel.Type type, Registration registration) {
254 282 try {
255 283 switch (type) {
256 284 case STRING: // String
... ... @@ -264,29 +292,38 @@ public class LwM2MTransportRequest {
264 292 case FLOAT: // Double
265 293 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Double.valueOf(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.valueOf(value.toString()));
266 294 case TIME: // Date
267   - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, new Date((Long) Integer.toUnsignedLong(Integer.valueOf(value.toString())))) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, new Date((Long) Integer.toUnsignedLong(Integer.valueOf(value.toString()))));
  295 + return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, new Date(Long.decode(value.toString()))) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, new Date((Long) Integer.toUnsignedLong(Integer.valueOf(value.toString()))));
268 296 case OPAQUE: // byte[] value, base64
269 297 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray()));
270 298 default:
271 299 }
272 300 return null;
273 301 } catch (NumberFormatException e) {
274   - String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
275   - log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
276   - return null;
  302 + String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
  303 + String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
  304 + patn, type, value, e.toString());
  305 + service.sentLogsToThingsboard(msg, registration);
  306 + log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
  307 + return null;
277 308 }
278 309 }
279 310
280   - private void handleResponse(Registration registration, final String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient) {
281   - executorService.submit(new Runnable() {
282   - @Override
283   - public void run() {
  311 + private void handleResponse(Registration registration, final String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient, boolean isDelayedUpdate) {
  312 + executorResponse.submit(() -> {
  313 + try {
  314 + sendResponse(registration, path, response, request, lwM2MClient, isDelayedUpdate);
  315 + } catch (Exception e) {
  316 + log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", registration.getEndpoint(), path, e);
  317 + }
  318 + });
  319 + }
284 320
285   - try {
286   - sendResponse(registration, path, response, request, lwM2MClient);
287   - } catch (RuntimeException t) {
288   - log.error("[{}] endpoint [{}] path [{}] error Unable to after send response.", registration.getEndpoint(), path, t.toString());
289   - }
  321 + private void handleResponseError(Registration registration, final String path, LwM2MClient lwM2MClient, boolean isDelayedUpdate) {
  322 + executorResponseError.submit(() -> {
  323 + try {
  324 + if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(path);
  325 + } catch (RuntimeException t) {
  326 + log.error("[{}] endpoint [{}] path [{}] RuntimeException Unable to after send response.", registration.getEndpoint(), path, t);
290 327 }
291 328 });
292 329 }
... ... @@ -298,7 +335,7 @@ public class LwM2MTransportRequest {
298 335 * @param response -
299 336 * @param lwM2MClient -
300 337 */
301   - private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient) {
  338 + private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient, boolean isDelayedUpdate) {
302 339 if (response instanceof ObserveResponse) {
303 340 service.onObservationResponse(registration, path, (ReadResponse) response);
304 341 } else if (response instanceof CancelObservationResponse) {
... ... @@ -329,7 +366,7 @@ public class LwM2MTransportRequest {
329 366 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", path, response);
330 367 } else if (response instanceof WriteResponse) {
331 368 log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", path, response);
332   - service.onAttributeUpdateOk(registration, path, (WriteRequest) request);
  369 + service.onAttributeUpdateOk(registration, path, (WriteRequest) request, isDelayedUpdate);
333 370 }
334 371 }
335 372 }
... ...
... ... @@ -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,36 @@ 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.CertificateEncodingException;
  56 +import java.security.cert.X509Certificate;
  57 +import java.security.interfaces.ECPublicKey;
  58 +import java.security.spec.ECGenParameterSpec;
  59 +import java.security.spec.ECParameterSpec;
  60 +import java.security.spec.ECPoint;
  61 +import java.security.spec.ECPrivateKeySpec;
  62 +import java.security.spec.ECPublicKeySpec;
  63 +import java.security.spec.KeySpec;
  64 +import java.util.Arrays;
  65 +
  66 +import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
  67 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK;
38 68 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
  69 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
39 70 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig;
40 71
41 72
... ... @@ -45,6 +76,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
45 76 @Configuration("LwM2MTransportServerConfiguration")
46 77 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
47 78 public class LwM2MTransportServerConfiguration {
  79 + private PublicKey publicKey;
  80 + private PrivateKey privateKey;
48 81
49 82 @Autowired
50 83 private LwM2MTransportContextServer context;
... ... @@ -53,50 +86,257 @@ public class LwM2MTransportServerConfiguration {
53 86 private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
54 87
55 88 @Primary
56   - @Bean(name = "LeshanServerCert")
57   - public LeshanServer getLeshanServerCert() {
58   - log.info("Starting LwM2M transport ServerCert... PostConstruct");
59   - return getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509);
  89 + @Bean(name = "leshanServerPsk")
  90 + @ConditionalOnExpression("('${transport.lwm2m.server.secure.start_psk:false}'=='true')")
  91 + public LeshanServer getLeshanServerPsk() {
  92 + log.info("Starting LwM2M transport ServerPsk... PostConstruct");
  93 + return getLeshanServer(this.context.getCtxServer().getServerPortNoSecPsk(), this.context.getCtxServer().getServerPortPsk(), PSK);
60 94 }
61 95
62   - @Bean(name = "leshanServerNoSecPskRpk")
63   - public LeshanServer getLeshanServerNoSecPskRpk() {
64   - log.info("Starting LwM2M transport ServerNoSecPskRpk... PostConstruct");
65   - return getLeshanServer(this.context.getCtxServer().getServerPort(), this.context.getCtxServer().getServerSecurePort(), RPK);
  96 + @Bean(name = "leshanServerRpk")
  97 + @ConditionalOnExpression("('${transport.lwm2m.server.secure.start_rpk:false}'=='true')")
  98 + public LeshanServer getLeshanServerRpk() {
  99 + log.info("Starting LwM2M transport ServerRpk... PostConstruct");
  100 + return getLeshanServer(this.context.getCtxServer().getServerPortNoSecRpk(), this.context.getCtxServer().getServerPortRpk(), RPK);
66 101 }
67 102
68   - private LeshanServer getLeshanServer(Integer serverPort, Integer serverSecurePort, LwM2MSecurityMode dtlsMode) {
  103 + @Bean(name = "leshanServerX509")
  104 + @ConditionalOnExpression("('${transport.lwm2m.server.secure.start_x509:false}'=='true')")
  105 + public LeshanServer getLeshanServerX509() {
  106 + log.info("Starting LwM2M transport ServerX509... PostConstruct");
  107 + return getLeshanServer(this.context.getCtxServer().getServerPortNoSecX509(), this.context.getCtxServer().getServerPortX509(), X509);
  108 + }
69 109
  110 + private LeshanServer getLeshanServer(Integer serverPortNoSec, Integer serverSecurePort, LwM2MSecurityMode dtlsMode) {
70 111 LeshanServerBuilder builder = new LeshanServerBuilder();
71   - builder.setLocalAddress(this.context.getCtxServer().getServerHost(), serverPort);
  112 + builder.setLocalAddress(this.context.getCtxServer().getServerHost(), serverPortNoSec);
72 113 builder.setLocalSecureAddress(this.context.getCtxServer().getServerSecureHost(), serverSecurePort);
73 114 builder.setEncoder(new DefaultLwM2mNodeEncoder());
74 115 LwM2mNodeDecoder decoder = new DefaultLwM2mNodeDecoder();
75 116 builder.setDecoder(decoder);
76   - builder.setEncoder(new DefaultLwM2mNodeEncoder(new LwM2mValueConverterImpl()));
  117 + builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
77 118
78 119 /** Create CoAP Config */
79   - builder.setCoapConfig(getCoapConfig());
  120 + builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort));
80 121
81 122 /** Define model provider (Create Models )*/
82 123 LwM2mModelProvider modelProvider = new VersionedModelProvider(this.context.getCtxServer().getModelsValue());
83 124 builder.setObjectModelProvider(modelProvider);
84 125
  126 + /** Create DTLS security mode
  127 + * There can be only one DTLS security mode
  128 + */
  129 + this.LwM2MSetSecurityStoreServer(builder, dtlsMode);
  130 +
85 131 /** Create DTLS Config */
86 132 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
87   - dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getCtxServer().isSupportDeprecatedCiphersEnable());
  133 + if (dtlsMode==PSK) {
  134 + dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getCtxServer().isRecommendedCiphers());
  135 + dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getCtxServer().isRecommendedSupportedGroups());
  136 + dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256);
  137 + }
  138 + else {
  139 + dtlsConfig.setRecommendedSupportedGroupsOnly(!this.context.getCtxServer().isRecommendedSupportedGroups());
  140 + dtlsConfig.setRecommendedCipherSuitesOnly(!this.context.getCtxServer().isRecommendedCiphers());
  141 + }
88 142 /** Set DTLS Config */
89 143 builder.setDtlsConfig(dtlsConfig);
90 144
91 145 /** Use a magic converter to support bad type send by the UI. */
92   - builder.setEncoder(new DefaultLwM2mNodeEncoder(new LwM2mValueConverterImpl()));
  146 + builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
93 147
94   - /** Create DTLS security mode
95   - * There can be only one DTLS security mode
96   - */
97   - new LwM2MSetSecurityStoreServer(builder, context, lwM2mInMemorySecurityStore, dtlsMode);
98 148
99 149 /** Create LWM2M server */
100 150 return builder.build();
101 151 }
  152 +
  153 + private void LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MSecurityMode dtlsMode) {
  154 + /** Set securityStore with new registrationStore */
  155 + EditableSecurityStore securityStore = lwM2mInMemorySecurityStore;
  156 + switch (dtlsMode) {
  157 + /** Use PSK only */
  158 + case PSK:
  159 + generatePSK_RPK();
  160 + infoParamsPSK();
  161 + break;
  162 + /** Use RPK only */
  163 + case RPK:
  164 + generatePSK_RPK();
  165 + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
  166 + this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  167 + builder.setPublicKey(this.publicKey);
  168 + builder.setPrivateKey(this.privateKey);
  169 + infoParamsRPK();
  170 + }
  171 + break;
  172 + /** Use x509 only */
  173 + case X509:
  174 + setServerWithX509Cert(builder);
  175 + break;
  176 + /** No security */
  177 + case NO_SEC:
  178 + builder.setTrustedCertificates(new X509Certificate[0]);
  179 + break;
  180 + /** Use x509 with EST */
  181 + case X509_EST:
  182 + // TODO support sentinel pool and make pool configurable
  183 + break;
  184 + case REDIS:
  185 + /**
  186 + * Set securityStore with new registrationStore (if use redis store)
  187 + * Connect to redis
  188 + */
  189 + Pool<Jedis> jedis = null;
  190 + try {
  191 + jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl()));
  192 + securityStore = new RedisSecurityStore(jedis);
  193 + builder.setRegistrationStore(new RedisRegistrationStore(jedis));
  194 + } catch (URISyntaxException e) {
  195 + log.error("", e);
  196 + }
  197 + break;
  198 + default:
  199 + }
  200 +
  201 + /** Set securityStore with registrationStore (if x509)*/
  202 + if (dtlsMode == X509) {
  203 + builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
  204 + @Override
  205 + protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
  206 + String expectedX509CommonName) {
  207 + return endpoint.startsWith(expectedX509CommonName);
  208 + }
  209 + }));
  210 + }
  211 +
  212 + /** Set securityStore with new registrationStore */
  213 + builder.setSecurityStore(securityStore);
  214 + }
  215 +
  216 + private void generatePSK_RPK() {
  217 + try {
  218 + /** Get Elliptic Curve Parameter spec for secp256r1 */
  219 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
  220 + algoParameters.init(new ECGenParameterSpec("secp256r1"));
  221 + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
  222 + if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) {
  223 + /** Get point values */
  224 + byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray());
  225 + byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray());
  226 + /** Create key specs */
  227 + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
  228 + parameterSpec);
  229 + /** Get keys */
  230 + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  231 + }
  232 + if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) {
  233 + /** Get point values */
  234 + byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray());
  235 + /** Create key specs */
  236 + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
  237 + /** Get keys */
  238 + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
  239 + }
  240 + } catch (GeneralSecurityException | IllegalArgumentException e) {
  241 + log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
  242 + throw new RuntimeException(e);
  243 + }
  244 + }
  245 +
  246 + private void infoParamsPSK() {
  247 + log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]",
  248 + Hex.encodeHexString(this.privateKey.getEncoded()),
  249 + this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerPortPsk()));
  250 + }
  251 +
  252 + private void infoParamsRPK() {
  253 + if (this.publicKey instanceof ECPublicKey) {
  254 + /** Get x coordinate */
  255 + byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
  256 + if (x[0] == 0)
  257 + x = Arrays.copyOfRange(x, 1, x.length);
  258 +
  259 + /** Get Y coordinate */
  260 + byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
  261 + if (y[0] == 0)
  262 + y = Arrays.copyOfRange(y, 1, y.length);
  263 +
  264 + /** Get Curves params */
  265 + String params = ((ECPublicKey) this.publicKey).getParams().toString();
  266 + log.info(
  267 + " \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
  268 + params, Hex.encodeHexString(x), Hex.encodeHexString(y),
  269 + Hex.encodeHexString(this.publicKey.getEncoded()),
  270 + Hex.encodeHexString(this.privateKey.getEncoded()));
  271 + } else {
  272 + throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
  273 + }
  274 + }
  275 +
  276 +
  277 + private void setServerWithX509Cert(LeshanServerBuilder builder) {
  278 + try {
  279 + if (this.context.getCtxServer().getKeyStoreValue() != null) {
  280 + setBuilderX509(builder);
  281 + X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias());
  282 + if (rootCAX509Cert != null) {
  283 + X509Certificate[] trustedCertificates = new X509Certificate[1];
  284 + trustedCertificates[0] = rootCAX509Cert;
  285 + builder.setTrustedCertificates(trustedCertificates);
  286 + } else {
  287 + /** by default trust all */
  288 + builder.setTrustedCertificates(new X509Certificate[0]);
  289 + }
  290 + } else {
  291 + /** by default trust all */
  292 + builder.setTrustedCertificates(new X509Certificate[0]);
  293 + log.error("Unable to load X509 files for LWM2MServer");
  294 + }
  295 + } catch (KeyStoreException ex) {
  296 + log.error("[{}] Unable to load X509 files server", ex.getMessage());
  297 + }
  298 + }
  299 +
  300 + private void setBuilderX509(LeshanServerBuilder builder) {
  301 + /**
  302 + * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
  303 + * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
  304 + */
  305 + try {
  306 + X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias());
  307 + PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray());
  308 + builder.setPrivateKey(privateKey);
  309 + builder.setCertificateChain(new X509Certificate[]{serverCertificate});
  310 + this.infoParamsX509(serverCertificate, privateKey);
  311 + } catch (Exception ex) {
  312 + log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
  313 + }
  314 +// /**
  315 +// * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
  316 +// * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
  317 +// */
  318 +// try {
  319 +// X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerPrivateS());
  320 +// this.privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray());
  321 +// if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  322 +// builder.setPrivateKey(this.privateKey);
  323 +// }
  324 +// if (serverCertificate != null) {
  325 +// builder.setCertificateChain(new X509Certificate[]{serverCertificate});
  326 +// this.infoParamsX509(serverCertificate);
  327 +// }
  328 +// } catch (Exception ex) {
  329 +// log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
  330 +// }
  331 + }
  332 +
  333 + private void infoParamsX509(X509Certificate certificate, PrivateKey privateKey) {
  334 + try {
  335 + log.info("Server uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",
  336 + Hex.encodeHexString(certificate.getEncoded()),
  337 + Hex.encodeHexString(privateKey.getEncoded()));
  338 + } catch (CertificateEncodingException e) {
  339 + log.error("", e);
  340 + }
  341 + }
102 342 }
... ...
... ... @@ -20,24 +20,32 @@ 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   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  25 +
26 26 import javax.annotation.PostConstruct;
27 27 import javax.annotation.PreDestroy;
28 28
29 29 @Slf4j
30   -@Service("LwM2MTransportServerInitializer")
  30 +@Component("LwM2MTransportServerInitializer")
31 31 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
32 32 public class LwM2MTransportServerInitializer {
33 33
34   - @Autowired
35   - @Qualifier("LeshanServerCert")
36   - private LeshanServer lhServerCert;
37 34
38 35 @Autowired
39   - @Qualifier("leshanServerNoSecPskRpk")
40   - private LeshanServer lhServerNoSecPskRpk;
  36 + private LwM2MTransportServiceImpl service;
  37 +
  38 + @Autowired(required = false)
  39 + @Qualifier("leshanServerX509")
  40 + private LeshanServer lhServerX509;
  41 +
  42 + @Autowired(required = false)
  43 + @Qualifier("leshanServerPsk")
  44 + private LeshanServer lhServerPsk;
  45 +
  46 + @Autowired(required = false)
  47 + @Qualifier("leshanServerRpk")
  48 + private LeshanServer lhServerRpk;
41 49
42 50 @Autowired
43 51 private LwM2MTransportContextServer context;
... ... @@ -45,28 +53,47 @@ public class LwM2MTransportServerInitializer {
45 53 @PostConstruct
46 54 public void init() {
47 55 if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC();
48   - if (this.context.getCtxServer().isServerStartAll()) {
49   - this.lhServerCert.start();
50   - this.lhServerNoSecPskRpk.start();
  56 + if (this.context.getCtxServer().isServerStartPsk()) {
  57 + this.startLhServerPsk();
  58 + }
  59 + if (this.context.getCtxServer().isServerStartRpk()) {
  60 + this.startLhServerRpk();
51 61 }
52   - else {
53   - if (this.context.getCtxServer().getServerDtlsMode() == LwM2MSecurityMode.X509.code) {
54   - this.lhServerCert.start();
55   - }
56   - else {
57   - this.lhServerNoSecPskRpk.start();
58   - }
  62 + if (this.context.getCtxServer().isServerStartX509()) {
  63 + this.startLhServerX509();
59 64 }
60 65 }
61 66
  67 + private void startLhServerPsk() {
  68 + this.lhServerPsk.start();
  69 + LwM2mServerListener lhServerPskListener = new LwM2mServerListener(this.lhServerPsk, service);
  70 + this.lhServerPsk.getRegistrationService().addListener(lhServerPskListener.registrationListener);
  71 + this.lhServerPsk.getPresenceService().addListener(lhServerPskListener.presenceListener);
  72 + this.lhServerPsk.getObservationService().addListener(lhServerPskListener.observationListener);
  73 + }
  74 +
  75 + private void startLhServerRpk() {
  76 + this.lhServerRpk.start();
  77 + LwM2mServerListener lhServerRpkListener = new LwM2mServerListener(this.lhServerRpk, service);
  78 + this.lhServerRpk.getRegistrationService().addListener(lhServerRpkListener.registrationListener);
  79 + this.lhServerRpk.getPresenceService().addListener(lhServerRpkListener.presenceListener);
  80 + this.lhServerRpk.getObservationService().addListener(lhServerRpkListener.observationListener);
  81 + }
  82 +
  83 + private void startLhServerX509() {
  84 + this.lhServerX509.start();
  85 + LwM2mServerListener lhServerCertListener = new LwM2mServerListener(this.lhServerX509, service);
  86 + this.lhServerX509.getRegistrationService().addListener(lhServerCertListener.registrationListener);
  87 + this.lhServerX509.getPresenceService().addListener(lhServerCertListener.presenceListener);
  88 + this.lhServerX509.getObservationService().addListener(lhServerCertListener.observationListener);
  89 + }
  90 +
62 91 @PreDestroy
63 92 public void shutdown() {
64 93 log.info("Stopping LwM2M transport Server!");
65   - try {
66   - lhServerCert.destroy();
67   - lhServerNoSecPskRpk.destroy();
68   - } finally {
69   - }
  94 + lhServerPsk.destroy();
  95 + lhServerRpk.destroy();
  96 + lhServerX509.destroy();
70 97 log.info("LwM2M transport Server stopped!");
71 98 }
72 99 }
... ...
... ... @@ -15,985 +15,42 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
18   -import com.google.gson.*;
19   -import lombok.SneakyThrows;
20   -import lombok.extern.slf4j.Slf4j;
21   -import org.eclipse.leshan.core.model.ResourceModel;
22   -import org.eclipse.leshan.core.node.LwM2mMultipleResource;
23   -import org.eclipse.leshan.core.node.LwM2mObject;
24   -import org.eclipse.leshan.core.node.LwM2mObjectInstance;
25   -import org.eclipse.leshan.core.node.LwM2mSingleResource;
26   -import org.eclipse.leshan.core.node.LwM2mResource;
27   -import org.eclipse.leshan.core.node.LwM2mPath;
28 18 import org.eclipse.leshan.core.observation.Observation;
29   -import org.eclipse.leshan.core.request.ContentFormat;
30   -import org.eclipse.leshan.core.request.WriteRequest;
31 19 import org.eclipse.leshan.core.response.ReadResponse;
32   -import org.eclipse.leshan.core.util.Hex;
33 20 import org.eclipse.leshan.server.californium.LeshanServer;
34 21 import org.eclipse.leshan.server.registration.Registration;
35   -import org.springframework.beans.factory.annotation.Autowired;
36   -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
37   -import org.springframework.stereotype.Service;
38 22 import org.thingsboard.server.common.data.Device;
39 23 import org.thingsboard.server.common.data.DeviceProfile;
40   -import org.thingsboard.server.common.transport.TransportService;
41   -import org.thingsboard.server.common.transport.TransportServiceCallback;
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.ToTransportUpdateCredentialsProto;
47   -import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
48   -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
49   -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
50   -import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
51   -import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
52   -import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
53   -import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
54   -import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
55   -import org.thingsboard.server.transport.lwm2m.server.client.ModelObject;
56   -import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
57   -import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
58   -import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
59 25
60   -import javax.annotation.PostConstruct;
61 26 import java.util.Collection;
62   -import java.util.UUID;
63   -import java.util.Random;
64   -import java.util.Map;
65   -import java.util.HashMap;
66   -import java.util.Arrays;
67   -import java.util.Set;
68   -import java.util.HashSet;
69   -import java.util.NoSuchElementException;
70 27 import java.util.Optional;
71   -import java.util.concurrent.ConcurrentHashMap;
72   -import java.util.concurrent.ConcurrentMap;
73   -import java.util.concurrent.CountDownLatch;
74   -import java.util.concurrent.TimeUnit;
75   -import java.util.concurrent.atomic.AtomicBoolean;
76   -import java.util.function.Predicate;
77   -import java.util.stream.Collectors;
78   -import java.util.stream.Stream;
79 28
80   -import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
81   -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.*;
  29 +public interface LwM2MTransportService {
82 30
83   -@Slf4j
84   -@Service("LwM2MTransportService")
85   -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
86   -public class LwM2MTransportService {
  31 + void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations);
87 32
88   - @Autowired
89   - private LwM2MJsonAdaptor adaptor;
  33 + void updatedReg(LeshanServer lwServer, Registration registration);
90 34
91   - @Autowired
92   - private TransportService transportService;
  35 + void unReg(LeshanServer lwServer, Registration registration, Collection<Observation> observations);
93 36
94   - @Autowired
95   - public LwM2MTransportContextServer context;
  37 + void onSleepingDev(Registration registration);
96 38
97   - @Autowired
98   - private LwM2MTransportRequest lwM2MTransportRequest;
  39 + void setCancelObservations(LeshanServer lwServer, Registration registration);
99 40
100   - @Autowired
101   - LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
  41 + void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path);
102 42
  43 + void onObservationResponse(Registration registration, String path, ReadResponse response);
103 44
104   - @PostConstruct
105   - public void init() {
106   - context.getScheduler().scheduleAtFixedRate(() -> checkInactivityAndReportActivity(), new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS);
107   - }
  45 + void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
108 46
109   - /**
110   - * Start registration device
111   - * Create session: Map<String <registrationId >, LwM2MClient>
112   - * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint)
113   - * 1.1 When we initialize the registration, we register the session by endpoint.
114   - * 1.2 If the server has incomplete requests (canceling the registration of the previous session),
115   - * delete the previous session only by the previous registration.getId
116   - * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId
117   - * 1.2 Remove from sessions Model by enpPoint
118   - * Next -> Create new LwM2MClient for current session -> setModelClient...
119   - *
120   - * @param lwServer - LeshanServer
121   - * @param registration - Registration LwM2M Client
122   - * @param previousObsersations - may be null
123   - */
124   - public void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations) {
125   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(lwServer, registration);
126   - if (lwM2MClient != null) {
127   - lwM2MClient.setLwM2MTransportService(this);
128   - lwM2MClient.setLwM2MTransportService(this);
129   - lwM2MClient.setSessionUuid(UUID.randomUUID());
130   - this.setLwM2MClient(lwServer, registration, lwM2MClient);
131   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId());
132   - if (sessionInfo != null) {
133   - lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
134   - lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
135   - lwM2MClient.setDeviceName(sessionInfo.getDeviceName());
136   - lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType());
137   - transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));
138   - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
139   - transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
140   - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client registration", registration.getId());
141   - } else {
142   - log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), sessionInfo);
143   - }
144   - } else {
145   - log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), lwM2MClient);
146   - }
147   - }
  47 + void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile);
148 48
149   - /**
150   - * @param lwServer - LeshanServer
151   - * @param registration - Registration LwM2M Client
152   - */
153   - public void updatedReg(LeshanServer lwServer, Registration registration) {
154   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId());
155   - if (sessionInfo != null) {
156   - log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
157   - } else {
158   - log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), sessionInfo);
159   - }
160   - }
  49 + void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt);
161 50
162   - /**
163   - * @param registration - Registration LwM2M Client
164   - * @param observations - All paths observations before unReg
165   - * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
166   - */
167   - public void unReg(Registration registration, Collection<Observation> observations) {
168   - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
169   - this.closeClientSession(registration);
170   - }
  51 + void doTrigger(LeshanServer lwServer, Registration registration, String path);
171 52
172   - private void closeClientSession(Registration registration) {
173   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration.getId());
174   - if (sessionInfo != null) {
175   - transportService.deregisterSession(sessionInfo);
176   - this.doCloseSession(sessionInfo);
177   - lwM2mInMemorySecurityStore.delRemoveSessionAndListener(registration.getId());
178   - if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) {
179   - this.syncSessionsAndProfiles();
180   - }
181   - log.info("Client: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
182   - } else {
183   - log.error("Client: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), sessionInfo);
184   - }
185   - }
  53 + void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
186 54
187   - public void onSleepingDev(Registration registration) {
188   - log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
189   - //TODO: associate endpointId with device information.
190   - }
191   -
192   - /**
193   - * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,
194   - * * if you need to do long time processing use a dedicated thread pool.
195   - *
196   - * @param registration
197   - */
198   -
199   - public void onAwakeDev(Registration registration) {
200   - log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
201   - //TODO: associate endpointId with device information.
202   - }
203   -
204   - /**
205   - * This method is used to sync with sessions
206   - * Removes a profile if not used in sessions
207   - */
208   - private void syncSessionsAndProfiles() {
209   - Map<UUID, AttrTelemetryObserveValue> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()
210   - .stream()
211   - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
212   - profilesClone.forEach((k, v) -> {
213   - String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet()
214   - .stream()
215   - .filter(e -> e.getValue().getProfileUuid().equals(k))
216   - .findFirst()
217   - .map(Map.Entry::getKey) // return the key of the matching entry if found
218   - .orElse("");
219   - if (registrationId.isEmpty()) {
220   - lwM2mInMemorySecurityStore.getProfiles().remove(k);
221   - }
222   - });
223   - }
224   -
225   - /**
226   - * Create new LwM2MClient for current session -> setModelClient...
227   - * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client
228   - * to get the client model with current values
229   - * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse
230   - *
231   - * @param lwServer - LeshanServer
232   - * @param registration - Registration LwM2M Client
233   - * @param lwM2MClient - object with All parameters off client
234   - */
235   - private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) {
236   - // #1
237   - Arrays.stream(registration.getObjectLinks()).forEach(url -> {
238   - ResultIds pathIds = new ResultIds(url.getUrl());
239   - if (pathIds.instanceId > -1 && pathIds.resourceId == -1) {
240   - lwM2MClient.getPendingRequests().add(url.getUrl());
241   - }
242   - });
243   - // #2
244   - Arrays.stream(registration.getObjectLinks()).forEach(url -> {
245   - ResultIds pathIds = new ResultIds(url.getUrl());
246   - if (pathIds.instanceId > -1 && pathIds.resourceId == -1) {
247   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ,
248   - ContentFormat.TLV.getName(), lwM2MClient, null, null, this.context.getCtxServer().getTimeout());
249   - }
250   - });
251   - }
252   -
253   - /**
254   - * @param registrationId - Id of Registration LwM2M Client
255   - * @return - sessionInfo after access connect client
256   - */
257   - private SessionInfoProto getValidateSessionInfo(String registrationId) {
258   - SessionInfoProto sessionInfo = null;
259   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registrationId);
260   - if (lwM2MClient != null) {
261   - ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();
262   - if (msg == null || msg.getDeviceInfo() == null) {
263   - log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);
264   - this.closeClientSession(lwM2MClient.getRegistration());
265   - } else {
266   - sessionInfo = SessionInfoProto.newBuilder()
267   - .setNodeId(this.context.getNodeId())
268   - .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())
269   - .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())
270   - .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
271   - .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
272   - .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
273   - .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
274   - .setDeviceName(msg.getDeviceInfo().getDeviceName())
275   - .setDeviceType(msg.getDeviceInfo().getDeviceType())
276   - .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
277   - .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
278   - .build();
279   - }
280   - }
281   - return sessionInfo;
282   - }
283   -
284   - /**
285   - * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe
286   - * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use
287   - * #1 Client`s starting info to send to thingsboard
288   - * #2 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection
289   - * #3 Start observe
290   - *
291   - * @param lwServer - LeshanServer
292   - * @param registration - Registration LwM2M Client
293   - */
294   -
295   - public void updatesAndSentModelParameter(LeshanServer lwServer, Registration registration) {
296   - // #1
297   -// this.setParametersToModelClient(registration, modelClient, deviceProfile);
298   - // #2
299   - this.updateAttrTelemetry(registration, true, null);
300   - // #3
301   - this.onSentObserveToClient(lwServer, registration);
302   - }
303   -
304   -
305   - /**
306   - * Sent Attribute and Telemetry to Thingsboard
307   - * #1 - get AttrName/TelemetryName with value:
308   - * #1.1 from Client
309   - * #1.2 from LwM2MClient:
310   - * -- resourceId == path from AttrTelemetryObserveValue.postAttributeProfile/postTelemetryProfile/postObserveProfile
311   - * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
312   - * #2 - set Attribute/Telemetry
313   - *
314   - * @param registration - Registration LwM2M Client
315   - */
316   - private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {
317   - JsonObject attributes = new JsonObject();
318   - JsonObject telemetrys = new JsonObject();
319   - if (start) {
320   - // #1.1
321   - JsonObject attributeClient = this.getAttributeClient(registration);
322   - if (attributeClient != null) {
323   - attributeClient.entrySet().forEach(p -> {
324   - attributes.add(p.getKey(), p.getValue());
325   - });
326   - }
327   - }
328   - // #1.2
329   - CountDownLatch cancelLatch = new CountDownLatch(1);
330   - this.getParametersFromProfile(attributes, telemetrys, registration, paths);
331   - cancelLatch.countDown();
332   - try {
333   - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
334   - } catch (InterruptedException e) {
335   - log.error("[{}] updateAttrTelemetry", e.toString());
336   - }
337   - if (attributes.getAsJsonObject().entrySet().size() > 0)
338   - this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration.getId());
339   - if (telemetrys.getAsJsonObject().entrySet().size() > 0)
340   - this.updateParametersOnThingsboard(telemetrys, DEVICE_TELEMETRY_TOPIC, registration.getId());
341   - }
342   -
343   - /**
344   - * get AttrName/TelemetryName with value from Client
345   - *
346   - * @param registration -
347   - * @return - JsonObject, format: {name: value}}
348   - */
349   - private JsonObject getAttributeClient(Registration registration) {
350   - if (registration.getAdditionalRegistrationAttributes().size() > 0) {
351   - JsonObject resNameValues = new JsonObject();
352   - registration.getAdditionalRegistrationAttributes().entrySet().forEach(entry -> {
353   - resNameValues.addProperty(entry.getKey(), entry.getValue());
354   - });
355   - return resNameValues;
356   - }
357   - return null;
358   - }
359   -
360   - /**
361   - * @param attributes - new JsonObject
362   - * @param telemetry - new JsonObject
363   - * @param registration - Registration LwM2M Client
364   - * result: add to JsonObject those resources to which the user is subscribed and they have a value
365   - * if path==null add All resources else only one
366   - * (attributes/telemetry): new {name(Attr/Telemetry):value}
367   - */
368   - private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
369   - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid());
370   - attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> {
371   - ResultIds pathIds = new ResultIds(p.getAsString().toString());
372   - if (pathIds.getResourceId() > -1) {
373   - if (path == null || path.contains(p.getAsString())) {
374   - this.addParameters(pathIds, p.getAsString().toString(), attributes, registration);
375   - }
376   - }
377   - });
378   - attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> {
379   - ResultIds pathIds = new ResultIds(p.getAsString().toString());
380   - if (pathIds.getResourceId() > -1) {
381   - if (path == null || path.contains(p.getAsString())) {
382   - this.addParameters(pathIds, p.getAsString().toString(), telemetry, registration);
383   - }
384   - }
385   - });
386   - }
387   -
388   - /**
389   - * @param pathIds - path resource
390   - * @param parameters - JsonObject attributes/telemetry
391   - * @param registration - Registration LwM2M Client
392   - */
393   - private void addParameters(ResultIds pathIds, String path, JsonObject parameters, Registration registration) {
394   - ModelObject modelObject = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getModelObjects().get(pathIds.getObjectId());
395   - JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid()).getPostKeyNameProfile();
396   - String resName = String.valueOf(names.get(path));
397   - if (modelObject != null && resName != null && !resName.isEmpty()) {
398   - String resValue = this.getResourceValue(modelObject, pathIds);
399   - if (resValue != null) {
400   - parameters.addProperty(resName, resValue);
401   - }
402   - }
403   - }
404   -
405   - /**
406   - * @param modelObject - ModelObject of Client
407   - * @param pathIds - path resource
408   - * @return - value of Resource or null
409   - */
410   - private String getResourceValue(ModelObject modelObject, ResultIds pathIds) {
411   - String resValue = null;
412   - if (modelObject.getInstances().get(pathIds.getInstanceId()) != null) {
413   - LwM2mObjectInstance instance = modelObject.getInstances().get(pathIds.getInstanceId());
414   - if (instance.getResource(pathIds.getResourceId()) != null) {
415   - resValue = instance.getResource(pathIds.getResourceId()).getType() == OPAQUE ?
416   - Hex.encodeHexString((byte[]) instance.getResource(pathIds.getResourceId()).getValue()).toLowerCase() :
417   - (instance.getResource(pathIds.getResourceId()).isMultiInstances()) ?
418   - instance.getResource(pathIds.getResourceId()).getValues().toString() :
419   - instance.getResource(pathIds.getResourceId()).getValue().toString();
420   - }
421   - }
422   - return resValue;
423   - }
424   -
425   - /**
426   - * Prepare Sent to Thigsboard callback - Attribute or Telemetry
427   - *
428   - * @param msg - JsonArray: [{name: value}]
429   - * @param topicName - Api Attribute or Telemetry
430   - * @param registrationId - Id of Registration LwM2M Client
431   - */
432   - public void updateParametersOnThingsboard(JsonElement msg, String topicName, String registrationId) {
433   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId);
434   - if (sessionInfo != null) {
435   - try {
436   - if (topicName.equals(LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC)) {
437   - PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(msg);
438   - TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(-1, postAttributeMsg);
439   - transportService.process(sessionInfo, postAttributeMsg, call);
440   - } else if (topicName.equals(LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC)) {
441   - PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(msg);
442   - TransportServiceCallback call = this.getPubAckCallbackSentAttrTelemetry(-1, postTelemetryMsg);
443   - transportService.process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSentAttrTelemetry(-1, call));
444   - }
445   - } catch (AdaptorException e) {
446   - log.error("[{}] Failed to process publish msg [{}]", topicName, e);
447   - log.info("[{}] Closing current session due to invalid publish", topicName);
448   - }
449   - } else {
450   - log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registrationId, sessionInfo);
451   - }
452   - }
453   -
454   - /**
455   - * Sent to Thingsboard Attribute || Telemetry
456   - *
457   - * @param msgId - always == -1
458   - * @param msg - JsonObject: [{name: value}]
459   - * @return - dummy
460   - */
461   - private <T> TransportServiceCallback<Void> getPubAckCallbackSentAttrTelemetry(final int msgId, final T msg) {
462   - return new TransportServiceCallback<Void>() {
463   - @Override
464   - public void onSuccess(Void dummy) {
465   - log.trace("Success to publish msg: {}, dummy: {}", msg, dummy);
466   - }
467   -
468   - @Override
469   - public void onError(Throwable e) {
470   - log.trace("[{}] Failed to publish msg: {}", msg, e);
471   - }
472   - };
473   - }
474   -
475   -
476   - /**
477   - * Start observe
478   - * #1 - Analyze:
479   - * #1.1 path in observe == (attribute or telemetry)
480   - * #1.2 recourseValue notNull
481   - * #2 Analyze after sent request (response):
482   - * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation
483   - * #2.2 Next: ObservationListener.onResponse *
484   - *
485   - * @param lwServer - LeshanServer
486   - * @param registration - Registration LwM2M Client
487   - */
488   - private void onSentObserveToClient(LeshanServer lwServer, Registration registration) {
489   - if (lwServer.getObservationService().getObservations(registration).size() > 0) {
490   - this.setCancelObservations(lwServer, registration);
491   - }
492   - UUID profileUUid = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid();
493   - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(profileUUid);
494   - attrTelemetryObserveValue.getPostObserveProfile().forEach(p -> {
495   - // #1.1
496   - String target = (getValidateObserve(attrTelemetryObserveValue.getPostAttributeProfile(), p.getAsString().toString())) ?
497   - p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ?
498   - p.getAsString().toString() : null;
499   - if (target != null) {
500   - // #1.2
501   - ResultIds pathIds = new ResultIds(target);
502   - ModelObject modelObject = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getModelObjects().get(pathIds.getObjectId());
503   - // #2
504   - if (modelObject != null) {
505   - if (getResourceValue(modelObject, pathIds) != null) {
506   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE,
507   - null, null, null, null, this.context.getCtxServer().getTimeout());
508   - }
509   - }
510   - }
511   - });
512   - }
513   -
514   - public void setCancelObservations(LeshanServer lwServer, Registration registration) {
515   - if (registration != null) {
516   - Set<Observation> observations = lwServer.getObservationService().getObservations(registration);
517   - observations.forEach(observation -> {
518   - this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString());
519   - });
520   - }
521   - }
522   -
523   - /**
524   - * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
525   - * At server side this will not remove the observation from the observation store, to do it you need to use
526   - * {@code ObservationService#cancelObservation()}
527   - */
528   - public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {
529   - CountDownLatch cancelLatch = new CountDownLatch(1);
530   - lwServer.getObservationService().cancelObservations(registration, path);
531   - cancelLatch.countDown();
532   - try {
533   - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
534   - } catch (InterruptedException e) {
535   - }
536   - }
537   -
538   - /**
539   - * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile
540   - * @param path - recourse from postObserveProfile
541   - * @return rez - true if path observe is in attribute/telemetry
542   - */
543   - private boolean getValidateObserve(JsonElement parameters, String path) {
544   - AtomicBoolean rez = new AtomicBoolean(false);
545   - if (parameters.isJsonArray()) {
546   - parameters.getAsJsonArray().forEach(p -> {
547   - if (p.getAsString().toString().equals(path)) rez.set(true);
548   - }
549   - );
550   - } else if (parameters.isJsonObject()) {
551   - rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString())
552   - .filter(path::equals).findAny().orElse(null) != null);
553   - }
554   - return rez.get();
555   - }
556   -
557   - /**
558   - * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
559   - *
560   - * @param registration - Registration LwM2M Client
561   - * @param path - observe
562   - * @param response - observe
563   - */
564   - @SneakyThrows
565   - public void onObservationResponse(Registration registration, String path, ReadResponse response) {
566   - if (response.getContent() != null) {
567   - if (response.getContent() instanceof LwM2mObject) {
568   - LwM2mObject content = (LwM2mObject) response.getContent();
569   - String target = "/" + content.getId();
570   - } else if (response.getContent() instanceof LwM2mObjectInstance) {
571   - LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent();
572   - } else if (response.getContent() instanceof LwM2mSingleResource) {
573   - LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();
574   - this.onObservationSetResourcesValue(registration, content.getValue(), null, path);
575   - } else if (response.getContent() instanceof LwM2mMultipleResource) {
576   - LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();
577   - this.onObservationSetResourcesValue(registration, null, content.getValues(), path);
578   - }
579   - }
580   - }
581   -
582   - /**
583   - * Sending observe value of resources to thingsboard
584   - * #1 Return old Resource from ModelObject
585   - * #2 Create new Resource with value from observation
586   - * #3 Create new Resources from old Resources
587   - * #4 Update new Resources (replace old Resource on new Resource)
588   - * #5 Remove old Instance from modelClient
589   - * #6 Create new Instance with new Resources values
590   - * #7 Update modelClient.getModelObjects(idObject) (replace old Instance on new Instance)
591   - *
592   - * @param registration - Registration LwM2M Client
593   - * @param value - LwM2mSingleResource response.getContent()
594   - * @param values - LwM2mSingleResource response.getContent()
595   - * @param path - resource
596   - */
597   - private void onObservationSetResourcesValue(Registration registration, Object value, Map<Integer, ?> values, String path) {
598   - ResultIds resultIds = new ResultIds(path);
599   - // #1
600   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registration.getId());
601   - ModelObject modelObject = lwM2MClient.getModelObjects().get(resultIds.getObjectId());
602   - Map<Integer, LwM2mObjectInstance> instancesModelObject = modelObject.getInstances();
603   - LwM2mObjectInstance instanceOld = (instancesModelObject.get(resultIds.instanceId) != null) ? instancesModelObject.get(resultIds.instanceId) : null;
604   - Map<Integer, LwM2mResource> resourcesOld = (instanceOld != null) ? instanceOld.getResources() : null;
605   - LwM2mResource resourceOld = (resourcesOld != null && resourcesOld.get(resultIds.getResourceId()) != null) ? resourcesOld.get(resultIds.getResourceId()) : null;
606   - // #2
607   - LwM2mResource resourceNew;
608   - if (resourceOld.isMultiInstances()) {
609   - resourceNew = LwM2mMultipleResource.newResource(resultIds.getResourceId(), values, resourceOld.getType());
610   - } else {
611   - resourceNew = LwM2mSingleResource.newResource(resultIds.getResourceId(), value, resourceOld.getType());
612   - }
613   - //#3
614   - Map<Integer, LwM2mResource> resourcesNew = new HashMap<>(resourcesOld);
615   - // #4
616   - resourcesNew.remove(resourceOld);
617   - // #5
618   - resourcesNew.put(resultIds.getResourceId(), resourceNew);
619   - // #6
620   - LwM2mObjectInstance instanceNew = new LwM2mObjectInstance(resultIds.instanceId, resourcesNew.values());
621   - // #7
622   - CountDownLatch respLatch = new CountDownLatch(1);
623   - lwM2MClient.getModelObjects().get(resultIds.getObjectId()).removeInstance(resultIds.instanceId);
624   - instancesModelObject.put(resultIds.instanceId, instanceNew);
625   - respLatch.countDown();
626   - try {
627   - respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
628   - } catch (InterruptedException ex) {
629   - }
630   - Set<String> paths = new HashSet<String>();
631   - paths.add(path);
632   - this.updateAttrTelemetry(registration, false, paths);
633   - }
634   -
635   - /**
636   - * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
637   - * config attr/telemetry... in profile
638   - */
639   - public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) {
640   - log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
641   - }
642   -
643   - /**
644   - * Update - sent request in change value resources in Client (path to resources from profile by keyName)
645   - * Only fo resources W
646   - * Delete - nothing
647   - *
648   - * @param msg -
649   - * @param sessionInfo -
650   - */
651   - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, SessionInfoProto sessionInfo) {
652   - if (msg.getSharedUpdatedCount() > 0) {
653   - JsonElement el = JsonConverter.toJson(msg);
654   - el.getAsJsonObject().entrySet().forEach(de -> {
655   - String profilePath = lwM2mInMemorySecurityStore.getProfiles().get(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()))
656   - .getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
657   - .filter(e -> e.getValue().getAsString().equals(de.getKey())).findFirst().map(Map.Entry::getKey)
658   - .orElse("");
659   - String path = !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdate(sessionInfo, de.getKey());
660   - if (path != null) {
661   - ResultIds resultIds = new ResultIds(path);
662   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
663   - ResourceModel.Operations operations = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).operations;
664   - String value = ((JsonPrimitive) de.getValue()).getAsString();
665   - if (operations.isWritable()) {
666   - lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
667   - ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout());
668   - log.info("[{}] path onAttributeUpdate", path);
669   - }
670   - else {
671   - log.error(LOG_LW2M_ERROR + ": Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
672   - String logMsg = String.format(LOG_LW2M_ERROR + " attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);
673   - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
674   - }
675   - }
676   - });
677   - } else if (msg.getSharedDeletedCount() > 0) {
678   - log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
679   - }
680   - }
681   -
682   - private String getPathAttributeUpdate(SessionInfoProto sessionInfo, String keyName) {
683   - try {
684   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
685   - Predicate<Map.Entry<Integer, ResourceModel>> predicateRes = res -> keyName.equals(splitCamelCaseString(res.getValue().name));
686   - Predicate<Map.Entry<Integer, ModelObject>> predicateObj = (obj -> {
687   - return obj.getValue().getObjectModel().resources.entrySet().stream().filter(predicateRes).findFirst().isPresent();
688   - });
689   - Stream<Map.Entry<Integer, ModelObject>> objectStream = lwM2MClient.getModelObjects().entrySet().stream().filter(predicateObj);
690   - Predicate<Map.Entry<Integer, ResourceModel>> predicateResFinal = (objectStream.count() > 0) ? predicateRes : res -> keyName.equals(res.getValue().name);
691   - Predicate<Map.Entry<Integer, ModelObject>> predicateObjFinal = (obj -> {
692   - return obj.getValue().getObjectModel().resources.entrySet().stream().filter(predicateResFinal).findFirst().isPresent();
693   - });
694   - Map.Entry<Integer, ModelObject> object = lwM2MClient.getModelObjects().entrySet().stream().filter(predicateObjFinal).findFirst().get();
695   - ModelObject modelObject = object.getValue();
696   - LwM2mObjectInstance instance = modelObject.getInstances().entrySet().stream().findFirst().get().getValue();
697   - ResourceModel resource = modelObject.getObjectModel().resources.entrySet().stream().filter(predicateResFinal).findFirst().get().getValue();
698   - return new LwM2mPath(object.getKey(), instance.getId(), resource.id).toString();
699   - } catch (NoSuchElementException e) {
700   - log.error("[{}] keyName [{}]", keyName, e.toString());
701   - return null;
702   - }
703   - }
704   -
705   - public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request) {
706   - ResultIds resultIds = new ResultIds(path);
707   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registration.getId());
708   - LwM2mResource resource = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances().get(resultIds.getInstanceId()).getResource(resultIds.getResourceId());
709   - if (resource.isMultiInstances()) {
710   - this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path);
711   - } else {
712   - this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path);
713   - }
714   - }
715   -
716   - /**
717   - * @param sessionInfo -
718   - * @param deviceProfile -
719   - */
720   - public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
721   - String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet()
722   - .stream()
723   - .filter(e -> e.getValue().getDeviceUuid().equals(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())))
724   - .findFirst()
725   - .map(Map.Entry::getKey)
726   - .orElse("");
727   - if (!registrationId.isEmpty()) {
728   - this.onDeviceUpdateChangeProfile(registrationId, deviceProfile);
729   - }
730   - }
731   -
732   - /**
733   - * @param sessionInfo -
734   - * @param device -
735   - * @param deviceProfileOpt -
736   - */
737   - public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
738   - Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()
739   - .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))
740   - .map(Map.Entry::getKey)
741   - .findFirst();
742   - registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
743   - }
744   -
745   - /**
746   - * Update parameters device in LwM2MClient
747   - * If new deviceProfile != old deviceProfile => update deviceProfile
748   - *
749   - * @param registrationId -
750   - * @param device -
751   - */
752   - private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {
753   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);
754   - lwM2MClient.setDeviceName(device.getName());
755   - if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {
756   - deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationId, deviceProfile));
757   - }
758   - }
759   -
760   - /**
761   - * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName)
762   - * #2 Update in lwM2MClient: ...Profile
763   - * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName)
764   - * #3.1 Attribute isChange (add&del)
765   - * #3.2 Telemetry isChange (add&del)
766   - * #3.3 KeyName isChange (add)
767   - * #4 update
768   - * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value ti thingsboard
769   - * #4.2 del
770   - * -- if add attributes includes del telemetry - result del for observe
771   - * #5
772   - * #5.1 Observe isChange (add&del)
773   - * #5.2 Observe.add
774   - * -- path Attr/Telemetry includes newObserve and does not include oldObserve: sent Request observe to Client
775   - * #5.3 Observe.del
776   - * -- different between newObserve and oldObserve: sent Request cancel observe to client
777   - *
778   - * @param registrationId -
779   - * @param deviceProfile -
780   - */
781   - public void onDeviceUpdateChangeProfile(String registrationId, DeviceProfile deviceProfile) {
782   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registrationId);
783   - AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid());
784   - if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) {
785   - LeshanServer lwServer = lwM2MClient.getLwServer();
786   - Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
787   - // #1
788   - JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile();
789   - Set<String> attributeSetOld = new Gson().fromJson(attributeOld, Set.class);
790   - JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile();
791   - Set<String> telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class);
792   - JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile();
793   - JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile();
794   -
795   - AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
796   - JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile();
797   - Set<String> attributeSetNew = new Gson().fromJson(attributeNew, Set.class);
798   - JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile();
799   - Set<String> telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class);
800   - JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile();
801   - JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile();
802   - // #2
803   - lwM2MClient.setDeviceProfileName(deviceProfile.getName());
804   - lwM2MClient.setProfileUuid(deviceProfile.getUuidId());
805   -
806   - // #3
807   - ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters();
808   - // #3.1
809   - if (!attributeOld.equals(attributeNew)) {
810   - ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, Set.class), attributeSetNew);
811   - sentAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd());
812   - sentAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel());
813   - }
814   - // #3.2
815   - if (!attributeOld.equals(attributeNew)) {
816   - ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, Set.class), telemetrySetNew);
817   - sentAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd());
818   - sentAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel());
819   - }
820   - // #3.3
821   - if (!keyNameOld.equals(keyNameNew)) {
822   - ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), ConcurrentHashMap.class),
823   - new Gson().fromJson(keyNameNew.toString(), ConcurrentHashMap.class));
824   - sentAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd());
825   - }
826   -
827   - // #4.1 add
828   - if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
829   - // update value in Resources
830   - this.updateResourceValueObserve(lwServer, registration, lwM2MClient, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ);
831   - // sent attr/telemetry to tingsboard for new path
832   - this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd());
833   - }
834   - // #4.2 del
835   - if (sentAttrToThingsboard.getPathPostParametersDel().size() > 0) {
836   - ResultsAnalyzerParameters sentAttrToThingsboardDel = this.getAnalyzerParameters(sentAttrToThingsboard.getPathPostParametersAdd(), sentAttrToThingsboard.getPathPostParametersDel());
837   - sentAttrToThingsboard.setPathPostParametersDel(sentAttrToThingsboardDel.getPathPostParametersDel());
838   - }
839   -
840   - // #5.1
841   - if (!observeOld.equals(observeNew)) {
842   - Set<String> observeSetOld = new Gson().fromJson(observeOld, Set.class);
843   - Set<String> observeSetNew = new Gson().fromJson(observeNew, Set.class);
844   - //#5.2 add
845   - // path Attr/Telemetry includes newObserve
846   - attributeSetOld.addAll(telemetrySetOld);
847   - ResultsAnalyzerParameters sentObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe
848   - attributeSetNew.addAll(telemetrySetNew);
849   - ResultsAnalyzerParameters sentObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe
850   - // does not include oldObserve
851   - ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd());
852   - // sent Request observe to Client
853   - this.updateResourceValueObserve(lwServer, registration, lwM2MClient, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE);
854   - // 5.3 del
855   - // sent Request cancel observe to Client
856   - this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel());
857   - }
858   - }
859   - }
860   -
861   - /**
862   - * Compare old list with new list after change AttrTelemetryObserve in config Profile
863   - *
864   - * @param parametersOld -
865   - * @param parametersNew -
866   - * @return ResultsAnalyzerParameters: add && new
867   - */
868   - private ResultsAnalyzerParameters getAnalyzerParameters(Set<String> parametersOld, Set<String> parametersNew) {
869   - ResultsAnalyzerParameters analyzerParameters = null;
870   - if (!parametersOld.equals(parametersNew)) {
871   - analyzerParameters = new ResultsAnalyzerParameters();
872   - analyzerParameters.setPathPostParametersAdd(parametersNew
873   - .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet()));
874   - analyzerParameters.setPathPostParametersDel(parametersOld
875   - .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet()));
876   - }
877   - return analyzerParameters;
878   - }
879   -
880   - private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {
881   - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
882   - Set<String> paths = keyNameNew.entrySet()
883   - .stream()
884   - .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
885   - .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())).keySet();
886   - analyzerParameters.setPathPostParametersAdd(paths);
887   - return analyzerParameters;
888   - }
889   -
890   - private ResultsAnalyzerParameters getAnalyzerParametersIn(Set<String> parametersObserve, Set<String> parameters) {
891   - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
892   - analyzerParameters.setPathPostParametersAdd(parametersObserve
893   - .stream().filter(p -> parameters.contains(p)).collect(Collectors.toSet()));
894   - return analyzerParameters;
895   - }
896   -
897   - /**
898   - * Update Resource value after change RezAttrTelemetry in config Profile
899   - * sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
900   - *
901   - * @param lwServer - LeshanServer
902   - * @param registration - Registration LwM2M Client
903   - * @param lwM2MClient - object with All parameters off client
904   - * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
905   - */
906   - private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient, Set<String> targets, String typeOper) {
907   - targets.stream().forEach(target -> {
908   - ResultIds pathIds = new ResultIds(target);
909   - if (pathIds.resourceId >= 0 && lwM2MClient.getModelObjects().get(pathIds.getObjectId())
910   - .getInstances().get(pathIds.getInstanceId()).getResource(pathIds.getResourceId()).getValue() != null) {
911   - if (GET_TYPE_OPER_READ.equals(typeOper)) {
912   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
913   - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout());
914   - } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
915   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
916   - null, null, null, null, this.context.getCtxServer().getTimeout());
917   - }
918   - }
919   - });
920   - }
921   -
922   - private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set<String> paramAnallyzer) {
923   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getlwM2MClient(registration.getId());
924   - paramAnallyzer.forEach(p -> {
925   - if (this.getResourceValue(lwM2MClient, p) != null) {
926   - this.setCancelObservationRecourse(lwServer, registration, p);
927   - }
928   - }
929   - );
930   - }
931   -
932   - private ResourceValue getResourceValue(LwM2MClient lwM2MClient, String path) {
933   - ResourceValue resourceValue = null;
934   - ResultIds pathIds = new ResultIds(path);
935   - if (pathIds.getResourceId() > -1) {
936   - LwM2mResource resource = lwM2MClient.getModelObjects().get(pathIds.getObjectId()).getInstances().get(pathIds.getInstanceId()).getResource(pathIds.getResourceId());
937   - if (resource.isMultiInstances()) {
938   - Map<Integer, ?> values = resource.getValues();
939   - if (resource.getValues().size() > 0) {
940   - resourceValue = new ResourceValue();
941   - resourceValue.setMultiInstances(resource.isMultiInstances());
942   - resourceValue.setValues(resource.getValues());
943   - }
944   - } else {
945   - if (resource.getValue() != null) {
946   - resourceValue = new ResourceValue();
947   - resourceValue.setMultiInstances(resource.isMultiInstances());
948   - resourceValue.setValue(resource.getValue());
949   - }
950   - }
951   - }
952   - return resourceValue;
953   - }
954   -
955   - /**
956   - * Trigger Server path = "/1/0/8"
957   - *
958   - * Trigger bootStrap path = "/1/0/9" - have to implemented on client
959   - */
960   - public void doTrigger(LeshanServer lwServer, Registration registration, String path) {
961   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,
962   - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout());
963   - }
964   -
965   - /**
966   - * Session device in thingsboard is closed
967   - *
968   - * @param sessionInfo - lwm2m client
969   - */
970   - private void doCloseSession(SessionInfoProto sessionInfo) {
971   - TransportProtos.SessionEvent event = SessionEvent.CLOSED;
972   - TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
973   - .setSessionType(TransportProtos.SessionType.ASYNC)
974   - .setEvent(event).build();
975   - transportService.process(sessionInfo, msg, null);
976   - }
977   -
978   - /**
979   - * Deregister session in transport
980   - * @param sessionInfo - lwm2m client
981   - */
982   - private void doDisconnect(SessionInfoProto sessionInfo) {
983   - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
984   - transportService.deregisterSession(sessionInfo);
985   - }
986   -
987   - private void checkInactivityAndReportActivity() {
988   - lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> transportService.reportActivity(this.getValidateSessionInfo(key)));
989   - }
990   -
991   - public void sentLogsToThingsboard(String msg, String registrationId) {
992   - if (msg != null) {
993   - JsonObject telemetrys = new JsonObject();
994   - telemetrys.addProperty(LOG_LW2M_TELEMETRY, msg);
995   - this.updateParametersOnThingsboard(telemetrys, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registrationId);
996   - }
997   - }
998 55
999 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.Link;
  24 +import org.eclipse.leshan.core.model.ResourceModel;
  25 +import org.eclipse.leshan.core.node.LwM2mMultipleResource;
  26 +import org.eclipse.leshan.core.node.LwM2mObject;
  27 +import org.eclipse.leshan.core.node.LwM2mObjectInstance;
  28 +import org.eclipse.leshan.core.node.LwM2mPath;
  29 +import org.eclipse.leshan.core.node.LwM2mSingleResource;
  30 +import org.eclipse.leshan.core.observation.Observation;
  31 +import org.eclipse.leshan.core.request.ContentFormat;
  32 +import org.eclipse.leshan.core.request.WriteRequest;
  33 +import org.eclipse.leshan.core.response.ReadResponse;
  34 +import org.eclipse.leshan.core.util.NamedThreadFactory;
  35 +import org.eclipse.leshan.server.californium.LeshanServer;
  36 +import org.eclipse.leshan.server.registration.Registration;
  37 +import org.springframework.beans.factory.annotation.Autowired;
  38 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  39 +import org.springframework.stereotype.Service;
  40 +import org.thingsboard.server.common.data.Device;
  41 +import org.thingsboard.server.common.data.DeviceProfile;
  42 +import org.thingsboard.server.common.transport.TransportService;
  43 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  44 +import org.thingsboard.server.common.transport.adaptor.JsonConverter;
  45 +import org.thingsboard.server.common.transport.service.DefaultTransportService;
  46 +import org.thingsboard.server.gen.transport.TransportProtos;
  47 +import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
  48 +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
  49 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
  50 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
  51 +import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
  52 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
  53 +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
  54 +import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
  55 +import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
  56 +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
  57 +
  58 +import javax.annotation.PostConstruct;
  59 +import java.util.ArrayList;
  60 +import java.util.Arrays;
  61 +import java.util.Collection;
  62 +import java.util.HashSet;
  63 +import java.util.LinkedHashSet;
  64 +import java.util.List;
  65 +import java.util.Map;
  66 +import java.util.Optional;
  67 +import java.util.Random;
  68 +import java.util.Set;
  69 +import java.util.UUID;
  70 +import java.util.concurrent.ConcurrentHashMap;
  71 +import java.util.concurrent.ConcurrentMap;
  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.DEVICE_ATTRIBUTES_REQUEST;
  84 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC;
  85 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC;
  86 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE;
  87 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ;
  88 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR;
  89 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO;
  90 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY;
  91 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE;
  92 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE;
  93 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVICE_CHANNEL;
  94 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getAckCallback;
  95 +
  96 +@Slf4j
  97 +@Service("LwM2MTransportService")
  98 +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
  99 +public class LwM2MTransportServiceImpl implements LwM2MTransportService {
  100 +
  101 + private ExecutorService executorRegistered;
  102 + private ExecutorService executorUpdateRegistered;
  103 + private ExecutorService executorUnRegistered;
  104 + private LwM2mValueConverterImpl converter;
  105 + protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  106 + protected final Lock writeLock = readWriteLock.writeLock();
  107 +
  108 +
  109 + @Autowired
  110 + private TransportService transportService;
  111 +
  112 + @Autowired
  113 + public LwM2MTransportContextServer context;
  114 +
  115 + @Autowired
  116 + private LwM2MTransportRequest lwM2MTransportRequest;
  117 +
  118 + @Autowired
  119 + LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
  120 +
  121 + @PostConstruct
  122 + public void init() {
  123 + this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS);
  124 + this.executorRegistered = Executors.newFixedThreadPool(this.context.getCtxServer().getRegisteredPoolSize(),
  125 + new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL)));
  126 + this.executorUpdateRegistered = Executors.newFixedThreadPool(this.context.getCtxServer().getUpdateRegisteredPoolSize(),
  127 + new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL)));
  128 + this.executorUnRegistered = Executors.newFixedThreadPool(this.context.getCtxServer().getUnRegisteredPoolSize(),
  129 + new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL)));
  130 + this.converter = LwM2mValueConverterImpl.getInstance();
  131 + }
  132 +
  133 + /**
  134 + * Start registration device
  135 + * Create session: Map<String <registrationId >, LwM2MClient>
  136 + * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint)
  137 + * 1.1 When we initialize the registration, we register the session by endpoint.
  138 + * 1.2 If the server has incomplete requests (canceling the registration of the previous session),
  139 + * delete the previous session only by the previous registration.getId
  140 + * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId
  141 + * 1.2 Remove from sessions Model by enpPoint
  142 + * Next -> Create new LwM2MClient for current session -> setModelClient...
  143 + *
  144 + * @param lwServer - LeshanServer
  145 + * @param registration - Registration LwM2M Client
  146 + * @param previousObsersations - may be null
  147 + */
  148 + public void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations) {
  149 + executorRegistered.submit(() -> {
  150 + try {
  151 +// log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
  152 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration);
  153 + if (lwM2MClient != null) {
  154 + lwM2MClient.setLwM2MTransportServiceImpl(this);
  155 + lwM2MClient.setSessionUuid(UUID.randomUUID());
  156 + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration);
  157 + this.setLwM2mFromClientValue(lwServer, registration, lwM2MClient);
  158 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  159 + if (sessionInfo != null) {
  160 + lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
  161 + lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  162 + lwM2MClient.setDeviceName(sessionInfo.getDeviceName());
  163 + lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType());
  164 + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));
  165 + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
  166 + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
  167 + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);
  168 + this.putDelayedUpdateResourcesThingsboard(lwM2MClient);
  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 + *
  184 + * @param lwServer - LeshanServer
  185 + * @param registration - Registration LwM2M Client
  186 + */
  187 + public void updatedReg(LeshanServer lwServer, Registration registration) {
  188 + executorUpdateRegistered.submit(() -> {
  189 + try {
  190 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  191 + if (sessionInfo != null) {
  192 + this.checkInactivity(sessionInfo);
  193 + log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
  194 + } else {
  195 + log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
  196 + }
  197 + } catch (Throwable t) {
  198 + log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);
  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(LeshanServer lwServer, Registration registration, Collection<Observation> observations) {
  209 + executorUnRegistered.submit(() -> {
  210 + try {
  211 + this.setCancelObservations(lwServer, registration);
  212 + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
  213 + this.closeClientSession(registration);
  214 + } catch (Throwable t) {
  215 + log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
  216 + }
  217 + });
  218 + }
  219 +
  220 + private void closeClientSession(Registration registration) {
  221 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  222 + if (sessionInfo != null) {
  223 + transportService.deregisterSession(sessionInfo);
  224 + this.doCloseSession(sessionInfo);
  225 + lwM2mInMemorySecurityStore.delRemoveSessionAndListener(registration.getId());
  226 + if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) {
  227 + this.syncSessionsAndProfiles();
  228 + }
  229 + log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
  230 + } else {
  231 + log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
  232 + }
  233 + }
  234 +
  235 + public void onSleepingDev(Registration registration) {
  236 + log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
  237 + //TODO: associate endpointId with device information.
  238 + }
  239 +
  240 + /**
  241 + * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,
  242 + * * if you need to do long time processing use a dedicated thread pool.
  243 + *
  244 + * @param registration -
  245 + */
  246 + protected void onAwakeDev(Registration registration) {
  247 + log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
  248 + //TODO: associate endpointId with device information.
  249 + }
  250 +
  251 + /**
  252 + * This method is used to sync with sessions
  253 + * Removes a profile if not used in sessions
  254 + */
  255 + private void syncSessionsAndProfiles() {
  256 + Map<UUID, AttrTelemetryObserveValue> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()
  257 + .stream()
  258 + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
  259 + profilesClone.forEach((k, v) -> {
  260 + String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet()
  261 + .stream()
  262 + .filter(e -> e.getValue().getProfileUuid().equals(k))
  263 + .findFirst()
  264 + .map(Map.Entry::getKey) // return the key of the matching entry if found
  265 + .orElse("");
  266 + if (registrationId.isEmpty()) {
  267 + lwM2mInMemorySecurityStore.getProfiles().remove(k);
  268 + }
  269 + });
  270 + }
  271 +
  272 + /**
  273 + * #0 Add new ObjectModel to context
  274 + * Create new LwM2MClient for current session -> setModelClient...
  275 + * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client
  276 + * to get the client model with current values
  277 + * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse
  278 + *
  279 + * @param lwServer - LeshanServer
  280 + * @param registration - Registration LwM2M Client
  281 + * @param lwM2MClient - object with All parameters off client
  282 + */
  283 + private void setLwM2mFromClientValue(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) {
  284 + Arrays.stream(registration.getObjectLinks()).forEach(url -> {
  285 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  286 + if (pathIds.isObjectInstance() && !pathIds.isResource()) {
  287 + // #1
  288 + lwM2MClient.getPendingRequests().add(url.getUrl());
  289 + // #2
  290 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
  291 + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
  292 + }
  293 + });
  294 +
  295 + // #1
  296 + for (Link url : registration.getObjectLinks()) {
  297 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  298 + if (pathIds.isObjectInstance() && !pathIds.isResource()) {
  299 + lwM2MClient.getPendingRequests().add(url.getUrl());
  300 + }
  301 + }
  302 + // #2
  303 + for (Link url : registration.getObjectLinks()) {
  304 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  305 + if (pathIds.isObjectInstance() && !pathIds.isResource()) {
  306 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
  307 + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
  308 + }
  309 + }
  310 +
  311 + // #1
  312 + Arrays.stream(registration.getObjectLinks()).forEach(url -> {
  313 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  314 + if (pathIds.isObjectInstance() && !pathIds.isResource()) {
  315 + lwM2MClient.getPendingRequests().add(url.getUrl());
  316 + }
  317 + });
  318 +
  319 + // #2
  320 + Arrays.stream(registration.getObjectLinks()).forEach(url -> {
  321 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  322 + if (pathIds.isObjectInstance() && !pathIds.isResource()) {
  323 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
  324 + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
  325 + }
  326 + });
  327 + }
  328 +
  329 + /**
  330 + * @param registration - Registration LwM2M Client
  331 + * @return - sessionInfo after access connect client
  332 + */
  333 + private SessionInfoProto getValidateSessionInfo(Registration registration) {
  334 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  335 + return getNewSessionInfoProto(lwM2MClient);
  336 +
  337 + }
  338 +
  339 + /**
  340 + * @param registrationId -
  341 + * @return -
  342 + */
  343 + private SessionInfoProto getValidateSessionInfo(String registrationId) {
  344 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
  345 + return getNewSessionInfoProto(lwM2MClient);
  346 + }
  347 +
  348 + private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) {
  349 + if (lwM2MClient != null) {
  350 + ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();
  351 + if (msg == null || msg.getDeviceInfo() == null) {
  352 + log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);
  353 + this.closeClientSession(lwM2MClient.getRegistration());
  354 + return null;
  355 + } else {
  356 + return SessionInfoProto.newBuilder()
  357 + .setNodeId(this.context.getNodeId())
  358 + .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())
  359 + .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())
  360 + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
  361 + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
  362 + .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
  363 + .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
  364 + .setDeviceName(msg.getDeviceInfo().getDeviceName())
  365 + .setDeviceType(msg.getDeviceInfo().getDeviceType())
  366 + .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
  367 + .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
  368 + .build();
  369 + }
  370 + }
  371 + return null;
  372 + }
  373 +
  374 + /**
  375 + * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe
  376 + * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use
  377 + * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection
  378 + * #2 Start observe
  379 + *
  380 + * @param lwM2MClient - LwM2M Client
  381 + */
  382 +
  383 + public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) {
  384 + // #1
  385 + this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null);
  386 + // #2
  387 + this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration());
  388 +
  389 + }
  390 +
  391 + /**
  392 + * If there is a difference in values between the current resource values and the shared attribute values
  393 + * when the client connects to the server
  394 + * #1 get attributes name from profile include name resources in ModelObject if resource isWritable
  395 + * #2.1 #1 size > 0 => send Request getAttributes to thingsboard
  396 + * #2.2 #1 size == 0 => continue normal process
  397 + *
  398 + * @param lwM2MClient - LwM2M Client
  399 + */
  400 + public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) {
  401 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
  402 + if (sessionInfo != null) {
  403 + //#1.1 + #1.2
  404 + List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);
  405 + if (attrSharedNames.size() > 0) {
  406 + //#2.1
  407 + try {
  408 + TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames);
  409 + lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId());
  410 + transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
  411 + } catch (AdaptorException e) {
  412 + log.warn("Failed to decode get attributes request", e);
  413 + }
  414 + }
  415 + // #2.2
  416 + else {
  417 + lwM2MClient.onSuccessOrErrorDelayedRequests(null);
  418 + }
  419 + }
  420 + }
  421 +
  422 + /**
  423 + * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
  424 + * #1 Get path resource by result attributesResponse
  425 + * #1.1 If two names have equal path => last time attribute
  426 + * #2.1 if there is a difference in values between the current resource values and the shared attribute values
  427 + * => sent to client Request Update of value (new value from shared attribute)
  428 + * and LwM2MClient.delayedRequests.add(path)
  429 + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values
  430 + *
  431 + * @param attributesResponse -
  432 + * @param sessionInfo -
  433 + */
  434 + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
  435 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo);
  436 + if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) {
  437 + attributesResponse.getSharedAttributeListList().forEach(attr -> {
  438 + String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey());
  439 + // #1.1
  440 + if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) {
  441 + lwM2MClient.getDelayedRequests().put(path, attr);
  442 + } else {
  443 + lwM2MClient.getDelayedRequests().put(path, attr);
  444 + }
  445 + });
  446 + // #2.1
  447 + lwM2MClient.getDelayedRequests().forEach((k, v) -> {
  448 + ArrayList<TransportProtos.KeyValueProto> listV = new ArrayList<>();
  449 + listV.add(v.getKv());
  450 + this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k);
  451 + });
  452 + lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId());
  453 + if (lwM2MClient.getDelayedRequests().size() == 0) {
  454 + lwM2MClient.onSuccessOrErrorDelayedRequests(null);
  455 + }
  456 + }
  457 + }
  458 +
  459 + private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {
  460 + if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) {
  461 + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
  462 + ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(),
  463 + true);
  464 + }
  465 + }
  466 +
  467 + /**
  468 + * Get names and keyNames from profile shared!!!! attr resources IsWritable
  469 + *
  470 + * @param lwM2MClient -
  471 + * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable
  472 + */
  473 + private List<String> getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) {
  474 + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid());
  475 + Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
  476 + ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class);
  477 +
  478 + ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
  479 + .stream()
  480 + .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&
  481 + context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))
  482 + .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
  483 +
  484 + Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
  485 + namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
  486 + return new ArrayList<>(namesIsWritable);
  487 + }
  488 +
  489 +
  490 + /**
  491 + * Sent Attribute and Telemetry to Thingsboard
  492 + * #1 - get AttrName/TelemetryName with value:
  493 + * #1.1 from Client
  494 + * #1.2 from LwM2MClient:
  495 + * -- resourceId == path from AttrTelemetryObserveValue.postAttributeProfile/postTelemetryProfile/postObserveProfile
  496 + * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
  497 + * #2 - set Attribute/Telemetry
  498 + *
  499 + * @param registration - Registration LwM2M Client
  500 + */
  501 + private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {
  502 + JsonObject attributes = new JsonObject();
  503 + JsonObject telemetries = new JsonObject();
  504 + if (start) {
  505 + // #1.1
  506 + JsonObject attributeClient = this.getAttributeClient(registration);
  507 + if (attributeClient != null) {
  508 + attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));
  509 + }
  510 + }
  511 + // #1.2
  512 + try {
  513 + writeLock.lock();
  514 + this.getParametersFromProfile(attributes, telemetries, registration, paths);
  515 + } catch (Exception e) {
  516 + log.error("UpdateAttrTelemetry", e);
  517 + } finally {
  518 + writeLock.unlock();
  519 + }
  520 + if (attributes.getAsJsonObject().entrySet().size() > 0)
  521 + this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);
  522 + if (telemetries.getAsJsonObject().entrySet().size() > 0)
  523 + this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
  524 + }
  525 +
  526 + /**
  527 + * get AttrName/TelemetryName with value from Client
  528 + *
  529 + * @param registration -
  530 + * @return - JsonObject, format: {name: value}}
  531 + */
  532 + private JsonObject getAttributeClient(Registration registration) {
  533 + if (registration.getAdditionalRegistrationAttributes().size() > 0) {
  534 + JsonObject resNameValues = new JsonObject();
  535 + registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty);
  536 + return resNameValues;
  537 + }
  538 + return null;
  539 + }
  540 +
  541 + /**
  542 + * @param attributes - new JsonObject
  543 + * @param telemetry - new JsonObject
  544 + * @param registration - Registration LwM2M Client
  545 + * result: add to JsonObject those resources to which the user is subscribed and they have a value
  546 + * if path==null add All resources else only one
  547 + * (attributes/telemetry): new {name(Attr/Telemetry):value}
  548 + */
  549 + private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
  550 + AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid());
  551 + attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> {
  552 + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
  553 + if (pathIds.isResource()) {
  554 + if (path == null || path.contains(p.getAsString())) {
  555 + this.addParameters(p.getAsString().toString(), attributes, registration);
  556 + }
  557 + }
  558 + });
  559 + attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> {
  560 + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
  561 + if (pathIds.isResource()) {
  562 + if (path == null || path.contains(p.getAsString())) {
  563 + this.addParameters(p.getAsString().toString(), telemetry, registration);
  564 + }
  565 + }
  566 + });
  567 + }
  568 +
  569 + /**
  570 + * @param parameters - JsonObject attributes/telemetry
  571 + * @param registration - Registration LwM2M Client
  572 + */
  573 + private void addParameters(String path, JsonObject parameters, Registration registration) {
  574 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId());
  575 + JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile();
  576 + String resName = String.valueOf(names.get(path));
  577 + if (resName != null && !resName.isEmpty()) {
  578 + try {
  579 + String resValue = this.getResourceValueToString(lwM2MClient, path);
  580 + if (resValue != null) {
  581 + parameters.addProperty(resName, resValue);
  582 + }
  583 + } catch (Exception e) {
  584 + log.error(e.getStackTrace().toString());
  585 + }
  586 + }
  587 + }
  588 +
  589 + /**
  590 + * Prepare Sent to Thigsboard callback - Attribute or Telemetry
  591 + *
  592 + * @param msg - JsonArray: [{name: value}]
  593 + * @param topicName - Api Attribute or Telemetry
  594 + * @param registration - Id of Registration LwM2M Client
  595 + */
  596 + public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
  597 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  598 + if (sessionInfo != null) {
  599 + context.sentParametersOnThingsboard(msg, topicName, sessionInfo);
  600 + } else {
  601 + log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
  602 + }
  603 + }
  604 +
  605 + /**
  606 + * Start observe
  607 + * #1 - Analyze:
  608 + * #1.1 path in observe == (attribute or telemetry)
  609 + * #2 Analyze after sent request (response):
  610 + * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation
  611 + * #2.2 Next: ObservationListener.onResponse *
  612 + *
  613 + * @param lwServer - LeshanServer
  614 + * @param registration - Registration LwM2M Client
  615 + */
  616 + private void onSentObserveToClient(LeshanServer lwServer, Registration registration) {
  617 + if (lwServer.getObservationService().getObservations(registration).size() > 0) {
  618 + this.setCancelObservations(lwServer, registration);
  619 + }
  620 + UUID profileUUid = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid();
  621 + AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(profileUUid);
  622 + attrTelemetryObserveValue.getPostObserveProfile().forEach(p -> {
  623 + // #1.1
  624 + String target = (getValidateObserve(attrTelemetryObserveValue.getPostAttributeProfile(), p.getAsString().toString())) ?
  625 + p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ?
  626 + p.getAsString().toString() : null;
  627 + if (target != null) {
  628 + // #2
  629 + if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) {
  630 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE,
  631 + null, null, null, null, this.context.getCtxServer().getTimeout(),
  632 + false);
  633 + }
  634 + }
  635 + });
  636 + }
  637 +
  638 + public void setCancelObservations(LeshanServer lwServer, Registration registration) {
  639 + if (registration != null) {
  640 + Set<Observation> observations = lwServer.getObservationService().getObservations(registration);
  641 + observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()));
  642 + }
  643 + }
  644 +
  645 + /**
  646 + * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
  647 + * At server side this will not remove the observation from the observation store, to do it you need to use
  648 + * {@code ObservationService#cancelObservation()}
  649 + */
  650 + public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {
  651 + lwServer.getObservationService().cancelObservations(registration, path);
  652 + }
  653 +
  654 + /**
  655 + * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile
  656 + * @param path - recourse from postObserveProfile
  657 + * @return rez - true if path observe is in attribute/telemetry
  658 + */
  659 + private boolean getValidateObserve(JsonElement parameters, String path) {
  660 + AtomicBoolean rez = new AtomicBoolean(false);
  661 + if (parameters.isJsonArray()) {
  662 + parameters.getAsJsonArray().forEach(p -> {
  663 + if (p.getAsString().toString().equals(path)) rez.set(true);
  664 + }
  665 + );
  666 + } else if (parameters.isJsonObject()) {
  667 + rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString())
  668 + .filter(path::equals).findAny().orElse(null) != null);
  669 + }
  670 + return rez.get();
  671 + }
  672 +
  673 + /**
  674 + * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
  675 + *
  676 + * @param registration - Registration LwM2M Client
  677 + * @param path - observe
  678 + * @param response - observe
  679 + */
  680 +
  681 + public void onObservationResponse(Registration registration, String path, ReadResponse response) {
  682 + if (response.getContent() != null) {
  683 + if (response.getContent() instanceof LwM2mObject) {
  684 +// LwM2mObject content = (LwM2mObject) response.getContent();
  685 + } else if (response.getContent() instanceof LwM2mObjectInstance) {
  686 +// LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent();
  687 + } else if (response.getContent() instanceof LwM2mSingleResource) {
  688 + LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();
  689 + this.onObservationSetResourcesValue(registration, content.getValue(), null, path);
  690 + } else if (response.getContent() instanceof LwM2mMultipleResource) {
  691 + LwM2mMultipleResource content = (LwM2mMultipleResource) response.getContent();
  692 + this.onObservationSetResourcesValue(registration, null, content.getValues(), path);
  693 + }
  694 + }
  695 + }
  696 +
  697 + /**
  698 + * Sending observe value of resources to thingsboard
  699 + * #1 Return old Value Resource from LwM2MClient
  700 + * #2 Update new Resources (replace old Resource Value on new Resource Value)
  701 + *
  702 + * @param registration - Registration LwM2M Client
  703 + * @param value - LwM2mSingleResource response.getContent()
  704 + * @param values - LwM2mSingleResource response.getContent()
  705 + * @param path - resource
  706 + */
  707 + private void onObservationSetResourcesValue(Registration registration, Object value, Map<Integer, ?> values, String path) {
  708 + boolean isChange = false;
  709 + try {
  710 + writeLock.lock();
  711 + // #1
  712 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  713 + LwM2mPath pathIds = new LwM2mPath(path);
  714 +// log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values);
  715 + ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds);
  716 + ResourceValue resValueOld = lwM2MClient.getResources().get(path);
  717 + // #2
  718 + if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) {
  719 + lwM2MClient.getResources().get(path).setValues(values);
  720 +// ResourceValue resourceValue = new ResourceValue(values, null, true);
  721 +// lwM2MClient.getResources().put(path, resourceValue);
  722 + isChange = true;
  723 + } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) {
  724 + lwM2MClient.getResources().get(path).setValue(value);
  725 +// ResourceValue resourceValueOld = lwM2MClient.getResources().get(path);
  726 +// lwM2MClient.getResources().remove(resourceValueOld);
  727 +// ResourceValue resourceValue = new ResourceValue(null, value, false);
  728 +// lwM2MClient.getResources().put(path, resourceValue);
  729 + log.warn("upDateResize: [{}] [{}] [{}] [{}]", lwM2MClient.getEndPoint(), lwM2MClient.getResources().size(), value, path);
  730 + isChange = true;
  731 + }
  732 + } catch (Exception e) {
  733 + log.error("#1_1 Update ResourcesValue after Observation is unsuccessfully path: [{}] value: [{}] [{}]", path, value, e.toString());
  734 + } finally {
  735 + writeLock.unlock();
  736 + }
  737 +
  738 + if (isChange) {
  739 + Set<String> paths = new HashSet<>();
  740 + paths.add(path);
  741 + this.updateAttrTelemetry(registration, false, paths);
  742 + }
  743 + }
  744 +
  745 + /**
  746 + * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
  747 + * config attr/telemetry... in profile
  748 + */
  749 + public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) {
  750 + log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
  751 + }
  752 +
  753 + /**
  754 + * Update - sent request in change value resources in Client
  755 + * Path to resources from profile equal keyName or from ModelObject equal name
  756 + * Only for resources: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
  757 + * Delete - nothing *
  758 + *
  759 + * @param msg -
  760 + */
  761 + public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
  762 + if (msg.getSharedUpdatedCount() > 0) {
  763 + JsonElement el = JsonConverter.toJson(msg);
  764 + el.getAsJsonObject().entrySet().forEach(de -> {
  765 + String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());
  766 + String value = de.getValue().getAsString();
  767 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
  768 + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  769 + ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));
  770 + if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) {
  771 + if (resourceModel != null && resourceModel.operations.isWritable()) {
  772 + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
  773 + ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(),
  774 + false);
  775 + } else {
  776 + log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
  777 + String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);
  778 + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  779 + }
  780 + } else {
  781 + log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);
  782 + 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);
  783 + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  784 + }
  785 + });
  786 + } else if (msg.getSharedDeletedCount() > 0) {
  787 + log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
  788 + }
  789 + }
  790 +
  791 + /**
  792 + * Get path to resource from profile equal keyName or from ModelObject equal name
  793 + * Only for resource: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
  794 + *
  795 + * @param sessionInfo -
  796 + * @param name -
  797 + * @return path if path isPresent in postProfile
  798 + */
  799 + private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) {
  800 + String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name);
  801 +// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name);
  802 + return !profilePath.isEmpty() ? profilePath : null;
  803 + }
  804 +
  805 + /**
  806 + * @param profile -
  807 + * @param path -
  808 + * @return true if path isPresent in postAttributeProfile
  809 + */
  810 + private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) {
  811 + Set<String> attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
  812 + return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
  813 + }
  814 +
  815 + /**
  816 + * @param profile -
  817 + * @param path -
  818 + * @return true if path isPresent in postAttributeProfile
  819 + */
  820 + private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) {
  821 + Set<String> telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), Set.class);
  822 + return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
  823 + }
  824 +
  825 +
  826 + /**
  827 + * Get path to resource from profile equal keyName
  828 + *
  829 + * @param sessionInfo -
  830 + * @param name -
  831 + * @return -
  832 + */
  833 + private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
  834 + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  835 + return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
  836 + .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey)
  837 + .orElse("");
  838 + }
  839 +
  840 + /**
  841 + * Update resource (attribute) value on thingsboard after update value in client
  842 + *
  843 + * @param registration -
  844 + * @param path -
  845 + * @param request -
  846 + */
  847 + public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) {
  848 + ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path));
  849 + if (resource.multiple) {
  850 + this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path);
  851 + } else {
  852 + this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path);
  853 + }
  854 + if (isDelayedUpdate) lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null)
  855 + .onSuccessOrErrorDelayedRequests(request.getPath().toString());
  856 + }
  857 +
  858 + /**
  859 + * @param sessionInfo -
  860 + * @param deviceProfile -
  861 + */
  862 + public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
  863 + Set<String> registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet()
  864 + .stream()
  865 + .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId()))
  866 + .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
  867 + if (registrationIds.size() > 0) {
  868 + this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);
  869 + }
  870 + }
  871 +
  872 + /**
  873 + * @param sessionInfo -
  874 + * @param device -
  875 + * @param deviceProfileOpt -
  876 + */
  877 + public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
  878 + Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()
  879 + .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))
  880 + .map(Map.Entry::getKey)
  881 + .findFirst();
  882 + registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
  883 + }
  884 +
  885 + /**
  886 + * Update parameters device in LwM2MClient
  887 + * If new deviceProfile != old deviceProfile => update deviceProfile
  888 + *
  889 + * @param registrationId -
  890 + * @param device -
  891 + */
  892 + private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {
  893 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);
  894 + lwM2MClient.setDeviceName(device.getName());
  895 + if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {
  896 + Set<String> registrationIds = new HashSet<>();
  897 + registrationIds.add(registrationId);
  898 + deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile));
  899 + }
  900 +
  901 + lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId());
  902 + }
  903 +
  904 + /**
  905 + * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName)
  906 + * #2 Update in lwM2MClient: ...Profile if changes from update device
  907 + * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName)
  908 + * #3.1 Attribute isChange (add&del)
  909 + * #3.2 Telemetry isChange (add&del)
  910 + * #3.3 KeyName isChange (add)
  911 + * #4 update
  912 + * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value to thingsboard
  913 + * #4.2 del
  914 + * -- if add attributes includes del telemetry - result del for observe
  915 + * #5
  916 + * #5.1 Observe isChange (add&del)
  917 + * #5.2 Observe.add
  918 + * -- path Attr/Telemetry includes newObserve and does not include oldObserve: sent Request observe to Client
  919 + * #5.3 Observe.del
  920 + * -- different between newObserve and oldObserve: sent Request cancel observe to client
  921 + *
  922 + * @param registrationIds -
  923 + * @param deviceProfile -
  924 + */
  925 + private void onDeviceUpdateChangeProfile(Set<String> registrationIds, DeviceProfile deviceProfile) {
  926 +
  927 + AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
  928 + if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) {
  929 +
  930 + // #1
  931 + JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile();
  932 + Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class);
  933 + JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile();
  934 + Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class);
  935 + JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile();
  936 + JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile();
  937 +
  938 + AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
  939 + JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile();
  940 + Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class);
  941 + JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile();
  942 + Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class);
  943 + JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile();
  944 + JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile();
  945 +
  946 + // #3
  947 + ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters();
  948 + // #3.1
  949 + if (!attributeOld.equals(attributeNew)) {
  950 + ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, Set.class), attributeSetNew);
  951 + sentAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd());
  952 + sentAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel());
  953 + }
  954 + // #3.2
  955 + if (!attributeOld.equals(attributeNew)) {
  956 + ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, Set.class), telemetrySetNew);
  957 + sentAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd());
  958 + sentAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel());
  959 + }
  960 + // #3.3
  961 + if (!keyNameOld.equals(keyNameNew)) {
  962 + ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), ConcurrentHashMap.class),
  963 + new Gson().fromJson(keyNameNew.toString(), ConcurrentHashMap.class));
  964 + sentAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd());
  965 + }
  966 +
  967 + // #4.1 add
  968 + if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
  969 + // update value in Resources
  970 + registrationIds.forEach(registrationId -> {
  971 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
  972 + LeshanServer lwServer = lwM2MClient.getLwServer();
  973 + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
  974 + log.warn("[{}] # 4.1", registration.getEndpoint());
  975 + this.updateResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ);
  976 + // sent attr/telemetry to tingsboard for new path
  977 + this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd());
  978 + });
  979 + }
  980 + // #4.2 del
  981 + if (sentAttrToThingsboard.getPathPostParametersDel().size() > 0) {
  982 + ResultsAnalyzerParameters sentAttrToThingsboardDel = this.getAnalyzerParameters(sentAttrToThingsboard.getPathPostParametersAdd(), sentAttrToThingsboard.getPathPostParametersDel());
  983 + sentAttrToThingsboard.setPathPostParametersDel(sentAttrToThingsboardDel.getPathPostParametersDel());
  984 + }
  985 +
  986 + // #5.1
  987 + if (!observeOld.equals(observeNew)) {
  988 + Set observeSetOld = new Gson().fromJson(observeOld, Set.class);
  989 + Set observeSetNew = new Gson().fromJson(observeNew, Set.class);
  990 + //#5.2 add
  991 + // path Attr/Telemetry includes newObserve
  992 + attributeSetOld.addAll(telemetrySetOld);
  993 + ResultsAnalyzerParameters sentObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe
  994 + attributeSetNew.addAll(telemetrySetNew);
  995 + ResultsAnalyzerParameters sentObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe
  996 + // does not include oldObserve
  997 + ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd());
  998 + // sent Request observe to Client
  999 + registrationIds.forEach(registrationId -> {
  1000 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(null, registrationId);
  1001 + LeshanServer lwServer = lwM2MClient.getLwServer();
  1002 + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
  1003 + log.warn("[{}] # 5.1", registration.getEndpoint());
  1004 + this.updateResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE);
  1005 + // 5.3 del
  1006 + // sent Request cancel observe to Client
  1007 + this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel());
  1008 + });
  1009 + }
  1010 + }
  1011 + }
  1012 +
  1013 + /**
  1014 + * Compare old list with new list after change AttrTelemetryObserve in config Profile
  1015 + *
  1016 + * @param parametersOld -
  1017 + * @param parametersNew -
  1018 + * @return ResultsAnalyzerParameters: add && new
  1019 + */
  1020 + private ResultsAnalyzerParameters getAnalyzerParameters(Set<String> parametersOld, Set<String> parametersNew) {
  1021 + ResultsAnalyzerParameters analyzerParameters = null;
  1022 + if (!parametersOld.equals(parametersNew)) {
  1023 + analyzerParameters = new ResultsAnalyzerParameters();
  1024 + analyzerParameters.setPathPostParametersAdd(parametersNew
  1025 + .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet()));
  1026 + analyzerParameters.setPathPostParametersDel(parametersOld
  1027 + .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet()));
  1028 + }
  1029 + return analyzerParameters;
  1030 + }
  1031 +
  1032 + private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {
  1033 + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
  1034 + Set<String> paths = keyNameNew.entrySet()
  1035 + .stream()
  1036 + .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
  1037 + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();
  1038 + analyzerParameters.setPathPostParametersAdd(paths);
  1039 + return analyzerParameters;
  1040 + }
  1041 +
  1042 + private ResultsAnalyzerParameters getAnalyzerParametersIn(Set<String> parametersObserve, Set<String> parameters) {
  1043 + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
  1044 + analyzerParameters.setPathPostParametersAdd(parametersObserve
  1045 + .stream().filter(parameters::contains).collect(Collectors.toSet()));
  1046 + return analyzerParameters;
  1047 + }
  1048 +
  1049 + /**
  1050 + * Update Resource value after change RezAttrTelemetry in config Profile
  1051 + * sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
  1052 + *
  1053 + * @param lwServer - LeshanServer
  1054 + * @param registration - Registration LwM2M Client
  1055 + * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
  1056 + */
  1057 + private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, Set<String> targets, String typeOper) {
  1058 + targets.forEach(target -> {
  1059 + LwM2mPath pathIds = new LwM2mPath(target);
  1060 + if (pathIds.isResource()) {
  1061 + if (GET_TYPE_OPER_READ.equals(typeOper)) {
  1062 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
  1063 + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
  1064 + false);
  1065 + } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
  1066 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
  1067 + null, null, null, null, this.context.getCtxServer().getTimeout(),
  1068 + false);
  1069 + }
  1070 + }
  1071 + });
  1072 + }
  1073 +
  1074 + private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set<String> paramAnallyzer) {
  1075 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  1076 + paramAnallyzer.forEach(p -> {
  1077 + if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) {
  1078 + this.setCancelObservationRecourse(lwServer, registration, p);
  1079 + }
  1080 + }
  1081 + );
  1082 + }
  1083 +
  1084 + private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) {
  1085 + ResourceValue resourceValue = null;
  1086 + if (pathIds.isResource()) {
  1087 + resourceValue = lwM2MClient.getResources().get(pathIds.toString());
  1088 + }
  1089 + return resourceValue;
  1090 + }
  1091 +
  1092 + /**
  1093 + * Trigger Server path = "/1/0/8"
  1094 + *
  1095 + * Trigger bootStrap path = "/1/0/9" - have to implemented on client
  1096 + */
  1097 + public void doTrigger(LeshanServer lwServer, Registration registration, String path) {
  1098 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,
  1099 + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
  1100 + false);
  1101 + }
  1102 +
  1103 + /**
  1104 + * Session device in thingsboard is closed
  1105 + *
  1106 + * @param sessionInfo - lwm2m client
  1107 + */
  1108 + private void doCloseSession(SessionInfoProto sessionInfo) {
  1109 + TransportProtos.SessionEvent event = SessionEvent.CLOSED;
  1110 + TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
  1111 + .setSessionType(TransportProtos.SessionType.ASYNC)
  1112 + .setEvent(event).build();
  1113 + transportService.process(sessionInfo, msg, null);
  1114 + }
  1115 +
  1116 + /**
  1117 + * Deregister session in transport
  1118 + *
  1119 + * @param sessionInfo - lwm2m client
  1120 + */
  1121 + public void doDisconnect(SessionInfoProto sessionInfo) {
  1122 + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
  1123 + transportService.deregisterSession(sessionInfo);
  1124 + }
  1125 +
  1126 + private void checkInactivityAndReportActivity() {
  1127 + lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key)));
  1128 + }
  1129 +
  1130 + /**
  1131 + * if sessionInfo removed from sessions, then new registerAsyncSession
  1132 + *
  1133 + * @param sessionInfo -
  1134 + */
  1135 + private void checkInactivity(SessionInfoProto sessionInfo) {
  1136 + if (transportService.reportActivity(sessionInfo) == null) {
  1137 + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));
  1138 + }
  1139 + }
  1140 +
  1141 + public void sentLogsToThingsboard(String msg, Registration registration) {
  1142 + if (msg != null) {
  1143 + JsonObject telemetries = new JsonObject();
  1144 + telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);
  1145 + this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration);
  1146 + }
  1147 + }
  1148 +
  1149 + /**
  1150 + * @param path - path resource
  1151 + * @return - value of Resource or null
  1152 + */
  1153 + private String getResourceValueToString(LwM2MClient lwM2MClient, String path) {
  1154 + LwM2mPath pathIds = new LwM2mPath(path);
  1155 + ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds);
  1156 + return (resourceValue == null) ? null :
  1157 + (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds);
  1158 + }
  1159 +}
... ...