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,7 +278,6 @@ public class DeviceController extends BaseController {
278 try { 278 try {
279 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS); 279 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
280 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials)); 280 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
281 - //log.info("0 LwM2M CredentialsUpdate start)  
282 tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null); 281 tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null);
283 logEntityAction(device.getId(), device, 282 logEntityAction(device.getId(), device,
284 device.getCustomerId(), 283 device.getCustomerId(),
@@ -15,17 +15,32 @@ @@ -15,17 +15,32 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
18 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.security.access.prepost.PreAuthorize; 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 import org.thingsboard.server.common.data.exception.ThingsboardException; 32 import org.thingsboard.server.common.data.exception.ThingsboardException;
22 import org.thingsboard.server.common.data.lwm2m.LwM2mObject; 33 import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
23 import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; 34 import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
24 import org.thingsboard.server.common.data.page.PageData; 35 import org.thingsboard.server.common.data.page.PageData;
25 import org.thingsboard.server.common.data.page.PageLink; 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 import org.thingsboard.server.queue.util.TbCoreComponent; 39 import org.thingsboard.server.queue.util.TbCoreComponent;
  40 +import org.thingsboard.server.service.security.permission.Resource;
27 41
28 import java.util.List; 42 import java.util.List;
  43 +import java.util.Map;
29 44
30 @Slf4j 45 @Slf4j
31 @RestController 46 @RestController
@@ -33,13 +48,16 @@ import java.util.List; @@ -33,13 +48,16 @@ import java.util.List;
33 @RequestMapping("/api") 48 @RequestMapping("/api")
34 public class DeviceLwm2mController extends BaseController { 49 public class DeviceLwm2mController extends BaseController {
35 50
36 -  
37 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 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 @ResponseBody 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 try { 59 try {
42 - return lwM2MModelsRepository.getLwm2mObjects(objectIds, null); 60 + return lwM2MModelsRepository.getLwm2mObjects(objectIds, searchText, sortProperty, sortOrder);
43 } catch (Exception e) { 61 } catch (Exception e) {
44 throw handleException(e); 62 throw handleException(e);
45 } 63 }
@@ -50,11 +68,11 @@ public class DeviceLwm2mController extends BaseController { @@ -50,11 +68,11 @@ public class DeviceLwm2mController extends BaseController {
50 @ResponseBody 68 @ResponseBody
51 public PageData<LwM2mObject> getLwm2mListObjects(@RequestParam int pageSize, 69 public PageData<LwM2mObject> getLwm2mListObjects(@RequestParam int pageSize,
52 @RequestParam int page, 70 @RequestParam int page,
53 - @RequestParam(required = false) String textSearch, 71 + @RequestParam(required = false) String searchText,
54 @RequestParam(required = false) String sortProperty, 72 @RequestParam(required = false) String sortProperty,
55 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 73 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
56 try { 74 try {
57 - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 75 + PageLink pageLink = createPageLink(pageSize, page, searchText, sortProperty, sortOrder);
58 return checkNotNull(lwM2MModelsRepository.findDeviceLwm2mObjects(getTenantId(), pageLink)); 76 return checkNotNull(lwM2MModelsRepository.findDeviceLwm2mObjects(getTenantId(), pageLink));
59 } catch (Exception e) { 77 } catch (Exception e) {
60 throw handleException(e); 78 throw handleException(e);
@@ -72,4 +90,40 @@ public class DeviceLwm2mController extends BaseController { @@ -72,4 +90,40 @@ public class DeviceLwm2mController extends BaseController {
72 throw handleException(e); 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,8 +23,11 @@ import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 23 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
24 import org.springframework.data.domain.PageImpl; 24 import org.springframework.data.domain.PageImpl;
25 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
26 -import org.thingsboard.server.common.data.lwm2m.*;  
27 import org.thingsboard.server.common.data.id.TenantId; 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 import org.thingsboard.server.common.data.page.PageData; 31 import org.thingsboard.server.common.data.page.PageData;
29 import org.thingsboard.server.common.data.page.PageLink; 32 import org.thingsboard.server.common.data.page.PageLink;
30 import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap; 33 import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap;
@@ -33,17 +36,24 @@ import org.thingsboard.server.dao.service.Validator; @@ -33,17 +36,24 @@ import org.thingsboard.server.dao.service.Validator;
33 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; 36 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
34 37
35 import java.math.BigInteger; 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 import java.security.cert.CertificateEncodingException; 44 import java.security.cert.CertificateEncodingException;
38 import java.security.cert.X509Certificate; 45 import java.security.cert.X509Certificate;
39 import java.security.spec.ECGenParameterSpec; 46 import java.security.spec.ECGenParameterSpec;
40 import java.security.spec.ECParameterSpec; 47 import java.security.spec.ECParameterSpec;
41 -import java.security.spec.ECPublicKeySpec;  
42 import java.security.spec.ECPoint; 48 import java.security.spec.ECPoint;
  49 +import java.security.spec.ECPublicKeySpec;
43 import java.security.spec.KeySpec; 50 import java.security.spec.KeySpec;
44 -import java.util.List;  
45 import java.util.ArrayList; 51 import java.util.ArrayList;
  52 +import java.util.Comparator;
  53 +import java.util.List;
  54 +import java.util.concurrent.atomic.AtomicInteger;
46 import java.util.function.Predicate; 55 import java.util.function.Predicate;
  56 +import java.util.stream.Collector;
47 import java.util.stream.Collectors; 57 import java.util.stream.Collectors;
48 import java.util.stream.IntStream; 58 import java.util.stream.IntStream;
49 59
@@ -70,22 +80,32 @@ public class LwM2MModelsRepository { @@ -70,22 +80,32 @@ public class LwM2MModelsRepository {
70 * Filter by Predicate (uses objectIds, if objectIds is null then it uses textSearch, 80 * Filter by Predicate (uses objectIds, if objectIds is null then it uses textSearch,
71 * if textSearch is null then it uses AllList from List<ObjectModel>) 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 * @param predicate 99 * @param predicate
81 * @return list of LwM2mObject 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 List<LwM2mObject> lwM2mObjects = new ArrayList<>(); 103 List<LwM2mObject> lwM2mObjects = new ArrayList<>();
85 List<ObjectModel> listObjects = (predicate == null) ? this.contextServer.getModelsValue() : 104 List<ObjectModel> listObjects = (predicate == null) ? this.contextServer.getModelsValue() :
86 contextServer.getModelsValue().stream() 105 contextServer.getModelsValue().stream()
87 .filter(predicate) 106 .filter(predicate)
88 .collect(Collectors.toList()); 107 .collect(Collectors.toList());
  108 +
89 listObjects.forEach(obj -> { 109 listObjects.forEach(obj -> {
90 LwM2mObject lwM2mObject = new LwM2mObject(); 110 LwM2mObject lwM2mObject = new LwM2mObject();
91 lwM2mObject.setId(obj.id); 111 lwM2mObject.setId(obj.id);
@@ -105,6 +125,29 @@ public class LwM2MModelsRepository { @@ -105,6 +125,29 @@ public class LwM2MModelsRepository {
105 lwM2mObject.setInstances(new LwM2mInstance[]{instance}); 125 lwM2mObject.setInstances(new LwM2mInstance[]{instance});
106 lwM2mObjects.add(lwM2mObject); 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 return lwM2mObjects; 151 return lwM2mObjects;
109 } 152 }
110 153
@@ -126,13 +169,32 @@ public class LwM2MModelsRepository { @@ -126,13 +169,32 @@ public class LwM2MModelsRepository {
126 * PageNumber = 1, PageSize = List<LwM2mObject>.size() 169 * PageNumber = 1, PageSize = List<LwM2mObject>.size()
127 */ 170 */
128 public PageData<LwM2mObject> findLwm2mListObjects(PageLink pageLink) { 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 PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext()); 176 PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext());
131 return pageData; 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 * @param securityMode 198 * @param securityMode
137 * @param bootstrapServerIs 199 * @param bootstrapServerIs
138 * @return ServerSecurityConfig more value is default: Important - port, host, publicKey 200 * @return ServerSecurityConfig more value is default: Important - port, host, publicKey
@@ -143,60 +205,60 @@ public class LwM2MModelsRepository { @@ -143,60 +205,60 @@ public class LwM2MModelsRepository {
143 } 205 }
144 206
145 /** 207 /**
146 - *  
147 * @param bootstrapServerIs 208 * @param bootstrapServerIs
148 * @param mode 209 * @param mode
149 * @return ServerSecurityConfig more value is default: Important - port, host, publicKey 210 * @return ServerSecurityConfig more value is default: Important - port, host, publicKey
150 */ 211 */
151 private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) { 212 private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) {
152 ServerSecurityConfig bsServ = new ServerSecurityConfig(); 213 ServerSecurityConfig bsServ = new ServerSecurityConfig();
  214 + bsServ.setBootstrapServerIs(bootstrapServerIs);
153 if (bootstrapServerIs) { 215 if (bootstrapServerIs) {
  216 + bsServ.setServerId(contextBootStrap.getBootstrapServerId());
154 switch (mode) { 217 switch (mode) {
155 case NO_SEC: 218 case NO_SEC:
156 bsServ.setHost(contextBootStrap.getBootstrapHost()); 219 bsServ.setHost(contextBootStrap.getBootstrapHost());
157 - bsServ.setPort(contextBootStrap.getBootstrapPort()); 220 + bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk());
158 bsServ.setServerPublicKey(""); 221 bsServ.setServerPublicKey("");
159 break; 222 break;
160 case PSK: 223 case PSK:
161 bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); 224 bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
162 - bsServ.setPort(contextBootStrap.getBootstrapSecurePort()); 225 + bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk());
163 bsServ.setServerPublicKey(""); 226 bsServ.setServerPublicKey("");
164 break; 227 break;
165 case RPK: 228 case RPK:
166 bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); 229 bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
167 - bsServ.setPort(contextBootStrap.getBootstrapSecurePort()); 230 + bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk());
168 bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); 231 bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));
169 break; 232 break;
170 case X509: 233 case X509:
171 bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); 234 bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
172 - bsServ.setPort(contextBootStrap.getBootstrapSecurePortCert()); 235 + bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509());
173 bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias())); 236 bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias()));
174 break; 237 break;
175 default: 238 default:
176 break; 239 break;
177 } 240 }
178 } else { 241 } else {
179 - bsServ.setBootstrapServerIs(bootstrapServerIs);  
180 - bsServ.setServerId(123); 242 + bsServ.setServerId(contextServer.getServerId());
181 switch (mode) { 243 switch (mode) {
182 case NO_SEC: 244 case NO_SEC:
183 bsServ.setHost(contextServer.getServerHost()); 245 bsServ.setHost(contextServer.getServerHost());
184 - bsServ.setPort(contextServer.getServerPort()); 246 + bsServ.setPort(contextServer.getServerPortNoSecPsk());
185 bsServ.setServerPublicKey(""); 247 bsServ.setServerPublicKey("");
186 break; 248 break;
187 case PSK: 249 case PSK:
188 bsServ.setHost(contextServer.getServerSecureHost()); 250 bsServ.setHost(contextServer.getServerSecureHost());
189 - bsServ.setPort(contextServer.getServerSecurePort()); 251 + bsServ.setPort(contextServer.getServerPortPsk());
190 bsServ.setServerPublicKey(""); 252 bsServ.setServerPublicKey("");
191 break; 253 break;
192 case RPK: 254 case RPK:
193 bsServ.setHost(contextServer.getServerSecureHost()); 255 bsServ.setHost(contextServer.getServerSecureHost());
194 - bsServ.setPort(contextServer.getServerSecurePort()); 256 + bsServ.setPort(contextServer.getServerPortRpk());
195 bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY())); 257 bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
196 break; 258 break;
197 case X509: 259 case X509:
198 bsServ.setHost(contextServer.getServerSecureHost()); 260 bsServ.setHost(contextServer.getServerSecureHost());
199 - bsServ.setPort(contextServer.getServerSecurePortCert()); 261 + bsServ.setPort(contextServer.getServerPortX509());
200 bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias())); 262 bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias()));
201 break; 263 break;
202 default: 264 default:
@@ -207,25 +269,23 @@ public class LwM2MModelsRepository { @@ -207,25 +269,23 @@ public class LwM2MModelsRepository {
207 } 269 }
208 270
209 /** 271 /**
210 - *  
211 * @param alias 272 * @param alias
212 * @return PublicKey format HexString or null 273 * @return PublicKey format HexString or null
213 */ 274 */
214 - private String getServerPublicKeyX509 (String alias) { 275 + private String getServerPublicKeyX509(String alias) {
215 try { 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 } catch (CertificateEncodingException | KeyStoreException e) { 279 } catch (CertificateEncodingException | KeyStoreException e) {
219 e.printStackTrace(); 280 e.printStackTrace();
220 } 281 }
221 - return null; 282 + return null;
222 } 283 }
223 284
224 /** 285 /**
225 - *  
226 * @param publicServerX 286 * @param publicServerX
227 * @param publicServerY 287 * @param publicServerY
228 - * @return PublicKey format HexString or null 288 + * @return PublicKey format HexString or null
229 */ 289 */
230 private String getRPKPublicKey(String publicServerX, String publicServerY) { 290 private String getRPKPublicKey(String publicServerX, String publicServerY) {
231 try { 291 try {
@@ -241,9 +301,9 @@ public class LwM2MModelsRepository { @@ -241,9 +301,9 @@ public class LwM2MModelsRepository {
241 KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), 301 KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
242 parameterSpec); 302 parameterSpec);
243 /** Get keys */ 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 } catch (GeneralSecurityException | IllegalArgumentException e) { 309 } catch (GeneralSecurityException | IllegalArgumentException e) {
@@ -49,34 +49,31 @@ import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; @@ -49,34 +49,31 @@ import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
49 import org.thingsboard.server.dao.device.DeviceCredentialsService; 49 import org.thingsboard.server.dao.device.DeviceCredentialsService;
50 import org.thingsboard.server.dao.device.DeviceProvisionService; 50 import org.thingsboard.server.dao.device.DeviceProvisionService;
51 import org.thingsboard.server.dao.device.DeviceService; 51 import org.thingsboard.server.dao.device.DeviceService;
  52 +import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
52 import org.thingsboard.server.dao.device.provision.ProvisionRequest; 53 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
53 import org.thingsboard.server.dao.device.provision.ProvisionResponse; 54 import org.thingsboard.server.dao.device.provision.ProvisionResponse;
54 import org.thingsboard.server.dao.relation.RelationService; 55 import org.thingsboard.server.dao.relation.RelationService;
  56 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
55 import org.thingsboard.server.dao.util.mapping.JacksonUtil; 57 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
56 import org.thingsboard.server.gen.transport.TransportProtos; 58 import org.thingsboard.server.gen.transport.TransportProtos;
57 -import org.thingsboard.server.gen.transport.TransportProtos.DeviceCredentialsProto;  
58 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; 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 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; 60 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
62 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg; 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 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; 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 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus; 65 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus;
67 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; 66 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
68 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; 67 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
69 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; 68 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
  69 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
70 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; 70 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
71 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; 71 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
72 -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;  
73 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 72 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
74 import org.thingsboard.server.queue.util.TbCoreComponent; 73 import org.thingsboard.server.queue.util.TbCoreComponent;
75 -import org.thingsboard.server.dao.device.provision.ProvisionFailedException;  
76 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 74 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
77 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 75 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
78 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 76 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
79 -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;  
80 import org.thingsboard.server.service.queue.TbClusterService; 77 import org.thingsboard.server.service.queue.TbClusterService;
81 import org.thingsboard.server.service.state.DeviceStateService; 78 import org.thingsboard.server.service.state.DeviceStateService;
82 79
@@ -326,6 +323,7 @@ public class DefaultTransportApiService implements TransportApiService { @@ -326,6 +323,7 @@ public class DefaultTransportApiService implements TransportApiService {
326 break; 323 break;
327 case MQTT_BASIC: 324 case MQTT_BASIC:
328 case X509_CERTIFICATE: 325 case X509_CERTIFICATE:
  326 + case LWM2M_CREDENTIALS:
329 provisionResponse.setCredentialsValue(deviceCredentials.getCredentialsValue()); 327 provisionResponse.setCredentialsValue(deviceCredentials.getCredentialsValue());
330 break; 328 break;
331 } 329 }
@@ -575,7 +575,13 @@ transport: @@ -575,7 +575,13 @@ transport:
575 timeout: "${LWM2M_TIMEOUT:120000}" 575 timeout: "${LWM2M_TIMEOUT:120000}"
576 # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}" 576 # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}"
577 model_path_file: "${LWM2M_MODEL_PATH_FILE:}" 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 secure: 585 secure:
580 # Only Certificate_x509: 586 # Only Certificate_x509:
581 # To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format 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,24 +594,19 @@ transport:
588 root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}" 594 root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}"
589 enable_gen_psk_rpk: "${ENABLE_GEN_PSK_RPK:true}" 595 enable_gen_psk_rpk: "${ENABLE_GEN_PSK_RPK:true}"
590 server: 596 server:
  597 + id: "${LWM2M_SERVER_ID:123}"
591 bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" 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 secure: 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 bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" 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 # Only RPK: Public & Private Key 610 # Only RPK: Public & Private Key
610 # create_rpk: "${CREATE_RPK:}" 611 # create_rpk: "${CREATE_RPK:}"
611 public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}" 612 public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}"
@@ -615,23 +616,26 @@ transport: @@ -615,23 +616,26 @@ transport:
615 alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}" 616 alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}"
616 bootstrap: 617 bootstrap:
617 enable: "${BOOTSTRAP:true}" 618 enable: "${BOOTSTRAP:true}"
  619 + id: "${LWM2M_SERVER_ID:111}"
618 bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" 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 secure: 624 secure:
622 - start_all: "${START_BOOTSTRAP_ALL:true}"  
623 - # If only startAll == false  
624 - dtls_mode: "${LWM2M_SECURITY_MODE_BS:1}"  
625 bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" 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 # Only RPK: Public & Private Key 632 # Only RPK: Public & Private Key
629 public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}" 633 public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}"
630 public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}" 634 public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}"
631 private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}" 635 private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}"
632 # Only Certificate_x509: 636 # Only Certificate_x509:
633 alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}" 637 alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}"
634 - # Redis 638 + # Redis
635 redis_url: "${LWM2M_REDIS_URL:''}" 639 redis_url: "${LWM2M_REDIS_URL:''}"
636 640
637 swagger: 641 swagger:
@@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
27 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
28 import org.thingsboard.server.common.data.page.PageData; 28 import org.thingsboard.server.common.data.page.PageData;
29 import org.thingsboard.server.common.data.page.PageLink; 29 import org.thingsboard.server.common.data.page.PageLink;
  30 +import org.thingsboard.server.common.data.security.DeviceCredentials;
30 import org.thingsboard.server.dao.device.provision.ProvisionRequest; 31 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
31 32
32 import java.util.List; 33 import java.util.List;
@@ -45,6 +46,8 @@ public interface DeviceService { @@ -45,6 +46,8 @@ public interface DeviceService {
45 46
46 Device saveDeviceWithAccessToken(Device device, String accessToken); 47 Device saveDeviceWithAccessToken(Device device, String accessToken);
47 48
  49 + Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials);
  50 +
48 Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId); 51 Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId);
49 52
50 Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId); 53 Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId);
@@ -20,7 +20,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; @@ -20,7 +20,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
20 import lombok.EqualsAndHashCode; 20 import lombok.EqualsAndHashCode;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
22 import org.thingsboard.server.common.data.device.data.DeviceData; 22 import org.thingsboard.server.common.data.device.data.DeviceData;
23 -import org.thingsboard.server.common.data.device.profile.DeviceProfileData;  
24 import org.thingsboard.server.common.data.id.CustomerId; 23 import org.thingsboard.server.common.data.id.CustomerId;
25 import org.thingsboard.server.common.data.id.DeviceId; 24 import org.thingsboard.server.common.data.id.DeviceId;
26 import org.thingsboard.server.common.data.id.DeviceProfileId; 25 import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -64,6 +63,17 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -64,6 +63,17 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
64 this.setDeviceData(device.getDeviceData()); 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 public TenantId getTenantId() { 77 public TenantId getTenantId() {
68 return tenantId; 78 return tenantId;
69 } 79 }
@@ -21,6 +21,4 @@ public enum DeviceCredentialsType { @@ -21,6 +21,4 @@ public enum DeviceCredentialsType {
21 X509_CERTIFICATE, 21 X509_CERTIFICATE,
22 MQTT_BASIC, 22 MQTT_BASIC,
23 LWM2M_CREDENTIALS 23 LWM2M_CREDENTIALS
24 -  
25 -  
26 } 24 }
@@ -77,6 +77,7 @@ enum CredentialsType { @@ -77,6 +77,7 @@ enum CredentialsType {
77 ACCESS_TOKEN = 0; 77 ACCESS_TOKEN = 0;
78 X509_CERTIFICATE = 1; 78 X509_CERTIFICATE = 1;
79 MQTT_BASIC = 2; 79 MQTT_BASIC = 2;
  80 + LWM2M_CREDENTIALS = 3;
80 } 81 }
81 82
82 message KeyValueProto { 83 message KeyValueProto {
@@ -65,15 +65,10 @@ @@ -65,15 +65,10 @@
65 <groupId>ch.qos.logback</groupId> 65 <groupId>ch.qos.logback</groupId>
66 <artifactId>logback-classic</artifactId> 66 <artifactId>logback-classic</artifactId>
67 </dependency> 67 </dependency>
68 - <!-- lechan start -->  
69 <dependency> 68 <dependency>
70 <groupId>org.eclipse.leshan</groupId> 69 <groupId>org.eclipse.leshan</groupId>
71 <artifactId>leshan-server-cf</artifactId> 70 <artifactId>leshan-server-cf</artifactId>
72 </dependency> 71 </dependency>
73 -<!-- <dependency>-->  
74 -<!-- <groupId>org.eclipse.leshan</groupId>-->  
75 -<!-- <artifactId>leshan-server-cf</artifactId>-->  
76 -<!-- </dependency> -->  
77 <dependency> 72 <dependency>
78 <groupId>org.eclipse.leshan</groupId> 73 <groupId>org.eclipse.leshan</groupId>
79 <artifactId>leshan-client-cf</artifactId> 74 <artifactId>leshan-client-cf</artifactId>
@@ -83,12 +78,6 @@ @@ -83,12 +78,6 @@
83 <groupId>org.eclipse.leshan</groupId> 78 <groupId>org.eclipse.leshan</groupId>
84 <artifactId>leshan-server-redis</artifactId> 79 <artifactId>leshan-server-redis</artifactId>
85 </dependency> 80 </dependency>
86 -<!-- <dependency>-->  
87 -<!-- <groupId>org.eclipse.californium</groupId>-->  
88 -<!-- <artifactId>californium-core</artifactId>-->  
89 -<!-- </dependency>-->  
90 - <!-- leshan finish -->  
91 -  
92 <dependency> 81 <dependency>
93 <groupId>org.springframework.boot</groupId> 82 <groupId>org.springframework.boot</groupId>
94 <artifactId>spring-boot-starter-test</artifactId> 83 <artifactId>spring-boot-starter-test</artifactId>
@@ -107,14 +96,20 @@ @@ -107,14 +96,20 @@
107 <dependency> 96 <dependency>
108 <groupId>org.eclipse.californium</groupId> 97 <groupId>org.eclipse.californium</groupId>
109 <artifactId>californium-core</artifactId> 98 <artifactId>californium-core</artifactId>
110 - <version>${californium.version}</version>  
111 - <type>test-jar</type> 99 + <type>test-jar</type>
112 <scope>test</scope> 100 <scope>test</scope>
113 </dependency> 101 </dependency>
114 <dependency> 102 <dependency>
115 <groupId>org.eclipse.californium</groupId> 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 <artifactId>element-connector</artifactId> 112 <artifactId>element-connector</artifactId>
117 - <version>${californium.version}</version>  
118 <type>test-jar</type> 113 <type>test-jar</type>
119 <scope>test</scope> 114 <scope>test</scope>
120 </dependency> 115 </dependency>
@@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap; @@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap;
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.eclipse.californium.scandium.config.DtlsConnectorConfig; 19 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 import org.eclipse.leshan.core.model.StaticModel; 20 import org.eclipse.leshan.core.model.StaticModel;
  21 +import org.eclipse.leshan.core.util.Hex;
21 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager; 22 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
22 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; 23 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
23 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder; 24 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
@@ -28,18 +29,42 @@ import org.springframework.context.annotation.Primary; @@ -28,18 +29,42 @@ import org.springframework.context.annotation.Primary;
28 import org.springframework.stereotype.Component; 29 import org.springframework.stereotype.Component;
29 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore; 30 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore;
30 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore; 31 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore;
31 -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MSetSecurityStoreBootstrap;  
32 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager; 32 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager;
33 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; 33 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
34 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; 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 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; 58 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
  59 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
37 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; 60 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig;
38 61
39 @Slf4j 62 @Slf4j
40 @Component 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 public class LwM2MTransportBootstrapServerConfiguration { 65 public class LwM2MTransportBootstrapServerConfiguration {
  66 + private PublicKey publicKey;
  67 + private PrivateKey privateKey;
43 68
44 @Autowired 69 @Autowired
45 private LwM2MTransportContextBootstrap contextBs; 70 private LwM2MTransportContextBootstrap contextBs;
@@ -53,27 +78,35 @@ public class LwM2MTransportBootstrapServerConfiguration { @@ -53,27 +78,35 @@ public class LwM2MTransportBootstrapServerConfiguration {
53 @Autowired 78 @Autowired
54 private LwM2MInMemoryBootstrapConfigStore lwM2MInMemoryBootstrapConfigStore; 79 private LwM2MInMemoryBootstrapConfigStore lwM2MInMemoryBootstrapConfigStore;
55 80
56 -  
57 @Primary 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 LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder(); 104 LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder();
72 - builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPort); 105 + builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPortNoSec);
73 builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapSecureHost(), bootstrapSecurePort); 106 builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapSecureHost(), bootstrapSecurePort);
74 107
75 /** Create CoAP Config */ 108 /** Create CoAP Config */
76 - builder.setCoapConfig(getCoapConfig ()); 109 + builder.setCoapConfig(getCoapConfig (bootstrapPortNoSec, bootstrapSecurePort));
77 110
78 /** ConfigStore */ 111 /** ConfigStore */
79 builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore); 112 builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
@@ -84,13 +117,22 @@ public class LwM2MTransportBootstrapServerConfiguration { @@ -84,13 +117,22 @@ public class LwM2MTransportBootstrapServerConfiguration {
84 /** Define model provider (Create Models )*/ 117 /** Define model provider (Create Models )*/
85 builder.setModel(new StaticModel(contextS.getCtxServer().getModelsValue())); 118 builder.setModel(new StaticModel(contextS.getCtxServer().getModelsValue()));
86 119
  120 + /** Create credentials */
  121 + LwM2MSetSecurityStoreBootstrap(builder, dtlsMode);
  122 +
87 /** Create and Set DTLS Config */ 123 /** Create and Set DTLS Config */
88 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); 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 builder.setDtlsConfig(dtlsConfig); 134 builder.setDtlsConfig(dtlsConfig);
91 135
92 - /** Create credentials */  
93 - new LwM2MSetSecurityStoreBootstrap(builder, contextBs, contextS, dtlsMode);  
94 136
95 BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore); 137 BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore);
96 builder.setSessionManager(sessionManager); 138 builder.setSessionManager(sessionManager);
@@ -98,4 +140,151 @@ public class LwM2MTransportBootstrapServerConfiguration { @@ -98,4 +140,151 @@ public class LwM2MTransportBootstrapServerConfiguration {
98 /** Create BootstrapServer */ 140 /** Create BootstrapServer */
99 return builder.build(); 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,57 +14,56 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 package org.thingsboard.server.transport.lwm2m.bootstrap; 16 package org.thingsboard.server.transport.lwm2m.bootstrap;
  17 +
17 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
18 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; 19 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
19 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.beans.factory.annotation.Qualifier; 21 import org.springframework.beans.factory.annotation.Qualifier;
21 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
22 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
23 -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;  
24 24
25 import javax.annotation.PostConstruct; 25 import javax.annotation.PostConstruct;
26 import javax.annotation.PreDestroy; 26 import javax.annotation.PreDestroy;
27 27
28 @Slf4j 28 @Slf4j
29 @Service 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 public class LwM2MTransportBootstrapServerInitializer { 31 public class LwM2MTransportBootstrapServerInitializer {
32 32
33 - @Autowired  
34 - @Qualifier("leshanBootstrapCert") 33 + @Autowired(required = false)
  34 + @Qualifier("leshanBootstrapX509")
35 private LeshanBootstrapServer lhBServerCert; 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 @Autowired 45 @Autowired
42 private LwM2MTransportContextBootstrap contextBS; 46 private LwM2MTransportContextBootstrap contextBS;
43 47
44 @PostConstruct 48 @PostConstruct
45 public void init() { 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 @PreDestroy 61 @PreDestroy
61 public void shutdown() throws InterruptedException { 62 public void shutdown() throws InterruptedException {
62 log.info("Stopping LwM2M transport Bootstrap Server!"); 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 log.info("LwM2M transport Bootstrap Server stopped!"); 67 log.info("LwM2M transport Bootstrap Server stopped!");
69 } 68 }
70 } 69 }
@@ -29,28 +29,41 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore; @@ -29,28 +29,41 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore;
29 import org.eclipse.leshan.server.security.SecurityInfo; 29 import org.eclipse.leshan.server.security.SecurityInfo;
30 import org.springframework.beans.factory.annotation.Autowired; 30 import org.springframework.beans.factory.annotation.Autowired;
31 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 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 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; 34 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  35 +import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
35 import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; 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 import org.thingsboard.server.transport.lwm2m.utils.TypeServer; 40 import org.thingsboard.server.transport.lwm2m.utils.TypeServer;
37 41
38 import java.io.IOException; 42 import java.io.IOException;
39 import java.security.GeneralSecurityException; 43 import java.security.GeneralSecurityException;
40 import java.util.Arrays; 44 import java.util.Arrays;
41 import java.util.List; 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 @Slf4j 55 @Slf4j
46 -@Component("LwM2MBootstrapSecurityStore") 56 +@Service("LwM2MBootstrapSecurityStore")
47 @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')") 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 public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { 58 public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
49 59
50 private final EditableBootstrapConfigStore bootstrapConfigStore; 60 private final EditableBootstrapConfigStore bootstrapConfigStore;
51 61
52 @Autowired 62 @Autowired
53 - LwM2MGetSecurityInfo lwM2MGetSecurityInfo; 63 + LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator;
  64 +
  65 + @Autowired
  66 + public LwM2MTransportContextServer context;
54 67
55 public LwM2MBootstrapSecurityStore(EditableBootstrapConfigStore bootstrapConfigStore) { 68 public LwM2MBootstrapSecurityStore(EditableBootstrapConfigStore bootstrapConfigStore) {
56 this.bootstrapConfigStore = bootstrapConfigStore; 69 this.bootstrapConfigStore = bootstrapConfigStore;
@@ -59,8 +72,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -59,8 +72,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
59 @Override 72 @Override
60 public List<SecurityInfo> getAllByEndpoint(String endPoint) { 73 public List<SecurityInfo> getAllByEndpoint(String endPoint) {
61 String endPointKey = endPoint; 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 /** add value to store from BootstrapJson */ 77 /** add value to store from BootstrapJson */
65 this.setBootstrapConfigScurityInfo(store); 78 this.setBootstrapConfigScurityInfo(store);
66 BootstrapConfig bsConfigNew = store.getBootstrapConfig(); 79 BootstrapConfig bsConfigNew = store.getBootstrapConfig();
@@ -73,7 +86,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -73,7 +86,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
73 } 86 }
74 bootstrapConfigStore.add(endPoint, bsConfigNew); 87 bootstrapConfigStore.add(endPoint, bsConfigNew);
75 } catch (InvalidConfigurationException e) { 88 } catch (InvalidConfigurationException e) {
76 - e.printStackTrace(); 89 + log.error("", e);
77 } 90 }
78 return store.getSecurityInfo() == null ? null : Arrays.asList(store.getSecurityInfo()); 91 return store.getSecurityInfo() == null ? null : Arrays.asList(store.getSecurityInfo());
79 } 92 }
@@ -83,17 +96,16 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -83,17 +96,16 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
83 96
84 @Override 97 @Override
85 public SecurityInfo getByIdentity(String identity) { 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 BootstrapConfig bsConfig = store.getBootstrapConfig(); 103 BootstrapConfig bsConfig = store.getBootstrapConfig();
92 if (bsConfig.security != null) { 104 if (bsConfig.security != null) {
93 try { 105 try {
94 bootstrapConfigStore.add(store.getEndPoint(), bsConfig); 106 bootstrapConfigStore.add(store.getEndPoint(), bsConfig);
95 } catch (InvalidConfigurationException e) { 107 } catch (InvalidConfigurationException e) {
96 - e.printStackTrace(); 108 + log.error("", e);
97 } 109 }
98 return store.getSecurityInfo(); 110 return store.getSecurityInfo();
99 } 111 }
@@ -141,28 +153,36 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -141,28 +153,36 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
141 private LwM2MBootstrapConfig getParametersBootstrap(ReadResultSecurityStore store) { 153 private LwM2MBootstrapConfig getParametersBootstrap(ReadResultSecurityStore store) {
142 try { 154 try {
143 JsonObject bootstrapJsonCredential = store.getBootstrapJsonCredential(); 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 } catch (JsonProcessingException e) { 180 } catch (JsonProcessingException e) {
163 log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndPoint(), e.getMessage()); 181 log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndPoint(), e.getMessage());
164 return null; 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,7 +195,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
175 * @return false if not sync between SecurityMode of Bootstrap credential and profile 195 * @return false if not sync between SecurityMode of Bootstrap credential and profile
176 */ 196 */
177 private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap profileServerBootstrap, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) { 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 lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode())); 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,7 +49,6 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
49 this.securityChecker = securityChecker; 49 this.securityChecker = securityChecker;
50 } 50 }
51 51
52 - @Override  
53 public BootstrapSession begin(String endpoint, Identity clientIdentity) { 52 public BootstrapSession begin(String endpoint, Identity clientIdentity) {
54 boolean authorized; 53 boolean authorized;
55 if (bsSecurityStore != null) { 54 if (bsSecurityStore != null) {
@@ -73,15 +73,15 @@ public class LWM2MGenerationPSkRPkECC { @@ -73,15 +73,15 @@ public class LWM2MGenerationPSkRPkECC {
73 try { 73 try {
74 kpg = KeyPairGenerator.getInstance(algorithm, provider); 74 kpg = KeyPairGenerator.getInstance(algorithm, provider);
75 } catch (NoSuchAlgorithmException e) { 75 } catch (NoSuchAlgorithmException e) {
76 - e.printStackTrace(); 76 + log.error("", e);
77 } catch (NoSuchProviderException e) { 77 } catch (NoSuchProviderException e) {
78 - e.printStackTrace(); 78 + log.error("", e);
79 } 79 }
80 ECGenParameterSpec ecsp = new ECGenParameterSpec(nameParameterSpec); 80 ECGenParameterSpec ecsp = new ECGenParameterSpec(nameParameterSpec);
81 try { 81 try {
82 kpg.initialize(ecsp); 82 kpg.initialize(ecsp);
83 } catch (InvalidAlgorithmParameterException e) { 83 } catch (InvalidAlgorithmParameterException e) {
84 - e.printStackTrace(); 84 + log.error("", e);
85 } 85 }
86 86
87 KeyPair kp = kpg.genKeyPair(); 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,12 +39,15 @@ import java.util.Optional;
39 import java.util.concurrent.CountDownLatch; 39 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.TimeUnit; 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 @Slf4j 47 @Slf4j
45 @Component("LwM2MGetSecurityInfo") 48 @Component("LwM2MGetSecurityInfo")
46 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 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 @Autowired 52 @Autowired
50 public LwM2MTransportContextServer contextS; 53 public LwM2MTransportContextServer contextS;
@@ -53,7 +56,13 @@ public class LwM2MGetSecurityInfo { @@ -53,7 +56,13 @@ public class LwM2MGetSecurityInfo {
53 public LwM2MTransportContextBootstrap contextBS; 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 CountDownLatch latch = new CountDownLatch(1); 66 CountDownLatch latch = new CountDownLatch(1);
58 final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1]; 67 final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1];
59 contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endPoint).build(), 68 contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endPoint).build(),
@@ -61,7 +70,7 @@ public class LwM2MGetSecurityInfo { @@ -61,7 +70,7 @@ public class LwM2MGetSecurityInfo {
61 @Override 70 @Override
62 public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { 71 public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
63 String credentialsBody = msg.getCredentialsBody(); 72 String credentialsBody = msg.getCredentialsBody();
64 - resultSecurityStore[0] = putSecurityInfo(endPoint, msg.getDeviceInfo().getDeviceName(), credentialsBody, keyValue); 73 + resultSecurityStore[0] = createSecurityInfo(endPoint, credentialsBody, keyValue);
65 resultSecurityStore[0].setMsg(msg); 74 resultSecurityStore[0].setMsg(msg);
66 Optional<DeviceProfile> deviceProfileOpt = LwM2MTransportHandler.decode(msg.getProfileBody().toByteArray()); 75 Optional<DeviceProfile> deviceProfileOpt = LwM2MTransportHandler.decode(msg.getProfileBody().toByteArray());
67 deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile)); 76 deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile));
@@ -70,20 +79,27 @@ public class LwM2MGetSecurityInfo { @@ -70,20 +79,27 @@ public class LwM2MGetSecurityInfo {
70 79
71 @Override 80 @Override
72 public void onError(Throwable e) { 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 latch.countDown(); 84 latch.countDown();
76 } 85 }
77 }); 86 });
78 try { 87 try {
79 latch.await(contextS.getCtxServer().getTimeout(), TimeUnit.MILLISECONDS); 88 latch.await(contextS.getCtxServer().getTimeout(), TimeUnit.MILLISECONDS);
80 } catch (InterruptedException e) { 89 } catch (InterruptedException e) {
81 - e.printStackTrace(); 90 + log.error("Failed to await credentials!", e);
82 } 91 }
83 return resultSecurityStore[0]; 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 ReadResultSecurityStore result = new ReadResultSecurityStore(); 103 ReadResultSecurityStore result = new ReadResultSecurityStore();
88 JsonObject objectMsg = LwM2MTransportHandler.validateJson(jsonStr); 104 JsonObject objectMsg = LwM2MTransportHandler.validateJson(jsonStr);
89 if (objectMsg != null && !objectMsg.isJsonNull()) { 105 if (objectMsg != null && !objectMsg.isJsonNull()) {
@@ -99,20 +115,21 @@ public class LwM2MGetSecurityInfo { @@ -99,20 +115,21 @@ public class LwM2MGetSecurityInfo {
99 if (keyValue.equals(TypeServer.BOOTSTRAP)) { 115 if (keyValue.equals(TypeServer.BOOTSTRAP)) {
100 result.setBootstrapJsonCredential(object); 116 result.setBootstrapJsonCredential(object);
101 result.setEndPoint(endPoint); 117 result.setEndPoint(endPoint);
  118 + result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code);
102 } else { 119 } else {
103 LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(object.get("securityConfigClientMode").getAsString().toLowerCase()); 120 LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(object.get("securityConfigClientMode").getAsString().toLowerCase());
104 switch (lwM2MSecurityMode) { 121 switch (lwM2MSecurityMode) {
105 case NO_SEC: 122 case NO_SEC:
106 - getClientSecurityInfoNoSec(result); 123 + createClientSecurityInfoNoSec(result);
107 break; 124 break;
108 case PSK: 125 case PSK:
109 - getClientSecurityInfoPSK(result, endPoint, object); 126 + createClientSecurityInfoPSK(result, endPoint, object);
110 break; 127 break;
111 case RPK: 128 case RPK:
112 - getClientSecurityInfoRPK(result, endPoint, object); 129 + createClientSecurityInfoRPK(result, endPoint, object);
113 break; 130 break;
114 case X509: 131 case X509:
115 - getClientSecurityInfoX509(result, endPoint); 132 + createClientSecurityInfoX509(result, endPoint);
116 break; 133 break;
117 default: 134 default:
118 break; 135 break;
@@ -123,12 +140,12 @@ public class LwM2MGetSecurityInfo { @@ -123,12 +140,12 @@ public class LwM2MGetSecurityInfo {
123 return result; 140 return result;
124 } 141 }
125 142
126 - private void getClientSecurityInfoNoSec(ReadResultSecurityStore result) { 143 + private void createClientSecurityInfoNoSec(ReadResultSecurityStore result) {
127 result.setSecurityInfo(null); 144 result.setSecurityInfo(null);
128 result.setSecurityMode(NO_SEC.code); 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 /** PSK Deserialization */ 149 /** PSK Deserialization */
133 String identity = (object.has("identity") && object.get("identity").isJsonPrimitive()) ? object.get("identity").getAsString() : null; 150 String identity = (object.has("identity") && object.get("identity").isJsonPrimitive()) ? object.get("identity").getAsString() : null;
134 if (identity != null && !identity.isEmpty()) { 151 if (identity != null && !identity.isEmpty()) {
@@ -148,7 +165,7 @@ public class LwM2MGetSecurityInfo { @@ -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 try { 169 try {
153 if (object.has("key") && object.get("key").isJsonPrimitive()) { 170 if (object.has("key") && object.get("key").isJsonPrimitive()) {
154 byte[] rpkkey = Hex.decodeHex(object.get("key").getAsString().toLowerCase().toCharArray()); 171 byte[] rpkkey = Hex.decodeHex(object.get("key").getAsString().toLowerCase().toCharArray());
@@ -163,7 +180,7 @@ public class LwM2MGetSecurityInfo { @@ -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 result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint)); 184 result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
168 result.setSecurityMode(X509.code); 185 result.setSecurityMode(X509.code);
169 } 186 }
@@ -33,17 +33,17 @@ import java.util.Optional; @@ -33,17 +33,17 @@ import java.util.Optional;
33 33
34 @Slf4j 34 @Slf4j
35 public class LwM2MSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener { 35 public class LwM2MSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
36 - private LwM2MTransportService service; 36 + private LwM2MTransportServiceImpl service;
37 private TransportProtos.SessionInfoProto sessionInfo; 37 private TransportProtos.SessionInfoProto sessionInfo;
38 38
39 - LwM2MSessionMsgListener(LwM2MTransportService service, TransportProtos.SessionInfoProto sessionInfo) { 39 + public LwM2MSessionMsgListener(LwM2MTransportServiceImpl service, TransportProtos.SessionInfoProto sessionInfo) {
40 this.service = service; 40 this.service = service;
41 this.sessionInfo = sessionInfo; 41 this.sessionInfo = sessionInfo;
42 } 42 }
43 43
44 @Override 44 @Override
45 public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) { 45 public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
46 - log.info("[{}] attributesResponse", getAttributesResponse); 46 + this.service.onGetAttributesResponse(getAttributesResponse, this.sessionInfo);
47 } 47 }
48 48
49 @Override 49 @Override
@@ -30,15 +30,26 @@ package org.thingsboard.server.transport.lwm2m.server; @@ -30,15 +30,26 @@ package org.thingsboard.server.transport.lwm2m.server;
30 * limitations under the License. 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 import lombok.extern.slf4j.Slf4j; 36 import lombok.extern.slf4j.Slf4j;
34 import org.springframework.beans.factory.annotation.Autowired; 37 import org.springframework.beans.factory.annotation.Autowired;
35 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 38 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
36 import org.springframework.stereotype.Component; 39 import org.springframework.stereotype.Component;
37 import org.thingsboard.server.common.transport.TransportContext; 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 import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer; 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 import javax.annotation.PostConstruct; 49 import javax.annotation.PostConstruct;
41 50
  51 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY;
  52 +
42 @Slf4j 53 @Slf4j
43 @Component 54 @Component
44 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 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,14 +58,87 @@ public class LwM2MTransportContextServer extends TransportContext {
47 private LwM2MTransportConfigServer ctxServer; 58 private LwM2MTransportConfigServer ctxServer;
48 59
49 @Autowired 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 @PostConstruct 73 @PostConstruct
53 public void init() { 74 public void init() {
54 this.ctxServer = lwM2MTransportConfigServer; 75 this.ctxServer = lwM2MTransportConfigServer;
55 } 76 }
56 77
57 - public LwM2MTransportConfigServer getCtxServer () { 78 + public LwM2MTransportConfigServer getCtxServer() {
58 return this.ctxServer; 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,7 +16,6 @@
16 package org.thingsboard.server.transport.lwm2m.server; 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
18 import com.fasterxml.jackson.databind.ObjectMapper; 18 import com.fasterxml.jackson.databind.ObjectMapper;
19 -import com.google.gson.Gson;  
20 import com.google.gson.JsonObject; 19 import com.google.gson.JsonObject;
21 import com.google.gson.JsonParser; 20 import com.google.gson.JsonParser;
22 import com.google.gson.JsonSyntaxException; 21 import com.google.gson.JsonSyntaxException;
@@ -24,57 +23,60 @@ import lombok.extern.slf4j.Slf4j; @@ -24,57 +23,60 @@ import lombok.extern.slf4j.Slf4j;
24 import org.apache.commons.lang3.StringUtils; 23 import org.apache.commons.lang3.StringUtils;
25 import org.eclipse.californium.core.network.config.NetworkConfig; 24 import org.eclipse.californium.core.network.config.NetworkConfig;
26 import org.eclipse.leshan.core.model.ResourceModel; 25 import org.eclipse.leshan.core.model.ResourceModel;
  26 +import org.eclipse.leshan.core.node.LwM2mMultipleResource;
27 import org.eclipse.leshan.core.node.LwM2mNode; 27 import org.eclipse.leshan.core.node.LwM2mNode;
28 import org.eclipse.leshan.core.node.LwM2mObject; 28 import org.eclipse.leshan.core.node.LwM2mObject;
29 import org.eclipse.leshan.core.node.LwM2mObjectInstance; 29 import org.eclipse.leshan.core.node.LwM2mObjectInstance;
  30 +import org.eclipse.leshan.core.node.LwM2mPath;
30 import org.eclipse.leshan.core.node.LwM2mSingleResource; 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 import org.eclipse.leshan.core.util.Hex; 33 import org.eclipse.leshan.core.util.Hex;
33 -import org.eclipse.leshan.server.californium.LeshanServer;  
34 import org.eclipse.leshan.server.californium.LeshanServerBuilder; 34 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
35 import org.nustaq.serialization.FSTConfiguration; 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 import org.thingsboard.server.common.data.DeviceProfile; 36 import org.thingsboard.server.common.data.DeviceProfile;
41 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; 37 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
  38 +import org.thingsboard.server.common.transport.TransportServiceCallback;
42 import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; 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 import java.io.File; 42 import java.io.File;
46 import java.io.IOException; 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 import java.util.Arrays; 44 import java.util.Arrays;
53 -import java.util.List;  
54 -import java.util.ArrayList; 45 +import java.util.Date;
55 import java.util.LinkedList; 46 import java.util.LinkedList;
56 -import java.util.Collections; 47 +import java.util.Optional;
57 48
58 @Slf4j 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 // We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to 54 // We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to
64 // send a Confirmable message to the time when an acknowledgement is no longer expected. 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 public static final String ATTRIBUTE = "attribute"; 58 public static final String ATTRIBUTE = "attribute";
69 public static final String TELEMETRY = "telemetry"; 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 public static final String OBSERVE = "observe"; 74 public static final String OBSERVE = "observe";
71 public static final String BOOTSTRAP = "bootstrap"; 75 public static final String BOOTSTRAP = "bootstrap";
72 public static final String SERVERS = "servers"; 76 public static final String SERVERS = "servers";
73 public static final String LWM2M_SERVER = "lwm2mServer"; 77 public static final String LWM2M_SERVER = "lwm2mServer";
74 public static final String BOOTSTRAP_SERVER = "bootstrapServer"; 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 public static final String LOG_LW2M_TELEMETRY = "logLwm2m"; 80 public static final String LOG_LW2M_TELEMETRY = "logLwm2m";
79 public static final String LOG_LW2M_INFO = "info"; 81 public static final String LOG_LW2M_INFO = "info";
80 public static final String LOG_LW2M_ERROR = "error"; 82 public static final String LOG_LW2M_ERROR = "error";
@@ -91,44 +93,55 @@ public class LwM2MTransportHandler{ @@ -91,44 +93,55 @@ public class LwM2MTransportHandler{
91 /** 93 /**
92 * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see 94 * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see
93 * section 5.3.3 of the LW M2M spec). 95 * section 5.3.3 of the LW M2M spec).
  96 + * if all resources are to be replaced
94 */ 97 */
95 public static final String POST_TYPE_OPER_WRITE_REPLACE = "replace"; 98 public static final String POST_TYPE_OPER_WRITE_REPLACE = "replace";
96 /** 99 /**
97 * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section 100 * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section
98 * 5.3.3 of the LW M2M spec). 101 * 5.3.3 of the LW M2M spec).
  102 + * if this is a partial update request
99 */ 103 */
100 public static final String PUT_TYPE_OPER_WRITE_UPDATE = "update"; 104 public static final String PUT_TYPE_OPER_WRITE_UPDATE = "update";
101 public static final String PUT_TYPE_OPER_WRITE_ATTRIBUTES = "wright-attributes"; 105 public static final String PUT_TYPE_OPER_WRITE_ATTRIBUTES = "wright-attributes";
102 106
103 public static final String EVENT_AWAKE = "AWAKE"; 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 NetworkConfig coapConfig; 145 NetworkConfig coapConfig;
133 File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME); 146 File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME);
134 if (configFile.isFile()) { 147 if (configFile.isFile()) {
@@ -138,28 +151,26 @@ public class LwM2MTransportHandler{ @@ -138,28 +151,26 @@ public class LwM2MTransportHandler{
138 coapConfig = LeshanServerBuilder.createDefaultNetworkConfig(); 151 coapConfig = LeshanServerBuilder.createDefaultNetworkConfig();
139 coapConfig.store(configFile); 152 coapConfig.store(configFile);
140 } 153 }
  154 + coapConfig.setString("COAP_PORT", Integer.toString(serverPortNoSec));
  155 + coapConfig.setString("COAP_SECURE_PORT", Integer.toString(serverSecurePort));
141 return coapConfig; 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 switch (type) { 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 default: 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,26 +193,25 @@ public class LwM2MTransportHandler{
182 attrTelemetryObserveValue.setPostAttributeProfile(profilesConfigData.get(ATTRIBUTE).getAsJsonArray()); 193 attrTelemetryObserveValue.setPostAttributeProfile(profilesConfigData.get(ATTRIBUTE).getAsJsonArray());
183 attrTelemetryObserveValue.setPostTelemetryProfile(profilesConfigData.get(TELEMETRY).getAsJsonArray()); 194 attrTelemetryObserveValue.setPostTelemetryProfile(profilesConfigData.get(TELEMETRY).getAsJsonArray());
184 attrTelemetryObserveValue.setPostObserveProfile(profilesConfigData.get(OBSERVE).getAsJsonArray()); 195 attrTelemetryObserveValue.setPostObserveProfile(profilesConfigData.get(OBSERVE).getAsJsonArray());
185 - return attrTelemetryObserveValue; 196 + return attrTelemetryObserveValue;
186 } 197 }
187 198
188 /** 199 /**
189 -  
190 * @return deviceProfileBody with Observe&Attribute&Telemetry From Thingsboard 200 * @return deviceProfileBody with Observe&Attribute&Telemetry From Thingsboard
191 * Example: with pathResource (use only pathResource) 201 * Example: with pathResource (use only pathResource)
192 * property: "observeAttr" 202 * property: "observeAttr"
193 * {"keyName": { 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 * "attribute":["/2/0/1","/3/0/9"], 208 * "attribute":["/2/0/1","/3/0/9"],
199 * "telemetry":["/1/0/1","/2/0/1","/6/0/1"], 209 * "telemetry":["/1/0/1","/2/0/1","/6/0/1"],
200 * "observe":["/2/0","/2/0/0","/4/0/2"]} 210 * "observe":["/2/0","/2/0/0","/4/0/2"]}
201 */ 211 */
202 public static JsonObject getObserveAttrTelemetryFromThingsboard(DeviceProfile deviceProfile) { 212 public static JsonObject getObserveAttrTelemetryFromThingsboard(DeviceProfile deviceProfile) {
203 if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { 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 Object observeAttr = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); 215 Object observeAttr = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties();
206 try { 216 try {
207 ObjectMapper mapper = new ObjectMapper(); 217 ObjectMapper mapper = new ObjectMapper();
@@ -209,7 +219,7 @@ public class LwM2MTransportHandler{ @@ -209,7 +219,7 @@ public class LwM2MTransportHandler{
209 JsonObject objectMsg = (observeAttrStr != null) ? validateJson(observeAttrStr) : null; 219 JsonObject objectMsg = (observeAttrStr != null) ? validateJson(observeAttrStr) : null;
210 return (getValidateCredentialsBodyFromThingsboard(objectMsg)) ? objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject() : null; 220 return (getValidateCredentialsBodyFromThingsboard(objectMsg)) ? objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject() : null;
211 } catch (IOException e) { 221 } catch (IOException e) {
212 - e.printStackTrace(); 222 + log.error("", e);
213 } 223 }
214 } 224 }
215 return null; 225 return null;
@@ -217,7 +227,6 @@ public class LwM2MTransportHandler{ @@ -217,7 +227,6 @@ public class LwM2MTransportHandler{
217 227
218 public static JsonObject getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) { 228 public static JsonObject getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) {
219 if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { 229 if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) {
220 - Lwm2mDeviceProfileTransportConfiguration lwm2mDeviceProfileTransportConfiguration = (Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();  
221 Object bootstrap = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); 230 Object bootstrap = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties();
222 try { 231 try {
223 ObjectMapper mapper = new ObjectMapper(); 232 ObjectMapper mapper = new ObjectMapper();
@@ -225,7 +234,7 @@ public class LwM2MTransportHandler{ @@ -225,7 +234,7 @@ public class LwM2MTransportHandler{
225 JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null; 234 JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null;
226 return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null; 235 return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null;
227 } catch (IOException e) { 236 } catch (IOException e) {
228 - e.printStackTrace(); 237 + log.error("", e);
229 } 238 }
230 } 239 }
231 return null; 240 return null;
@@ -276,7 +285,7 @@ public class LwM2MTransportHandler{ @@ -276,7 +285,7 @@ public class LwM2MTransportHandler{
276 jsonValidFlesh = jsonValidFlesh.replaceAll("\n", ""); 285 jsonValidFlesh = jsonValidFlesh.replaceAll("\n", "");
277 jsonValidFlesh = jsonValidFlesh.replaceAll("\t", ""); 286 jsonValidFlesh = jsonValidFlesh.replaceAll("\t", "");
278 jsonValidFlesh = jsonValidFlesh.replaceAll(" ", ""); 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 try { 289 try {
281 object = new JsonParser().parse(jsonValid).getAsJsonObject(); 290 object = new JsonParser().parse(jsonValid).getAsJsonObject();
282 } catch (JsonSyntaxException e) { 291 } catch (JsonSyntaxException e) {
@@ -288,7 +297,7 @@ public class LwM2MTransportHandler{ @@ -288,7 +297,7 @@ public class LwM2MTransportHandler{
288 297
289 public static <T> Optional<T> decode(byte[] byteArray) { 298 public static <T> Optional<T> decode(byte[] byteArray) {
290 try { 299 try {
291 - FSTConfiguration config = FSTConfiguration.createDefaultConfiguration();; 300 + FSTConfiguration config = FSTConfiguration.createDefaultConfiguration();
292 T msg = (T) config.asObject(byteArray); 301 T msg = (T) config.asObject(byteArray);
293 return Optional.ofNullable(msg); 302 return Optional.ofNullable(msg);
294 } catch (IllegalArgumentException e) { 303 } catch (IllegalArgumentException e) {
@@ -297,37 +306,29 @@ public class LwM2MTransportHandler{ @@ -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 LinkedList<String> linkedList = new LinkedList<String>((Arrays.asList(s.split(" ")))); 311 LinkedList<String> linkedList = new LinkedList<String>((Arrays.asList(s.split(" "))));
325 - linkedList.stream().forEach(str-> { 312 + linkedList.forEach(str -> {
326 String strOut = str.replaceAll("\\W", "").replaceAll("_", "").toUpperCase(); 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 else linkedListOut.add(strOut); 315 else linkedListOut.add(strOut);
329 }); 316 });
330 linkedListOut.set(0, (linkedListOut.get(0).substring(0, 1).toLowerCase() + linkedListOut.get(0).substring(1))); 317 linkedListOut.set(0, (linkedListOut.get(0).substring(0, 1).toLowerCase() + linkedListOut.get(0).substring(1)));
331 return StringUtils.join(linkedListOut, ""); 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,26 +20,28 @@ import org.eclipse.californium.core.coap.Response;
20 import org.eclipse.leshan.core.attributes.Attribute; 20 import org.eclipse.leshan.core.attributes.Attribute;
21 import org.eclipse.leshan.core.attributes.AttributeSet; 21 import org.eclipse.leshan.core.attributes.AttributeSet;
22 import org.eclipse.leshan.core.model.ResourceModel; 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 import org.eclipse.leshan.core.node.LwM2mSingleResource; 25 import org.eclipse.leshan.core.node.LwM2mSingleResource;
24 import org.eclipse.leshan.core.node.ObjectLink; 26 import org.eclipse.leshan.core.node.ObjectLink;
25 import org.eclipse.leshan.core.observation.Observation; 27 import org.eclipse.leshan.core.observation.Observation;
  28 +import org.eclipse.leshan.core.request.CancelObservationRequest;
26 import org.eclipse.leshan.core.request.ContentFormat; 29 import org.eclipse.leshan.core.request.ContentFormat;
27 -import org.eclipse.leshan.core.request.WriteRequest;  
28 import org.eclipse.leshan.core.request.DiscoverRequest; 30 import org.eclipse.leshan.core.request.DiscoverRequest;
29 import org.eclipse.leshan.core.request.DownlinkRequest; 31 import org.eclipse.leshan.core.request.DownlinkRequest;
  32 +import org.eclipse.leshan.core.request.ExecuteRequest;
30 import org.eclipse.leshan.core.request.ObserveRequest; 33 import org.eclipse.leshan.core.request.ObserveRequest;
31 -import org.eclipse.leshan.core.request.CancelObservationRequest;  
32 import org.eclipse.leshan.core.request.ReadRequest; 34 import org.eclipse.leshan.core.request.ReadRequest;
33 -import org.eclipse.leshan.core.request.ExecuteRequest;  
34 import org.eclipse.leshan.core.request.WriteAttributesRequest; 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 import org.eclipse.leshan.core.response.CancelObservationResponse; 37 import org.eclipse.leshan.core.response.CancelObservationResponse;
40 import org.eclipse.leshan.core.response.DeleteResponse; 38 import org.eclipse.leshan.core.response.DeleteResponse;
41 -import org.eclipse.leshan.core.response.ExecuteResponse;  
42 import org.eclipse.leshan.core.response.DiscoverResponse; 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 import org.eclipse.leshan.core.response.WriteAttributesResponse; 45 import org.eclipse.leshan.core.response.WriteAttributesResponse;
44 import org.eclipse.leshan.core.response.WriteResponse; 46 import org.eclipse.leshan.core.response.WriteResponse;
45 import org.eclipse.leshan.core.util.Hex; 47 import org.eclipse.leshan.core.util.Hex;
@@ -50,6 +52,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -50,6 +52,7 @@ import org.springframework.beans.factory.annotation.Autowired;
50 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 52 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
51 import org.springframework.stereotype.Service; 53 import org.springframework.stereotype.Service;
52 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; 54 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
  55 +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
53 56
54 import javax.annotation.PostConstruct; 57 import javax.annotation.PostConstruct;
55 import java.util.ArrayList; 58 import java.util.ArrayList;
@@ -65,32 +68,36 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle @@ -65,32 +68,36 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
65 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_DISCOVER; 68 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_DISCOVER;
66 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE; 69 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE;
67 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ; 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 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE; 73 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE;
71 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_OBSERVE_CANCEL; 74 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_OBSERVE_CANCEL;
72 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE; 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 @Slf4j 80 @Slf4j
77 @Service("LwM2MTransportRequest") 81 @Service("LwM2MTransportRequest")
78 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 82 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
79 public class LwM2MTransportRequest { 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 @Autowired 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 @PostConstruct 94 @PostConstruct
93 public void init() { 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 public Collection<Registration> doGetRegistrations(LeshanServer lwServer) { 103 public Collection<Registration> doGetRegistrations(LeshanServer lwServer) {
@@ -113,16 +120,13 @@ public class LwM2MTransportRequest { @@ -113,16 +120,13 @@ public class LwM2MTransportRequest {
113 * @param lwM2MClient 120 * @param lwM2MClient
114 * @param observation 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 if (registration != null && resultIds.getObjectId() >= 0) { 126 if (registration != null && resultIds.getObjectId() >= 0) {
120 DownlinkRequest request = null; 127 DownlinkRequest request = null;
121 ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null; 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 timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; 130 timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
127 switch (typeOper) { 131 switch (typeOper) {
128 case GET_TYPE_OPER_READ: 132 case GET_TYPE_OPER_READ:
@@ -132,10 +136,10 @@ public class LwM2MTransportRequest { @@ -132,10 +136,10 @@ public class LwM2MTransportRequest {
132 request = new DiscoverRequest(target); 136 request = new DiscoverRequest(target);
133 break; 137 break;
134 case GET_TYPE_OPER_OBSERVE: 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 } else if (resultIds.getObjectId() >= 0) { 143 } else if (resultIds.getObjectId() >= 0) {
140 request = new ObserveRequest(resultIds.getObjectId()); 144 request = new ObserveRequest(resultIds.getObjectId());
141 } 145 }
@@ -144,27 +148,30 @@ public class LwM2MTransportRequest { @@ -144,27 +148,30 @@ public class LwM2MTransportRequest {
144 request = new CancelObservationRequest(observation); 148 request = new CancelObservationRequest(observation);
145 break; 149 break;
146 case POST_TYPE_OPER_EXECUTE: 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 } else { 153 } else {
150 request = new ExecuteRequest(target); 154 request = new ExecuteRequest(target);
151 } 155 }
152 break; 156 break;
153 case POST_TYPE_OPER_WRITE_REPLACE: 157 case POST_TYPE_OPER_WRITE_REPLACE:
154 // Request to write a <b>String Single-Instance Resource</b> using the TLV content format. 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 break; 168 break;
163 case PUT_TYPE_OPER_WRITE_UPDATE: 169 case PUT_TYPE_OPER_WRITE_UPDATE:
164 if (resultIds.getResourceId() >= 0) { 170 if (resultIds.getResourceId() >= 0) {
165 ResourceModel resourceModel = lwServer.getModelProvider().getObjectModel(registration).getObjectModel(resultIds.getObjectId()).resources.get(resultIds.getResourceId()); 171 ResourceModel resourceModel = lwServer.getModelProvider().getObjectModel(registration).getObjectModel(resultIds.getObjectId()).resources.get(resultIds.getResourceId());
166 ResourceModel.Type typeRes = resourceModel.type; 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 break; 176 break;
170 case PUT_TYPE_OPER_WRITE_ATTRIBUTES: 177 case PUT_TYPE_OPER_WRITE_ATTRIBUTES:
@@ -202,55 +209,76 @@ public class LwM2MTransportRequest { @@ -202,55 +209,76 @@ public class LwM2MTransportRequest {
202 Attribute pmin = new Attribute(MINIMUM_PERIOD, Integer.toUnsignedLong(Integer.valueOf("1"))); 209 Attribute pmin = new Attribute(MINIMUM_PERIOD, Integer.toUnsignedLong(Integer.valueOf("1")));
203 Attribute[] attrs = {pmin}; 210 Attribute[] attrs = {pmin};
204 AttributeSet attrSet = new AttributeSet(attrs); 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 } else if (resultIds.getObjectId() >= 0) { 216 } else if (resultIds.getObjectId() >= 0) {
210 request = new WriteAttributesRequest(resultIds.getObjectId(), attrSet); 217 request = new WriteAttributesRequest(resultIds.getObjectId(), attrSet);
211 } 218 }
212 break; 219 break;
213 default: 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 lwServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> { 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 if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) { 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 }, e -> { 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 request.getPath().toString(), e.toString()); 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 try { 282 try {
255 switch (type) { 283 switch (type) {
256 case STRING: // String 284 case STRING: // String
@@ -264,29 +292,38 @@ public class LwM2MTransportRequest { @@ -264,29 +292,38 @@ public class LwM2MTransportRequest {
264 case FLOAT: // Double 292 case FLOAT: // Double
265 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Double.valueOf(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.valueOf(value.toString())); 293 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Double.valueOf(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.valueOf(value.toString()));
266 case TIME: // Date 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 case OPAQUE: // byte[] value, base64 296 case OPAQUE: // byte[] value, base64
269 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())); 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 default: 298 default:
271 } 299 }
272 return null; 300 return null;
273 } catch (NumberFormatException e) { 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,7 +335,7 @@ public class LwM2MTransportRequest {
298 * @param response - 335 * @param response -
299 * @param lwM2MClient - 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 if (response instanceof ObserveResponse) { 339 if (response instanceof ObserveResponse) {
303 service.onObservationResponse(registration, path, (ReadResponse) response); 340 service.onObservationResponse(registration, path, (ReadResponse) response);
304 } else if (response instanceof CancelObservationResponse) { 341 } else if (response instanceof CancelObservationResponse) {
@@ -329,7 +366,7 @@ public class LwM2MTransportRequest { @@ -329,7 +366,7 @@ public class LwM2MTransportRequest {
329 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", path, response); 366 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", path, response);
330 } else if (response instanceof WriteResponse) { 367 } else if (response instanceof WriteResponse) {
331 log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", path, response); 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,10 +20,16 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; 20 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
21 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; 21 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
22 import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder; 22 import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
  23 +import org.eclipse.leshan.core.util.Hex;
23 import org.eclipse.leshan.server.californium.LeshanServer; 24 import org.eclipse.leshan.server.californium.LeshanServer;
24 import org.eclipse.leshan.server.californium.LeshanServerBuilder; 25 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
25 import org.eclipse.leshan.server.model.LwM2mModelProvider; 26 import org.eclipse.leshan.server.model.LwM2mModelProvider;
26 import org.eclipse.leshan.server.model.VersionedModelProvider; 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 import org.springframework.beans.factory.annotation.Autowired; 33 import org.springframework.beans.factory.annotation.Autowired;
28 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 34 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
29 import org.springframework.context.annotation.Bean; 35 import org.springframework.context.annotation.Bean;
@@ -31,11 +37,36 @@ import org.springframework.context.annotation.ComponentScan; @@ -31,11 +37,36 @@ import org.springframework.context.annotation.ComponentScan;
31 import org.springframework.context.annotation.Configuration; 37 import org.springframework.context.annotation.Configuration;
32 import org.springframework.context.annotation.Primary; 38 import org.springframework.context.annotation.Primary;
33 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; 39 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
34 -import org.thingsboard.server.transport.lwm2m.server.secure.LwM2MSetSecurityStoreServer;  
35 import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore; 40 import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
36 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; 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 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; 68 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
  69 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
39 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; 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,6 +76,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
45 @Configuration("LwM2MTransportServerConfiguration") 76 @Configuration("LwM2MTransportServerConfiguration")
46 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 77 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
47 public class LwM2MTransportServerConfiguration { 78 public class LwM2MTransportServerConfiguration {
  79 + private PublicKey publicKey;
  80 + private PrivateKey privateKey;
48 81
49 @Autowired 82 @Autowired
50 private LwM2MTransportContextServer context; 83 private LwM2MTransportContextServer context;
@@ -53,50 +86,257 @@ public class LwM2MTransportServerConfiguration { @@ -53,50 +86,257 @@ public class LwM2MTransportServerConfiguration {
53 private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; 86 private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
54 87
55 @Primary 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 LeshanServerBuilder builder = new LeshanServerBuilder(); 111 LeshanServerBuilder builder = new LeshanServerBuilder();
71 - builder.setLocalAddress(this.context.getCtxServer().getServerHost(), serverPort); 112 + builder.setLocalAddress(this.context.getCtxServer().getServerHost(), serverPortNoSec);
72 builder.setLocalSecureAddress(this.context.getCtxServer().getServerSecureHost(), serverSecurePort); 113 builder.setLocalSecureAddress(this.context.getCtxServer().getServerSecureHost(), serverSecurePort);
73 builder.setEncoder(new DefaultLwM2mNodeEncoder()); 114 builder.setEncoder(new DefaultLwM2mNodeEncoder());
74 LwM2mNodeDecoder decoder = new DefaultLwM2mNodeDecoder(); 115 LwM2mNodeDecoder decoder = new DefaultLwM2mNodeDecoder();
75 builder.setDecoder(decoder); 116 builder.setDecoder(decoder);
76 - builder.setEncoder(new DefaultLwM2mNodeEncoder(new LwM2mValueConverterImpl())); 117 + builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
77 118
78 /** Create CoAP Config */ 119 /** Create CoAP Config */
79 - builder.setCoapConfig(getCoapConfig()); 120 + builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort));
80 121
81 /** Define model provider (Create Models )*/ 122 /** Define model provider (Create Models )*/
82 LwM2mModelProvider modelProvider = new VersionedModelProvider(this.context.getCtxServer().getModelsValue()); 123 LwM2mModelProvider modelProvider = new VersionedModelProvider(this.context.getCtxServer().getModelsValue());
83 builder.setObjectModelProvider(modelProvider); 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 /** Create DTLS Config */ 131 /** Create DTLS Config */
86 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); 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 /** Set DTLS Config */ 142 /** Set DTLS Config */
89 builder.setDtlsConfig(dtlsConfig); 143 builder.setDtlsConfig(dtlsConfig);
90 144
91 /** Use a magic converter to support bad type send by the UI. */ 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 /** Create LWM2M server */ 149 /** Create LWM2M server */
100 return builder.build(); 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,24 +20,32 @@ import org.eclipse.leshan.server.californium.LeshanServer;
20 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
21 import org.springframework.beans.factory.annotation.Qualifier; 21 import org.springframework.beans.factory.annotation.Qualifier;
22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
23 -import org.springframework.stereotype.Service; 23 +import org.springframework.stereotype.Component;
24 import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC; 24 import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
25 -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; 25 +
26 import javax.annotation.PostConstruct; 26 import javax.annotation.PostConstruct;
27 import javax.annotation.PreDestroy; 27 import javax.annotation.PreDestroy;
28 28
29 @Slf4j 29 @Slf4j
30 -@Service("LwM2MTransportServerInitializer") 30 +@Component("LwM2MTransportServerInitializer")
31 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 31 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
32 public class LwM2MTransportServerInitializer { 32 public class LwM2MTransportServerInitializer {
33 33
34 - @Autowired  
35 - @Qualifier("LeshanServerCert")  
36 - private LeshanServer lhServerCert;  
37 34
38 @Autowired 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 @Autowired 50 @Autowired
43 private LwM2MTransportContextServer context; 51 private LwM2MTransportContextServer context;
@@ -45,28 +53,47 @@ public class LwM2MTransportServerInitializer { @@ -45,28 +53,47 @@ public class LwM2MTransportServerInitializer {
45 @PostConstruct 53 @PostConstruct
46 public void init() { 54 public void init() {
47 if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC(); 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 @PreDestroy 91 @PreDestroy
63 public void shutdown() { 92 public void shutdown() {
64 log.info("Stopping LwM2M transport Server!"); 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 log.info("LwM2M transport Server stopped!"); 97 log.info("LwM2M transport Server stopped!");
71 } 98 }
72 } 99 }
@@ -15,985 +15,42 @@ @@ -15,985 +15,42 @@
15 */ 15 */
16 package org.thingsboard.server.transport.lwm2m.server; 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 import org.eclipse.leshan.core.observation.Observation; 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 import org.eclipse.leshan.core.response.ReadResponse; 19 import org.eclipse.leshan.core.response.ReadResponse;
32 -import org.eclipse.leshan.core.util.Hex;  
33 import org.eclipse.leshan.server.californium.LeshanServer; 20 import org.eclipse.leshan.server.californium.LeshanServer;
34 import org.eclipse.leshan.server.registration.Registration; 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 import org.thingsboard.server.common.data.Device; 22 import org.thingsboard.server.common.data.Device;
39 import org.thingsboard.server.common.data.DeviceProfile; 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 import org.thingsboard.server.gen.transport.TransportProtos; 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 import java.util.Collection; 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 import java.util.Optional; 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 +}