Commit 15d725570d25ad7d81ee18435fb1c65bccba7d0f

Authored by Andrii Shvaika
2 parents ab5c803a e9f5fd27

Merge with master

Showing 59 changed files with 2035 additions and 736 deletions

Too many changes to show.

To preserve performance only 59 of 142 files are displayed.

... ... @@ -146,6 +146,10 @@
146 146 <scope>runtime</scope>
147 147 </dependency>
148 148 <dependency>
  149 + <groupId>org.springframework.integration</groupId>
  150 + <artifactId>spring-integration-redis</artifactId>
  151 + </dependency>
  152 + <dependency>
149 153 <groupId>org.springframework.boot</groupId>
150 154 <artifactId>spring-boot-starter-security</artifactId>
151 155 </dependency>
... ...
... ... @@ -78,14 +78,24 @@ CREATE TABLE IF NOT EXISTS firmware (
78 78 CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
79 79 );
80 80
  81 +ALTER TABLE dashboard
  82 + ADD COLUMN IF NOT EXISTS image varchar(1000000);
  83 +
81 84 ALTER TABLE device_profile
  85 + ADD COLUMN IF NOT EXISTS image varchar(1000000),
82 86 ADD COLUMN IF NOT EXISTS firmware_id uuid,
83   - ADD COLUMN IF NOT EXISTS software_id uuid;
  87 + ADD COLUMN IF NOT EXISTS software_id uuid,
  88 + ADD COLUMN IF NOT EXISTS default_dashboard_id uuid;
84 89
85 90 ALTER TABLE device
86 91 ADD COLUMN IF NOT EXISTS firmware_id uuid,
87 92 ADD COLUMN IF NOT EXISTS software_id uuid;
88 93
  94 +ALTER TABLE alarm
  95 + ADD COLUMN IF NOT EXISTS customer_id uuid;
  96 +
  97 +DELETE FROM relation WHERE from_type = 'TENANT' AND relation_type_group = 'RULE_CHAIN';
  98 +
89 99 DO $$
90 100 BEGIN
91 101 IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN
... ... @@ -100,6 +110,12 @@ DO $$
100 110 FOREIGN KEY (firmware_id) REFERENCES firmware(id);
101 111 END IF;
102 112
  113 + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_dashboard_device_profile') THEN
  114 + ALTER TABLE device_profile
  115 + ADD CONSTRAINT fk_default_dashboard_device_profile
  116 + FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id);
  117 + END IF;
  118 +
103 119 IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN
104 120 ALTER TABLE device
105 121 ADD CONSTRAINT fk_firmware_device
... ... @@ -114,3 +130,7 @@ DO $$
114 130 END;
115 131 $$;
116 132
  133 +
  134 +ALTER TABLE api_usage_state
  135 + ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32);
  136 +UPDATE api_usage_state SET alarm_exec = 'ENABLED' WHERE alarm_exec IS NULL;
... ...
... ... @@ -197,6 +197,40 @@ public class AlarmController extends BaseController {
197 197 }
198 198
199 199 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  200 + @RequestMapping(value = "/alarms", method = RequestMethod.GET)
  201 + @ResponseBody
  202 + public PageData<AlarmInfo> getAllAlarms(
  203 + @RequestParam(required = false) String searchStatus,
  204 + @RequestParam(required = false) String status,
  205 + @RequestParam int pageSize,
  206 + @RequestParam int page,
  207 + @RequestParam(required = false) String textSearch,
  208 + @RequestParam(required = false) String sortProperty,
  209 + @RequestParam(required = false) String sortOrder,
  210 + @RequestParam(required = false) Long startTime,
  211 + @RequestParam(required = false) Long endTime,
  212 + @RequestParam(required = false) Boolean fetchOriginator
  213 + ) throws ThingsboardException {
  214 + AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus);
  215 + AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status);
  216 + if (alarmSearchStatus != null && alarmStatus != null) {
  217 + throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " +
  218 + "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
  219 + }
  220 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  221 +
  222 + try {
  223 + if (getCurrentUser().isCustomerUser()) {
  224 + return checkNotNull(alarmService.findCustomerAlarms(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
  225 + } else {
  226 + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
  227 + }
  228 + } catch (Exception e) {
  229 + throw handleException(e);
  230 + }
  231 + }
  232 +
  233 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
200 234 @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET)
201 235 @ResponseBody
202 236 public AlarmSeverity getHighestAlarmSeverity(
... ...
... ... @@ -106,6 +106,8 @@ public abstract class BaseApiUsageState {
106 106 return apiUsageState.getEmailExecState();
107 107 case SMS:
108 108 return apiUsageState.getSmsExecState();
  109 + case ALARM:
  110 + return apiUsageState.getAlarmExecState();
109 111 default:
110 112 return ApiUsageStateValue.ENABLED;
111 113 }
... ... @@ -132,6 +134,9 @@ public abstract class BaseApiUsageState {
132 134 case SMS:
133 135 apiUsageState.setSmsExecState(value);
134 136 break;
  137 + case ALARM:
  138 + apiUsageState.setAlarmExecState(value);
  139 + break;
135 140 }
136 141 return !currentValue.equals(value);
137 142 }
... ...
... ... @@ -24,8 +24,9 @@ import org.thingsboard.server.common.data.DataConstants;
24 24 import org.thingsboard.server.common.data.Device;
25 25 import org.thingsboard.server.common.data.DeviceProfile;
26 26 import org.thingsboard.server.common.data.FirmwareInfo;
27   -import org.thingsboard.server.common.data.firmware.FirmwareUtil;
28 27 import org.thingsboard.server.common.data.firmware.FirmwareType;
  28 +import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus;
  29 +import org.thingsboard.server.common.data.firmware.FirmwareUtil;
29 30 import org.thingsboard.server.common.data.id.DeviceId;
30 31 import org.thingsboard.server.common.data.id.FirmwareId;
31 32 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -66,11 +67,11 @@ import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE;
66 67 import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE;
67 68 import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS;
68 69 import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION;
  70 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  71 +import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
69 72 import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey;
70 73 import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey;
71 74 import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey;
72   -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
73   -import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
74 75
75 76 @Slf4j
76 77 @Service
... ...
... ... @@ -27,6 +27,9 @@ import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
27 27 import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
28 28 import org.thingsboard.server.common.data.EntityView;
29 29 import org.thingsboard.server.common.data.Tenant;
  30 +import org.thingsboard.server.common.data.alarm.Alarm;
  31 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
  32 +import org.thingsboard.server.common.data.alarm.AlarmQuery;
30 33 import org.thingsboard.server.common.data.id.EntityViewId;
31 34 import org.thingsboard.server.common.data.id.TenantId;
32 35 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
... ... @@ -34,9 +37,13 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
34 37 import org.thingsboard.server.common.data.kv.TsKvEntry;
35 38 import org.thingsboard.server.common.data.page.PageData;
36 39 import org.thingsboard.server.common.data.page.PageLink;
  40 +import org.thingsboard.server.common.data.page.TimePageLink;
37 41 import org.thingsboard.server.common.data.rule.RuleChain;
38 42 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
39 43 import org.thingsboard.server.common.data.rule.RuleNode;
  44 +import org.thingsboard.server.dao.alarm.AlarmDao;
  45 +import org.thingsboard.server.dao.alarm.AlarmService;
  46 +import org.thingsboard.server.dao.entity.EntityService;
40 47 import org.thingsboard.server.dao.entityview.EntityViewService;
41 48 import org.thingsboard.server.dao.rule.RuleChainService;
42 49 import org.thingsboard.server.dao.tenant.TenantService;
... ... @@ -72,6 +79,15 @@ public class DefaultDataUpdateService implements DataUpdateService {
72 79 @Autowired
73 80 private TimeseriesService tsService;
74 81
  82 + @Autowired
  83 + private AlarmService alarmService;
  84 +
  85 + @Autowired
  86 + private EntityService entityService;
  87 +
  88 + @Autowired
  89 + private AlarmDao alarmDao;
  90 +
75 91 @Override
76 92 public void updateData(String fromVersion) throws Exception {
77 93 switch (fromVersion) {
... ... @@ -90,14 +106,25 @@ public class DefaultDataUpdateService implements DataUpdateService {
90 106 case "3.2.2":
91 107 log.info("Updating data from version 3.2.2 to 3.3.0 ...");
92 108 tenantsDefaultEdgeRuleChainUpdater.updateEntities(null);
  109 + tenantsAlarmsCustomerUpdater.updateEntities(null);
93 110 break;
94 111 default:
95 112 throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
96 113 }
97 114 }
98 115
99   - private PaginatedUpdater<String, Tenant> tenantsDefaultRuleChainUpdater =
100   - new PaginatedUpdater<String, Tenant>() {
  116 + private final PaginatedUpdater<String, Tenant> tenantsDefaultRuleChainUpdater =
  117 + new PaginatedUpdater<>() {
  118 +
  119 + @Override
  120 + protected String getName() {
  121 + return "Tenants default rule chain updater";
  122 + }
  123 +
  124 + @Override
  125 + protected boolean forceReportTotal() {
  126 + return true;
  127 + }
101 128
102 129 @Override
103 130 protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
... ... @@ -117,8 +144,18 @@ public class DefaultDataUpdateService implements DataUpdateService {
117 144 }
118 145 };
119 146
120   - private PaginatedUpdater<String, Tenant> tenantsDefaultEdgeRuleChainUpdater =
121   - new PaginatedUpdater<String, Tenant>() {
  147 + private final PaginatedUpdater<String, Tenant> tenantsDefaultEdgeRuleChainUpdater =
  148 + new PaginatedUpdater<>() {
  149 +
  150 + @Override
  151 + protected String getName() {
  152 + return "Tenants default edge rule chain updater";
  153 + }
  154 +
  155 + @Override
  156 + protected boolean forceReportTotal() {
  157 + return true;
  158 + }
122 159
123 160 @Override
124 161 protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
... ... @@ -138,8 +175,18 @@ public class DefaultDataUpdateService implements DataUpdateService {
138 175 }
139 176 };
140 177
141   - private PaginatedUpdater<String, Tenant> tenantsRootRuleChainUpdater =
142   - new PaginatedUpdater<String, Tenant>() {
  178 + private final PaginatedUpdater<String, Tenant> tenantsRootRuleChainUpdater =
  179 + new PaginatedUpdater<>() {
  180 +
  181 + @Override
  182 + protected String getName() {
  183 + return "Tenants root rule chain updater";
  184 + }
  185 +
  186 + @Override
  187 + protected boolean forceReportTotal() {
  188 + return true;
  189 + }
143 190
144 191 @Override
145 192 protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
... ... @@ -192,8 +239,18 @@ public class DefaultDataUpdateService implements DataUpdateService {
192 239 }
193 240 };
194 241
195   - private PaginatedUpdater<String, Tenant> tenantsEntityViewsUpdater =
196   - new PaginatedUpdater<String, Tenant>() {
  242 + private final PaginatedUpdater<String, Tenant> tenantsEntityViewsUpdater =
  243 + new PaginatedUpdater<>() {
  244 +
  245 + @Override
  246 + protected String getName() {
  247 + return "Tenants entity views updater";
  248 + }
  249 +
  250 + @Override
  251 + protected boolean forceReportTotal() {
  252 + return true;
  253 + }
197 254
198 255 @Override
199 256 protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
... ... @@ -261,4 +318,48 @@ public class DefaultDataUpdateService implements DataUpdateService {
261 318 }, MoreExecutors.directExecutor());
262 319 }
263 320
  321 + private final PaginatedUpdater<String, Tenant> tenantsAlarmsCustomerUpdater =
  322 + new PaginatedUpdater<>() {
  323 +
  324 + @Override
  325 + protected String getName() {
  326 + return "Tenants alarms customer updater";
  327 + }
  328 +
  329 + @Override
  330 + protected boolean forceReportTotal() {
  331 + return true;
  332 + }
  333 +
  334 + @Override
  335 + protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
  336 + return tenantService.findTenants(pageLink);
  337 + }
  338 +
  339 + @Override
  340 + protected void updateEntity(Tenant tenant) {
  341 + updateTenantAlarmsCustomer(tenant.getId());
  342 + }
  343 + };
  344 +
  345 + private void updateTenantAlarmsCustomer(TenantId tenantId) {
  346 + AlarmQuery alarmQuery = new AlarmQuery(null, new TimePageLink(100), null, null, false);
  347 + PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, alarmQuery);
  348 + boolean hasNext = true;
  349 + while (hasNext) {
  350 + for (Alarm alarm : alarms.getData()) {
  351 + if (alarm.getCustomerId() == null && alarm.getOriginator() != null) {
  352 + alarm.setCustomerId(entityService.fetchEntityCustomerId(tenantId, alarm.getOriginator()));
  353 + alarmDao.save(tenantId, alarm);
  354 + }
  355 + }
  356 + if (alarms.hasNext()) {
  357 + alarmQuery.setPageLink(alarmQuery.getPageLink().nextPageLink());
  358 + alarms = alarmDao.findAlarms(tenantId, alarmQuery);
  359 + } else {
  360 + hasNext = false;
  361 + }
  362 + }
  363 + }
  364 +
264 365 }
... ...
... ... @@ -15,16 +15,20 @@
15 15 */
16 16 package org.thingsboard.server.service.install.update;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 19 import org.thingsboard.server.common.data.SearchTextBased;
19 20 import org.thingsboard.server.common.data.id.UUIDBased;
20 21 import org.thingsboard.server.common.data.page.PageData;
21 22 import org.thingsboard.server.common.data.page.PageLink;
22 23
  24 +@Slf4j
23 25 public abstract class PaginatedUpdater<I, D extends SearchTextBased<? extends UUIDBased>> {
24 26
25 27 private static final int DEFAULT_LIMIT = 100;
  28 + private int updated = 0;
26 29
27 30 public void updateEntities(I id) {
  31 + updated = 0;
28 32 PageLink pageLink = new PageLink(DEFAULT_LIMIT);
29 33 boolean hasNext = true;
30 34 while (hasNext) {
... ... @@ -32,13 +36,25 @@ public abstract class PaginatedUpdater<I, D extends SearchTextBased<? extends UU
32 36 for (D entity : entities.getData()) {
33 37 updateEntity(entity);
34 38 }
  39 + updated += entities.getData().size();
35 40 hasNext = entities.hasNext();
36 41 if (hasNext) {
  42 + log.info("{}: {} entities updated so far...", getName(), updated);
37 43 pageLink = pageLink.nextPageLink();
  44 + } else {
  45 + if (updated > DEFAULT_LIMIT || forceReportTotal()) {
  46 + log.info("{}: {} total entities updated.", getName(), updated);
  47 + }
38 48 }
39 49 }
40 50 }
41 51
  52 + protected boolean forceReportTotal() {
  53 + return false;
  54 + }
  55 +
  56 + protected abstract String getName();
  57 +
42 58 protected abstract PageData<D> findEntities(I id, PageLink pageLink);
43 59
44 60 protected abstract void updateEntity(D entity);
... ...
... ... @@ -309,6 +309,8 @@ public class DefaultMailService implements MailService {
309 309 case EMAIL:
310 310 case SMS:
311 311 return "send";
  312 + case ALARM:
  313 + return "create";
312 314 default:
313 315 throw new RuntimeException("Not implemented!");
314 316 }
... ... @@ -327,6 +329,8 @@ public class DefaultMailService implements MailService {
327 329 case EMAIL:
328 330 case SMS:
329 331 return "sent";
  332 + case ALARM:
  333 + return "created";
330 334 default:
331 335 throw new RuntimeException("Not implemented!");
332 336 }
... ...
... ... @@ -44,6 +44,7 @@ import java.util.Base64;
44 44 import java.util.Comparator;
45 45 import java.util.List;
46 46 import java.util.stream.Collectors;
  47 +import java.util.stream.Stream;
47 48
48 49 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
49 50 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_SEARCH_TEXT;
... ... @@ -130,7 +131,7 @@ public class DefaultTbResourceService implements TbResourceService {
130 131 List<TbResource> resources = resourceService.findTenantResourcesByResourceTypeAndObjectIds(tenantId, ResourceType.LWM2M_MODEL,
131 132 objectIds);
132 133 return resources.stream()
133   - .map(this::toLwM2mObject)
  134 + .flatMap(s -> Stream.ofNullable(toLwM2mObject(s)))
134 135 .sorted(getComparator(sortProperty, sortOrder))
135 136 .collect(Collectors.toList());
136 137 }
... ... @@ -141,7 +142,7 @@ public class DefaultTbResourceService implements TbResourceService {
141 142 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
142 143 PageData<TbResource> resourcePageData = resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, ResourceType.LWM2M_MODEL, pageLink);
143 144 return resourcePageData.getData().stream()
144   - .map(this::toLwM2mObject)
  145 + .flatMap(s -> Stream.ofNullable(toLwM2mObject(s)))
145 146 .sorted(getComparator(sortProperty, sortOrder))
146 147 .collect(Collectors.toList());
147 148 }
... ... @@ -190,9 +191,14 @@ public class DefaultTbResourceService implements TbResourceService {
190 191 resources.add(lwM2MResourceObserve);
191 192 }
192 193 });
193   - instance.setResources(resources.toArray(LwM2mResourceObserve[]::new));
194   - lwM2mObject.setInstances(new LwM2mInstance[]{instance});
195   - return lwM2mObject;
  194 + if (resources.size() > 0) {
  195 + instance.setResources(resources.toArray(LwM2mResourceObserve[]::new));
  196 + lwM2mObject.setInstances(new LwM2mInstance[]{instance});
  197 + return lwM2mObject;
  198 + }
  199 + else {
  200 + return null;
  201 + }
196 202 }
197 203 } catch (IOException | InvalidDDFFileException e) {
198 204 log.error("Could not parse the XML of objectModel with name [{}]", resource.getSearchText(), e);
... ...
... ... @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
23 23 import org.checkerframework.checker.nullness.qual.Nullable;
24 24 import org.springframework.beans.factory.annotation.Autowired;
25 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
26 27 import org.thingsboard.server.common.data.alarm.Alarm;
27 28 import org.thingsboard.server.common.data.alarm.AlarmInfo;
28 29 import org.thingsboard.server.common.data.alarm.AlarmQuery;
... ... @@ -43,6 +44,8 @@ import org.thingsboard.server.dao.alarm.AlarmOperationResult;
43 44 import org.thingsboard.server.dao.alarm.AlarmService;
44 45 import org.thingsboard.server.gen.transport.TransportProtos;
45 46 import org.thingsboard.server.queue.discovery.PartitionService;
  47 +import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
  48 +import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
46 49 import org.thingsboard.server.service.queue.TbClusterService;
47 50 import org.thingsboard.server.service.subscription.SubscriptionManagerService;
48 51 import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
... ... @@ -58,12 +61,18 @@ import java.util.Optional;
58 61 public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService implements AlarmSubscriptionService {
59 62
60 63 private final AlarmService alarmService;
  64 + private final TbApiUsageClient apiUsageClient;
  65 + private final TbApiUsageStateService apiUsageStateService;
61 66
62 67 public DefaultAlarmSubscriptionService(TbClusterService clusterService,
63 68 PartitionService partitionService,
64   - AlarmService alarmService) {
  69 + AlarmService alarmService,
  70 + TbApiUsageClient apiUsageClient,
  71 + TbApiUsageStateService apiUsageStateService) {
65 72 super(clusterService, partitionService);
66 73 this.alarmService = alarmService;
  74 + this.apiUsageClient = apiUsageClient;
  75 + this.apiUsageStateService = apiUsageStateService;
67 76 }
68 77
69 78 @Autowired(required = false)
... ... @@ -78,10 +87,13 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
78 87
79 88 @Override
80 89 public Alarm createOrUpdateAlarm(Alarm alarm) {
81   - AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);
  90 + AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm, apiUsageStateService.getApiUsageState(alarm.getTenantId()).isAlarmCreationEnabled());
82 91 if (result.isSuccessful()) {
83 92 onAlarmUpdated(result);
84 93 }
  94 + if (result.isCreated()) {
  95 + apiUsageClient.report(alarm.getTenantId(), null, ApiUsageRecordKey.CREATED_ALARMS_COUNT);
  96 + }
85 97 return result.getAlarm();
86 98 }
87 99
... ... @@ -128,6 +140,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
128 140 }
129 141
130 142 @Override
  143 + public ListenableFuture<PageData<AlarmInfo>> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) {
  144 + return alarmService.findCustomerAlarms(tenantId, customerId, query);
  145 + }
  146 +
  147 + @Override
131 148 public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus) {
132 149 return alarmService.findHighestAlarmSeverity(tenantId, entityId, alarmSearchStatus, alarmStatus);
133 150 }
... ...
... ... @@ -26,7 +26,7 @@
26 26 </appender>
27 27
28 28 <logger name="org.thingsboard.server" level="INFO" />
29   - <logger name="org.thingsboard.server.transport.snmp" level="TRACE" />
  29 + <logger name="org.thingsboard.server.transport.snmp" level="DEBUG" />
30 30
31 31 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />-->
32 32 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />-->
... ...
... ... @@ -673,6 +673,8 @@ transport:
673 673 recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
674 674 response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}"
675 675 registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}"
  676 + registration_store_pool_size: "${LWM2M_REGISTRATION_STORE_POOL_SIZE:100}"
  677 + clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}"
676 678 update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
677 679 un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
678 680 log_max_length: "${LWM2M_LOG_MAX_LENGTH:100}"
... ...
... ... @@ -313,7 +313,8 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
313 313 Collections.sort(loadedDeviceProfileInfos, deviceProfileInfoIdComparator);
314 314
315 315 List<DeviceProfileInfo> deviceProfileInfos = deviceProfiles.stream().map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(),
316   - deviceProfile.getName(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList());
  316 + deviceProfile.getName(), deviceProfile.getImage(), deviceProfile.getDefaultDashboardId(),
  317 + deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList());
317 318
318 319 Assert.assertEquals(deviceProfileInfos, loadedDeviceProfileInfos);
319 320
... ...
... ... @@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.page.PageLink;
32 32 import org.thingsboard.server.common.data.security.Authority;
33 33 import org.thingsboard.server.controller.AbstractControllerTest;
34 34 import org.thingsboard.server.dao.exception.DataValidationException;
35   -import org.thingsboard.server.dao.service.AbstractServiceTest;
36 35 import org.thingsboard.server.dao.service.DaoSqlTest;
37 36
38 37 import java.util.ArrayList;
... ... @@ -57,7 +56,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest {
57 56 "<Resources>\n" +
58 57 "<Item ID=\"0\">\n" +
59 58 "<Name>LWM2M</Name>\n" +
60   - "<Operations></Operations>\n" +
  59 + "<Operations>RW</Operations>\n" +
61 60 "<MultipleInstances>Single</MultipleInstances>\n" +
62 61 "<Mandatory>Mandatory</Mandatory>\n" +
63 62 "<Type>String</Type>\n" +
... ...
... ... @@ -177,7 +177,7 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
177 177 executor = Executors.newScheduledThreadPool(10);
178 178 loginTenantAdmin();
179 179
180   - String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"};
  180 + String[] resources = new String[]{"1.xml", "2.xml", "3.xml"};
181 181 for (String resourceName : resources) {
182 182 TbResource lwModel = new TbResource();
183 183 lwModel.setResourceType(ResourceType.LWM2M_MODEL);
... ...
... ... @@ -26,17 +26,21 @@ import java.util.List;
26 26 public class AlarmOperationResult {
27 27 private final Alarm alarm;
28 28 private final boolean successful;
  29 + private final boolean created;
29 30 private final List<EntityId> propagatedEntitiesList;
30 31
31 32 public AlarmOperationResult(Alarm alarm, boolean successful) {
32   - this.alarm = alarm;
33   - this.successful = successful;
34   - this.propagatedEntitiesList = Collections.emptyList();
  33 + this(alarm, successful, Collections.emptyList());
35 34 }
36 35
37 36 public AlarmOperationResult(Alarm alarm, boolean successful, List<EntityId> propagatedEntitiesList) {
  37 + this(alarm, successful, false, propagatedEntitiesList);
  38 + }
  39 +
  40 + public AlarmOperationResult(Alarm alarm, boolean successful, boolean created, List<EntityId> propagatedEntitiesList) {
38 41 this.alarm = alarm;
39 42 this.successful = successful;
  43 + this.created = created;
40 44 this.propagatedEntitiesList = propagatedEntitiesList;
41 45 }
42 46 }
... ...
... ... @@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.id.EntityId;
29 29 import org.thingsboard.server.common.data.id.TenantId;
30 30 import org.thingsboard.server.common.data.page.PageData;
31 31 import org.thingsboard.server.common.data.query.AlarmData;
32   -import org.thingsboard.server.common.data.query.AlarmDataPageLink;
33 32 import org.thingsboard.server.common.data.query.AlarmDataQuery;
34 33
35 34 import java.util.Collection;
... ... @@ -41,6 +40,8 @@ public interface AlarmService {
41 40
42 41 AlarmOperationResult createOrUpdateAlarm(Alarm alarm);
43 42
  43 + AlarmOperationResult createOrUpdateAlarm(Alarm alarm, boolean alarmCreationEnabled);
  44 +
44 45 AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId);
45 46
46 47 ListenableFuture<AlarmOperationResult> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs);
... ... @@ -53,6 +54,8 @@ public interface AlarmService {
53 54
54 55 ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query);
55 56
  57 + ListenableFuture<PageData<AlarmInfo>> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query);
  58 +
56 59 AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
57 60 AlarmStatus alarmStatus);
58 61
... ...
... ... @@ -28,6 +28,8 @@ public interface EntityService {
28 28
29 29 ListenableFuture<String> fetchEntityNameAsync(TenantId tenantId, EntityId entityId);
30 30
  31 + CustomerId fetchEntityCustomerId(TenantId tenantId, EntityId entityId);
  32 +
31 33 void deleteEntityRelations(TenantId tenantId, EntityId entityId);
32 34
33 35 long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query);
... ...
... ... @@ -23,7 +23,8 @@ public enum ApiFeature {
23 23 RE("ruleEngineApiState", "Rule Engine execution"),
24 24 JS("jsExecutionApiState", "JavaScript functions execution"),
25 25 EMAIL("emailApiState", "Email messages"),
26   - SMS("smsApiState", "SMS messages");
  26 + SMS("smsApiState", "SMS messages"),
  27 + ALARM("alarmApiState", "Created alarms");
27 28
28 29 @Getter
29 30 private final String apiStateKey;
... ...
... ... @@ -25,13 +25,16 @@ public enum ApiUsageRecordKey {
25 25 RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit"),
26 26 JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit"),
27 27 EMAIL_EXEC_COUNT(ApiFeature.EMAIL, "emailCount", "emailLimit"),
28   - SMS_EXEC_COUNT(ApiFeature.SMS, "smsCount", "smsLimit");
  28 + SMS_EXEC_COUNT(ApiFeature.SMS, "smsCount", "smsLimit"),
  29 + CREATED_ALARMS_COUNT(ApiFeature.ALARM, "createdAlarmsCount", "createdAlarmsLimit");
  30 +
29 31 private static final ApiUsageRecordKey[] JS_RECORD_KEYS = {JS_EXEC_COUNT};
30 32 private static final ApiUsageRecordKey[] RE_RECORD_KEYS = {RE_EXEC_COUNT};
31 33 private static final ApiUsageRecordKey[] DB_RECORD_KEYS = {STORAGE_DP_COUNT};
32 34 private static final ApiUsageRecordKey[] TRANSPORT_RECORD_KEYS = {TRANSPORT_MSG_COUNT, TRANSPORT_DP_COUNT};
33 35 private static final ApiUsageRecordKey[] EMAIL_RECORD_KEYS = {EMAIL_EXEC_COUNT};
34 36 private static final ApiUsageRecordKey[] SMS_RECORD_KEYS = {SMS_EXEC_COUNT};
  37 + private static final ApiUsageRecordKey[] ALARM_RECORD_KEYS = {CREATED_ALARMS_COUNT};
35 38
36 39 @Getter
37 40 private final ApiFeature apiFeature;
... ... @@ -60,6 +63,8 @@ public enum ApiUsageRecordKey {
60 63 return EMAIL_RECORD_KEYS;
61 64 case SMS:
62 65 return SMS_RECORD_KEYS;
  66 + case ALARM:
  67 + return ALARM_RECORD_KEYS;
63 68 default:
64 69 return new ApiUsageRecordKey[]{};
65 70 }
... ...
... ... @@ -25,34 +25,21 @@ import org.thingsboard.server.common.data.id.ApiUsageStateId;
25 25
26 26 @ToString
27 27 @EqualsAndHashCode(callSuper = true)
  28 +@Getter
  29 +@Setter
28 30 public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenantId {
29 31
30 32 private static final long serialVersionUID = 8250339805336035966L;
31 33
32   - @Getter
33   - @Setter
34 34 private TenantId tenantId;
35   - @Getter
36   - @Setter
37 35 private EntityId entityId;
38   - @Getter
39   - @Setter
40 36 private ApiUsageStateValue transportState;
41   - @Getter
42   - @Setter
43 37 private ApiUsageStateValue dbStorageState;
44   - @Getter
45   - @Setter
46 38 private ApiUsageStateValue reExecState;
47   - @Getter
48   - @Setter
49 39 private ApiUsageStateValue jsExecState;
50   - @Getter
51   - @Setter
52 40 private ApiUsageStateValue emailExecState;
53   - @Getter
54   - @Setter
55 41 private ApiUsageStateValue smsExecState;
  42 + private ApiUsageStateValue alarmExecState;
56 43
57 44 public ApiUsageState() {
58 45 super();
... ... @@ -72,6 +59,7 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
72 59 this.jsExecState = ur.getJsExecState();
73 60 this.emailExecState = ur.getEmailExecState();
74 61 this.smsExecState = ur.getSmsExecState();
  62 + this.alarmExecState = ur.getAlarmExecState();
75 63 }
76 64
77 65 public boolean isTransportEnabled() {
... ... @@ -97,4 +85,8 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
97 85 public boolean isSmsSendEnabled(){
98 86 return !ApiUsageStateValue.DISABLED.equals(smsExecState);
99 87 }
  88 +
  89 + public boolean isAlarmCreationEnabled() {
  90 + return alarmExecState != ApiUsageStateValue.DISABLED;
  91 + }
100 92 }
... ...
... ... @@ -30,6 +30,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
30 30 private TenantId tenantId;
31 31 @NoXss
32 32 private String title;
  33 + private String image;
33 34 @Valid
34 35 private Set<ShortCustomerInfo> assignedCustomers;
35 36
... ... @@ -45,6 +46,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
45 46 super(dashboardInfo);
46 47 this.tenantId = dashboardInfo.getTenantId();
47 48 this.title = dashboardInfo.getTitle();
  49 + this.image = dashboardInfo.getImage();
48 50 this.assignedCustomers = dashboardInfo.getAssignedCustomers();
49 51 }
50 52
... ... @@ -64,6 +66,14 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
64 66 this.title = title;
65 67 }
66 68
  69 + public String getImage() {
  70 + return image;
  71 + }
  72 +
  73 + public void setImage(String image) {
  74 + this.image = image;
  75 + }
  76 +
67 77 public Set<ShortCustomerInfo> getAssignedCustomers() {
68 78 return assignedCustomers;
69 79 }
... ...
... ... @@ -92,26 +92,6 @@ public class DataConstants {
92 92 public static final String CLIENT_ID = "clientId";
93 93 public static final String USERNAME = "username";
94 94 public static final String PASSWORD = "password";
95   -
96   -//<<<<<<< HEAD
97   -//=======
98   -// //firmware
99   -// //telemetry
100   -// public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title";
101   -// public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version";
102   -// public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
103   -// public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
104   -// public static final String TARGET_FIRMWARE_TS = "target_fw_ts";
105   -// public static final String FIRMWARE_STATE = "fw_state";
106   -//
107   -// //attributes
108   -// //telemetry
109   -// public static final String FIRMWARE_TITLE = "fw_title";
110   -// public static final String FIRMWARE_VERSION = "fw_version";
111   -// public static final String FIRMWARE_SIZE = "fw_size";
112   -// public static final String FIRMWARE_CHECKSUM = "fw_checksum";
113   -// public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm";
114   -//>>>>>>> origin/master
115 95 public static final String EDGE_MSG_SOURCE = "edge";
116 96 public static final String MSG_SOURCE_KEY = "source";
117 97
... ...
... ... @@ -21,6 +21,7 @@ import lombok.Data;
21 21 import lombok.EqualsAndHashCode;
22 22 import lombok.extern.slf4j.Slf4j;
23 23 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
  24 +import org.thingsboard.server.common.data.id.DashboardId;
24 25 import org.thingsboard.server.common.data.id.DeviceProfileId;
25 26 import org.thingsboard.server.common.data.id.FirmwareId;
26 27 import org.thingsboard.server.common.data.id.RuleChainId;
... ... @@ -43,11 +44,13 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
43 44 private String name;
44 45 @NoXss
45 46 private String description;
  47 + private String image;
46 48 private boolean isDefault;
47 49 private DeviceProfileType type;
48 50 private DeviceTransportType transportType;
49 51 private DeviceProfileProvisionType provisionType;
50 52 private RuleChainId defaultRuleChainId;
  53 + private DashboardId defaultDashboardId;
51 54 @NoXss
52 55 private String defaultQueueName;
53 56 @Valid
... ... @@ -74,8 +77,10 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
74 77 this.tenantId = deviceProfile.getTenantId();
75 78 this.name = deviceProfile.getName();
76 79 this.description = deviceProfile.getDescription();
  80 + this.image = deviceProfile.getImage();
77 81 this.isDefault = deviceProfile.isDefault();
78 82 this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId();
  83 + this.defaultDashboardId = deviceProfile.getDefaultDashboardId();
79 84 this.defaultQueueName = deviceProfile.getDefaultQueueName();
80 85 this.setProfileData(deviceProfile.getProfileData());
81 86 this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
... ...
... ... @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
20 20 import lombok.EqualsAndHashCode;
21 21 import lombok.ToString;
22 22 import lombok.Value;
  23 +import org.thingsboard.server.common.data.id.DashboardId;
23 24 import org.thingsboard.server.common.data.id.EntityId;
24 25 import org.thingsboard.server.common.data.id.EntityIdFactory;
25 26
... ... @@ -30,21 +31,29 @@ import java.util.UUID;
30 31 @ToString(callSuper = true)
31 32 public class DeviceProfileInfo extends EntityInfo {
32 33
  34 + private final String image;
  35 + private final DashboardId defaultDashboardId;
33 36 private final DeviceProfileType type;
34 37 private final DeviceTransportType transportType;
35 38
36 39 @JsonCreator
37 40 public DeviceProfileInfo(@JsonProperty("id") EntityId id,
38 41 @JsonProperty("name") String name,
  42 + @JsonProperty("image") String image,
  43 + @JsonProperty("defaultDashboardId") DashboardId defaultDashboardId,
39 44 @JsonProperty("type") DeviceProfileType type,
40 45 @JsonProperty("transportType") DeviceTransportType transportType) {
41 46 super(id, name);
  47 + this.image = image;
  48 + this.defaultDashboardId = defaultDashboardId;
42 49 this.type = type;
43 50 this.transportType = transportType;
44 51 }
45 52
46   - public DeviceProfileInfo(UUID uuid, String name, DeviceProfileType type, DeviceTransportType transportType) {
  53 + public DeviceProfileInfo(UUID uuid, String name, String image, UUID defaultDashboardId, DeviceProfileType type, DeviceTransportType transportType) {
47 54 super(EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE_PROFILE, uuid), name);
  55 + this.image = image;
  56 + this.defaultDashboardId = defaultDashboardId != null ? new DashboardId(defaultDashboardId) : null;
48 57 this.type = type;
49 58 this.transportType = transportType;
50 59 }
... ...
... ... @@ -21,9 +21,11 @@ import lombok.AllArgsConstructor;
21 21 import lombok.Builder;
22 22 import lombok.Data;
23 23 import org.thingsboard.server.common.data.BaseData;
  24 +import org.thingsboard.server.common.data.HasCustomerId;
24 25 import org.thingsboard.server.common.data.HasName;
25 26 import org.thingsboard.server.common.data.HasTenantId;
26 27 import org.thingsboard.server.common.data.id.AlarmId;
  28 +import org.thingsboard.server.common.data.id.CustomerId;
27 29 import org.thingsboard.server.common.data.id.EntityId;
28 30 import org.thingsboard.server.common.data.id.TenantId;
29 31
... ... @@ -35,9 +37,10 @@ import java.util.List;
35 37 @Data
36 38 @Builder
37 39 @AllArgsConstructor
38   -public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId {
  40 +public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, HasCustomerId {
39 41
40 42 private TenantId tenantId;
  43 + private CustomerId customerId;
41 44 private String type;
42 45 private EntityId originator;
43 46 private AlarmSeverity severity;
... ... @@ -62,6 +65,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId {
62 65 super(alarm.getId());
63 66 this.createdTime = alarm.getCreatedTime();
64 67 this.tenantId = alarm.getTenantId();
  68 + this.customerId = alarm.getCustomerId();
65 69 this.type = alarm.getType();
66 70 this.originator = alarm.getOriginator();
67 71 this.severity = alarm.getSeverity();
... ...
... ... @@ -82,7 +82,7 @@ public class MqttTopics {
82 82 public static final String DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
83 83 public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
84 84 public static final String DEVICE_FIRMWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + ERROR;
85   - public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT = BASE_DEVICE_API_TOPIC_V2 + "%s" + RESPONSE + "/"+ "%s" + CHUNK + "%d";
  85 + public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT = BASE_DEVICE_API_TOPIC_V2 + "/%s" + RESPONSE + "/"+ "%s" + CHUNK + "%d";
86 86
87 87 public static final String DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
88 88 public static final String DEVICE_SOFTWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
... ...
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUpdateStatus.java renamed from application/src/main/java/org/thingsboard/server/service/firmware/FirmwareUpdateStatus.java
... ... @@ -13,7 +13,7 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.service.firmware;
  16 +package org.thingsboard.server.common.data.firmware;
17 17
18 18 public enum FirmwareUpdateStatus {
19 19 QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
... ...
... ... @@ -50,6 +50,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
50 50 private int maxRuleNodeExecutionsPerMessage;
51 51 private long maxEmails;
52 52 private long maxSms;
  53 + private long maxCreatedAlarms;
53 54
54 55 private int defaultStorageTtlDays;
55 56
... ... @@ -72,6 +73,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
72 73 return maxEmails;
73 74 case SMS_EXEC_COUNT:
74 75 return maxSms;
  76 + case CREATED_ALARMS_COUNT:
  77 + return maxCreatedAlarms;
75 78 }
76 79 return 0L;
77 80 }
... ...
... ... @@ -53,6 +53,10 @@
53 53 <artifactId>spring-context</artifactId>
54 54 </dependency>
55 55 <dependency>
  56 + <groupId>org.springframework.integration</groupId>
  57 + <artifactId>spring-integration-redis</artifactId>
  58 + </dependency>
  59 + <dependency>
56 60 <groupId>org.slf4j</groupId>
57 61 <artifactId>slf4j-api</artifactId>
58 62 </dependency>
... ...
... ... @@ -46,6 +46,7 @@ import java.util.UUID;
46 46 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.BOOTSTRAP_SERVER;
47 47 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR;
48 48 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
  49 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY;
49 50 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_SERVER;
50 51 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SERVERS;
51 52 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getBootstrapParametersFromThingsboard;
... ... @@ -165,13 +166,13 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
165 166 lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
166 167 lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
167 168 String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndpoint());
168   - helper.sendParametersOnThingsboardTelemetry(helper.getKvLogyToThingsboard(logMsg), sessionInfo);
  169 + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo);
169 170 return lwM2MBootstrapConfig;
170 171 } else {
171 172 log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndpoint());
172 173 log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint());
173 174 String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint());
174   - helper.sendParametersOnThingsboardTelemetry(helper.getKvLogyToThingsboard(logMsg), sessionInfo);
  175 + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo);
175 176 return null;
176 177 }
177 178 }
... ...
... ... @@ -65,6 +65,14 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
65 65 private int registeredPoolSize;
66 66
67 67 @Getter
  68 + @Value("${transport.lwm2m.registration_store_pool_size:}")
  69 + private int registrationStorePoolSize;
  70 +
  71 + @Getter
  72 + @Value("${transport.lwm2m.clean_period_in_sec:}")
  73 + private int cleanPeriodInSec;
  74 +
  75 + @Getter
68 76 @Value("${transport.lwm2m.update_registered_pool_size:}")
69 77 private int updateRegisteredPoolSize;
70 78
... ...
... ... @@ -23,13 +23,12 @@ import com.google.gson.JsonElement;
23 23 import com.google.gson.JsonObject;
24 24 import com.google.gson.reflect.TypeToken;
25 25 import lombok.extern.slf4j.Slf4j;
  26 +import org.eclipse.leshan.core.model.ObjectModel;
26 27 import org.eclipse.leshan.core.model.ResourceModel;
27   -import org.eclipse.leshan.core.node.LwM2mMultipleResource;
28 28 import org.eclipse.leshan.core.node.LwM2mObject;
29 29 import org.eclipse.leshan.core.node.LwM2mObjectInstance;
30 30 import org.eclipse.leshan.core.node.LwM2mPath;
31 31 import org.eclipse.leshan.core.node.LwM2mResource;
32   -import org.eclipse.leshan.core.node.LwM2mSingleResource;
33 32 import org.eclipse.leshan.core.observation.Observation;
34 33 import org.eclipse.leshan.core.request.ContentFormat;
35 34 import org.eclipse.leshan.core.request.WriteRequest;
... ... @@ -56,6 +55,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
56 55 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
57 56 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
58 57 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
  58 +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
59 59 import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
60 60 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
61 61 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
... ... @@ -85,19 +85,21 @@ import java.util.stream.Collectors;
85 85
86 86 import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
87 87 import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
88   -import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
  88 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED;
  89 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING;
89 90 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
90 91 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto;
91 92 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.CLIENT_NOT_AUTHORIZED;
92 93 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST;
93   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FR_OBJECT_ID;
94   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FR_PATH_RESOURCE_VER_ID;
  94 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_ID;
  95 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID;
95 96 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR;
96 97 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
  98 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY;
97 99 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
98 100 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_STRATEGY_2;
99   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
100 101 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
  102 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_All;
101 103 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
102 104 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE;
103 105 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
... ... @@ -105,6 +107,9 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
105 107 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ;
106 108 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
107 109 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
  110 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE;
  111 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID;
  112 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID;
108 113 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertJsonArrayToSet;
109 114 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId;
110 115 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
... ... @@ -119,19 +124,20 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
119 124
120 125 private ExecutorService registrationExecutor;
121 126 private ExecutorService updateRegistrationExecutor;
122   - private ExecutorService unregistrationExecutor;
  127 + private ExecutorService unRegistrationExecutor;
123 128 private LwM2mValueConverterImpl converter;
124 129
125 130 private final TransportService transportService;
126 131 private final LwM2mTransportContext context;
127   - private final LwM2MTransportServerConfig config;
128   - private final FirmwareDataCache firmwareDataCache;
129   - private final LwM2mTransportServerHelper helper;
  132 + public final LwM2MTransportServerConfig config;
  133 + public final FirmwareDataCache firmwareDataCache;
  134 + public final LwM2mTransportServerHelper helper;
130 135 private final LwM2MJsonAdaptor adaptor;
131 136 private final LwM2mClientContext clientContext;
132 137 private final LwM2mTransportRequest lwM2mTransportRequest;
133 138 private final TbLwM2MDtlsSessionStore sessionStore;
134 139
  140 +
135 141 public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper,
136 142 LwM2mClientContext clientContext,
137 143 @Lazy LwM2mTransportRequest lwM2mTransportRequest,
... ... @@ -153,7 +159,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
153 159 this.context.getScheduler().scheduleAtFixedRate(this::reportActivity, new Random().nextInt((int) config.getSessionReportTimeout()), config.getSessionReportTimeout(), TimeUnit.MILLISECONDS);
154 160 this.registrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getRegisteredPoolSize(), "LwM2M registration");
155 161 this.updateRegistrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getUpdateRegisteredPoolSize(), "LwM2M update registration");
156   - this.unregistrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getUnRegisteredPoolSize(), "LwM2M unregistration");
  162 + this.unRegistrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getUnRegisteredPoolSize(), "LwM2M unRegistration");
157 163 this.converter = LwM2mValueConverterImpl.getInstance();
158 164 }
159 165
... ... @@ -184,6 +190,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
184 190 transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
185 191 transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null);
186 192 this.getInfoFirmwareUpdate(lwM2MClient);
  193 + this.getInfoSoftwareUpdate(lwM2MClient);
187 194 this.initLwM2mFromClientValue(registration, lwM2MClient);
188 195 this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId());
189 196 } else {
... ... @@ -216,11 +223,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
216 223 request.send();
217 224 }
218 225 }
  226 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client update Registration", registration.getId());
219 227 } else {
220 228 log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
  229 + this.sendLogsToThingsboard(LOG_LW2M_ERROR + ": Client update Registration", registration.getId());
221 230 }
222 231 } catch (Throwable t) {
223 232 log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);
  233 + this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage()), registration.getId());
224 234 }
225 235 });
226 236 }
... ... @@ -231,13 +241,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
231 241 * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
232 242 */
233 243 public void unReg(Registration registration, Collection<Observation> observations) {
234   - unregistrationExecutor.submit(() -> {
  244 + unRegistrationExecutor.submit(() -> {
235 245 try {
236   - this.setCancelObservations(registration);
  246 + this.setCancelObservationsAll(registration);
237 247 this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
238 248 this.closeClientSession(registration);
  249 + ;
239 250 } catch (Throwable t) {
240 251 log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
  252 + this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId());
241 253 }
242 254 });
243 255 }
... ... @@ -263,13 +275,18 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
263 275 //TODO: associate endpointId with device information.
264 276 }
265 277
  278 + /**
  279 + * Cancel observation for All objects for this registration
  280 + */
266 281 @Override
267   - public void setCancelObservations(Registration registration) {
  282 + public void setCancelObservationsAll(Registration registration) {
268 283 if (registration != null) {
269   - Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
270   - observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration,
271   - convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL,
272   - null, null, this.config.getTimeout(), null));
  284 + lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL,
  285 + null, null, this.config.getTimeout(), null);
  286 +// Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
  287 +// observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration,
  288 +// convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL,
  289 +// null, null, this.config.getTimeout(), null));
273 290 }
274 291 }
275 292
... ... @@ -283,34 +300,43 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
283 300 @Override
284 301 public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) {
285 302 if (response.getContent() != null) {
286   - Object value = null;
287   - if (response.getContent() instanceof LwM2mObject) {
288   - LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
289   - if (rpcRequest != null) {
290   - value = lwM2mObject.toString();
  303 + LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
  304 + ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider());
  305 + if (objectModelVersion != null) {
  306 + if (response.getContent() instanceof LwM2mObject) {
  307 + LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
  308 + this.updateObjectResourceValue(registration, lwM2mObject, path);
  309 + } else if (response.getContent() instanceof LwM2mObjectInstance) {
  310 + LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent();
  311 + this.updateObjectInstanceResourceValue(registration, lwM2mObjectInstance, path);
  312 + } else if (response.getContent() instanceof LwM2mResource) {
  313 + LwM2mResource lwM2mResource = (LwM2mResource) response.getContent();
  314 + this.updateResourcesValue(registration, lwM2mResource, path);
291 315 }
292   - this.updateObjectResourceValue(registration, lwM2mObject, path);
293   - } else if (response.getContent() instanceof LwM2mObjectInstance) {
294   - LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent();
295   - if (rpcRequest != null) {
296   - value = lwM2mObjectInstance.toString();
297   - }
298   - this.updateObjectInstanceResourceValue(registration, lwM2mObjectInstance, path);
299   - } else if (response.getContent() instanceof LwM2mResource) {
300   - LwM2mResource lwM2mResource = (LwM2mResource) response.getContent();
301   - if (rpcRequest != null) {
302   - value = lwM2mResource.isMultiInstances() ? ((LwM2mMultipleResource) lwM2mResource).toString() :
303   - ((LwM2mSingleResource) lwM2mResource).toString();
304   - }
305   - this.updateResourcesValue(registration, lwM2mResource, path);
306 316 }
307 317 if (rpcRequest != null) {
308   - rpcRequest.setValueMsg(String.format("%s", value));
309   - this.sentRpcRequest(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE);
  318 + this.sendRpcRequestAfterReadResponse(registration, lwM2MClient, path, response, rpcRequest);
310 319 }
311 320 }
312 321 }
313 322
  323 + private void sendRpcRequestAfterReadResponse(Registration registration, LwM2mClient lwM2MClient, String pathIdVer, ReadResponse response,
  324 + Lwm2mClientRpcRequest rpcRequest) {
  325 + Object value = null;
  326 + if (response.getContent() instanceof LwM2mObject) {
  327 + value = lwM2MClient.objectToString((LwM2mObject) response.getContent(), this.converter, pathIdVer);
  328 + } else if (response.getContent() instanceof LwM2mObjectInstance) {
  329 + value = lwM2MClient.instanceToString((LwM2mObjectInstance) response.getContent(), this.converter, pathIdVer);
  330 + } else if (response.getContent() instanceof LwM2mResource) {
  331 + value = lwM2MClient.resourceToString((LwM2mResource) response.getContent(), this.converter, pathIdVer);
  332 + }
  333 + String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO,
  334 + READ, pathIdVer, value);
  335 + this.sendLogsToThingsboard(msg, registration.getId());
  336 + rpcRequest.setValueMsg(String.format("%s", value));
  337 + this.sentRpcRequest(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE);
  338 + }
  339 +
314 340 /**
315 341 * Update - send request in change value resources in Client
316 342 * 1. FirmwareUpdate:
... ... @@ -330,9 +356,16 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
330 356 String pathName = tsKvProto.getKv().getKey();
331 357 String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
332 358 Object valueNew = getValueFromKvProto(tsKvProto.getKv());
333   - //TODO: react on change of the firmware name.
334   - if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
  359 + if ((FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName)
  360 + && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())))
  361 + || (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.TITLE).equals(pathName)
  362 + && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) {
335 363 this.getInfoFirmwareUpdate(lwM2MClient);
  364 + } else if ((FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.VERSION).equals(pathName)
  365 + && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion())))
  366 + || (FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.TITLE).equals(pathName)
  367 + && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) {
  368 + this.getInfoSoftwareUpdate(lwM2MClient);
336 369 }
337 370 if (pathIdVer != null) {
338 371 ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config
... ... @@ -357,8 +390,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
357 390 msg.getSharedUpdatedList().forEach(tsKvProto -> {
358 391 String pathName = tsKvProto.getKv().getKey();
359 392 Object valueNew = getValueFromKvProto(tsKvProto.getKv());
360   - if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
361   - lwM2MClient.getFrUpdate().setCurrentFwVersion((String) valueNew);
  393 + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) {
  394 + lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew);
362 395 }
363 396 });
364 397 log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
... ... @@ -402,12 +435,12 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
402 435 }
403 436
404 437 @Override
405   - public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest, SessionInfoProto sessionInfo) {
  438 + public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) {
  439 + log.warn("4) RPC-OK finish to [{}]", toDeviceRpcRequestMsg);
406 440 Lwm2mClientRpcRequest lwm2mClientRpcRequest = null;
407 441 try {
408   - log.info("[{}] toDeviceRpcRequest", toDeviceRequest);
409 442 Registration registration = clientContext.getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).getRegistration();
410   - lwm2mClientRpcRequest = this.getDeviceRpcRequest(toDeviceRequest, sessionInfo, registration);
  443 + lwm2mClientRpcRequest = this.getDeviceRpcRequest(toDeviceRpcRequestMsg, sessionInfo, registration);
411 444 if (lwm2mClientRpcRequest.getErrorMsg() != null) {
412 445 lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
413 446 this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
... ... @@ -459,12 +492,27 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
459 492 lwm2mClientRpcRequest.setValue(rpcRequest.get(lwm2mClientRpcRequest.valueKey).getAsString());
460 493 }
461 494 if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) {
462   - lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
  495 + ConcurrentHashMap<String, Object> params = new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
  496 + .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
  497 + }.getType());
  498 + if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) {
  499 + ConcurrentHashMap<String, Object> paramsResourceId = convertParamsToResourceId(params, sessionInfo);
  500 + if (paramsResourceId.size() > 0) {
  501 + lwm2mClientRpcRequest.setParams(paramsResourceId);
  502 + }
  503 + } else {
  504 + lwm2mClientRpcRequest.setParams(params);
  505 + }
  506 + } else if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonArray()) {
  507 + new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
463 508 .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
464   - }.getType()));
  509 + }.getType());
465 510 }
466 511 lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
467   - if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) {
  512 + if (!(OBSERVE_READ_ALL == lwm2mClientRpcRequest.getTypeOper()
  513 + || DISCOVER_All == lwm2mClientRpcRequest.getTypeOper()
  514 + || OBSERVE_CANCEL == lwm2mClientRpcRequest.getTypeOper())
  515 + && lwm2mClientRpcRequest.getTargetIdVer() == null) {
468 516 lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " +
469 517 lwm2mClientRpcRequest.keyNameKey + " is null or bad format");
470 518 }
... ... @@ -488,6 +536,21 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
488 536 return lwm2mClientRpcRequest;
489 537 }
490 538
  539 + private ConcurrentHashMap<String, Object> convertParamsToResourceId(ConcurrentHashMap<String, Object> params,
  540 + SessionInfoProto sessionInfo) {
  541 + ConcurrentHashMap<String, Object> paramsIdVer = new ConcurrentHashMap<>();
  542 + params.forEach((k, v) -> {
  543 + String targetIdVer = this.getPresentPathIntoProfile(sessionInfo, k);
  544 + if (targetIdVer != null) {
  545 + LwM2mPath targetId = new LwM2mPath(convertPathFromIdVerToObjectId(targetIdVer));
  546 + if (targetId.isResource()) {
  547 + paramsIdVer.put(String.valueOf(targetId.getResourceId()), v);
  548 + }
  549 + }
  550 + });
  551 + return paramsIdVer;
  552 + }
  553 +
491 554 public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
492 555 rpcRequest.setResponseCode(requestCode);
493 556 if (LOG_LW2M_ERROR.equals(typeMsg)) {
... ... @@ -561,7 +624,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
561 624 */
562 625 @Override
563 626 public void onAwakeDev(Registration registration) {
564   - log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
  627 + log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
565 628 this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId());
566 629 //TODO: associate endpointId with device information.
567 630 }
... ... @@ -577,7 +640,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
577 640 if (logMsg.length() > 1024) {
578 641 logMsg = logMsg.substring(0, 1024);
579 642 }
580   - this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvLogyToThingsboard(logMsg), sessionInfo);
  643 + this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo);
581 644 }
582 645 }
583 646
... ... @@ -614,7 +677,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
614 677 /**
615 678 * @param registration -
616 679 * @param lwM2mObject -
617   - * @param pathIdVer -
  680 + * @param pathIdVer -
618 681 */
619 682 private void updateObjectResourceValue(Registration registration, LwM2mObject lwM2mObject, String pathIdVer) {
620 683 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer));
... ... @@ -651,18 +714,63 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
651 714 private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
652 715 LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
653 716 if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) {
654   - if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) &&
655   - lwM2MClient.getFrUpdate().getCurrentFwVersion() != null
656   - && !lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())
657   - && lwM2MClient.isUpdateFw()) {
658   -
659   - /** version != null
660   - * set setClient_fw_version = value
661   - **/
662   - lwM2MClient.setUpdateFw(false);
663   - lwM2MClient.getFrUpdate().setClientFwVersion(lwM2mResource.getValue().toString());
664   - log.warn("updateFirmwareClient3");
665   - this.updateFirmwareClient(lwM2MClient);
  717 + /** version != null
  718 + * set setClient_fw_info... = value
  719 + **/
  720 + if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
  721 + lwM2MClient.getFwUpdate().initReadValue(this, lwM2mTransportRequest, path);
  722 + }
  723 + if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
  724 + lwM2MClient.getSwUpdate().initReadValue(this, lwM2mTransportRequest, path);
  725 + }
  726 +
  727 + /**
  728 + * Before operation Execute (FwUpdate) inspection Update Result :
  729 + * - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED
  730 + * - before start operation Execute (FwUpdate) Update Result = 0 - Initial value
  731 + * - start Execute (FwUpdate)
  732 + * After finished operation Execute (FwUpdate) inspection Update Result :
  733 + * - after start operation Execute (FwUpdate): fw_state = UPDATING
  734 + * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully")
  735 + * - finished operation Execute (FwUpdate)
  736 + */
  737 + if (lwM2MClient.getFwUpdate() != null
  738 + && (convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
  739 + if (DOWNLOADED.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
  740 + && lwM2MClient.getFwUpdate().conditionalFwExecuteStart()) {
  741 + lwM2MClient.getFwUpdate().executeFwSwWare(this, lwM2mTransportRequest);
  742 + } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
  743 + && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterSuccess()) {
  744 + lwM2MClient.getFwUpdate().finishFwSwUpdate(this, true);
  745 + } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
  746 + && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterError()) {
  747 + lwM2MClient.getFwUpdate().finishFwSwUpdate(this, false);
  748 + }
  749 + }
  750 +
  751 + /**
  752 + * Before operation Execute (SwUpdate) inspection Update Result :
  753 + * - after finished operation Write result: success (SwUpdate): fw_state = DOWNLOADED
  754 + * - before operation Execute (SwUpdate) Update Result = 3 - Successfully Downloaded and package integrity verified
  755 + * - start Execute (SwUpdate)
  756 + * After finished operation Execute (SwUpdate) inspection Update Result :
  757 + * - after start operation Execute (SwUpdate): fw_state = UPDATING
  758 + * - after success finished operation Execute (SwUpdate) Update Result == 2 "Software successfully installed.""
  759 + * - after success finished operation Execute (SwUpdate) Update Result == 2 "Software successfully installed.""
  760 + * - finished operation Execute (SwUpdate)
  761 + */
  762 + if (lwM2MClient.getSwUpdate() != null
  763 + && (convertPathFromObjectIdToIdVer(SW_RESULT_ID, registration).equals(path))) {
  764 + if (DOWNLOADED.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
  765 + && lwM2MClient.getSwUpdate().conditionalSwUpdateExecute()) {
  766 + lwM2MClient.getSwUpdate().executeFwSwWare(this, lwM2mTransportRequest);
  767 + } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
  768 + && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterSuccess()) {
  769 + lwM2MClient.getSwUpdate().finishFwSwUpdate(this, true);
  770 + } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
  771 + && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterError()) {
  772 + lwM2MClient.getSwUpdate().finishFwSwUpdate(this, false);
  773 + }
666 774 }
667 775 Set<String> paths = new HashSet<>();
668 776 paths.add(path);
... ... @@ -684,7 +792,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
684 792 */
685 793 private void updateAttrTelemetry(Registration registration, Set<String> paths) {
686 794 try {
687   - ResultsAddKeyValueProto results = getParametersFromProfile(registration, paths);
  795 + ResultsAddKeyValueProto results = this.getParametersFromProfile(registration, paths);
688 796 SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration);
689 797 if (results != null && sessionInfo != null) {
690 798 if (results.getResultAttributes().size() > 0) {
... ... @@ -818,7 +926,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
818 926 Object finalvalueKvProto = valueKvProto;
819 927 Gson gson = new GsonBuilder().create();
820 928 resourceValue.getValues().forEach((k, v) -> {
821   - Object val = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
  929 + Object val = this.converter.convertValue(v, currentType, expectedType,
822 930 new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
823 931 JsonElement element = gson.toJsonTree(val, val.getClass());
824 932 ((JsonObject) finalvalueKvProto).add(String.valueOf(k), element);
... ... @@ -910,7 +1018,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
910 1018 */
911 1019 private void onDeviceProfileUpdate(Set<String> registrationIds, DeviceProfile deviceProfile) {
912 1020 LwM2mClientProfile lwM2MClientProfileOld = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone();
913   - if (clientContext.toClientProfile(deviceProfile) != null) {
  1021 + if (clientContext.profileUpdate(deviceProfile) != null) {
914 1022 // #1
915 1023 JsonArray attributeOld = lwM2MClientProfileOld.getPostAttributeProfile();
916 1024 Set<String> attributeSetOld = convertJsonArrayToSet(attributeOld);
... ... @@ -920,7 +1028,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
920 1028 JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile();
921 1029 JsonObject attributeLwm2mOld = lwM2MClientProfileOld.getPostAttributeLwm2mProfile();
922 1030
923   - LwM2mClientProfile lwM2MClientProfileNew = clientContext.getProfiles().get(deviceProfile.getUuidId());
  1031 + LwM2mClientProfile lwM2MClientProfileNew = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone();
924 1032 JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile();
925 1033 Set<String> attributeSetNew = convertJsonArrayToSet(attributeNew);
926 1034 JsonArray telemetryNew = lwM2MClientProfileNew.getPostTelemetryProfile();
... ... @@ -968,8 +1076,6 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
968 1076 registrationIds.forEach(registrationId -> {
969 1077 Registration registration = clientContext.getRegistration(registrationId);
970 1078 this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
971   - // send attr/telemetry to tingsboard for new path
972   - this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd());
973 1079 });
974 1080 }
975 1081 // #4.2 del
... ... @@ -1248,7 +1354,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1248 1354 * @param registration - Registration LwM2M Client
1249 1355 * @return - sessionInfo after access connect client
1250 1356 */
1251   - private SessionInfoProto getSessionInfoOrCloseSession(Registration registration) {
  1357 + public SessionInfoProto getSessionInfoOrCloseSession(Registration registration) {
1252 1358 return getSessionInfoOrCloseSession(clientContext.getOrRegister(registration));
1253 1359 }
1254 1360
... ... @@ -1296,7 +1402,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1296 1402 TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(null, keyNamesMap.values());
1297 1403 transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
1298 1404 } catch (AdaptorException e) {
1299   - log.warn("Failed to decode get attributes request", e);
  1405 + log.trace("Failed to decode get attributes request", e);
1300 1406 }
1301 1407 }
1302 1408
... ... @@ -1304,62 +1410,72 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1304 1410 }
1305 1411
1306 1412 public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) {
1307   - SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
1308   - if (sessionInfo != null) {
1309   - TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
1310   - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
1311   - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
1312   - .setTenantIdMSB(sessionInfo.getTenantIdMSB())
1313   - .setTenantIdLSB(sessionInfo.getTenantIdLSB())
1314   - .setType(FirmwareType.FIRMWARE.name())
1315   - .build();
1316   - transportService.process(sessionInfo, getFirmwareRequestMsg,
1317   - new TransportServiceCallback<>() {
1318   - @Override
1319   - public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
1320   - if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
1321   - lwM2MClient.getFrUpdate().setCurrentFwVersion(response.getVersion());
1322   - lwM2MClient.getFrUpdate().setCurrentFwId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
1323   - lwM2MClient.setUpdateFw(true);
1324   - readRequestToClientFirmwareVer(lwM2MClient.getRegistration());
1325   - } else {
1326   - log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
  1413 + if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) {
  1414 + SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
  1415 + if (sessionInfo != null) {
  1416 + DefaultLwM2MTransportMsgHandler serviceImpl = this;
  1417 + transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.FIRMWARE.name()),
  1418 + new TransportServiceCallback<>() {
  1419 + @Override
  1420 + public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
  1421 + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())
  1422 + && response.getType().equals(FirmwareType.FIRMWARE.name())) {
  1423 + lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion());
  1424 + lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle());
  1425 + lwM2MClient.getFwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
  1426 + lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
  1427 + } else {
  1428 + log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
  1429 + }
1327 1430 }
1328   - }
1329 1431
1330   - @Override
1331   - public void onError(Throwable e) {
1332   - log.trace("Failed to process credentials ", e);
1333   - }
1334   - });
  1432 + @Override
  1433 + public void onError(Throwable e) {
  1434 + log.trace("Failed to process firmwareUpdate ", e);
  1435 + }
  1436 + });
  1437 + }
1335 1438 }
1336 1439 }
1337 1440
1338   - /**
1339   - * @param registration
1340   - */
1341   - public void readRequestToClientFirmwareVer(Registration registration) {
1342   - String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration);
1343   - lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(),
1344   - null, config.getTimeout(), null);
1345   - }
  1441 + public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient) {
  1442 + if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) {
  1443 + SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
  1444 + if (sessionInfo != null) {
  1445 + DefaultLwM2MTransportMsgHandler serviceImpl = this;
  1446 + transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.SOFTWARE.name()),
  1447 + new TransportServiceCallback<>() {
  1448 + @Override
  1449 + public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
  1450 + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())
  1451 + && response.getType().equals(FirmwareType.SOFTWARE.name())) {
  1452 + lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion());
  1453 + lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle());
  1454 + lwM2MClient.getSwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
  1455 + lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
  1456 + } else {
  1457 + log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
  1458 + }
  1459 + }
1346 1460
1347   - /**
1348   - * @param lwM2MClient -
1349   - */
1350   - public void updateFirmwareClient(LwM2mClient lwM2MClient) {
1351   - if (!lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())) {
1352   - int chunkSize = 0;
1353   - int chunk = 0;
1354   - byte[] firmwareChunk = firmwareDataCache.get(lwM2MClient.getFrUpdate().getCurrentFwId().toString(), chunkSize, chunk);
1355   - String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(FR_OBJECT_ID);
1356   - String targetIdVer = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0;
1357   - lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
1358   - firmwareChunk, config.getTimeout(), null);
1359   - log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion());
  1461 + @Override
  1462 + public void onError(Throwable e) {
  1463 + log.trace("Failed to process softwareUpdate ", e);
  1464 + }
  1465 + });
  1466 + }
1360 1467 }
1361 1468 }
1362 1469
  1470 + private TransportProtos.GetFirmwareRequestMsg createFirmwareRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) {
  1471 + return TransportProtos.GetFirmwareRequestMsg.newBuilder()
  1472 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  1473 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  1474 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  1475 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  1476 + .setType(nameFwSW)
  1477 + .build();
  1478 + }
1363 1479
1364 1480 /**
1365 1481 * !!! sharedAttr === profileAttr !!!
... ... @@ -1386,4 +1502,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1386 1502 objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)));
1387 1503 }
1388 1504
  1505 + public LwM2MTransportServerConfig getConfig() {
  1506 + return this.config;
  1507 + }
  1508 +
1389 1509 }
... ...
... ... @@ -28,6 +28,7 @@ import org.eclipse.leshan.server.californium.registration.CaliforniumRegistratio
28 28 import org.eclipse.leshan.server.model.LwM2mModelProvider;
29 29 import org.eclipse.leshan.server.security.EditableSecurityStore;
30 30 import org.springframework.stereotype.Component;
  31 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
31 32 import org.thingsboard.server.common.data.StringUtils;
32 33 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
33 34 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
... ... @@ -57,6 +58,8 @@ import java.security.spec.InvalidParameterSpecException;
57 58 import java.security.spec.KeySpec;
58 59 import java.security.spec.PKCS8EncodedKeySpec;
59 60 import java.util.Arrays;
  61 +import java.util.concurrent.Executors;
  62 +import java.util.concurrent.ScheduledExecutorService;
60 63
61 64 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
62 65 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
... ... @@ -122,7 +125,6 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
122 125 /* Use a magic converter to support bad type send by the UI. */
123 126 builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
124 127
125   -
126 128 /* Create CoAP Config */
127 129 builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort()));
128 130
... ...
... ... @@ -86,21 +86,22 @@ public class LwM2mServerListener {
86 86
87 87 @Override
88 88 public void cancelled(Observation observation) {
89   - String msg = String.format("%s: Cancel Observation %s.", LOG_LW2M_INFO, observation.getPath());
  89 + String msg = String.format("%s: Canceled Observation %s.", LOG_LW2M_INFO, observation.getPath());
90 90 service.sendLogsToThingsboard(msg, observation.getRegistrationId());
91   - log.trace(msg);
  91 + log.warn(msg);
92 92 }
93 93
94 94 @Override
95 95 public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
96 96 if (registration != null) {
97   - try {
98   - service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
99   - registration), response, null);
100   - } catch (Exception e) {
101   - log.error("Observation/Read onResponse", e);
102   -
103   - }
  97 +// if (observation.getPath().isResource() || observation.getPath().isResourceInstance()) {
  98 +// String msg = String.format("%s: Successful Observation %s.", LOG_LW2M_INFO,
  99 +// observation.getPath());
  100 +// log.warn(msg);
  101 +// service.sendLogsToThingsboard(msg, registration.getId());
  102 +// }
  103 + service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
  104 + registration), response, null);
104 105 }
105 106 }
106 107
... ... @@ -113,9 +114,8 @@ public class LwM2mServerListener {
113 114 public void newObservation(Observation observation, Registration registration) {
114 115 String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
115 116 observation.getPath());
  117 + log.warn(msg);
116 118 service.sendLogsToThingsboard(msg, registration.getId());
117   - log.trace(msg);
118 119 }
119 120 };
120   -
121 121 }
... ...
... ... @@ -21,6 +21,7 @@ import org.eclipse.leshan.server.registration.Registration;
21 21 import org.thingsboard.server.common.data.Device;
22 22 import org.thingsboard.server.common.data.DeviceProfile;
23 23 import org.thingsboard.server.gen.transport.TransportProtos;
  24 +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
24 25 import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
25 26
26 27 import java.util.Collection;
... ... @@ -36,7 +37,7 @@ public interface LwM2mTransportMsgHandler {
36 37
37 38 void onSleepingDev(Registration registration);
38 39
39   - void setCancelObservations(Registration registration);
  40 + void setCancelObservationsAll(Registration registration);
40 41
41 42 void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest);
42 43
... ... @@ -63,4 +64,6 @@ public interface LwM2mTransportMsgHandler {
63 64 void onAwakeDev(Registration registration);
64 65
65 66 void sendLogsToThingsboard(String msg, String registrationId);
  67 +
  68 + LwM2MTransportServerConfig getConfig();
66 69 }
... ...
... ... @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.eclipse.californium.core.coap.CoAP;
21 21 import org.eclipse.californium.core.coap.Response;
  22 +import org.eclipse.leshan.core.Link;
22 23 import org.eclipse.leshan.core.model.ResourceModel;
23 24 import org.eclipse.leshan.core.node.LwM2mNode;
24 25 import org.eclipse.leshan.core.node.LwM2mPath;
... ... @@ -35,7 +36,6 @@ import org.eclipse.leshan.core.request.ObserveRequest;
35 36 import org.eclipse.leshan.core.request.ReadRequest;
36 37 import org.eclipse.leshan.core.request.WriteRequest;
37 38 import org.eclipse.leshan.core.request.exception.ClientSleepingException;
38   -import org.eclipse.leshan.core.response.CancelObservationResponse;
39 39 import org.eclipse.leshan.core.response.DeleteResponse;
40 40 import org.eclipse.leshan.core.response.DiscoverResponse;
41 41 import org.eclipse.leshan.core.response.ExecuteResponse;
... ... @@ -68,15 +68,26 @@ import java.util.stream.Collectors;
68 68 import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
69 69 import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST;
70 70 import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND;
  71 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED;
  72 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.FAILED;
  73 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType;
71 74 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT;
72   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FR_PATH_RESOURCE_VER_ID;
  75 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID;
  76 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID;
73 77 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR;
74 78 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
75 79 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
76 80 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
  81 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
  82 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_All;
  83 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
77 84 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
78 85 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
79   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_CHANNEL;
  86 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
  87 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE;
  88 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL;
  89 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID;
  90 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID;
80 91 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId;
81 92 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
82 93 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.createWriteAttributeRequest;
... ... @@ -86,20 +97,20 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c
86 97 @TbLwM2mTransportComponent
87 98 @RequiredArgsConstructor
88 99 public class LwM2mTransportRequest {
89   - private ExecutorService executorResponse;
  100 + private ExecutorService responseRequestExecutor;
90 101
91 102 public LwM2mValueConverterImpl converter;
92 103
93 104 private final LwM2mTransportContext context;
94 105 private final LwM2MTransportServerConfig config;
95 106 private final LwM2mClientContext lwM2mClientContext;
96   - private final DefaultLwM2MTransportMsgHandler serviceImpl;
  107 + private final DefaultLwM2MTransportMsgHandler handler;
97 108
98 109 @PostConstruct
99 110 public void init() {
100 111 this.converter = LwM2mValueConverterImpl.getInstance();
101   - executorResponse = Executors.newFixedThreadPool(this.config.getResponsePoolSize(),
102   - new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL)));
  112 + responseRequestExecutor = Executors.newFixedThreadPool(this.config.getResponsePoolSize(),
  113 + new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL)));
103 114 }
104 115
105 116 /**
... ... @@ -115,154 +126,186 @@ public class LwM2mTransportRequest {
115 126 String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
116 127 try {
117 128 String target = convertPathFromIdVerToObjectId(targetIdVer);
118   - DownlinkRequest request = null;
119 129 ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
120 130 LwM2mClient lwM2MClient = this.lwM2mClientContext.getOrRegister(registration);
121 131 LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null;
122 132 if (!OBSERVE_READ_ALL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) {
123 133 if (lwM2MClient.isValidObjectVersion(targetIdVer)) {
124 134 timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
125   - ResourceModel resourceModel = null;
126   - switch (typeOper) {
127   - case READ:
128   - request = new ReadRequest(contentFormat, target);
129   - break;
130   - case DISCOVER:
131   - request = new DiscoverRequest(target);
132   - break;
133   - case OBSERVE:
134   - if (resultIds.isResource()) {
135   - request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
136   - } else if (resultIds.isObjectInstance()) {
137   - request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId());
138   - } else if (resultIds.getObjectId() >= 0) {
139   - request = new ObserveRequest(contentFormat, resultIds.getObjectId());
140   - }
141   - break;
142   - case OBSERVE_CANCEL:
143   - /*
144   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
145   - At server side this will not remove the observation from the observation store, to do it you need to use
146   - {@code ObservationService#cancelObservation()}
147   - */
148   - context.getServer().getObservationService().cancelObservations(registration, target);
149   - break;
150   - case EXECUTE:
151   - resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config
152   - .getModelProvider());
153   - if (params != null && !resourceModel.multiple) {
154   - request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModel.type, ResourceModel.Type.STRING, resultIds));
155   - } else {
156   - request = new ExecuteRequest(target);
157   - }
158   - break;
159   - case WRITE_REPLACE:
160   - // Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
161   - resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
162   - if (contentFormat.equals(ContentFormat.TLV)) {
163   - request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(),
164   - resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type,
165   - registration, rpcRequest);
166   - }
167   - // Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON)
168   - else if (!contentFormat.equals(ContentFormat.TLV)) {
169   - request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
170   - resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type,
171   - registration, rpcRequest);
172   - }
173   - break;
174   - case WRITE_UPDATE:
175   - if (resultIds.isResource()) {
176   - /**
177   - * send request: path = '/3/0' node == wM2mObjectInstance
178   - * with params == "\"resources\": {15: resource:{id:15. value:'+01'...}}
179   - **/
180   - Collection<LwM2mResource> resources = lwM2MClient.getNewOneResourceForInstance(
181   - targetIdVer, params,
182   - this.config.getModelProvider(),
183   - this.converter);
184   - request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
185   - resultIds.getObjectInstanceId(), resources);
186   - }
187   -
188   - /**
189   - * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}"
190   - *
191   - * int rscId = resultIds.getObjectInstanceId();
192   - */
193   -
194   - else if (resultIds.isObjectInstance()) {
195   - if (((ConcurrentHashMap) params).size() > 0) {
196   - Collection<LwM2mResource> resources = lwM2MClient.getNewManyResourcesForInstance(
197   - targetIdVer, params,
198   - this.config.getModelProvider(),
199   - this.converter);
200   - request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
201   - resultIds.getObjectInstanceId(), resources);
202   - }
203   - } else if (resultIds.getObjectId() >= 0) {
204   - request = new ObserveRequest(resultIds.getObjectId());
205   - }
206   - break;
207   - case WRITE_ATTRIBUTES:
208   - request = createWriteAttributeRequest(target, params);
209   - break;
210   - case DELETE:
211   - request = new DeleteRequest(target);
212   - break;
213   - }
214   -
  135 + DownlinkRequest request = createRequest (registration, lwM2MClient, typeOper, contentFormat, target,
  136 + targetIdVer, resultIds, params, rpcRequest);
215 137 if (request != null) {
216 138 try {
217 139 this.sendRequest(registration, lwM2MClient, request, timeoutInMs, rpcRequest);
218 140 } catch (ClientSleepingException e) {
219 141 DownlinkRequest finalRequest = request;
220 142 long finalTimeoutInMs = timeoutInMs;
221   - lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest));
  143 + Lwm2mClientRpcRequest finalRpcRequest = rpcRequest;
  144 + lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest));
222 145 } catch (Exception e) {
223 146 log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
224 147 }
225   - } else if (OBSERVE_CANCEL == typeOper) {
226   - log.trace("[{}], [{}] - [{}] SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
227   - if (rpcRequest != null) {
228   - rpcRequest.setInfoMsg(null);
229   - serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);
  148 + }
  149 + else if (WRITE_UPDATE.name().equals(typeOper.name())) {
  150 + Lwm2mClientRpcRequest rpcRequestClone = (Lwm2mClientRpcRequest) rpcRequest.clone();
  151 + if (rpcRequestClone != null) {
  152 + String errorMsg = String.format("Path %s params is not valid", targetIdVer);
  153 + handler.sentRpcRequest(rpcRequestClone, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
  154 + rpcRequest = null;
230 155 }
231   - } else {
  156 + }
  157 + else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) {
232 158 log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
233 159 if (rpcRequest != null) {
  160 + ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
234 161 String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
235   - serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
  162 + handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
236 163 }
237 164 }
238 165 } else if (rpcRequest != null) {
239 166 String errorMsg = String.format("Path %s not found in object version", targetIdVer);
240   - serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
  167 + handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
  168 + }
  169 + } else if (OBSERVE_READ_ALL.name().equals(typeOper.name()) || DISCOVER_All.name().equals(typeOper.name())) {
  170 + Set<String> paths;
  171 + if (OBSERVE_READ_ALL.name().equals(typeOper.name())) {
  172 + Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
  173 + paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
  174 + } else {
  175 + assert registration != null;
  176 + Link[] objectLinks = registration.getSortedObjectLinks();
  177 + paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet());
  178 + String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
  179 + typeOper.name(), paths);
  180 + handler.sendLogsToThingsboard(msg, registration.getId());
241 181 }
242   - } else if (OBSERVE_READ_ALL.name().equals(typeOper.name())) {
243   - Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
244   - Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
245   - String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO,
246   - OBSERVE_READ_ALL.type, observationPaths);
247   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
248   - log.trace("[{}] [{}], [{}]", typeOper.name(), registration.getEndpoint(), msg);
249 182 if (rpcRequest != null) {
250   - String valueMsg = String.format("Observation paths - %s", observationPaths);
251   - serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
  183 + String valueMsg = String.format("Paths - %s", paths);
  184 + handler.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
252 185 }
  186 + } else if (OBSERVE_CANCEL.name().equals(typeOper.name())) {
  187 + int observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration);
  188 + String observeCancelMsgAll = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO,
  189 + OBSERVE_CANCEL.name(), observeCancelCnt);
  190 + this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsgAll, rpcRequest);
253 191 }
254 192 } catch (Exception e) {
255 193 String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
256 194 typeOper.name(), e.getMessage());
257   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
258   - try {
259   - throw new Exception(e);
260   - } catch (Exception exception) {
261   - exception.printStackTrace();
  195 + handler.sendLogsToThingsboard(msg, registration.getId());
  196 + if (rpcRequest != null) {
  197 + String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage());
  198 + handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
262 199 }
263 200 }
264 201 }
265 202
  203 + private DownlinkRequest createRequest (Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
  204 + ContentFormat contentFormat, String target, String targetIdVer,
  205 + LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) {
  206 + DownlinkRequest request = null;
  207 + switch (typeOper) {
  208 + case READ:
  209 + request = new ReadRequest(contentFormat, target);
  210 + break;
  211 + case DISCOVER:
  212 + request = new DiscoverRequest(target);
  213 + break;
  214 + case OBSERVE:
  215 + String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer);
  216 + log.warn(msg);
  217 + if (resultIds.isResource()) {
  218 + Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
  219 + Set<Observation> paths = observations.stream().filter(observation -> observation.getPath().equals(resultIds)).collect(Collectors.toSet());
  220 + if (paths.size() == 0) {
  221 + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
  222 + } else {
  223 + request = new ReadRequest(contentFormat, target);
  224 + }
  225 + } else if (resultIds.isObjectInstance()) {
  226 + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId());
  227 + } else if (resultIds.getObjectId() >= 0) {
  228 + request = new ObserveRequest(contentFormat, resultIds.getObjectId());
  229 + }
  230 + break;
  231 + case OBSERVE_CANCEL:
  232 + /*
  233 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
  234 + At server side this will not remove the observation from the observation store, to do it you need to use
  235 + {@code ObservationService#cancelObservation()}
  236 + */
  237 + int observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target);
  238 + String observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO,
  239 + OBSERVE_CANCEL.name(), target, observeCancelCnt);
  240 + this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, rpcRequest);
  241 + break;
  242 + case EXECUTE:
  243 + ResourceModel resourceModelExe = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
  244 + if (params != null && !resourceModelExe.multiple) {
  245 + request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExe.type, ResourceModel.Type.STRING, resultIds));
  246 + } else {
  247 + request = new ExecuteRequest(target);
  248 + }
  249 + break;
  250 + case WRITE_REPLACE:
  251 + /**
  252 + * Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
  253 + * Type from resourceModel -> STRING, INTEGER, FLOAT, BOOLEAN, OPAQUE, TIME, OBJLNK
  254 + * contentFormat -> TLV, TLV, TLV, TLV, OPAQUE, TLV, LINK
  255 + * JSON, TEXT;
  256 + **/
  257 + ResourceModel resourceModelWrite = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
  258 + contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat);
  259 + request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
  260 + resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type,
  261 + registration, rpcRequest);
  262 + break;
  263 + case WRITE_UPDATE:
  264 + if (resultIds.isResource()) {
  265 + /**
  266 + * send request: path = '/3/0' node == wM2mObjectInstance
  267 + * with params == "\"resources\": {15: resource:{id:15. value:'+01'...}}
  268 + **/
  269 + Collection<LwM2mResource> resources = lwM2MClient.getNewResourceForInstance(
  270 + targetIdVer, params,
  271 + this.config.getModelProvider(),
  272 + this.converter);
  273 + contentFormat = getContentFormatByResourceModelType(lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()),
  274 + contentFormat);
  275 + request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
  276 + resultIds.getObjectInstanceId(), resources);
  277 + }
  278 + /**
  279 + * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}"
  280 + * int rscId = resultIds.getObjectInstanceId();
  281 + * contentFormat – Format of the payload (TLV or JSON).
  282 + */
  283 + else if (resultIds.isObjectInstance()) {
  284 + if (((ConcurrentHashMap) params).size() > 0) {
  285 + Collection<LwM2mResource> resources = lwM2MClient.getNewResourcesForInstance(
  286 + targetIdVer, params,
  287 + this.config.getModelProvider(),
  288 + this.converter);
  289 + if (resources.size() > 0) {
  290 + contentFormat = contentFormat.equals(ContentFormat.JSON) ? contentFormat : ContentFormat.TLV;
  291 + request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
  292 + resultIds.getObjectInstanceId(), resources);
  293 + }
  294 + }
  295 + } else if (resultIds.getObjectId() >= 0) {
  296 + request = new ObserveRequest(resultIds.getObjectId());
  297 + }
  298 + break;
  299 + case WRITE_ATTRIBUTES:
  300 + request = createWriteAttributeRequest(target, params);
  301 + break;
  302 + case DELETE:
  303 + request = new DeleteRequest(target);
  304 + break;
  305 + }
  306 + return request;
  307 + }
  308 +
266 309 /**
267 310 * @param registration -
268 311 * @param request -
... ... @@ -273,52 +316,66 @@ public class LwM2mTransportRequest {
273 316 private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request,
274 317 long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
275 318 context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
  319 +
276 320 if (!lwM2MClient.isInit()) {
277   - lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
  321 + lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
278 322 }
279 323 if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
280 324 this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest);
281 325 } else {
282 326 String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(),
283 327 ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
284   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  328 + handler.sendLogsToThingsboard(msg, registration.getId());
285 329 log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
286 330 ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
287 331 if (!lwM2MClient.isInit()) {
288   - lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
  332 + lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
289 333 }
  334 + /** Not Found */
290 335 if (rpcRequest != null) {
291   - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
  336 + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
292 337 }
293   - /* Not Found
294   - set setClient_fw_version = empty
295   - */
296   - if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
297   - lwM2MClient.setUpdateFw(false);
298   - lwM2MClient.getFrUpdate().setClientFwVersion("");
299   - log.warn("updateFirmwareClient1");
300   - serviceImpl.updateFirmwareClient(lwM2MClient);
  338 + /** Not Found
  339 + set setClient_fw_info... = empty
  340 + **/
  341 + if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
  342 + lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString());
  343 + }
  344 + if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
  345 + lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString());
  346 + }
  347 + if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
  348 + this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage());
  349 + }
  350 + if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) {
  351 + this.afterExecuteFwSwUpdateError(registration, request, response.getErrorMessage());
301 352 }
302 353 }
303 354 }, e -> {
304   - /* version == null
305   - set setClient_fw_version = empty
306   - */
307   - if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
308   - lwM2MClient.setUpdateFw(false);
309   - lwM2MClient.getFrUpdate().setClientFwVersion("");
310   - log.warn("updateFirmwareClient2");
311   - serviceImpl.updateFirmwareClient(lwM2MClient);
  355 + /** version == null
  356 + set setClient_fw_info... = empty
  357 + **/
  358 + if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
  359 + lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString());
  360 + }
  361 + if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
  362 + lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString());
  363 + }
  364 + if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
  365 + this.afterWriteFwSWUpdateError(registration, request, e.getMessage());
  366 + }
  367 + if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) {
  368 + this.afterExecuteFwSwUpdateError(registration, request, e.getMessage());
312 369 }
313 370 if (!lwM2MClient.isInit()) {
314   - lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
  371 + lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
315 372 }
316 373 String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s",
317 374 LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage());
318   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  375 + handler.sendLogsToThingsboard(msg, registration.getId());
319 376 log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
320 377 if (rpcRequest != null) {
321   - serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
  378 + handler.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
322 379 }
323 380 });
324 381 }
... ... @@ -360,11 +417,11 @@ public class LwM2mTransportRequest {
360 417 String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
361 418 String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
362 419 patn, type, value, e.toString());
363   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  420 + handler.sendLogsToThingsboard(msg, registration.getId());
364 421 log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
365 422 if (rpcRequest != null) {
366 423 String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
367   - serviceImpl.sentRpcRequest(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
  424 + handler.sentRpcRequest(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
368 425 }
369 426 return null;
370 427 }
... ... @@ -372,7 +429,7 @@ public class LwM2mTransportRequest {
372 429
373 430 private void handleResponse(Registration registration, final String path, LwM2mResponse response,
374 431 DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
375   - executorResponse.submit(() -> {
  432 + responseRequestExecutor.submit(() -> {
376 433 try {
377 434 this.sendResponse(registration, path, response, request, rpcRequest);
378 435 } catch (Exception e) {
... ... @@ -391,39 +448,37 @@ public class LwM2mTransportRequest {
391 448 private void sendResponse(Registration registration, String path, LwM2mResponse response,
392 449 DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
393 450 String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
  451 + String msgLog = "";
394 452 if (response instanceof ReadResponse) {
395   - serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
396   - } else if (response instanceof CancelObservationResponse) {
397   - log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response);
398   -
  453 + handler.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
399 454 } else if (response instanceof DeleteResponse) {
400   - log.info("[{}] Path [{}] DeleteResponse 5_Send", pathIdVer, response);
  455 + log.warn("[{}] Path [{}] DeleteResponse 5_Send", pathIdVer, response);
401 456 } else if (response instanceof DiscoverResponse) {
402   - log.info("[{}] [{}] - [{}] [{}] Discovery value: [{}]", registration.getEndpoint(),
403   - ((Response) response.getCoapResponse()).getCode(), response.getCode(),
404   - request.getPath().toString(), ((DiscoverResponse) response).getObjectLinks());
  457 + String discoverValue = Link.serialize(((DiscoverResponse)response).getObjectLinks());
  458 + msgLog = String.format("%s: type operation: %s path: %s value: %s",
  459 + LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue);
  460 + handler.sendLogsToThingsboard(msgLog, registration.getId());
  461 + log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response);
405 462 if (rpcRequest != null) {
406   - String discoveryMsg = String.format("%s",
407   - Arrays.stream(((DiscoverResponse) response).getObjectLinks()).collect(Collectors.toSet()));
408   - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), discoveryMsg, LOG_LW2M_VALUE);
  463 + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE);
409 464 }
410 465 } else if (response instanceof ExecuteResponse) {
411   - log.info("[{}] Path [{}] ExecuteResponse 7_Send", pathIdVer, response);
  466 + log.warn("[{}] Path [{}] ExecuteResponse 7_Send", pathIdVer, response);
412 467 } else if (response instanceof WriteAttributesResponse) {
413   - log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
  468 + log.warn("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
414 469 } else if (response instanceof WriteResponse) {
415   - log.info("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
  470 + log.warn("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
416 471 this.infoWriteResponse(registration, response, request);
417   - serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
  472 + handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
418 473 }
419 474 if (rpcRequest != null) {
420 475 if (response instanceof ExecuteResponse
421 476 || response instanceof WriteAttributesResponse
422 477 || response instanceof DeleteResponse) {
423 478 rpcRequest.setInfoMsg(null);
424   - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
  479 + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
425 480 } else if (response instanceof WriteResponse) {
426   - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
  481 + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
427 482 }
428 483 }
429 484 }
... ... @@ -447,21 +502,73 @@ public class LwM2mTransportRequest {
447 502 Math.min(valueLength, config.getLogMaxLength())));
448 503 }
449 504 value = valueLength > config.getLogMaxLength() ? value + "..." : value;
450   - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path - %s length - %s value - %s",
  505 + msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s",
451 506 LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value);
452 507 } else {
453 508 value = this.converter.convertValue(singleResource.getValue(),
454 509 singleResource.getType(), ResourceModel.Type.STRING, request.getPath());
455   - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path - %s value - %s",
  510 + msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s",
456 511 LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value);
457 512 }
458 513 if (msg != null) {
459   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
460   - log.warn("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName(), registration.getEndpoint(),
461   - ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), value);
  514 + handler.sendLogsToThingsboard(msg, registration.getId());
  515 + if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
  516 + this.afterWriteSuccessFwSwUpdate(registration, request);
  517 + }
462 518 }
463 519 } catch (Exception e) {
464 520 log.trace("Fail convert value from request to string. ", e);
465 521 }
466 522 }
  523 +
  524 + /**
  525 + * After finish operation FwSwUpdate Write (success):
  526 + * fw_state/sw_state = DOWNLOADED
  527 + * send operation Execute
  528 + */
  529 + private void afterWriteSuccessFwSwUpdate(Registration registration, DownlinkRequest request) {
  530 + LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
  531 + if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
  532 + lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name());
  533 + lwM2MClient.getFwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
  534 + }
  535 + if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
  536 + lwM2MClient.getSwUpdate().setStateUpdate(DOWNLOADED.name());
  537 + lwM2MClient.getSwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
  538 + }
  539 + }
  540 +
  541 + /**
  542 + * After finish operation FwSwUpdate Write (error): fw_state = FAILED
  543 + */
  544 + private void afterWriteFwSWUpdateError(Registration registration, DownlinkRequest request, String msgError) {
  545 + LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
  546 + if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
  547 + lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name());
  548 + lwM2MClient.getFwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
  549 + }
  550 + if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
  551 + lwM2MClient.getSwUpdate().setStateUpdate(FAILED.name());
  552 + lwM2MClient.getSwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
  553 + }
  554 + }
  555 +
  556 + private void afterExecuteFwSwUpdateError(Registration registration, DownlinkRequest request, String msgError) {
  557 + LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
  558 + if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) {
  559 + lwM2MClient.getFwUpdate().sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
  560 + }
  561 + if (request.getPath().toString().equals(SW_INSTALL_ID) && lwM2MClient.getSwUpdate() != null) {
  562 + lwM2MClient.getSwUpdate().sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
  563 + }
  564 + }
  565 +
  566 + private void afterObserveCancel(Registration registration, int observeCancelCnt, String observeCancelMsg, Lwm2mClientRpcRequest rpcRequest) {
  567 + handler.sendLogsToThingsboard(observeCancelMsg, registration.getId());
  568 + log.warn("[{}]", observeCancelMsg);
  569 + if (rpcRequest != null) {
  570 + rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt));
  571 + handler.sentRpcRequest(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO);
  572 + }
  573 + }
467 574 }
... ...
... ... @@ -38,6 +38,7 @@ import org.eclipse.leshan.core.model.InvalidDDFFileException;
38 38 import org.eclipse.leshan.core.model.ObjectModel;
39 39 import org.eclipse.leshan.core.model.ResourceModel;
40 40 import org.eclipse.leshan.core.node.codec.CodecException;
  41 +import org.eclipse.leshan.core.request.ContentFormat;
41 42 import org.springframework.stereotype.Component;
42 43 import org.thingsboard.server.common.transport.TransportServiceCallback;
43 44 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
... ... @@ -54,7 +55,6 @@ import java.util.ArrayList;
54 55 import java.util.List;
55 56
56 57 import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V;
57   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY;
58 58
59 59 @Slf4j
60 60 @Component
... ... @@ -69,7 +69,7 @@ public class LwM2mTransportServerHelper {
69 69 * send to Thingsboard Attribute || Telemetry
70 70 *
71 71 * @param msg - JsonObject: [{name: value}]
72   - * @return - dummy
  72 + * @return - dummyWriteReplace {\"targetIdVer\":\"/19_1.0/0/0\",\"value\":0082}
73 73 */
74 74 private <T> TransportServiceCallback<Void> getPubAckCallbackSendAttrTelemetry(final T msg) {
75 75 return new TransportServiceCallback<>() {
... ... @@ -136,16 +136,16 @@ public class LwM2mTransportServerHelper {
136 136 }
137 137
138 138 /**
139   - *
140   - * @param logMsg - info about Logs
  139 + * @param value - info about Logs
141 140 * @return- KeyValueProto for telemetry (Logs)
142 141 */
143   - public List<TransportProtos.KeyValueProto> getKvLogyToThingsboard(String logMsg) {
  142 + public List<TransportProtos.KeyValueProto> getKvStringtoThingsboard(String key, String value) {
144 143 List<TransportProtos.KeyValueProto> result = new ArrayList<>();
  144 + value = value.replaceAll("<", "").replaceAll(">", "");
145 145 result.add(TransportProtos.KeyValueProto.newBuilder()
146   - .setKey(LOG_LW2M_TELEMETRY)
  146 + .setKey(key)
147 147 .setType(TransportProtos.KeyValueType.STRING_V)
148   - .setStringV(logMsg).build());
  148 + .setStringV(value).build());
149 149 return result;
150 150 }
151 151
... ... @@ -181,8 +181,7 @@ public class LwM2mTransportServerHelper {
181 181 }
182 182
183 183 /**
184   - *
185   - * @param currentType -
  184 + * @param currentType -
186 185 * @param resourcePath -
187 186 * @return
188 187 */
... ... @@ -204,6 +203,28 @@ public class LwM2mTransportServerHelper {
204 203 throw new CodecException("Invalid ResourceModel_Type for resource %s, got %s", resourcePath, currentType);
205 204 }
206 205
  206 + public static ContentFormat convertResourceModelTypeToContentFormat(ResourceModel.Type type) {
  207 + switch (type) {
  208 + case BOOLEAN:
  209 + case STRING:
  210 + case TIME:
  211 + case INTEGER:
  212 + case FLOAT:
  213 + return ContentFormat.TLV;
  214 + case OPAQUE:
  215 + return ContentFormat.OPAQUE;
  216 + case OBJLNK:
  217 + return ContentFormat.LINK;
  218 + default:
  219 + }
  220 + throw new CodecException("Invalid ResourceModel_Type for %s ContentFormat.", type);
  221 + }
  222 +
  223 + public static ContentFormat getContentFormatByResourceModelType(ResourceModel resourceModel, ContentFormat contentFormat) {
  224 + return contentFormat.equals(ContentFormat.TLV) ? convertResourceModelTypeToContentFormat(resourceModel.type) :
  225 + contentFormat;
  226 + }
  227 +
207 228 public static Object getValueFromKvProto(TransportProtos.KeyValueProto kv) {
208 229 switch (kv.getType()) {
209 230 case BOOLEAN_V:
... ...
... ... @@ -43,6 +43,7 @@ import org.eclipse.leshan.server.registration.Registration;
43 43 import org.nustaq.serialization.FSTConfiguration;
44 44 import org.thingsboard.server.common.data.DeviceProfile;
45 45 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
  46 +import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus;
46 47 import org.thingsboard.server.common.data.id.TenantId;
47 48 import org.thingsboard.server.common.transport.TransportServiceCallback;
48 49 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
... ... @@ -70,6 +71,12 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK;
70 71 import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
71 72 import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING;
72 73 import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME;
  74 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED;
  75 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADING;
  76 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.FAILED;
  77 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATED;
  78 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING;
  79 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.VERIFIED;
73 80 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
74 81 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
75 82
... ... @@ -97,7 +104,8 @@ public class LwM2mTransportUtil {
97 104
98 105 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
99 106
100   - public static final String LOG_LW2M_TELEMETRY = "logLwm2m";
  107 + public static final String
  108 + LOG_LW2M_TELEMETRY = "LwM2MLog";
101 109 public static final String LOG_LW2M_INFO = "info";
102 110 public static final String LOG_LW2M_ERROR = "error";
103 111 public static final String LOG_LW2M_WARN = "warn";
... ... @@ -107,11 +115,41 @@ public class LwM2mTransportUtil {
107 115 public static final int LWM2M_STRATEGY_2 = 2;
108 116
109 117 public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";
110   -
111   - public static final Integer FR_OBJECT_ID = 5;
112   - public static final Integer FR_RESOURCE_VER_ID = 7;
113   - public static final String FR_PATH_RESOURCE_VER_ID = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_PATH
114   - + "0" + LWM2M_SEPARATOR_PATH + FR_RESOURCE_VER_ID;
  118 + public static final String LWM2M_VERSION_DEFAULT = "1.0";
  119 +
  120 + // FirmWare
  121 + public static final String FW_UPDATE = "Firmware update";
  122 + public static final Integer FW_ID = 5;
  123 + // Package W
  124 + public static final String FW_PACKAGE_ID = "/5/0/0";
  125 + // State R
  126 + public static final String FW_STATE_ID = "/5/0/3";
  127 + // Update Result R
  128 + public static final String FW_RESULT_ID = "/5/0/5";
  129 + // PkgName R
  130 + public static final String FW_NAME_ID = "/5/0/6";
  131 + // PkgVersion R
  132 + public static final String FW_VER_ID = "/5/0/7";
  133 + // Update E
  134 + public static final String FW_UPDATE_ID = "/5/0/2";
  135 +
  136 + // SoftWare
  137 + public static final String SW_UPDATE = "Software update";
  138 + public static final Integer SW_ID = 9;
  139 + // Package W
  140 + public static final String SW_PACKAGE_ID = "/9/0/2";
  141 + // Update State R
  142 + public static final String SW_UPDATE_STATE_ID = "/9/0/7";
  143 + // Update Result R
  144 + public static final String SW_RESULT_ID = "/9/0/9";
  145 + // PkgName R
  146 + public static final String SW_NAME_ID = "/9/0/0";
  147 + // PkgVersion R
  148 + public static final String SW_VER_ID = "/9/0/1";
  149 + // Install E
  150 + public static final String SW_INSTALL_ID = "/9/0/4";
  151 + // Uninstall E
  152 + public static final String SW_UN_INSTALL_ID = "/9/0/6";
115 153
116 154 public enum LwM2mTypeServer {
117 155 BOOTSTRAP(0, "bootstrap"),
... ... @@ -144,19 +182,20 @@ public class LwM2mTransportUtil {
144 182 */
145 183 READ(0, "Read"),
146 184 DISCOVER(1, "Discover"),
147   - OBSERVE_READ_ALL(2, "ObserveReadAll"),
  185 + DISCOVER_All(2, "DiscoverAll"),
  186 + OBSERVE_READ_ALL(3, "ObserveReadAll"),
148 187 /**
149 188 * POST
150 189 */
151   - OBSERVE(3, "Observe"),
152   - OBSERVE_CANCEL(4, "ObserveCancel"),
153   - EXECUTE(5, "Execute"),
  190 + OBSERVE(4, "Observe"),
  191 + OBSERVE_CANCEL(5, "ObserveCancel"),
  192 + EXECUTE(6, "Execute"),
154 193 /**
155 194 * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see
156 195 * section 5.3.3 of the LW M2M spec).
157 196 * if all resources are to be replaced
158 197 */
159   - WRITE_REPLACE(6, "WriteReplace"),
  198 + WRITE_REPLACE(7, "WriteReplace"),
160 199 /*
161 200 PUT
162 201 */
... ... @@ -165,11 +204,18 @@ public class LwM2mTransportUtil {
165 204 * 5.3.3 of the LW M2M spec).
166 205 * if this is a partial update request
167 206 */
168   - WRITE_UPDATE(7, "WriteUpdate"),
169   - WRITE_ATTRIBUTES(8, "WriteAttributes"),
170   - DELETE(9, "Delete");
171   -
172   -// READ_INFO_FW(10, "ReadInfoFirmware");
  207 + WRITE_UPDATE(8, "WriteUpdate"),
  208 + WRITE_ATTRIBUTES(9, "WriteAttributes"),
  209 + DELETE(10, "Delete"),
  210 +
  211 + // only for RPC
  212 + FW_READ_INFO(11, "FirmwareReadInfo"),
  213 + FW_UPDATE(12, "FirmwareUpdate"),
  214 + FW_UPDATE_URL(14, "FirmwareUpdateUrl"),
  215 + SW_READ_INFO(15, "SoftwareReadInfo"),
  216 + SW_UPDATE(16, "SoftwareUpdate"),
  217 + SW_UPDATE_URL(17, "SoftwareUpdateUrl"),
  218 + SW_UNINSTALL(18, "SoftwareUninstall");
173 219
174 220 public int code;
175 221 public String type;
... ... @@ -189,10 +235,290 @@ public class LwM2mTransportUtil {
189 235 }
190 236 }
191 237
  238 + /**
  239 + * /** State R
  240 + * 0: Idle (before downloading or after successful updating)
  241 + * 1: Downloading (The data sequence is on the way)
  242 + * 2: Downloaded
  243 + * 3: Updating
  244 + */
  245 + public enum StateFw {
  246 + IDLE(0, "Idle"),
  247 + DOWNLOADING(1, "Downloading"),
  248 + DOWNLOADED(2, "Downloaded"),
  249 + UPDATING(3, "Updating");
  250 +
  251 + public int code;
  252 + public String type;
  253 +
  254 + StateFw(int code, String type) {
  255 + this.code = code;
  256 + this.type = type;
  257 + }
  258 +
  259 + public static StateFw fromStateFwByType(String type) {
  260 + for (StateFw to : StateFw.values()) {
  261 + if (to.type.equals(type)) {
  262 + return to;
  263 + }
  264 + }
  265 + throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type));
  266 + }
  267 +
  268 + public static StateFw fromStateFwByCode(int code) {
  269 + for (StateFw to : StateFw.values()) {
  270 + if (to.code == code) {
  271 + return to;
  272 + }
  273 + }
  274 + throw new IllegalArgumentException(String.format("Unsupported FW State code : %s", code));
  275 + }
  276 + }
  277 +
  278 + /**
  279 + * FW Update Result
  280 + * 0: Initial value. Once the updating process is initiated (Download /Update), this Resource MUST be reset to Initial value.
  281 + * 1: Firmware updated successfully.
  282 + * 2: Not enough flash memory for the new firmware package.
  283 + * 3: Out of RAM during downloading process.
  284 + * 4: Connection lost during downloading process.
  285 + * 5: Integrity check failure for new downloaded package.
  286 + * 6: Unsupported package type.
  287 + * 7: Invalid URI.
  288 + * 8: Firmware update failed.
  289 + * 9: Unsupported protocol.
  290 + */
  291 + public enum UpdateResultFw {
  292 + INITIAL(0, "Initial value", false),
  293 + UPDATE_SUCCESSFULLY(1, "Firmware updated successfully", false),
  294 + NOT_ENOUGH(2, "Not enough flash memory for the new firmware package", false),
  295 + OUT_OFF_MEMORY(3, "Out of RAM during downloading process", false),
  296 + CONNECTION_LOST(4, "Connection lost during downloading process", true),
  297 + INTEGRITY_CHECK_FAILURE(5, "Integrity check failure for new downloaded package", true),
  298 + UNSUPPORTED_TYPE(6, "Unsupported package type", false),
  299 + INVALID_URI(7, "Invalid URI", false),
  300 + UPDATE_FAILED(8, "Firmware update failed", false),
  301 + UNSUPPORTED_PROTOCOL(9, "Unsupported protocol", false);
  302 +
  303 + public int code;
  304 + public String type;
  305 + public boolean isAgain;
  306 +
  307 + UpdateResultFw(int code, String type, boolean isAgain) {
  308 + this.code = code;
  309 + this.type = type;
  310 + this.isAgain = isAgain;
  311 + }
  312 +
  313 + public static UpdateResultFw fromUpdateResultFwByType(String type) {
  314 + for (UpdateResultFw to : UpdateResultFw.values()) {
  315 + if (to.type.equals(type)) {
  316 + return to;
  317 + }
  318 + }
  319 + throw new IllegalArgumentException(String.format("Unsupported FW Update Result type : %s", type));
  320 + }
  321 +
  322 + public static UpdateResultFw fromUpdateResultFwByCode(int code) {
  323 + for (UpdateResultFw to : UpdateResultFw.values()) {
  324 + if (to.code == code) {
  325 + return to;
  326 + }
  327 + }
  328 + throw new IllegalArgumentException(String.format("Unsupported FW Update Result code : %s", code));
  329 + }
  330 + }
  331 +
  332 + /**
  333 + * FirmwareUpdateStatus {
  334 + * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
  335 + */
  336 + public static FirmwareUpdateStatus EqualsFwSateToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) {
  337 + switch (updateResultFw) {
  338 + case INITIAL:
  339 + switch (stateFw) {
  340 + case IDLE:
  341 + return VERIFIED;
  342 + case DOWNLOADING:
  343 + return DOWNLOADING;
  344 + case DOWNLOADED:
  345 + return DOWNLOADED;
  346 + case UPDATING:
  347 + return UPDATING;
  348 + }
  349 + case UPDATE_SUCCESSFULLY:
  350 + return UPDATED;
  351 + case NOT_ENOUGH:
  352 + case OUT_OFF_MEMORY:
  353 + case CONNECTION_LOST:
  354 + case INTEGRITY_CHECK_FAILURE:
  355 + case UNSUPPORTED_TYPE:
  356 + case INVALID_URI:
  357 + case UPDATE_FAILED:
  358 + case UNSUPPORTED_PROTOCOL:
  359 + return FAILED;
  360 + default:
  361 + throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", stateFw.name(), updateResultFw.name());
  362 + }
  363 + }
  364 +
  365 + /**
  366 + * SW Update State R
  367 + * 0: INITIAL Before downloading. (see 5.1.2.1)
  368 + * 1: DOWNLOAD STARTED The downloading process has started and is on-going. (see 5.1.2.2)
  369 + * 2: DOWNLOADED The package has been completely downloaded (see 5.1.2.3)
  370 + * 3: DELIVERED In that state, the package has been correctly downloaded and is ready to be installed. (see 5.1.2.4)
  371 + * If executing the Install Resource failed, the state remains at DELIVERED.
  372 + * If executing the Install Resource was successful, the state changes from DELIVERED to INSTALLED.
  373 + * After executing the UnInstall Resource, the state changes to INITIAL.
  374 + * 4: INSTALLED
  375 + */
  376 + public enum UpdateStateSw {
  377 + INITIAL(0, "Initial"),
  378 + DOWNLOAD_STARTED(1, "DownloadStarted"),
  379 + DOWNLOADED(2, "Downloaded"),
  380 + DELIVERED(3, "Delivered"),
  381 + INSTALLED(4, "Installed");
  382 +
  383 + public int code;
  384 + public String type;
  385 +
  386 + UpdateStateSw(int code, String type) {
  387 + this.code = code;
  388 + this.type = type;
  389 + }
  390 +
  391 + public static UpdateStateSw fromUpdateStateSwByType(String type) {
  392 + for (UpdateStateSw to : UpdateStateSw.values()) {
  393 + if (to.type.equals(type)) {
  394 + return to;
  395 + }
  396 + }
  397 + throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", type));
  398 + }
  399 +
  400 + public static UpdateStateSw fromUpdateStateSwByCode(int code) {
  401 + for (UpdateStateSw to : UpdateStateSw.values()) {
  402 + if (to.code == code) {
  403 + return to;
  404 + }
  405 + }
  406 + throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", code));
  407 + }
  408 + }
  409 +
  410 + /**
  411 + * SW Update Result
  412 + * Contains the result of downloading or installing/uninstalling the software
  413 + * 0: Initial value.
  414 + * - Prior to download any new package in the Device, Update Result MUST be reset to this initial value.
  415 + * - One side effect of executing the Uninstall resource is to reset Update Result to this initial value "0".
  416 + * 1: Downloading.
  417 + * - The package downloading process is on-going.
  418 + * 2: Software successfully installed.
  419 + * 3: Successfully Downloaded and package integrity verified
  420 + * (( 4-49, for expansion, of other scenarios))
  421 + * ** Failed
  422 + * 50: Not enough storage for the new software package.
  423 + * 51: Out of memory during downloading process.
  424 + * 52: Connection lost during downloading process.
  425 + * 53: Package integrity check failure.
  426 + * 54: Unsupported package type.
  427 + * 56: Invalid URI
  428 + * 57: Device defined update error
  429 + * 58: Software installation failure
  430 + * 59: Uninstallation Failure during forUpdate(arg=0)
  431 + * 60-200 : (for expansion, selection to be in blocks depending on new introduction of features)
  432 + * This Resource MAY be reported by sending Observe operation.
  433 + */
  434 + public enum UpdateResultSw {
  435 + INITIAL(0, "Initial value", false),
  436 + DOWNLOADING(1, "Downloading", false),
  437 + SUCCESSFULLY_INSTALLED(2, "Software successfully installed", false),
  438 + SUCCESSFULLY_DOWNLOADED_VERIFIED(3, "Successfully Downloaded and package integrity verified", false),
  439 + NOT_ENOUGH_STORAGE(50, "Not enough storage for the new software package", true),
  440 + OUT_OFF_MEMORY(51, "Out of memory during downloading process", true),
  441 + CONNECTION_LOST(52, "Connection lost during downloading process", false),
  442 + PACKAGE_CHECK_FAILURE(53, "Package integrity check failure.", false),
  443 + UNSUPPORTED_PACKAGE_TYPE(54, "Unsupported package type", false),
  444 + INVALID_URI(56, "Invalid URI", true),
  445 + UPDATE_ERROR(57, "Device defined update error", true),
  446 + INSTALL_FAILURE(58, "Software installation failure", true),
  447 + UN_INSTALL_FAILURE(59, "Uninstallation Failure during forUpdate(arg=0)", true);
  448 +
  449 + public int code;
  450 + public String type;
  451 + public boolean isAgain;
  452 +
  453 + UpdateResultSw(int code, String type, boolean isAgain) {
  454 + this.code = code;
  455 + this.type = type;
  456 + this.isAgain = isAgain;
  457 + }
  458 +
  459 + public static UpdateResultSw fromUpdateResultSwByType(String type) {
  460 + for (UpdateResultSw to : UpdateResultSw.values()) {
  461 + if (to.type.equals(type)) {
  462 + return to;
  463 + }
  464 + }
  465 + throw new IllegalArgumentException(String.format("Unsupported SW Update Result type : %s", type));
  466 + }
  467 +
  468 + public static UpdateResultSw fromUpdateResultSwByCode(int code) {
  469 + for (UpdateResultSw to : UpdateResultSw.values()) {
  470 + if (to.code == code) {
  471 + return to;
  472 + }
  473 + }
  474 + throw new IllegalArgumentException(String.format("Unsupported SW Update Result code : %s", code));
  475 + }
  476 + }
  477 +
  478 + /**
  479 + * FirmwareUpdateStatus {
  480 + * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
  481 + */
  482 + public static FirmwareUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) {
  483 + switch (updateResultSw) {
  484 + case INITIAL:
  485 + switch (updateStateSw) {
  486 + case INITIAL:
  487 + case DOWNLOAD_STARTED:
  488 + return DOWNLOADING;
  489 + case DOWNLOADED:
  490 + return DOWNLOADED;
  491 + case DELIVERED:
  492 + return VERIFIED;
  493 + }
  494 + case DOWNLOADING:
  495 + return DOWNLOADING;
  496 + case SUCCESSFULLY_INSTALLED:
  497 + return UPDATED;
  498 + case SUCCESSFULLY_DOWNLOADED_VERIFIED:
  499 + return VERIFIED;
  500 + case NOT_ENOUGH_STORAGE:
  501 + case OUT_OFF_MEMORY:
  502 + case CONNECTION_LOST:
  503 + case PACKAGE_CHECK_FAILURE:
  504 + case UNSUPPORTED_PACKAGE_TYPE:
  505 + case INVALID_URI:
  506 + case UPDATE_ERROR:
  507 + case INSTALL_FAILURE:
  508 + case UN_INSTALL_FAILURE:
  509 + return FAILED;
  510 + default:
  511 + throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", updateStateSw.name(), updateResultSw.name());
  512 + }
  513 + }
  514 +
192 515 public static final String EVENT_AWAKE = "AWAKE";
  516 + public static final String RESPONSE_REQUEST_CHANNEL = "RESP_REQ";
193 517 public static final String RESPONSE_CHANNEL = "RESP";
  518 + public static final String OBSERVE_CHANNEL = "OBSERVE";
194 519
195   - public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException {
  520 + public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath
  521 + resourcePath) throws CodecException {
196 522 switch (type) {
197 523 case BOOLEAN:
198 524 case INTEGER:
... ... @@ -256,7 +582,7 @@ public class LwM2mTransportUtil {
256 582 * "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}}
257 583 */
258 584 public static LwM2mClientProfile toLwM2MClientProfile(DeviceProfile deviceProfile) {
259   - if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) {
  585 + if (((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) {
260 586 Object profile = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties();
261 587 try {
262 588 ObjectMapper mapper = new ObjectMapper();
... ... @@ -375,7 +701,8 @@ public class LwM2mTransportUtil {
375 701 return StringUtils.join(linkedListOut, "");
376 702 }
377 703
378   - public static <T> TransportServiceCallback<Void> getAckCallback(LwM2mClient lwM2MClient, int requestId, String typeTopic) {
  704 + public static <T> TransportServiceCallback<Void> getAckCallback(LwM2mClient lwM2MClient,
  705 + int requestId, String typeTopic) {
379 706 return new TransportServiceCallback<Void>() {
380 707 @Override
381 708 public void onSuccess(Void dummy) {
... ... @@ -420,7 +747,8 @@ public class LwM2mTransportUtil {
420 747 return null;
421 748 }
422 749
423   - public static String validPathIdVer(String pathIdVer, Registration registration) throws IllegalArgumentException {
  750 + public static String validPathIdVer(String pathIdVer, Registration registration) throws
  751 + IllegalArgumentException {
424 752 if (!pathIdVer.contains(LWM2M_SEPARATOR_PATH)) {
425 753 throw new IllegalArgumentException(String.format("Error:"));
426 754 } else {
... ... @@ -436,6 +764,7 @@ public class LwM2mTransportUtil {
436 764
437 765 public static String convertPathFromObjectIdToIdVer(String path, Registration registration) {
438 766 String ver = registration.getSupportedObject().get(new LwM2mPath(path).getObjectId());
  767 + ver = ver != null ? ver : LWM2M_VERSION_DEFAULT;
439 768 try {
440 769 String[] keyArray = path.split(LWM2M_SEPARATOR_PATH);
441 770 if (keyArray.length > 1) {
... ... @@ -531,7 +860,7 @@ public class LwM2mTransportUtil {
531 860 case "ObjectLink":
532 861 return OBJLNK;
533 862 default:
534   - return null;
  863 + return null;
535 864 }
536 865 }
537 866 }
... ...
... ... @@ -82,7 +82,7 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
82 82 if (objectModel != null)
83 83 return objectModel.resources.get(resourceId);
84 84 else
85   - log.warn("TbResources (Object model) with id [{}/0/{}] not found on the server", objectId, resourceId);
  85 + log.trace("TbResources (Object model) with id [{}/0/{}] not found on the server", objectId, resourceId);
86 86 return null;
87 87 } catch (Exception e) {
88 88 log.error("", e);
... ...
... ... @@ -64,7 +64,6 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
64 64 }
65 65 return result.build();
66 66 } catch (RuntimeException e) {
67   - log.warn("Failed to decode get attributes request", e);
68 67 throw new AdaptorException(e);
69 68 }
70 69 }
... ...
... ... @@ -18,7 +18,11 @@ package org.thingsboard.server.transport.lwm2m.server.client;
18 18 import lombok.Getter;
19 19 import lombok.Setter;
20 20 import lombok.extern.slf4j.Slf4j;
  21 +import org.eclipse.leshan.core.model.ObjectModel;
21 22 import org.eclipse.leshan.core.model.ResourceModel;
  23 +import org.eclipse.leshan.core.node.LwM2mMultipleResource;
  24 +import org.eclipse.leshan.core.node.LwM2mObject;
  25 +import org.eclipse.leshan.core.node.LwM2mObjectInstance;
22 26 import org.eclipse.leshan.core.node.LwM2mPath;
23 27 import org.eclipse.leshan.core.node.LwM2mResource;
24 28 import org.eclipse.leshan.core.node.LwM2mSingleResource;
... ... @@ -28,9 +32,9 @@ import org.eclipse.leshan.server.security.SecurityInfo;
28 32 import org.thingsboard.server.common.data.Device;
29 33 import org.thingsboard.server.common.data.DeviceProfile;
30 34 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
  35 +import org.thingsboard.server.common.data.firmware.FirmwareType;
31 36 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
32 37 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
33   -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
34 38 import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
35 39 import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest;
36 40 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
... ... @@ -47,11 +51,14 @@ import java.util.concurrent.ConcurrentLinkedQueue;
47 51 import java.util.concurrent.CopyOnWriteArrayList;
48 52 import java.util.stream.Collectors;
49 53
  54 +import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
  55 +import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING;
50 56 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
51 57 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TRANSPORT_DEFAULT_LWM2M_VERSION;
52 58 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId;
53   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId;
  59 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
54 60 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName;
  61 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId;
55 62
56 63 @Slf4j
57 64 public class LwM2mClient implements Cloneable {
... ... @@ -75,25 +82,27 @@ public class LwM2mClient implements Cloneable {
75 82 private UUID profileId;
76 83 @Getter
77 84 @Setter
78   - private volatile LwM2mFirmwareUpdate frUpdate;
  85 + private volatile LwM2mFwSwUpdate fwUpdate;
  86 + @Getter
  87 + @Setter
  88 + private volatile LwM2mFwSwUpdate swUpdate;
79 89 @Getter
80 90 @Setter
81 91 private Registration registration;
82 92
83 93 private ValidateDeviceCredentialsResponse credentials;
  94 +
84 95 @Getter
85 96 private final Map<String, ResourceValue> resources;
86 97 @Getter
87 98 private final Map<String, TsKvProto> delayedRequests;
88 99 @Getter
  100 + @Setter
89 101 private final List<String> pendingReadRequests;
90 102 @Getter
91 103 private final Queue<LwM2mQueuedRequest> queuedRequests;
92 104 @Getter
93 105 private boolean init;
94   - @Getter
95   - @Setter
96   - private volatile boolean updateFw;
97 106
98 107 public Object clone() throws CloneNotSupportedException {
99 108 return super.clone();
... ... @@ -110,9 +119,10 @@ public class LwM2mClient implements Cloneable {
110 119 this.profileId = profileId;
111 120 this.sessionId = sessionId;
112 121 this.init = false;
113   - this.updateFw = false;
114 122 this.queuedRequests = new ConcurrentLinkedQueue<>();
115   - this.frUpdate = new LwM2mFirmwareUpdate();
  123 +
  124 + this.fwUpdate = new LwM2mFwSwUpdate(this, FirmwareType.FIRMWARE);
  125 + this.swUpdate = new LwM2mFwSwUpdate(this, FirmwareType.SOFTWARE);
116 126 if (this.credentials != null && this.credentials.hasDeviceInfo()) {
117 127 this.session = createSession(nodeId, sessionId, credentials);
118 128 this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB());
... ... @@ -165,15 +175,15 @@ public class LwM2mClient implements Cloneable {
165 175 .build();
166 176 }
167 177
168   - public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) {
169   - if (this.resources.get(pathRez) != null && this.resources.get(pathRez).getResourceModel() != null) {
170   - this.resources.get(pathRez).setLwM2mResource(rez);
  178 + public boolean saveResourceValue(String pathRezIdVer, LwM2mResource rez, LwM2mModelProvider modelProvider) {
  179 + if (this.resources.get(pathRezIdVer) != null && this.resources.get(pathRezIdVer).getResourceModel() != null) {
  180 + this.resources.get(pathRezIdVer).setLwM2mResource(rez);
171 181 return true;
172 182 } else {
173   - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez));
  183 + LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer));
174 184 ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId());
175 185 if (resourceModel != null) {
176   - this.resources.put(pathRez, new ResourceValue(rez, resourceModel));
  186 + this.resources.put(pathRezIdVer, new ResourceValue(rez, resourceModel));
177 187 return true;
178 188 } else {
179 189 return false;
... ... @@ -181,6 +191,24 @@ public class LwM2mClient implements Cloneable {
181 191 }
182 192 }
183 193
  194 + public Object getResourceValue(String pathRezIdVer, String pathRezId) {
  195 + String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
  196 + if (this.resources.get(pathRez) != null) {
  197 + return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ?
  198 + this.resources.get(pathRez).getLwM2mResource().getValues() :
  199 + this.resources.get(pathRez).getLwM2mResource().getValue();
  200 + }
  201 + return null;
  202 + }
  203 +
  204 + public Object getResourceName (String pathRezIdVer, String pathRezId) {
  205 + String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
  206 + if (this.resources.get(pathRez) != null) {
  207 + return this.resources.get(pathRez).getResourceModel().name;
  208 + }
  209 + return null;
  210 + }
  211 +
184 212 public ResourceModel getResourceModel(String pathIdVer, LwM2mModelProvider modelProvider) {
185 213 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer));
186 214 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
... ... @@ -189,8 +217,55 @@ public class LwM2mClient implements Cloneable {
189 217 .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
190 218 }
191 219
192   - public Collection<LwM2mResource> getNewOneResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider,
193   - LwM2mValueConverterImpl converter) {
  220 + public ObjectModel getObjectModel(String pathIdVer, LwM2mModelProvider modelProvider) {
  221 + LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer));
  222 + String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
  223 + String verRez = getVerFromPathIdVerOrId(pathIdVer);
  224 + return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
  225 + .getObjectModel(pathIds.getObjectId()) : null;
  226 + }
  227 +
  228 + public String objectToString (LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) {
  229 + StringBuilder builder = new StringBuilder();
  230 + builder.append("LwM2mObject [id=").append(lwM2mObject.getId()).append(", instances={");
  231 + lwM2mObject.getInstances().forEach((instId, inst) -> {
  232 + builder.append(instId).append("=").append(this.instanceToString(inst, converter, pathIdVer)).append(", ");
  233 + });
  234 + int startInd = builder.lastIndexOf(", ");
  235 + if (startInd > 0) {
  236 + builder.delete(startInd, startInd + 2);
  237 + }
  238 + builder.append("}]");
  239 + return builder.toString();
  240 + }
  241 + public String instanceToString (LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) {
  242 + StringBuilder builder = new StringBuilder();
  243 + builder.append("LwM2mObjectInstance [id=").append(objectInstance.getId()).append(", resources={");
  244 + objectInstance.getResources().forEach((resId, res) -> {
  245 + builder.append(resId).append("=").append(this.resourceToString (res, converter, pathIdVer)).append(", ");
  246 + });
  247 + int startInd = builder.lastIndexOf(", ");
  248 + if (startInd > 0) {
  249 + builder.delete(startInd, startInd + 2);
  250 + }
  251 + builder.append("}]");
  252 + return builder.toString();
  253 + }
  254 +
  255 + public String resourceToString (LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) {
  256 + if (!OPAQUE.equals(lwM2mResource.getType())) {
  257 + return lwM2mResource.isMultiInstances() ? ((LwM2mMultipleResource) lwM2mResource).toString() :
  258 + ((LwM2mSingleResource) lwM2mResource).toString();
  259 + }
  260 + else {
  261 + return String.format("LwM2mSingleResource [id=%s, value=%s, type=%s]", lwM2mResource.getId(),
  262 + converter.convertValue(lwM2mResource.getValue(),
  263 + OPAQUE, STRING, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))), lwM2mResource.getType().name());
  264 + }
  265 + }
  266 +
  267 + public Collection<LwM2mResource> getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider,
  268 + LwM2mValueConverterImpl converter) {
194 269 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer));
195 270 Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
196 271 Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
... ... @@ -204,8 +279,8 @@ public class LwM2mClient implements Cloneable {
204 279 return resources;
205 280 }
206 281
207   - public Collection<LwM2mResource> getNewManyResourcesForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider,
208   - LwM2mValueConverterImpl converter) {
  282 + public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider,
  283 + LwM2mValueConverterImpl converter) {
209 284 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer));
210 285 Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
211 286 Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
... ...
... ... @@ -55,7 +55,7 @@ public interface LwM2mClientContext {
55 55
56 56 Map<UUID, LwM2mClientProfile> setProfiles(Map<UUID, LwM2mClientProfile> profiles);
57 57
58   - LwM2mClientProfile toClientProfile(DeviceProfile deviceProfile);
  58 + LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile);
59 59
60 60 Set<String> getSupportedIdVerInClient(Registration registration);
61 61
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.transport.lwm2m.server.client;
17 17
18 18 import lombok.RequiredArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
19 20 import org.eclipse.leshan.core.node.LwM2mPath;
20 21 import org.eclipse.leshan.server.registration.Registration;
21 22 import org.eclipse.leshan.server.security.EditableSecurityStore;
... ... @@ -40,6 +41,7 @@ import java.util.concurrent.ConcurrentHashMap;
40 41 import static org.eclipse.leshan.core.SecurityMode.NO_SEC;
41 42 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
42 43
  44 +@Slf4j
43 45 @Service
44 46 @TbLwM2mTransportComponent
45 47 @RequiredArgsConstructor
... ... @@ -112,8 +114,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
112 114 EndpointSecurityInfo securityInfo = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endpoint, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
113 115 if (securityInfo.getSecurityMode() != null) {
114 116 if (securityInfo.getDeviceProfile() != null) {
115   - toClientProfile(securityInfo.getDeviceProfile());
116   - UUID profileUuid = securityInfo.getDeviceProfile().getUuidId();
  117 + UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile())!= null ?
  118 + securityInfo.getDeviceProfile().getUuidId() : null;
  119 + // TODO: for tests bug.
  120 + if (profileUuid== null) {
  121 + log.warn("input parameters toClientProfile if the result is null: [{}]", securityInfo.getDeviceProfile());
  122 + }
117 123 LwM2mClient client;
118 124 if (securityInfo.getSecurityInfo() != null) {
119 125 client = new LwM2mClient(context.getNodeId(), securityInfo.getSecurityInfo().getEndpoint(),
... ... @@ -141,7 +147,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
141 147 LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID());
142 148 lwM2mClientsByEndpoint.put(registration.getEndpoint(), client);
143 149 lwM2mClientsByRegistrationId.put(registration.getId(), client);
144   - toClientProfile(credentials.getDeviceProfile());
  150 + profileUpdate(credentials.getDeviceProfile());
145 151 }
146 152
147 153 @Override
... ... @@ -170,13 +176,16 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
170 176 }
171 177
172 178 @Override
173   - public LwM2mClientProfile toClientProfile(DeviceProfile deviceProfile) {
174   - LwM2mClientProfile lwM2MClientProfile = profiles.get(deviceProfile.getUuidId());
175   - if (lwM2MClientProfile == null) {
176   - lwM2MClientProfile = LwM2mTransportUtil.toLwM2MClientProfile(deviceProfile);
  179 + public LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile) {
  180 + LwM2mClientProfile lwM2MClientProfile = deviceProfile != null ?
  181 + LwM2mTransportUtil.toLwM2MClientProfile(deviceProfile) : null;
  182 + if (lwM2MClientProfile != null) {
177 183 profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile);
  184 + return lwM2MClientProfile;
  185 + }
  186 + else {
  187 + return null;
178 188 }
179   - return lwM2MClientProfile;
180 189 }
181 190
182 191 /**
... ...
1   -/**
2   - * Copyright © 2016-2021 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.client;
17   -
18   -import lombok.Data;
19   -
20   -import java.util.UUID;
21   -
22   -@Data
23   -public class LwM2mFirmwareUpdate {
24   - private volatile String clientFwVersion;
25   - private volatile String currentFwVersion;
26   - private volatile UUID currentFwId;
27   -}
  1 +/**
  2 + * Copyright © 2016-2021 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.client;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.Setter;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.apache.commons.lang3.StringUtils;
  22 +import org.eclipse.leshan.core.request.ContentFormat;
  23 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  24 +import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus;
  25 +import org.thingsboard.server.gen.transport.TransportProtos;
  26 +import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
  27 +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportRequest;
  28 +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
  29 +
  30 +import java.util.ArrayList;
  31 +import java.util.List;
  32 +import java.util.UUID;
  33 +import java.util.concurrent.CopyOnWriteArrayList;
  34 +
  35 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE;
  36 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  37 +import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
  38 +import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING;
  39 +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey;
  40 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID;
  41 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID;
  42 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID;
  43 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID;
  44 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE;
  45 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID;
  46 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_VER_ID;
  47 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR;
  48 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
  49 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
  50 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE;
  51 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
  52 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID;
  53 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID;
  54 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID;
  55 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID;
  56 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UN_INSTALL_ID;
  57 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE;
  58 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID;
  59 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID;
  60 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
  61 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString;
  62 +
  63 +@Slf4j
  64 +public class LwM2mFwSwUpdate {
  65 + // 5/0/6 PkgName
  66 + // 9/0/0 PkgName
  67 + @Getter
  68 + @Setter
  69 + private volatile String currentTitle;
  70 + // 5/0/7 PkgVersion
  71 + // 9/0/1 PkgVersion
  72 + @Getter
  73 + @Setter
  74 + private volatile String currentVersion;
  75 + @Getter
  76 + @Setter
  77 + private volatile UUID currentId;
  78 + @Getter
  79 + @Setter
  80 + private volatile String stateUpdate;
  81 + @Getter
  82 + private String pathPackageId;
  83 + @Getter
  84 + private String pathStateId;
  85 + @Getter
  86 + private String pathResultId;
  87 + @Getter
  88 + private String pathNameId;
  89 + @Getter
  90 + private String pathVerId;
  91 + @Getter
  92 + private String pathInstallId;
  93 + @Getter
  94 + private String pathUnInstallId;
  95 + @Getter
  96 + private String wUpdate;
  97 + @Getter
  98 + @Setter
  99 + private volatile boolean infoFwSwUpdate = false;
  100 + private final FirmwareType type;
  101 + @Getter
  102 + LwM2mClient lwM2MClient;
  103 + @Getter
  104 + @Setter
  105 + private final List<String> pendingInfoRequestsStart;
  106 +
  107 + public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, FirmwareType type) {
  108 + this.lwM2MClient = lwM2MClient;
  109 + this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>();
  110 + this.type = type;
  111 + this.stateUpdate = null;
  112 + this.initPathId();
  113 + }
  114 +
  115 + private void initPathId() {
  116 + if (FIRMWARE.equals(this.type)) {
  117 + this.pathPackageId = FW_PACKAGE_ID;
  118 + this.pathStateId = FW_STATE_ID;
  119 + this.pathResultId = FW_RESULT_ID;
  120 + this.pathNameId = FW_NAME_ID;
  121 + this.pathVerId = FW_VER_ID;
  122 + this.pathInstallId = FW_UPDATE_ID;
  123 + this.wUpdate = FW_UPDATE;
  124 + } else if (SOFTWARE.equals(this.type)) {
  125 + this.pathPackageId = SW_PACKAGE_ID;
  126 + this.pathStateId = SW_UPDATE_STATE_ID;
  127 + this.pathResultId = SW_RESULT_ID;
  128 + this.pathNameId = SW_NAME_ID;
  129 + this.pathVerId = SW_VER_ID;
  130 + this.pathInstallId = SW_INSTALL_ID;
  131 + this.pathUnInstallId = SW_UN_INSTALL_ID;
  132 + this.wUpdate = SW_UPDATE;
  133 + }
  134 + }
  135 +
  136 + public void initReadValue(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request, String pathIdVer) {
  137 + if (pathIdVer != null) {
  138 + this.pendingInfoRequestsStart.remove(pathIdVer);
  139 + }
  140 + if (this.pendingInfoRequestsStart.size() == 0) {
  141 + this.infoFwSwUpdate = false;
  142 + if (!FirmwareUpdateStatus.DOWNLOADING.name().equals(this.stateUpdate)) {
  143 + boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart() :
  144 + this.conditionalSwUpdateStart();
  145 + if (conditionalStart) {
  146 + this.writeFwSwWare(handler, request);
  147 + }
  148 + }
  149 + }
  150 + }
  151 +
  152 + /**
  153 + * Send FsSw to Lwm2mClient:
  154 + * before operation Write: fw_state = DOWNLOADING
  155 + */
  156 + private void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
  157 + this.stateUpdate = FirmwareUpdateStatus.DOWNLOADING.name();
  158 +// this.observeStateUpdate();
  159 + this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
  160 + int chunkSize = 0;
  161 + int chunk = 0;
  162 + byte[] firmwareChunk = handler.firmwareDataCache.get(this.currentId.toString(), chunkSize, chunk);
  163 + String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration());
  164 + request.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
  165 + firmwareChunk, handler.config.getTimeout(), null);
  166 + }
  167 +
  168 + public void sendLogs(DefaultLwM2MTransportMsgHandler handler, String typeOper, String typeInfo, String msgError) {
  169 + this.sendSateOnThingsBoard(handler);
  170 + String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.",
  171 + typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate);
  172 + if (LOG_LW2M_ERROR.equals(typeInfo)) {
  173 + msg = String.format("%s Error: %s", msg, msgError);
  174 + }
  175 + handler.sendLogsToThingsboard(msg, lwM2MClient.getRegistration().getId());
  176 + }
  177 +
  178 +
  179 + /**
  180 + * After inspection Update Result
  181 + * fw_state/sw_state = UPDATING
  182 + * send execute
  183 + */
  184 + public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
  185 + this.setStateUpdate(UPDATING.name());
  186 + this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
  187 + request.sendAllRequest(this.lwM2MClient.getRegistration(), this.pathInstallId, EXECUTE, ContentFormat.TLV.getName(),
  188 + null, 0, null);
  189 + }
  190 +
  191 +
  192 + /**
  193 + * Firmware start:
  194 + * -- Если Update Result -errors (более 1) - Это означает что пред. апдейт не прошел.
  195 + * - Запускаем апдейт в независимости от состяния прошивки и ее версии.
  196 + * -- Если Update Result - не errors (менее или равно 1) и ver не пустой - Это означает что пред. апдейт прошел.
  197 + * -- Если Update Result - не errors и ver пустой - Это означает что апдейта еще не было.
  198 + * - Проверяем поменялась ли версия и запускаем новый апдейт.
  199 + */
  200 + private boolean conditionalFwUpdateStart() {
  201 + Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
  202 + // #1/#2
  203 + return updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code ||
  204 + (
  205 + (updateResultFw <= LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code
  206 + ) &&
  207 + (
  208 + (this.currentVersion != null && !this.currentVersion.equals(this.lwM2MClient.getResourceValue(null, this.pathVerId))) ||
  209 + (this.currentTitle != null && !this.currentTitle.equals(this.lwM2MClient.getResourceValue(null, this.pathNameId)))
  210 + )
  211 + );
  212 + }
  213 +
  214 +
  215 + /**
  216 + * Before operation Execute inspection Update Result :
  217 + * 0 - Initial value
  218 + */
  219 + public boolean conditionalFwExecuteStart() {
  220 + Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
  221 + return LwM2mTransportUtil.UpdateResultFw.INITIAL.code == updateResult;
  222 + }
  223 +
  224 + /**
  225 + * After operation Execute success inspection Update Result :
  226 + * 1 - "Firmware updated successfully"
  227 + */
  228 + public boolean conditionalFwExecuteAfterSuccess() {
  229 + Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
  230 + return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult;
  231 + }
  232 +
  233 + /**
  234 + * After operation Execute success inspection Update Result :
  235 + * > 1 error: "Firmware updated successfully"
  236 + */
  237 + public boolean conditionalFwExecuteAfterError() {
  238 + Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
  239 + return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult;
  240 + }
  241 +
  242 + /**
  243 + * Software start
  244 + * -- Если Update Result -errors (равно и более 50) - Это означает что пред. апдейт не прошел.
  245 + * * - Запускаем апдейт в независимости от состяния прошивки и ее версии.
  246 + * -- Если Update Result - не errors (менее 50) и ver не пустой - Это означает что пред. апдейт прошел.
  247 + * -- Если Update Result - не errors и ver пустой - Это означает что апдейта еще не было или пред. апдейт UnInstall
  248 + * -- Если Update Result - не errors и ver не пустой - Это означает что пред. апдейт UnInstall
  249 + * - Проверяем поменялась ли версия и запускаем новый апдейт.
  250 + */
  251 + private boolean conditionalSwUpdateStart() {
  252 + Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
  253 + // #1/#2
  254 + return updateResultSw >= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code ||
  255 + (
  256 + (updateResultSw <= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code
  257 + ) &&
  258 + (
  259 + (this.currentVersion != null && !this.currentVersion.equals(this.lwM2MClient.getResourceValue(null, this.pathVerId))) ||
  260 + (this.currentTitle != null && !this.currentTitle.equals(this.lwM2MClient.getResourceValue(null, this.pathNameId)))
  261 + )
  262 + );
  263 + }
  264 +
  265 + /**
  266 + * Before operation Execute inspection Update Result :
  267 + * 3 - Successfully Downloaded and package integrity verified
  268 + */
  269 + public boolean conditionalSwUpdateExecute() {
  270 + Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
  271 + return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_DOWNLOADED_VERIFIED.code == updateResult;
  272 + }
  273 +
  274 + /**
  275 + * After finish operation Execute (success):
  276 + * -- inspection Update Result:
  277 + * ---- FW если Update Result == 1 ("Firmware updated successfully") или SW если Update Result == 2 ("Software successfully installed.")
  278 + * -- fw_state/sw_state = UPDATED
  279 + *
  280 + * After finish operation Execute (error):
  281 + * -- inspection updateResult and send to thingsboard info about error
  282 + * --- send to telemetry ( key - this is name Update Result in model) (
  283 + * -- fw_state/sw_state = FAILED
  284 + */
  285 + public void finishFwSwUpdate(DefaultLwM2MTransportMsgHandler handler, boolean success) {
  286 + Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
  287 + String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type :
  288 + LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type;
  289 + String key = splitCamelCaseString((String) this.lwM2MClient.getResourceName(null, this.pathResultId));
  290 + if (success) {
  291 + this.stateUpdate = FirmwareUpdateStatus.UPDATED.name();
  292 + this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
  293 + } else {
  294 + this.stateUpdate = FirmwareUpdateStatus.FAILED.name();
  295 + this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, value);
  296 + }
  297 + handler.helper.sendParametersOnThingsboardTelemetry(
  298 + handler.helper.getKvStringtoThingsboard(key, value), this.lwM2MClient.getSession());
  299 + }
  300 +
  301 + /**
  302 + * After operation Execute success inspection Update Result :
  303 + * 2 - "Software successfully installed."
  304 + */
  305 + public boolean conditionalSwExecuteAfterSuccess() {
  306 + Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
  307 + return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_INSTALLED.code == updateResult;
  308 + }
  309 +
  310 + /**
  311 + * After operation Execute success inspection Update Result :
  312 + * >= 50 - error "NOT_ENOUGH_STORAGE"
  313 + */
  314 + public boolean conditionalSwExecuteAfterError() {
  315 + Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
  316 + return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult;
  317 + }
  318 +
  319 + private void observeStateUpdate(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
  320 + request.sendAllRequest(lwM2MClient.getRegistration(),
  321 + convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE,
  322 + null, null, 0, null);
  323 + request.sendAllRequest(lwM2MClient.getRegistration(),
  324 + convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE,
  325 + null, null, 0, null);
  326 + }
  327 +
  328 + public void sendSateOnThingsBoard(DefaultLwM2MTransportMsgHandler handler) {
  329 + if (StringUtils.trimToNull(this.stateUpdate) != null) {
  330 + List<TransportProtos.KeyValueProto> result = new ArrayList<>();
  331 + TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(this.type, STATE));
  332 + kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(stateUpdate);
  333 + result.add(kvProto.build());
  334 + handler.helper.sendParametersOnThingsboardTelemetry(result,
  335 + handler.getSessionInfoOrCloseSession(this.lwM2MClient.getRegistration()));
  336 + }
  337 + }
  338 +
  339 + public void sendReadObserveInfo(LwM2mTransportRequest request) {
  340 + this.infoFwSwUpdate = true;
  341 + this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
  342 + this.pathVerId, this.lwM2MClient.getRegistration()));
  343 + this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
  344 + this.pathNameId, this.lwM2MClient.getRegistration()));
  345 + this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
  346 + this.pathStateId, this.lwM2MClient.getRegistration()));
  347 + this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
  348 + this.pathResultId, this.lwM2MClient.getRegistration()));
  349 + this.pendingInfoRequestsStart.forEach(pathIdVer -> {
  350 + request.sendAllRequest(this.lwM2MClient.getRegistration(), pathIdVer, OBSERVE, ContentFormat.TLV.getName(),
  351 + null, 0, null);
  352 + });
  353 +
  354 + }
  355 +}
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client;
17 17
18 18 import com.google.gson.JsonObject;
19 19 import lombok.Data;
  20 +import lombok.extern.slf4j.Slf4j;
20 21 import org.eclipse.leshan.core.request.ContentFormat;
21 22 import org.eclipse.leshan.server.registration.Registration;
22 23 import org.thingsboard.server.gen.transport.TransportProtos;
... ... @@ -27,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
27 28
28 29 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validPathIdVer;
29 30
  31 +@Slf4j
30 32 @Data
31 33 public class Lwm2mClientRpcRequest {
32 34 public final String targetIdVerKey = "targetIdVer";
... ... @@ -107,4 +109,14 @@ public class Lwm2mClientRpcRequest {
107 109 .setRequestId(this.requestId)
108 110 .build();
109 111 }
  112 +
  113 + @Override
  114 + public Object clone() {
  115 + try {
  116 + return super.clone();
  117 + } catch (CloneNotSupportedException e) {
  118 + log.error("", e);
  119 + }
  120 + return null;
  121 + }
110 122 }
... ...
... ... @@ -26,9 +26,7 @@ import org.eclipse.leshan.core.util.NamedThreadFactory;
26 26 import org.eclipse.leshan.core.util.Validate;
27 27 import org.eclipse.leshan.server.californium.observation.ObserveUtil;
28 28 import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
29   -import org.eclipse.leshan.server.redis.JedisLock;
30 29 import org.eclipse.leshan.server.redis.RedisRegistrationStore;
31   -import org.eclipse.leshan.server.redis.SingleInstanceJedisLock;
32 30 import org.eclipse.leshan.server.redis.serialization.ObservationSerDes;
33 31 import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes;
34 32 import org.eclipse.leshan.server.registration.Deregistration;
... ... @@ -38,11 +36,12 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate;
38 36 import org.eclipse.leshan.server.registration.UpdatedRegistration;
39 37 import org.slf4j.Logger;
40 38 import org.slf4j.LoggerFactory;
  39 +import org.springframework.data.redis.connection.RedisClusterConnection;
  40 +import org.springframework.data.redis.connection.RedisConnection;
41 41 import org.springframework.data.redis.connection.RedisConnectionFactory;
42   -import redis.clients.jedis.Jedis;
43   -import redis.clients.jedis.ScanParams;
44   -import redis.clients.jedis.ScanResult;
45   -import redis.clients.jedis.Transaction;
  42 +import org.springframework.data.redis.core.Cursor;
  43 +import org.springframework.data.redis.core.ScanOptions;
  44 +import org.springframework.integration.redis.util.RedisLockRegistry;
46 45
47 46 import java.net.InetSocketAddress;
48 47 import java.util.ArrayList;
... ... @@ -50,13 +49,14 @@ import java.util.Arrays;
50 49 import java.util.Collection;
51 50 import java.util.Collections;
52 51 import java.util.Iterator;
  52 +import java.util.LinkedList;
53 53 import java.util.List;
54   -import java.util.NoSuchElementException;
55 54 import java.util.Set;
56 55 import java.util.concurrent.Executors;
57 56 import java.util.concurrent.ScheduledExecutorService;
58 57 import java.util.concurrent.ScheduledFuture;
59 58 import java.util.concurrent.TimeUnit;
  59 +import java.util.concurrent.locks.Lock;
60 60
61 61 import static java.nio.charset.StandardCharsets.UTF_8;
62 62
... ... @@ -92,7 +92,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
92 92 private final int cleanLimit; // maximum number to clean in a clean period
93 93 private final long gracePeriod; // in seconds
94 94
95   - private final JedisLock lock;
  95 + private final RedisLockRegistry redisLock;
96 96
97 97 public TbLwM2mRedisRegistrationStore(RedisConnectionFactory connectionFactory) {
98 98 this(connectionFactory, DEFAULT_CLEAN_PERIOD, DEFAULT_GRACE_PERIOD, DEFAULT_CLEAN_LIMIT); // default clean period 60s
... ... @@ -106,20 +106,12 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
106 106
107 107 public TbLwM2mRedisRegistrationStore(RedisConnectionFactory connectionFactory, ScheduledExecutorService schedExecutor, long cleanPeriodInSec,
108 108 long lifetimeGracePeriodInSec, int cleanLimit) {
109   - this(connectionFactory, schedExecutor, cleanPeriodInSec, lifetimeGracePeriodInSec, cleanLimit, new SingleInstanceJedisLock());
110   - }
111   -
112   - /**
113   - * @since 1.1
114   - */
115   - public TbLwM2mRedisRegistrationStore(RedisConnectionFactory connectionFactory, ScheduledExecutorService schedExecutor, long cleanPeriodInSec,
116   - long lifetimeGracePeriodInSec, int cleanLimit, JedisLock redisLock) {
117 109 this.connectionFactory = connectionFactory;
118 110 this.schedExecutor = schedExecutor;
119 111 this.cleanPeriod = cleanPeriodInSec;
120 112 this.cleanLimit = cleanLimit;
121 113 this.gracePeriod = lifetimeGracePeriodInSec;
122   - this.lock = redisLock;
  114 + this.redisLock = new RedisLockRegistry(connectionFactory, "Registration");
123 115 }
124 116
125 117 /* *************** Redis Key utility function **************** */
... ... @@ -135,76 +127,79 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
135 127 return (prefix + registrationID).getBytes();
136 128 }
137 129
138   - private byte[] toLockKey(String endpoint) {
139   - return toKey(LOCK_EP, endpoint);
  130 + private String toLockKey(String endpoint) {
  131 + return new String(toKey(LOCK_EP, endpoint));
140 132 }
141 133
142   - private byte[] toLockKey(byte[] endpoint) {
143   - return toKey(LOCK_EP.getBytes(UTF_8), endpoint);
  134 + private String toLockKey(byte[] endpoint) {
  135 + return new String(toKey(LOCK_EP.getBytes(UTF_8), endpoint));
144 136 }
145 137
146 138 /* *************** Leshan Registration API **************** */
147 139
148 140 @Override
149 141 public Deregistration addRegistration(Registration registration) {
150   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
151   - byte[] lockValue = null;
152   - byte[] lockKey = toLockKey(registration.getEndpoint());
  142 + Lock lock = null;
  143 + try (var connection = connectionFactory.getConnection()) {
  144 + String lockKey = toLockKey(registration.getEndpoint());
153 145
154 146 try {
155   - lockValue = lock.acquire(j, lockKey);
156   -
  147 + lock = redisLock.obtain(lockKey);
  148 + lock.lock();
157 149 // add registration
158 150 byte[] k = toEndpointKey(registration.getEndpoint());
159   - byte[] old = j.getSet(k, serializeReg(registration));
  151 + byte[] old = connection.getSet(k, serializeReg(registration));
160 152
161 153 // add registration: secondary indexes
162 154 byte[] regid_idx = toRegIdKey(registration.getId());
163   - j.set(regid_idx, registration.getEndpoint().getBytes(UTF_8));
  155 + connection.set(regid_idx, registration.getEndpoint().getBytes(UTF_8));
164 156 byte[] addr_idx = toRegAddrKey(registration.getSocketAddress());
165   - j.set(addr_idx, registration.getEndpoint().getBytes(UTF_8));
  157 + connection.set(addr_idx, registration.getEndpoint().getBytes(UTF_8));
166 158
167 159 // Add or update expiration
168   - addOrUpdateExpiration(j, registration);
  160 + addOrUpdateExpiration(connection, registration);
169 161
170 162 if (old != null) {
171 163 Registration oldRegistration = deserializeReg(old);
172 164 // remove old secondary index
173 165 if (!registration.getId().equals(oldRegistration.getId()))
174   - j.del(toRegIdKey(oldRegistration.getId()));
  166 + connection.del(toRegIdKey(oldRegistration.getId()));
175 167 if (!oldRegistration.getSocketAddress().equals(registration.getSocketAddress())) {
176   - removeAddrIndex(j, oldRegistration);
  168 + removeAddrIndex(connection, oldRegistration);
177 169 }
178 170 // remove old observation
179   - Collection<Observation> obsRemoved = unsafeRemoveAllObservations(j, oldRegistration.getId());
  171 + Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, oldRegistration.getId());
180 172
181 173 return new Deregistration(oldRegistration, obsRemoved);
182 174 }
183 175
184 176 return null;
185 177 } finally {
186   - lock.release(j, lockKey, lockValue);
  178 + if (lock != null) {
  179 + lock.unlock();
  180 + }
187 181 }
188 182 }
189 183 }
190 184
191 185 @Override
192 186 public UpdatedRegistration updateRegistration(RegistrationUpdate update) {
193   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
  187 + Lock lock = null;
  188 + try (var connection = connectionFactory.getConnection()) {
194 189
195 190 // Fetch the registration ep by registration ID index
196   - byte[] ep = j.get(toRegIdKey(update.getRegistrationId()));
  191 + byte[] ep = connection.get(toRegIdKey(update.getRegistrationId()));
197 192 if (ep == null) {
198 193 return null;
199 194 }
200 195
201   - byte[] lockValue = null;
202   - byte[] lockKey = toLockKey(ep);
  196 + String lockKey = toLockKey(ep);
203 197 try {
204   - lockValue = lock.acquire(j, lockKey);
  198 + lock = redisLock.obtain(lockKey);
  199 + lock.lock();
205 200
206 201 // Fetch the registration
207   - byte[] data = j.get(toEndpointKey(ep));
  202 + byte[] data = connection.get(toEndpointKey(ep));
208 203 if (data == null) {
209 204 return null;
210 205 }
... ... @@ -214,40 +209,42 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
214 209 Registration updatedRegistration = update.update(r);
215 210
216 211 // Store the new registration
217   - j.set(toEndpointKey(updatedRegistration.getEndpoint()), serializeReg(updatedRegistration));
  212 + connection.set(toEndpointKey(updatedRegistration.getEndpoint()), serializeReg(updatedRegistration));
218 213
219 214 // Add or update expiration
220   - addOrUpdateExpiration(j, updatedRegistration);
  215 + addOrUpdateExpiration(connection, updatedRegistration);
221 216
222 217 // Update secondary index :
223 218 // If registration is already associated to this address we don't care as we only want to keep the most
224 219 // recent binding.
225 220 byte[] addr_idx = toRegAddrKey(updatedRegistration.getSocketAddress());
226   - j.set(addr_idx, updatedRegistration.getEndpoint().getBytes(UTF_8));
  221 + connection.set(addr_idx, updatedRegistration.getEndpoint().getBytes(UTF_8));
227 222 if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) {
228   - removeAddrIndex(j, r);
  223 + removeAddrIndex(connection, r);
229 224 }
230 225
231 226 return new UpdatedRegistration(r, updatedRegistration);
232 227
233 228 } finally {
234   - lock.release(j, lockKey, lockValue);
  229 + if (lock != null) {
  230 + lock.unlock();
  231 + }
235 232 }
236 233 }
237 234 }
238 235
239 236 @Override
240 237 public Registration getRegistration(String registrationId) {
241   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
242   - return getRegistration(j, registrationId);
  238 + try (var connection = connectionFactory.getConnection()) {
  239 + return getRegistration(connection, registrationId);
243 240 }
244 241 }
245 242
246 243 @Override
247 244 public Registration getRegistrationByEndpoint(String endpoint) {
248 245 Validate.notNull(endpoint);
249   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
250   - byte[] data = j.get(toEndpointKey(endpoint));
  246 + try (var connection = connectionFactory.getConnection()) {
  247 + byte[] data = connection.get(toEndpointKey(endpoint));
251 248 if (data == null) {
252 249 return null;
253 250 }
... ... @@ -258,12 +255,12 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
258 255 @Override
259 256 public Registration getRegistrationByAdress(InetSocketAddress address) {
260 257 Validate.notNull(address);
261   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
262   - byte[] ep = j.get(toRegAddrKey(address));
  258 + try (var connection = connectionFactory.getConnection()) {
  259 + byte[] ep = connection.get(toRegAddrKey(address));
263 260 if (ep == null) {
264 261 return null;
265 262 }
266   - byte[] data = j.get(toEndpointKey(ep));
  263 + byte[] data = connection.get(toEndpointKey(ep));
267 264 if (data == null) {
268 265 return null;
269 266 }
... ... @@ -273,140 +270,99 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
273 270
274 271 @Override
275 272 public Iterator<Registration> getAllRegistrations() {
276   - return new TbLwM2mRedisRegistrationStore.RedisIterator(connectionFactory, new ScanParams().match(REG_EP + "*").count(100));
277   - }
278   -
279   - protected class RedisIterator implements Iterator<Registration> {
280   -
281   - private final RedisConnectionFactory connectionFactory;
282   - private final ScanParams scanParams;
283   -
284   - private String cursor;
285   - private List<Registration> scanResult;
286   -
287   - public RedisIterator(RedisConnectionFactory connectionFactory, ScanParams scanParams) {
288   - this.connectionFactory = connectionFactory;
289   - this.scanParams = scanParams;
290   - // init scan result
291   - scanNext("0");
292   - }
293   -
294   - private void scanNext(String cursor) {
295   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
296   - do {
297   - ScanResult<byte[]> sr = j.scan(cursor.getBytes(), scanParams);
298   -
299   - this.scanResult = new ArrayList<>();
300   - if (sr.getResult() != null && !sr.getResult().isEmpty()) {
301   - for (byte[] value : j.mget(sr.getResult().toArray(new byte[][]{}))) {
302   - this.scanResult.add(deserializeReg(value));
303   - }
304   - }
305   -
306   - cursor = sr.getCursor();
307   - } while (!"0".equals(cursor) && scanResult.isEmpty());
308   -
309   - this.cursor = cursor;
310   - }
311   - }
312   -
313   - @Override
314   - public boolean hasNext() {
315   - if (!scanResult.isEmpty()) {
316   - return true;
317   - }
318   - if ("0".equals(cursor)) {
319   - // no more elements to scan
320   - return false;
321   - }
322   -
323   - // read more elements
324   - scanNext(cursor);
325   - return !scanResult.isEmpty();
326   - }
327   -
328   - @Override
329   - public Registration next() {
330   - if (!hasNext()) {
331   - throw new NoSuchElementException();
  273 + try (var connection = connectionFactory.getConnection()) {
  274 + Collection<Registration> list = new LinkedList<>();
  275 + ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(REG_EP + "*").build();
  276 + List<Cursor<byte[]>> scans = new ArrayList<>();
  277 + if (connection instanceof RedisClusterConnection) {
  278 + ((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {
  279 + scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));
  280 + });
  281 + } else {
  282 + scans.add(connection.scan(scanOptions));
332 283 }
333   - return scanResult.remove(0);
334   - }
335 284
336   - @Override
337   - public void remove() {
338   - throw new UnsupportedOperationException();
  285 + scans.forEach(scan -> {
  286 + scan.forEachRemaining(key -> {
  287 + byte[] element = connection.get(key);
  288 + list.add(deserializeReg(element));
  289 + });
  290 + });
  291 + return list.iterator();
339 292 }
340 293 }
341 294
342 295 @Override
343 296 public Deregistration removeRegistration(String registrationId) {
344   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
345   - return removeRegistration(j, registrationId, false);
  297 + try (var connection = connectionFactory.getConnection()) {
  298 + return removeRegistration(connection, registrationId, false);
346 299 }
347 300 }
348 301
349   - private Deregistration removeRegistration(Jedis j, String registrationId, boolean removeOnlyIfNotAlive) {
  302 + private Deregistration removeRegistration(RedisConnection connection, String registrationId, boolean removeOnlyIfNotAlive) {
350 303 // fetch the client ep by registration ID index
351   - byte[] ep = j.get(toRegIdKey(registrationId));
  304 + byte[] ep = connection.get(toRegIdKey(registrationId));
352 305 if (ep == null) {
353 306 return null;
354 307 }
355 308
356   - byte[] lockValue = null;
357   - byte[] lockKey = toLockKey(ep);
  309 + Lock lock = null;
  310 + String lockKey = toLockKey(ep);
358 311 try {
359   - lockValue = lock.acquire(j, lockKey);
  312 + lock = redisLock.obtain(lockKey);
  313 + lock.lock();
360 314
361 315 // fetch the client
362   - byte[] data = j.get(toEndpointKey(ep));
  316 + byte[] data = connection.get(toEndpointKey(ep));
363 317 if (data == null) {
364 318 return null;
365 319 }
366 320 Registration r = deserializeReg(data);
367 321
368 322 if (!removeOnlyIfNotAlive || !r.isAlive(gracePeriod)) {
369   - long nbRemoved = j.del(toRegIdKey(r.getId()));
  323 + long nbRemoved = connection.del(toRegIdKey(r.getId()));
370 324 if (nbRemoved > 0) {
371   - j.del(toEndpointKey(r.getEndpoint()));
372   - Collection<Observation> obsRemoved = unsafeRemoveAllObservations(j, r.getId());
373   - removeAddrIndex(j, r);
374   - removeExpiration(j, r);
  325 + connection.del(toEndpointKey(r.getEndpoint()));
  326 + Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, r.getId());
  327 + removeAddrIndex(connection, r);
  328 + removeExpiration(connection, r);
375 329 return new Deregistration(r, obsRemoved);
376 330 }
377 331 }
378 332 return null;
379 333 } finally {
380   - lock.release(j, lockKey, lockValue);
  334 + if (lock != null) {
  335 + lock.unlock();
  336 + }
381 337 }
382 338 }
383 339
384   - private void removeAddrIndex(Jedis j, Registration registration) {
  340 + private void removeAddrIndex(RedisConnection connection, Registration registration) {
385 341 // Watch the key to remove.
386 342 byte[] regAddrKey = toRegAddrKey(registration.getSocketAddress());
387   - j.watch(regAddrKey);
  343 + connection.watch(regAddrKey);
388 344
389   - byte[] epFromAddr = j.get(regAddrKey);
  345 + byte[] epFromAddr = connection.get(regAddrKey);
390 346 // Delete the key if needed.
391 347 if (Arrays.equals(epFromAddr, registration.getEndpoint().getBytes(UTF_8))) {
392 348 // Try to delete the key
393   - Transaction transaction = j.multi();
394   - transaction.del(regAddrKey);
395   - transaction.exec();
  349 + connection.multi();
  350 + connection.del(regAddrKey);
  351 + connection.exec();
396 352 // if transaction failed this is not an issue as the socket address is probably reused and we don't neeed to
397 353 // delete it anymore.
398 354 } else {
399 355 // the key must not be deleted.
400   - j.unwatch();
  356 + connection.unwatch();
401 357 }
402 358 }
403 359
404   - private void addOrUpdateExpiration(Jedis j, Registration registration) {
405   - j.zadd(EXP_EP, registration.getExpirationTimeStamp(gracePeriod), registration.getEndpoint().getBytes(UTF_8));
  360 + private void addOrUpdateExpiration(RedisConnection connection, Registration registration) {
  361 + connection.zAdd(EXP_EP, registration.getExpirationTimeStamp(gracePeriod), registration.getEndpoint().getBytes(UTF_8));
406 362 }
407 363
408   - private void removeExpiration(Jedis j, Registration registration) {
409   - j.zrem(EXP_EP, registration.getEndpoint().getBytes(UTF_8));
  364 + private void removeExpiration(RedisConnection connection, Registration registration) {
  365 + connection.zRem(EXP_EP, registration.getEndpoint().getBytes(UTF_8));
410 366 }
411 367
412 368 private byte[] toRegIdKey(String registrationId) {
... ... @@ -441,33 +397,35 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
441 397 */
442 398 @Override
443 399 public Collection<Observation> addObservation(String registrationId, Observation observation) {
444   -
445 400 List<Observation> removed = new ArrayList<>();
446   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
  401 + try (var connection = connectionFactory.getConnection()) {
447 402
448 403 // fetch the client ep by registration ID index
449   - byte[] ep = j.get(toRegIdKey(registrationId));
  404 + byte[] ep = connection.get(toRegIdKey(registrationId));
450 405 if (ep == null) {
451 406 return null;
452 407 }
453 408
454   - byte[] lockValue = null;
455   - byte[] lockKey = toLockKey(ep);
  409 + Lock lock = null;
  410 + String lockKey = toLockKey(ep);
456 411
457 412 try {
458   - lockValue = lock.acquire(j, lockKey);
  413 + lock = redisLock.obtain(lockKey);
  414 + lock.lock();
459 415
460 416 // cancel existing observations for the same path and registration id.
461   - for (Observation obs : getObservations(j, registrationId)) {
  417 + for (Observation obs : getObservations(connection, registrationId)) {
462 418 if (observation.getPath().equals(obs.getPath())
463 419 && !Arrays.equals(observation.getId(), obs.getId())) {
464 420 removed.add(obs);
465   - unsafeRemoveObservation(j, registrationId, obs.getId());
  421 + unsafeRemoveObservation(connection, registrationId, obs.getId());
466 422 }
467 423 }
468 424
469 425 } finally {
470   - lock.release(j, lockKey, lockValue);
  426 + if (lock != null) {
  427 + lock.unlock();
  428 + }
471 429 }
472 430 }
473 431 return removed;
... ... @@ -475,29 +433,32 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
475 433
476 434 @Override
477 435 public Observation removeObservation(String registrationId, byte[] observationId) {
478   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
  436 + try (var connection = connectionFactory.getConnection()) {
479 437
480 438 // fetch the client ep by registration ID index
481   - byte[] ep = j.get(toRegIdKey(registrationId));
  439 + byte[] ep = connection.get(toRegIdKey(registrationId));
482 440 if (ep == null) {
483 441 return null;
484 442 }
485 443
486 444 // remove observation
487   - byte[] lockValue = null;
488   - byte[] lockKey = toLockKey(ep);
  445 + Lock lock = null;
  446 + String lockKey = toLockKey(ep);
489 447 try {
490   - lockValue = lock.acquire(j, lockKey);
  448 + lock = redisLock.obtain(lockKey);
  449 + lock.lock();
491 450
492 451 Observation observation = build(get(new Token(observationId)));
493 452 if (observation != null && registrationId.equals(observation.getRegistrationId())) {
494   - unsafeRemoveObservation(j, registrationId, observationId);
  453 + unsafeRemoveObservation(connection, registrationId, observationId);
495 454 return observation;
496 455 }
497 456 return null;
498 457
499 458 } finally {
500   - lock.release(j, lockKey, lockValue);
  459 + if (lock != null) {
  460 + lock.unlock();
  461 + }
501 462 }
502 463 }
503 464 }
... ... @@ -509,15 +470,15 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
509 470
510 471 @Override
511 472 public Collection<Observation> getObservations(String registrationId) {
512   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
513   - return getObservations(j, registrationId);
  473 + try (var connection = connectionFactory.getConnection()) {
  474 + return getObservations(connection, registrationId);
514 475 }
515 476 }
516 477
517   - private Collection<Observation> getObservations(Jedis j, String registrationId) {
  478 + private Collection<Observation> getObservations(RedisConnection connection, String registrationId) {
518 479 Collection<Observation> result = new ArrayList<>();
519   - for (byte[] token : j.lrange(toKey(OBS_TKNS_REGID_IDX, registrationId), 0, -1)) {
520   - byte[] obs = j.get(toKey(OBS_TKN, token));
  480 + for (byte[] token : connection.lRange(toKey(OBS_TKNS_REGID_IDX, registrationId), 0, -1)) {
  481 + byte[] obs = connection.get(toKey(OBS_TKN, token));
521 482 if (obs != null) {
522 483 result.add(build(deserializeObs(obs)));
523 484 }
... ... @@ -527,22 +488,24 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
527 488
528 489 @Override
529 490 public Collection<Observation> removeObservations(String registrationId) {
530   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
  491 + try (var connection = connectionFactory.getConnection()) {
531 492 // check registration exists
532   - Registration registration = getRegistration(j, registrationId);
  493 + Registration registration = getRegistration(connection, registrationId);
533 494 if (registration == null)
534 495 return Collections.emptyList();
535 496
536 497 // get endpoint and create lock
537 498 String endpoint = registration.getEndpoint();
538   - byte[] lockValue = null;
539   - byte[] lockKey = toKey(LOCK_EP, endpoint);
  499 + Lock lock = null;
  500 + String lockKey = toLockKey(endpoint);
540 501 try {
541   - lockValue = lock.acquire(j, lockKey);
542   -
543   - return unsafeRemoveAllObservations(j, registrationId);
  502 + lock = redisLock.obtain(lockKey);
  503 + lock.lock();
  504 + return unsafeRemoveAllObservations(connection, registrationId);
544 505 } finally {
545   - lock.release(j, lockKey, lockValue);
  506 + if (lock != null) {
  507 + lock.unlock();
  508 + }
546 509 }
547 510 }
548 511 }
... ... @@ -565,31 +528,32 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
565 528 String endpoint = ObserveUtil.validateCoapObservation(obs);
566 529 org.eclipse.californium.core.observe.Observation previousObservation = null;
567 530
568   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
569   - byte[] lockValue = null;
570   - byte[] lockKey = toKey(LOCK_EP, endpoint);
  531 + try (var connection = connectionFactory.getConnection()) {
  532 + Lock lock = null;
  533 + String lockKey = toLockKey(endpoint);
571 534 try {
572   - lockValue = lock.acquire(j, lockKey);
  535 + lock = redisLock.obtain(lockKey);
  536 + lock.lock();
573 537
574 538 String registrationId = ObserveUtil.extractRegistrationId(obs);
575   - if (!j.exists(toRegIdKey(registrationId)))
  539 + if (!connection.exists(toRegIdKey(registrationId)))
576 540 throw new ObservationStoreException("no registration for this Id");
577 541 byte[] key = toKey(OBS_TKN, obs.getRequest().getToken().getBytes());
578 542 byte[] serializeObs = serializeObs(obs);
579 543 byte[] previousValue;
580 544 if (ifAbsent) {
581   - previousValue = j.get(key);
  545 + previousValue = connection.get(key);
582 546 if (previousValue == null || previousValue.length == 0) {
583   - j.set(key, serializeObs);
  547 + connection.set(key, serializeObs);
584 548 } else {
585 549 return deserializeObs(previousValue);
586 550 }
587 551 } else {
588   - previousValue = j.getSet(key, serializeObs);
  552 + previousValue = connection.getSet(key, serializeObs);
589 553 }
590 554
591 555 // secondary index to get the list by registrationId
592   - j.lpush(toKey(OBS_TKNS_REGID_IDX, registrationId), obs.getRequest().getToken().getBytes());
  556 + connection.lPush(toKey(OBS_TKNS_REGID_IDX, registrationId), obs.getRequest().getToken().getBytes());
593 557
594 558 // log any collisions
595 559 if (previousValue != null && previousValue.length != 0) {
... ... @@ -599,7 +563,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
599 563 previousObservation.getRequest(), obs.getRequest());
600 564 }
601 565 } finally {
602   - lock.release(j, lockKey, lockValue);
  566 + if (lock != null) {
  567 + lock.unlock();
  568 + }
603 569 }
604 570 }
605 571 return previousObservation;
... ... @@ -607,17 +573,17 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
607 573
608 574 @Override
609 575 public void remove(Token token) {
610   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
  576 + try (var connection = connectionFactory.getConnection()) {
611 577 byte[] tokenKey = toKey(OBS_TKN, token.getBytes());
612 578
613 579 // fetch the observation by token
614   - byte[] serializedObs = j.get(tokenKey);
  580 + byte[] serializedObs = connection.get(tokenKey);
615 581 if (serializedObs == null)
616 582 return;
617 583
618 584 org.eclipse.californium.core.observe.Observation obs = deserializeObs(serializedObs);
619 585 String registrationId = ObserveUtil.extractRegistrationId(obs);
620   - Registration registration = getRegistration(j, registrationId);
  586 + Registration registration = getRegistration(connection, registrationId);
621 587 if (registration == null) {
622 588 LOG.warn("Unable to remove observation {}, registration {} does not exist anymore", obs.getRequest(),
623 589 registrationId);
... ... @@ -625,14 +591,17 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
625 591 }
626 592
627 593 String endpoint = registration.getEndpoint();
628   - byte[] lockValue = null;
629   - byte[] lockKey = toKey(LOCK_EP, endpoint);
  594 + Lock lock = null;
  595 + String lockKey = toLockKey(endpoint);
630 596 try {
631   - lockValue = lock.acquire(j, lockKey);
  597 + lock = redisLock.obtain(lockKey);
  598 + lock.lock();
632 599
633   - unsafeRemoveObservation(j, registrationId, token.getBytes());
  600 + unsafeRemoveObservation(connection, registrationId, token.getBytes());
634 601 } finally {
635   - lock.release(j, lockKey, lockValue);
  602 + if (lock != null) {
  603 + lock.unlock();
  604 + }
636 605 }
637 606 }
638 607
... ... @@ -640,8 +609,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
640 609
641 610 @Override
642 611 public org.eclipse.californium.core.observe.Observation get(Token token) {
643   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
644   - byte[] obs = j.get(toKey(OBS_TKN, token.getBytes()));
  612 + try (var connection = connectionFactory.getConnection()) {
  613 + byte[] obs = connection.get(toKey(OBS_TKN, token.getBytes()));
645 614 if (obs == null) {
646 615 return null;
647 616 } else {
... ... @@ -652,12 +621,12 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
652 621
653 622 /* *************** Observation utility functions **************** */
654 623
655   - private Registration getRegistration(Jedis j, String registrationId) {
656   - byte[] ep = j.get(toRegIdKey(registrationId));
  624 + private Registration getRegistration(RedisConnection connection, String registrationId) {
  625 + byte[] ep = connection.get(toRegIdKey(registrationId));
657 626 if (ep == null) {
658 627 return null;
659 628 }
660   - byte[] data = j.get(toEndpointKey(ep));
  629 + byte[] data = connection.get(toEndpointKey(ep));
661 630 if (data == null) {
662 631 return null;
663 632 }
... ... @@ -665,25 +634,25 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
665 634 return deserializeReg(data);
666 635 }
667 636
668   - private void unsafeRemoveObservation(Jedis j, String registrationId, byte[] observationId) {
669   - if (j.del(toKey(OBS_TKN, observationId)) > 0L) {
670   - j.lrem(toKey(OBS_TKNS_REGID_IDX, registrationId), 0, observationId);
  637 + private void unsafeRemoveObservation(RedisConnection connection, String registrationId, byte[] observationId) {
  638 + if (connection.del(toKey(OBS_TKN, observationId)) > 0L) {
  639 + connection.lRem(toKey(OBS_TKNS_REGID_IDX, registrationId), 0, observationId);
671 640 }
672 641 }
673 642
674   - private Collection<Observation> unsafeRemoveAllObservations(Jedis j, String registrationId) {
  643 + private Collection<Observation> unsafeRemoveAllObservations(RedisConnection connection, String registrationId) {
675 644 Collection<Observation> removed = new ArrayList<>();
676 645 byte[] regIdKey = toKey(OBS_TKNS_REGID_IDX, registrationId);
677 646
678 647 // fetch all observations by token
679   - for (byte[] token : j.lrange(regIdKey, 0, -1)) {
680   - byte[] obs = j.get(toKey(OBS_TKN, token));
  648 + for (byte[] token : connection.lRange(regIdKey, 0, -1)) {
  649 + byte[] obs = connection.get(toKey(OBS_TKN, token));
681 650 if (obs != null) {
682 651 removed.add(build(deserializeObs(obs)));
683 652 }
684   - j.del(toKey(OBS_TKN, token));
  653 + connection.del(toKey(OBS_TKN, token));
685 654 }
686   - j.del(regIdKey);
  655 + connection.del(regIdKey);
687 656
688 657 return removed;
689 658 }
... ... @@ -754,14 +723,14 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
754 723 @Override
755 724 public void run() {
756 725
757   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
758   - Set<byte[]> endpointsExpired = j.zrangeByScore(EXP_EP, Double.NEGATIVE_INFINITY,
  726 + try (var connection = connectionFactory.getConnection()) {
  727 + Set<byte[]> endpointsExpired = connection.zRangeByScore(EXP_EP, Double.NEGATIVE_INFINITY,
759 728 System.currentTimeMillis(), 0, cleanLimit);
760 729
761 730 for (byte[] endpoint : endpointsExpired) {
762   - Registration r = deserializeReg(j.get(toEndpointKey(endpoint)));
  731 + Registration r = deserializeReg(connection.get(toEndpointKey(endpoint)));
763 732 if (!r.isAlive(gracePeriod)) {
764   - Deregistration dereg = removeRegistration(j, r.getId(), true);
  733 + Deregistration dereg = removeRegistration(connection, r.getId(), true);
765 734 if (dereg != null)
766 735 expirationListener.registrationExpired(dereg.getRegistration(), dereg.getObservations());
767 736 }
... ...
... ... @@ -20,13 +20,15 @@ import org.eclipse.leshan.server.security.EditableSecurityStore;
20 20 import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
21 21 import org.eclipse.leshan.server.security.SecurityInfo;
22 22 import org.eclipse.leshan.server.security.SecurityStoreListener;
  23 +import org.springframework.data.redis.connection.RedisClusterConnection;
23 24 import org.springframework.data.redis.connection.RedisConnectionFactory;
24   -import redis.clients.jedis.Jedis;
25   -import redis.clients.jedis.ScanParams;
26   -import redis.clients.jedis.ScanResult;
  25 +import org.springframework.data.redis.core.Cursor;
  26 +import org.springframework.data.redis.core.ScanOptions;
27 27
  28 +import java.util.ArrayList;
28 29 import java.util.Collection;
29 30 import java.util.LinkedList;
  31 +import java.util.List;
30 32
31 33 public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
32 34 private static final String SEC_EP = "SEC#EP#";
... ... @@ -42,8 +44,8 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
42 44
43 45 @Override
44 46 public SecurityInfo getByEndpoint(String endpoint) {
45   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
46   - byte[] data = j.get((SEC_EP + endpoint).getBytes());
  47 + try (var connection = connectionFactory.getConnection()) {
  48 + byte[] data = connection.get((SEC_EP + endpoint).getBytes());
47 49 if (data == null) {
48 50 return null;
49 51 } else {
... ... @@ -54,12 +56,12 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
54 56
55 57 @Override
56 58 public SecurityInfo getByIdentity(String identity) {
57   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
58   - String ep = j.hget(PSKID_SEC, identity);
  59 + try (var connection = connectionFactory.getConnection()) {
  60 + byte[] ep = connection.hGet(PSKID_SEC.getBytes(), identity.getBytes());
59 61 if (ep == null) {
60 62 return null;
61 63 } else {
62   - byte[] data = j.get((SEC_EP + ep).getBytes());
  64 + byte[] data = connection.get((SEC_EP + new String(ep)).getBytes());
63 65 if (data == null) {
64 66 return null;
65 67 } else {
... ... @@ -71,18 +73,24 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
71 73
72 74 @Override
73 75 public Collection<SecurityInfo> getAll() {
74   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
75   - ScanParams params = new ScanParams().match(SEC_EP + "*").count(100);
  76 + try (var connection = connectionFactory.getConnection()) {
76 77 Collection<SecurityInfo> list = new LinkedList<>();
77   - String cursor = "0";
78   - do {
79   - ScanResult<byte[]> res = j.scan(cursor.getBytes(), params);
80   - for (byte[] key : res.getResult()) {
81   - byte[] element = j.get(key);
  78 + ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build();
  79 + List<Cursor<byte[]>> scans = new ArrayList<>();
  80 + if (connection instanceof RedisClusterConnection) {
  81 + ((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {
  82 + scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));
  83 + });
  84 + } else {
  85 + scans.add(connection.scan(scanOptions));
  86 + }
  87 +
  88 + scans.forEach(scan -> {
  89 + scan.forEachRemaining(key -> {
  90 + byte[] element = connection.get(key);
82 91 list.add(deserialize(element));
83   - }
84   - cursor = res.getCursor();
85   - } while (!"0".equals(cursor));
  92 + });
  93 + });
86 94 return list;
87 95 }
88 96 }
... ... @@ -90,21 +98,21 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
90 98 @Override
91 99 public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {
92 100 byte[] data = serialize(info);
93   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
  101 + try (var connection = connectionFactory.getConnection()) {
94 102 if (info.getIdentity() != null) {
95 103 // populate the secondary index (security info by PSK id)
96   - String oldEndpoint = j.hget(PSKID_SEC, info.getIdentity());
97   - if (oldEndpoint != null && !oldEndpoint.equals(info.getEndpoint())) {
  104 + String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()));
  105 + if (!oldEndpoint.equals(info.getEndpoint())) {
98 106 throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
99 107 }
100   - j.hset(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
  108 + connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
101 109 }
102 110
103   - byte[] previousData = j.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);
  111 + byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);
104 112 SecurityInfo previous = previousData == null ? null : deserialize(previousData);
105 113 String previousIdentity = previous == null ? null : previous.getIdentity();
106 114 if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
107   - j.hdel(PSKID_SEC, previousIdentity);
  115 + connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
108 116 }
109 117
110 118 return previous;
... ... @@ -113,15 +121,15 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
113 121
114 122 @Override
115 123 public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
116   - try (Jedis j = (Jedis) connectionFactory.getConnection().getNativeConnection()) {
117   - byte[] data = j.get((SEC_EP + endpoint).getBytes());
  124 + try (var connection = connectionFactory.getConnection()) {
  125 + byte[] data = connection.get((SEC_EP + endpoint).getBytes());
118 126
119 127 if (data != null) {
120 128 SecurityInfo info = deserialize(data);
121 129 if (info.getIdentity() != null) {
122   - j.hdel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
  130 + connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
123 131 }
124   - j.del((SEC_EP + endpoint).getBytes());
  132 + connection.del((SEC_EP + endpoint).getBytes());
125 133 if (listener != null) {
126 134 listener.securityInfoRemoved(infosAreCompromised, info);
127 135 }
... ...
... ... @@ -74,7 +74,7 @@ public class TbLwM2mSecurityStore implements EditableSecurityStore {
74 74 add(securityInfo);
75 75 }
76 76 } catch (NonUniqueSecurityInfoException e) {
77   - log.warn("Failed to add security info: {}", securityInfo, e);
  77 + log.trace("Failed to add security info: {}", securityInfo, e);
78 78 }
79 79 }
80 80 return securityInfo;
... ... @@ -90,7 +90,7 @@ public class TbLwM2mSecurityStore implements EditableSecurityStore {
90 90 add(securityInfo);
91 91 }
92 92 } catch (NonUniqueSecurityInfoException e) {
93   - log.warn("Failed to add security info: {}", securityInfo, e);
  93 + log.trace("Failed to add security info: {}", securityInfo, e);
94 94 }
95 95 }
96 96 return securityInfo;
... ...
... ... @@ -26,6 +26,7 @@ import org.springframework.context.annotation.Lazy;
26 26 import org.springframework.stereotype.Component;
27 27 import org.thingsboard.server.cache.TBRedisCacheConfiguration;
28 28 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
  29 +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
29 30 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
30 31
31 32 import java.util.Optional;
... ... @@ -38,6 +39,9 @@ public class TbLwM2mStoreFactory {
38 39 private Optional<TBRedisCacheConfiguration> redisConfiguration;
39 40
40 41 @Autowired
  42 + private LwM2MTransportServerConfig config;
  43 +
  44 + @Autowired
41 45 @Lazy
42 46 private LwM2mClientContext clientContext;
43 47
... ... @@ -47,7 +51,7 @@ public class TbLwM2mStoreFactory {
47 51 @Bean
48 52 private CaliforniumRegistrationStore registrationStore() {
49 53 return redisConfiguration.isPresent() && useRedis ?
50   - new TbLwM2mRedisRegistrationStore(redisConfiguration.get().redisConnectionFactory()) : new InMemoryRegistrationStore();
  54 + new TbLwM2mRedisRegistrationStore(redisConfiguration.get().redisConnectionFactory()) : new InMemoryRegistrationStore(config.getCleanPeriodInSec());
51 55 }
52 56
53 57 @Bean
... ...
... ... @@ -121,9 +121,11 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter {
121 121 /** let's assume we received an ISO 8601 format date */
122 122 try {
123 123 return new Date(Long.decode(value.toString()));
124   -// DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
125   -// XMLGregorianCalendar cal = datatypeFactory.newXMLGregorianCalendar((String) value);
126   -// return cal.toGregorianCalendar().getTime();
  124 + /**
  125 + DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
  126 + XMLGregorianCalendar cal = datatypeFactory.newXMLGregorianCalendar((String) value);
  127 + return cal.toGregorianCalendar().getTime();
  128 + **/
127 129 } catch (IllegalArgumentException e) {
128 130 log.debug("Unable to convert string to date", e);
129 131 throw new CodecException("Unable to convert string (%s) to date for resource %s", value,
... ...
... ... @@ -48,6 +48,8 @@ public interface AlarmDao extends Dao<Alarm> {
48 48
49 49 PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query);
50 50
  51 + PageData<AlarmInfo> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query);
  52 +
51 53 PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
52 54 AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
53 55
... ...
... ... @@ -41,7 +41,6 @@ import org.thingsboard.server.common.data.id.TenantId;
41 41 import org.thingsboard.server.common.data.page.PageData;
42 42 import org.thingsboard.server.common.data.query.AlarmData;
43 43 import org.thingsboard.server.common.data.query.AlarmDataQuery;
44   -import org.thingsboard.server.common.data.query.DeviceTypeFilter;
45 44 import org.thingsboard.server.common.data.relation.EntityRelation;
46 45 import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
47 46 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
... ... @@ -102,6 +101,11 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
102 101
103 102 @Override
104 103 public AlarmOperationResult createOrUpdateAlarm(Alarm alarm) {
  104 + return createOrUpdateAlarm(alarm, true);
  105 + }
  106 +
  107 + @Override
  108 + public AlarmOperationResult createOrUpdateAlarm(Alarm alarm, boolean alarmCreationEnabled) {
105 109 alarmDataValidator.validate(alarm, Alarm::getTenantId);
106 110 try {
107 111 if (alarm.getStartTs() == 0L) {
... ... @@ -110,9 +114,13 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
110 114 if (alarm.getEndTs() == 0L) {
111 115 alarm.setEndTs(alarm.getStartTs());
112 116 }
  117 + alarm.setCustomerId(entityService.fetchEntityCustomerId(alarm.getTenantId(), alarm.getOriginator()));
113 118 if (alarm.getId() == null) {
114 119 Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get();
115 120 if (existing == null || existing.getStatus().isCleared()) {
  121 + if (!alarmCreationEnabled) {
  122 + throw new IllegalStateException("Alarm creation is disabled");
  123 + }
116 124 return createAlarm(alarm);
117 125 } else {
118 126 return updateAlarm(existing, alarm);
... ... @@ -158,7 +166,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
158 166 log.debug("New Alarm : {}", alarm);
159 167 Alarm saved = alarmDao.save(alarm.getTenantId(), alarm);
160 168 List<EntityId> propagatedEntitiesList = createAlarmRelations(saved);
161   - return new AlarmOperationResult(saved, true, propagatedEntitiesList);
  169 + return new AlarmOperationResult(saved, true, true, propagatedEntitiesList);
162 170 }
163 171
164 172 private List<EntityId> createAlarmRelations(Alarm alarm) throws InterruptedException, ExecutionException {
... ... @@ -292,26 +300,39 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
292 300 public ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) {
293 301 PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, query);
294 302 if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
295   - List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(alarms.getData().size());
296   - for (AlarmInfo alarmInfo : alarms.getData()) {
297   - alarmFutures.add(Futures.transform(
298   - entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {
299   - if (originatorName == null) {
300   - originatorName = "Deleted";
301   - }
302   - alarmInfo.setOriginatorName(originatorName);
303   - return alarmInfo;
304   - }, MoreExecutors.directExecutor()
305   - ));
306   - }
307   - return Futures.transform(Futures.successfulAsList(alarmFutures),
308   - alarmInfos -> new PageData<>(alarmInfos, alarms.getTotalPages(), alarms.getTotalElements(),
309   - alarms.hasNext()), MoreExecutors.directExecutor());
  303 + return fetchAlarmsOriginators(tenantId, alarms);
310 304 }
311 305 return Futures.immediateFuture(alarms);
312 306 }
313 307
314 308 @Override
  309 + public ListenableFuture<PageData<AlarmInfo>> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) {
  310 + PageData<AlarmInfo> alarms = alarmDao.findCustomerAlarms(tenantId, customerId, query);
  311 + if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
  312 + return fetchAlarmsOriginators(tenantId, alarms);
  313 + }
  314 + return Futures.immediateFuture(alarms);
  315 + }
  316 +
  317 + private ListenableFuture<PageData<AlarmInfo>> fetchAlarmsOriginators(TenantId tenantId, PageData<AlarmInfo> alarms) {
  318 + List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(alarms.getData().size());
  319 + for (AlarmInfo alarmInfo : alarms.getData()) {
  320 + alarmFutures.add(Futures.transform(
  321 + entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {
  322 + if (originatorName == null) {
  323 + originatorName = "Deleted";
  324 + }
  325 + alarmInfo.setOriginatorName(originatorName);
  326 + return alarmInfo;
  327 + }, MoreExecutors.directExecutor()
  328 + ));
  329 + }
  330 + return Futures.transform(Futures.successfulAsList(alarmFutures),
  331 + alarmInfos -> new PageData<>(alarmInfos, alarms.getTotalPages(), alarms.getTotalElements(),
  332 + alarms.hasNext()), MoreExecutors.directExecutor());
  333 + }
  334 +
  335 + @Override
315 336 public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
316 337 AlarmStatus alarmStatus) {
317 338 Set<AlarmStatus> statusList = null;
... ... @@ -342,6 +363,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
342 363 existing.setStatus(alarm.getStatus());
343 364 existing.setSeverity(alarm.getSeverity());
344 365 existing.setDetails(alarm.getDetails());
  366 + existing.setCustomerId(alarm.getCustomerId());
345 367 existing.setPropagate(existing.isPropagate() || alarm.isPropagate());
346 368 List<String> existingPropagateRelationTypes = existing.getPropagateRelationTypes();
347 369 List<String> newRelationTypes = alarm.getPropagateRelationTypes();
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.dao.asset;
17 17
18 18
19   -import com.google.common.base.Function;
20 19 import com.google.common.util.concurrent.Futures;
21 20 import com.google.common.util.concurrent.ListenableFuture;
22 21 import com.google.common.util.concurrent.MoreExecutors;
... ... @@ -46,11 +45,10 @@ import org.thingsboard.server.common.data.id.EntityId;
46 45 import org.thingsboard.server.common.data.id.TenantId;
47 46 import org.thingsboard.server.common.data.page.PageData;
48 47 import org.thingsboard.server.common.data.page.PageLink;
49   -import org.thingsboard.server.common.data.page.TimePageLink;
50 48 import org.thingsboard.server.common.data.relation.EntityRelation;
51 49 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
52   -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
53 50 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
  51 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
54 52 import org.thingsboard.server.dao.customer.CustomerDao;
55 53 import org.thingsboard.server.dao.entity.AbstractEntityService;
56 54 import org.thingsboard.server.dao.exception.DataValidationException;
... ... @@ -59,8 +57,8 @@ import org.thingsboard.server.dao.service.PaginatedRemover;
59 57 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
60 58 import org.thingsboard.server.dao.tenant.TenantDao;
61 59
62   -import javax.annotation.Nullable;
63 60 import java.util.ArrayList;
  61 +import java.util.Arrays;
64 62 import java.util.Collections;
65 63 import java.util.Comparator;
66 64 import java.util.List;
... ... @@ -180,15 +178,16 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
180 178 throw new RuntimeException("Exception while finding entity views for assetId [" + assetId + "]", e);
181 179 }
182 180
183   - List<Object> list = new ArrayList<>();
184   - list.add(asset.getTenantId());
185   - list.add(asset.getName());
186   - Cache cache = cacheManager.getCache(ASSET_CACHE);
187   - cache.evict(list);
  181 + removeAssetFromCacheByName(asset.getTenantId(), asset.getName());
188 182
189 183 assetDao.removeById(tenantId, assetId.getId());
190 184 }
191 185
  186 + private void removeAssetFromCacheByName(TenantId tenantId, String name) {
  187 + Cache cache = cacheManager.getCache(ASSET_CACHE);
  188 + cache.evict(Arrays.asList(tenantId, name));
  189 + }
  190 +
192 191 @Override
193 192 public PageData<Asset> findAssetsByTenantId(TenantId tenantId, PageLink pageLink) {
194 193 log.trace("Executing findAssetsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
... ... @@ -397,6 +396,13 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
397 396
398 397 @Override
399 398 protected void validateUpdate(TenantId tenantId, Asset asset) {
  399 + Asset old = assetDao.findById(asset.getTenantId(), asset.getId().getId());
  400 + if (old == null) {
  401 + throw new DataValidationException("Can't update non existing asset!");
  402 + }
  403 + if (!old.getName().equals(asset.getName())) {
  404 + removeAssetFromCacheByName(tenantId, old.getName());
  405 + }
400 406 }
401 407
402 408 @Override
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.dashboard;
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.apache.commons.lang3.StringUtils;
  21 +import org.hibernate.exception.ConstraintViolationException;
21 22 import org.springframework.beans.factory.annotation.Autowired;
22 23 import org.springframework.context.annotation.Lazy;
23 24 import org.springframework.stereotype.Service;
... ... @@ -166,7 +167,16 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
166 167 log.trace("Executing deleteDashboard [{}]", dashboardId);
167 168 Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
168 169 deleteEntityRelations(tenantId, dashboardId);
169   - dashboardDao.removeById(tenantId, dashboardId.getId());
  170 + try {
  171 + dashboardDao.removeById(tenantId, dashboardId.getId());
  172 + } catch (Exception t) {
  173 + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
  174 + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_default_dashboard_device_profile")) {
  175 + throw new DataValidationException("The dashboard referenced by the device profiles cannot be deleted!");
  176 + } else {
  177 + throw t;
  178 + }
  179 + }
170 180 }
171 181
172 182 @Override
... ...
... ... @@ -36,6 +36,7 @@ import org.springframework.cache.CacheManager;
36 36 import org.springframework.cache.annotation.Cacheable;
37 37 import org.springframework.stereotype.Service;
38 38 import org.springframework.util.CollectionUtils;
  39 +import org.thingsboard.server.common.data.DashboardInfo;
39 40 import org.thingsboard.server.common.data.Device;
40 41 import org.thingsboard.server.common.data.DeviceProfile;
41 42 import org.thingsboard.server.common.data.DeviceProfileInfo;
... ... @@ -61,9 +62,12 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
61 62 import org.thingsboard.server.common.data.id.TenantId;
62 63 import org.thingsboard.server.common.data.page.PageData;
63 64 import org.thingsboard.server.common.data.page.PageLink;
  65 +import org.thingsboard.server.common.data.rule.RuleChain;
  66 +import org.thingsboard.server.dao.dashboard.DashboardService;
64 67 import org.thingsboard.server.dao.entity.AbstractEntityService;
65 68 import org.thingsboard.server.dao.exception.DataValidationException;
66 69 import org.thingsboard.server.dao.firmware.FirmwareService;
  70 +import org.thingsboard.server.dao.rule.RuleChainService;
67 71 import org.thingsboard.server.dao.service.DataValidator;
68 72 import org.thingsboard.server.dao.service.PaginatedRemover;
69 73 import org.thingsboard.server.dao.service.Validator;
... ... @@ -117,6 +121,12 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
117 121 @Autowired
118 122 private FirmwareService firmwareService;
119 123
  124 + @Autowired
  125 + private RuleChainService ruleChainService;
  126 +
  127 + @Autowired
  128 + private DashboardService dashboardService;
  129 +
120 130 private final Lock findOrCreateLock = new ReentrantLock();
121 131
122 132 @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}")
... ... @@ -336,7 +346,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
336 346 }
337 347
338 348 private DataValidator<DeviceProfile> deviceProfileValidator =
339   - new DataValidator<DeviceProfile>() {
  349 + new DataValidator<>() {
340 350 @Override
341 351 protected void validateDataImpl(TenantId tenantId, DeviceProfile deviceProfile) {
342 352 if (StringUtils.isEmpty(deviceProfile.getName())) {
... ... @@ -402,6 +412,20 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
402 412 }
403 413 }
404 414
  415 + if (deviceProfile.getDefaultRuleChainId() != null) {
  416 + RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, deviceProfile.getDefaultRuleChainId());
  417 + if (ruleChain == null) {
  418 + throw new DataValidationException("Can't assign non-existent rule chain!");
  419 + }
  420 + }
  421 +
  422 + if (deviceProfile.getDefaultDashboardId() != null) {
  423 + DashboardInfo dashboard = dashboardService.findDashboardInfoById(tenantId, deviceProfile.getDefaultDashboardId());
  424 + if (dashboard == null) {
  425 + throw new DataValidationException("Can't assign non-existent dashboard!");
  426 + }
  427 + }
  428 +
405 429 if (deviceProfile.getFirmwareId() != null) {
406 430 Firmware firmware = firmwareService.findFirmwareById(tenantId, deviceProfile.getFirmwareId());
407 431 if (firmware == null) {
... ...
... ... @@ -84,6 +84,7 @@ import org.thingsboard.common.util.JacksonUtil;
84 84
85 85 import javax.annotation.Nullable;
86 86 import java.util.ArrayList;
  87 +import java.util.Arrays;
87 88 import java.util.Collections;
88 89 import java.util.Comparator;
89 90 import java.util.List;
... ... @@ -245,7 +246,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
245 246 ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
246 247 if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_name_unq_key")) {
247 248 // remove device from cache in case null value cached in the distributed redis.
248   - removeDeviceFromCache(device.getTenantId(), device.getName());
  249 + removeDeviceFromCacheByName(device.getTenantId(), device.getName());
249 250 throw new DataValidationException("Device with such name already exists!");
250 251 } else {
251 252 throw t;
... ... @@ -322,17 +323,14 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
322 323 }
323 324 deleteEntityRelations(tenantId, deviceId);
324 325
325   - removeDeviceFromCache(tenantId, device.getName());
  326 + removeDeviceFromCacheByName(tenantId, device.getName());
326 327
327 328 deviceDao.removeById(tenantId, deviceId.getId());
328 329 }
329 330
330   - private void removeDeviceFromCache(TenantId tenantId, String name) {
331   - List<Object> list = new ArrayList<>();
332   - list.add(tenantId);
333   - list.add(name);
  331 + private void removeDeviceFromCacheByName(TenantId tenantId, String name) {
334 332 Cache cache = cacheManager.getCache(DEVICE_CACHE);
335   - cache.evict(list);
  333 + cache.evict(Arrays.asList(tenantId, name));
336 334 }
337 335
338 336 @Override
... ... @@ -671,6 +669,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
671 669 if (old == null) {
672 670 throw new DataValidationException("Can't update non existing device!");
673 671 }
  672 + if (!old.getName().equals(device.getName())) {
  673 + removeDeviceFromCacheByName(tenantId, old.getName());
  674 + }
674 675 }
675 676
676 677 @Override
... ...
... ... @@ -58,7 +58,6 @@ import org.thingsboard.server.common.data.id.TenantId;
58 58 import org.thingsboard.server.common.data.id.UserId;
59 59 import org.thingsboard.server.common.data.page.PageData;
60 60 import org.thingsboard.server.common.data.page.PageLink;
61   -import org.thingsboard.server.common.data.page.TimePageLink;
62 61 import org.thingsboard.server.common.data.relation.EntityRelation;
63 62 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
64 63 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
... ... @@ -80,6 +79,7 @@ import javax.annotation.PostConstruct;
80 79 import java.net.InetSocketAddress;
81 80 import java.net.Proxy;
82 81 import java.util.ArrayList;
  82 +import java.util.Arrays;
83 83 import java.util.Collections;
84 84 import java.util.Comparator;
85 85 import java.util.HashMap;
... ... @@ -222,17 +222,18 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
222 222
223 223 Edge edge = edgeDao.findById(tenantId, edgeId.getId());
224 224
225   - List<Object> list = new ArrayList<>();
226   - list.add(edge.getTenantId());
227   - list.add(edge.getName());
228   - Cache cache = cacheManager.getCache(EDGE_CACHE);
229   - cache.evict(list);
230   -
231 225 deleteEntityRelations(tenantId, edgeId);
232 226
  227 + removeEdgeFromCacheByName(edge.getTenantId(), edge.getName());
  228 +
233 229 edgeDao.removeById(tenantId, edgeId.getId());
234 230 }
235 231
  232 + private void removeEdgeFromCacheByName(TenantId tenantId, String name) {
  233 + Cache cache = cacheManager.getCache(EDGE_CACHE);
  234 + cache.evict(Arrays.asList(tenantId, name));
  235 + }
  236 +
236 237 @Override
237 238 public PageData<Edge> findEdgesByTenantId(TenantId tenantId, PageLink pageLink) {
238 239 log.trace("Executing findEdgesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
... ... @@ -423,6 +424,10 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
423 424
424 425 @Override
425 426 protected void validateUpdate(TenantId tenantId, Edge edge) {
  427 + Edge old = edgeDao.findById(edge.getTenantId(), edge.getId().getId());
  428 + if (!old.getName().equals(edge.getName())) {
  429 + removeEdgeFromCacheByName(tenantId, old.getName());
  430 + }
426 431 }
427 432
428 433 @Override
... ...