Commit 4843baf7b96cbf79ed512de4aa521ad9c0f85609

Authored by zbeacon
2 parents b45d514b 5ca0e05a

Merge branch 'master' of https://github.com/thingsboard/thingsboard into feature…

…/firmware-checksum-autogenerating
Showing 84 changed files with 1742 additions and 535 deletions

Too many changes to show.

To preserve performance only 84 of 164 files are displayed.

@@ -63,6 +63,8 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -63,6 +63,8 @@ CREATE TABLE IF NOT EXISTS firmware (
63 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, 63 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
64 created_time bigint NOT NULL, 64 created_time bigint NOT NULL,
65 tenant_id uuid NOT NULL, 65 tenant_id uuid NOT NULL,
  66 + device_profile_id uuid,
  67 + type varchar(32) NOT NULL,
66 title varchar(255) NOT NULL, 68 title varchar(255) NOT NULL,
67 version varchar(255) NOT NULL, 69 version varchar(255) NOT NULL,
68 file_name varchar(255), 70 file_name varchar(255),
@@ -77,10 +79,12 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -77,10 +79,12 @@ CREATE TABLE IF NOT EXISTS firmware (
77 ); 79 );
78 80
79 ALTER TABLE device_profile 81 ALTER TABLE device_profile
80 - ADD COLUMN IF NOT EXISTS firmware_id uuid; 82 + ADD COLUMN IF NOT EXISTS firmware_id uuid,
  83 + ADD COLUMN IF NOT EXISTS software_id uuid;
81 84
82 ALTER TABLE device 85 ALTER TABLE device
83 - ADD COLUMN IF NOT EXISTS firmware_id uuid; 86 + ADD COLUMN IF NOT EXISTS firmware_id uuid,
  87 + ADD COLUMN IF NOT EXISTS software_id uuid;
84 88
85 DO $$ 89 DO $$
86 BEGIN 90 BEGIN
@@ -90,11 +94,23 @@ DO $$ @@ -90,11 +94,23 @@ DO $$
90 FOREIGN KEY (firmware_id) REFERENCES firmware(id); 94 FOREIGN KEY (firmware_id) REFERENCES firmware(id);
91 END IF; 95 END IF;
92 96
  97 + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN
  98 + ALTER TABLE device_profile
  99 + ADD CONSTRAINT fk_software_device_profile
  100 + FOREIGN KEY (firmware_id) REFERENCES firmware(id);
  101 + END IF;
  102 +
93 IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN 103 IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN
94 ALTER TABLE device 104 ALTER TABLE device
95 ADD CONSTRAINT fk_firmware_device 105 ADD CONSTRAINT fk_firmware_device
96 FOREIGN KEY (firmware_id) REFERENCES firmware(id); 106 FOREIGN KEY (firmware_id) REFERENCES firmware(id);
97 END IF; 107 END IF;
  108 +
  109 + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN
  110 + ALTER TABLE device
  111 + ADD CONSTRAINT fk_software_device
  112 + FOREIGN KEY (firmware_id) REFERENCES firmware(id);
  113 + END IF;
98 END; 114 END;
99 $$; 115 $$;
100 116
@@ -22,6 +22,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -22,6 +22,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
22 import org.springframework.context.event.EventListener; 22 import org.springframework.context.event.EventListener;
23 import org.springframework.core.annotation.Order; 23 import org.springframework.core.annotation.Order;
24 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
  25 +import org.thingsboard.common.util.ThingsBoardExecutors;
25 import org.thingsboard.common.util.ThingsBoardThreadFactory; 26 import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 import org.thingsboard.server.actors.ActorSystemContext; 27 import org.thingsboard.server.actors.ActorSystemContext;
27 import org.thingsboard.server.actors.DefaultTbActorSystem; 28 import org.thingsboard.server.actors.DefaultTbActorSystem;
@@ -108,7 +109,7 @@ public class DefaultActorService extends TbApplicationEventListener<PartitionCha @@ -108,7 +109,7 @@ public class DefaultActorService extends TbApplicationEventListener<PartitionCha
108 if (poolSize == 1) { 109 if (poolSize == 1) {
109 return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName)); 110 return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName));
110 } else { 111 } else {
111 - return Executors.newWorkStealingPool(poolSize); 112 + return ThingsBoardExecutors.newWorkStealingPool(poolSize, dispatcherName);
112 } 113 }
113 } 114 }
114 115
@@ -146,12 +146,16 @@ public class DeviceProfileController extends BaseController { @@ -146,12 +146,16 @@ public class DeviceProfileController extends BaseController {
146 checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); 146 checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
147 147
148 boolean isFirmwareChanged = false; 148 boolean isFirmwareChanged = false;
  149 + boolean isSoftwareChanged = false;
149 150
150 if (!created) { 151 if (!created) {
151 DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId()); 152 DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId());
152 if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) { 153 if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
153 isFirmwareChanged = true; 154 isFirmwareChanged = true;
154 } 155 }
  156 + if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) {
  157 + isSoftwareChanged = true;
  158 + }
155 } 159 }
156 160
157 DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); 161 DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
@@ -164,9 +168,8 @@ public class DeviceProfileController extends BaseController { @@ -164,9 +168,8 @@ public class DeviceProfileController extends BaseController {
164 null, 168 null,
165 created ? ActionType.ADDED : ActionType.UPDATED, null); 169 created ? ActionType.ADDED : ActionType.UPDATED, null);
166 170
167 - if (isFirmwareChanged) {  
168 - firmwareStateService.update(savedDeviceProfile);  
169 - } 171 + firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
  172 +
170 sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(), 173 sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
171 deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); 174 deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
172 return savedDeviceProfile; 175 return savedDeviceProfile;
@@ -35,6 +35,8 @@ import org.thingsboard.server.common.data.FirmwareInfo; @@ -35,6 +35,8 @@ import org.thingsboard.server.common.data.FirmwareInfo;
35 import org.thingsboard.server.common.data.audit.ActionType; 35 import org.thingsboard.server.common.data.audit.ActionType;
36 import org.thingsboard.server.common.data.exception.ThingsboardException; 36 import org.thingsboard.server.common.data.exception.ThingsboardException;
37 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; 37 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
  38 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  39 +import org.thingsboard.server.common.data.id.DeviceProfileId;
38 import org.thingsboard.server.common.data.id.FirmwareId; 40 import org.thingsboard.server.common.data.id.FirmwareId;
39 import org.thingsboard.server.common.data.page.PageData; 41 import org.thingsboard.server.common.data.page.PageData;
40 import org.thingsboard.server.common.data.page.PageLink; 42 import org.thingsboard.server.common.data.page.PageLink;
@@ -135,6 +137,8 @@ public class FirmwareController extends BaseController { @@ -135,6 +137,8 @@ public class FirmwareController extends BaseController {
135 Firmware firmware = new Firmware(firmwareId); 137 Firmware firmware = new Firmware(firmwareId);
136 firmware.setCreatedTime(info.getCreatedTime()); 138 firmware.setCreatedTime(info.getCreatedTime());
137 firmware.setTenantId(getTenantId()); 139 firmware.setTenantId(getTenantId());
  140 + firmware.setDeviceProfileId(info.getDeviceProfileId());
  141 + firmware.setType(info.getType());
138 firmware.setTitle(info.getTitle()); 142 firmware.setTitle(info.getTitle());
139 firmware.setVersion(info.getVersion()); 143 firmware.setVersion(info.getVersion());
140 firmware.setAdditionalInfo(info.getAdditionalInfo()); 144 firmware.setAdditionalInfo(info.getAdditionalInfo());
@@ -178,17 +182,22 @@ public class FirmwareController extends BaseController { @@ -178,17 +182,22 @@ public class FirmwareController extends BaseController {
178 } 182 }
179 183
180 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 184 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
181 - @RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET) 185 + @RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET)
182 @ResponseBody 186 @ResponseBody
183 - public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData, 187 + public PageData<FirmwareInfo> getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId,
  188 + @PathVariable("type") String strType,
  189 + @PathVariable("hasData") boolean hasData,
184 @RequestParam int pageSize, 190 @RequestParam int pageSize,
185 @RequestParam int page, 191 @RequestParam int page,
186 @RequestParam(required = false) String textSearch, 192 @RequestParam(required = false) String textSearch,
187 @RequestParam(required = false) String sortProperty, 193 @RequestParam(required = false) String sortProperty,
188 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 194 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  195 + checkParameter("deviceProfileId", strDeviceProfileId);
  196 + checkParameter("type", strType);
189 try { 197 try {
190 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 198 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
191 - return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndHasData(getTenantId(), hasData, pageLink)); 199 + return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(),
  200 + new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink));
192 } catch (Exception e) { 201 } catch (Exception e) {
193 throw handleException(e); 202 throw handleException(e);
194 } 203 }
@@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestParam;
32 import org.springframework.web.bind.annotation.ResponseBody; 32 import org.springframework.web.bind.annotation.ResponseBody;
33 import org.springframework.web.bind.annotation.ResponseStatus; 33 import org.springframework.web.bind.annotation.ResponseStatus;
34 import org.springframework.web.bind.annotation.RestController; 34 import org.springframework.web.bind.annotation.RestController;
  35 +import org.thingsboard.common.util.JacksonUtil;
35 import org.thingsboard.rule.engine.api.MailService; 36 import org.thingsboard.rule.engine.api.MailService;
36 import org.thingsboard.server.common.data.EntityType; 37 import org.thingsboard.server.common.data.EntityType;
37 import org.thingsboard.server.common.data.User; 38 import org.thingsboard.server.common.data.User;
@@ -89,13 +90,14 @@ public class UserController extends BaseController { @@ -89,13 +90,14 @@ public class UserController extends BaseController {
89 try { 90 try {
90 UserId userId = new UserId(toUUID(strUserId)); 91 UserId userId = new UserId(toUUID(strUserId));
91 User user = checkUserId(userId, Operation.READ); 92 User user = checkUserId(userId, Operation.READ);
92 - if(!user.getAdditionalInfo().isNull()) {  
93 - processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD);  
94 - processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD);  
95 - }  
96 - UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());  
97 - if(userCredentials.isEnabled()) {  
98 - addUserCredentialsEnabled((ObjectNode) user.getAdditionalInfo()); 93 + if(user.getAdditionalInfo().isObject()) {
  94 + ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo();
  95 + processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD);
  96 + processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD);
  97 + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
  98 + if(userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) {
  99 + additionalInfo.put("userCredentialsEnabled", true);
  100 + }
99 } 101 }
100 return user; 102 return user;
101 } catch (Exception e) { 103 } catch (Exception e) {
@@ -103,14 +105,6 @@ public class UserController extends BaseController { @@ -103,14 +105,6 @@ public class UserController extends BaseController {
103 } 105 }
104 } 106 }
105 107
106 - private void addUserCredentialsEnabled(ObjectNode additionalInfo) {  
107 - if(!additionalInfo.isNull()) {  
108 - if(!additionalInfo.has("userCredentialsEnabled")) {  
109 - additionalInfo.put("userCredentialsEnabled", true);  
110 - }  
111 - }  
112 - }  
113 -  
114 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 108 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
115 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET) 109 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET)
116 @ResponseBody 110 @ResponseBody
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.beans.factory.annotation.Value; 23 import org.springframework.beans.factory.annotation.Value;
24 import org.springframework.context.annotation.Lazy; 24 import org.springframework.context.annotation.Lazy;
25 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 import org.thingsboard.rule.engine.api.MailService; 27 import org.thingsboard.rule.engine.api.MailService;
27 import org.thingsboard.server.common.data.ApiFeature; 28 import org.thingsboard.server.common.data.ApiFeature;
28 import org.thingsboard.server.common.data.ApiUsageRecordKey; 29 import org.thingsboard.server.common.data.ApiUsageRecordKey;
@@ -146,7 +147,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -146,7 +147,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
146 this.scheduler = scheduler; 147 this.scheduler = scheduler;
147 this.tenantProfileCache = tenantProfileCache; 148 this.tenantProfileCache = tenantProfileCache;
148 this.mailService = mailService; 149 this.mailService = mailService;
149 - this.mailExecutor = Executors.newSingleThreadExecutor(); 150 + this.mailExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("api-usage-svc-mail"));
150 } 151 }
151 152
152 @PostConstruct 153 @PostConstruct
@@ -19,13 +19,17 @@ import com.google.common.util.concurrent.FutureCallback; @@ -19,13 +19,17 @@ import com.google.common.util.concurrent.FutureCallback;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
21 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; 21 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
  22 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
22 import org.thingsboard.server.common.data.DataConstants; 23 import org.thingsboard.server.common.data.DataConstants;
23 import org.thingsboard.server.common.data.Device; 24 import org.thingsboard.server.common.data.Device;
24 import org.thingsboard.server.common.data.DeviceProfile; 25 import org.thingsboard.server.common.data.DeviceProfile;
25 import org.thingsboard.server.common.data.FirmwareInfo; 26 import org.thingsboard.server.common.data.FirmwareInfo;
  27 +import org.thingsboard.server.common.data.firmware.FirmwareUtil;
  28 +import org.thingsboard.server.common.data.firmware.FirmwareType;
26 import org.thingsboard.server.common.data.id.DeviceId; 29 import org.thingsboard.server.common.data.id.DeviceId;
27 import org.thingsboard.server.common.data.id.FirmwareId; 30 import org.thingsboard.server.common.data.id.FirmwareId;
28 import org.thingsboard.server.common.data.id.TenantId; 31 import org.thingsboard.server.common.data.id.TenantId;
  32 +import org.thingsboard.server.common.data.kv.AttributeKey;
29 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 33 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
30 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; 34 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
31 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 35 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@@ -43,37 +47,49 @@ import org.thingsboard.server.queue.TbQueueProducer; @@ -43,37 +47,49 @@ import org.thingsboard.server.queue.TbQueueProducer;
43 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 47 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
44 import org.thingsboard.server.queue.provider.TbCoreQueueFactory; 48 import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
45 import org.thingsboard.server.queue.util.TbCoreComponent; 49 import org.thingsboard.server.queue.util.TbCoreComponent;
  50 +import org.thingsboard.server.service.queue.TbClusterService;
46 51
47 import javax.annotation.Nullable; 52 import javax.annotation.Nullable;
48 import java.util.ArrayList; 53 import java.util.ArrayList;
49 -import java.util.Arrays;  
50 import java.util.Collections; 54 import java.util.Collections;
  55 +import java.util.HashSet;
51 import java.util.List; 56 import java.util.List;
  57 +import java.util.Set;
52 import java.util.UUID; 58 import java.util.UUID;
53 import java.util.function.Consumer; 59 import java.util.function.Consumer;
54 -  
55 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM;  
56 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM;  
57 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE;  
58 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE;  
59 -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION; 60 +import java.util.function.Function;
  61 +
  62 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM;
  63 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM;
  64 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE;
  65 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE;
  66 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE;
  67 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS;
  68 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION;
  69 +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey;
  70 +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey;
  71 +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;
60 74
61 @Slf4j 75 @Slf4j
62 @Service 76 @Service
63 @TbCoreComponent 77 @TbCoreComponent
64 public class DefaultFirmwareStateService implements FirmwareStateService { 78 public class DefaultFirmwareStateService implements FirmwareStateService {
65 79
  80 + private final TbClusterService tbClusterService;
66 private final FirmwareService firmwareService; 81 private final FirmwareService firmwareService;
67 private final DeviceService deviceService; 82 private final DeviceService deviceService;
68 private final DeviceProfileService deviceProfileService; 83 private final DeviceProfileService deviceProfileService;
69 private final RuleEngineTelemetryService telemetryService; 84 private final RuleEngineTelemetryService telemetryService;
70 private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer; 85 private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer;
71 86
72 - public DefaultFirmwareStateService(FirmwareService firmwareService, 87 + public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService,
73 DeviceService deviceService, 88 DeviceService deviceService,
74 DeviceProfileService deviceProfileService, 89 DeviceProfileService deviceProfileService,
75 RuleEngineTelemetryService telemetryService, 90 RuleEngineTelemetryService telemetryService,
76 TbCoreQueueFactory coreQueueFactory) { 91 TbCoreQueueFactory coreQueueFactory) {
  92 + this.tbClusterService = tbClusterService;
77 this.firmwareService = firmwareService; 93 this.firmwareService = firmwareService;
78 this.deviceService = deviceService; 94 this.deviceService = deviceService;
79 this.deviceProfileService = deviceProfileService; 95 this.deviceProfileService = deviceProfileService;
@@ -83,6 +99,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -83,6 +99,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
83 99
84 @Override 100 @Override
85 public void update(Device device, Device oldDevice) { 101 public void update(Device device, Device oldDevice) {
  102 + updateFirmware(device, oldDevice);
  103 + updateSoftware(device, oldDevice);
  104 + }
  105 +
  106 + private void updateFirmware(Device device, Device oldDevice) {
86 FirmwareId newFirmwareId = device.getFirmwareId(); 107 FirmwareId newFirmwareId = device.getFirmwareId();
87 if (newFirmwareId == null) { 108 if (newFirmwareId == null) {
88 DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); 109 DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
@@ -97,35 +118,84 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -97,35 +118,84 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
97 } 118 }
98 if (!newFirmwareId.equals(oldFirmwareId)) { 119 if (!newFirmwareId.equals(oldFirmwareId)) {
99 // Device was updated and new firmware is different from previous firmware. 120 // Device was updated and new firmware is different from previous firmware.
100 - send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis()); 121 + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE);
101 } 122 }
102 } else { 123 } else {
103 // Device was updated and new firmware is not set. 124 // Device was updated and new firmware is not set.
104 - remove(device); 125 + remove(device, FIRMWARE);
105 } 126 }
106 } else if (newFirmwareId != null) { 127 } else if (newFirmwareId != null) {
107 // Device was created and firmware is defined. 128 // Device was created and firmware is defined.
108 - send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis()); 129 + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE);
  130 + }
  131 + }
  132 +
  133 + private void updateSoftware(Device device, Device oldDevice) {
  134 + FirmwareId newSoftwareId = device.getSoftwareId();
  135 + if (newSoftwareId == null) {
  136 + DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
  137 + newSoftwareId = newDeviceProfile.getSoftwareId();
  138 + }
  139 + if (oldDevice != null) {
  140 + if (newSoftwareId != null) {
  141 + FirmwareId oldSoftwareId = oldDevice.getSoftwareId();
  142 + if (oldSoftwareId == null) {
  143 + DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId());
  144 + oldSoftwareId = oldDeviceProfile.getSoftwareId();
  145 + }
  146 + if (!newSoftwareId.equals(oldSoftwareId)) {
  147 + // Device was updated and new firmware is different from previous firmware.
  148 + send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE);
  149 + }
  150 + } else {
  151 + // Device was updated and new firmware is not set.
  152 + remove(device, SOFTWARE);
  153 + }
  154 + } else if (newSoftwareId != null) {
  155 + // Device was created and firmware is defined.
  156 + send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE);
109 } 157 }
110 } 158 }
111 159
112 @Override 160 @Override
113 - public void update(DeviceProfile deviceProfile) { 161 + public void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged) {
114 TenantId tenantId = deviceProfile.getTenantId(); 162 TenantId tenantId = deviceProfile.getTenantId();
115 163
  164 + if (isFirmwareChanged) {
  165 + update(tenantId, deviceProfile, FIRMWARE);
  166 + }
  167 + if (isSoftwareChanged) {
  168 + update(tenantId, deviceProfile, SOFTWARE);
  169 + }
  170 + }
  171 +
  172 + private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) {
  173 + Function<PageLink, PageData<Device>> getDevicesFunction;
116 Consumer<Device> updateConsumer; 174 Consumer<Device> updateConsumer;
  175 +
  176 + switch (firmwareType) {
  177 + case FIRMWARE:
  178 + getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl);
  179 + break;
  180 + case SOFTWARE:
  181 + getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl);
  182 + break;
  183 + default:
  184 + log.warn("Unsupported firmware type: [{}]", firmwareType);
  185 + return;
  186 + }
  187 +
117 if (deviceProfile.getFirmwareId() != null) { 188 if (deviceProfile.getFirmwareId() != null) {
118 long ts = System.currentTimeMillis(); 189 long ts = System.currentTimeMillis();
119 - updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts); 190 + updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType);
120 } else { 191 } else {
121 - updateConsumer = this::remove; 192 + updateConsumer = d -> remove(d, firmwareType);
122 } 193 }
123 194
124 PageLink pageLink = new PageLink(100); 195 PageLink pageLink = new PageLink(100);
125 PageData<Device> pageData; 196 PageData<Device> pageData;
126 do { 197 do {
127 - pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pageLink);  
128 - 198 + pageData = getDevicesFunction.apply(pageLink);
129 pageData.getData().forEach(updateConsumer); 199 pageData.getData().forEach(updateConsumer);
130 200
131 if (pageData.hasNext()) { 201 if (pageData.hasNext()) {
@@ -140,16 +210,17 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -140,16 +210,17 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
140 FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB())); 210 FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()));
141 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); 211 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
142 TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); 212 TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB()));
  213 + FirmwareType firmwareType = FirmwareType.valueOf(msg.getType());
143 long ts = msg.getTs(); 214 long ts = msg.getTs();
144 215
145 Device device = deviceService.findDeviceById(tenantId, deviceId); 216 Device device = deviceService.findDeviceById(tenantId, deviceId);
146 if (device == null) { 217 if (device == null) {
147 log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId); 218 log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId);
148 } else { 219 } else {
149 - FirmwareId currentFirmwareId = device.getFirmwareId();  
150 - 220 + FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
151 if (currentFirmwareId == null) { 221 if (currentFirmwareId == null) {
152 - currentFirmwareId = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()).getFirmwareId(); 222 + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId());
  223 + currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
153 } 224 }
154 225
155 if (targetFirmwareId.equals(currentFirmwareId)) { 226 if (targetFirmwareId.equals(currentFirmwareId)) {
@@ -162,7 +233,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -162,7 +233,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
162 return isSuccess; 233 return isSuccess;
163 } 234 }
164 235
165 - private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts) { 236 + private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) {
166 ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder() 237 ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder()
167 .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) 238 .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
168 .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) 239 .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
@@ -170,6 +241,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -170,6 +241,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
170 .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) 241 .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
171 .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()) 242 .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits())
172 .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()) 243 .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits())
  244 + .setType(firmwareType.name())
173 .setTs(ts) 245 .setTs(ts)
174 .build(); 246 .build();
175 247
@@ -183,10 +255,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -183,10 +255,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
183 fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); 255 fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null);
184 256
185 List<TsKvEntry> telemetry = new ArrayList<>(); 257 List<TsKvEntry> telemetry = new ArrayList<>();
186 - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle())));  
187 - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion())));  
188 - telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(DataConstants.TARGET_FIRMWARE_TS, ts)));  
189 - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name()))); 258 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle())));
  259 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion())));
  260 + telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts)));
  261 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name())));
190 262
191 telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { 263 telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
192 @Override 264 @Override
@@ -206,7 +278,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -206,7 +278,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
206 TenantId tenantId = device.getTenantId(); 278 TenantId tenantId = device.getTenantId();
207 DeviceId deviceId = device.getId(); 279 DeviceId deviceId = device.getId();
208 280
209 - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.INITIATED.name())); 281 + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name()));
210 282
211 telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { 283 telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() {
212 @Override 284 @Override
@@ -221,13 +293,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -221,13 +293,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
221 }); 293 });
222 294
223 List<AttributeKvEntry> attributes = new ArrayList<>(); 295 List<AttributeKvEntry> attributes = new ArrayList<>();
  296 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle())));
  297 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion())));
  298 + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize())));
  299 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm().name())));
  300 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum())));
224 301
225 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle())));  
226 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion())));  
227 -  
228 - attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, firmware.getDataSize())));  
229 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm().name())));  
230 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum())));  
231 telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { 302 telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() {
232 @Override 303 @Override
233 public void onSuccess(@Nullable Void tmp) { 304 public void onSuccess(@Nullable Void tmp) {
@@ -241,13 +312,15 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @@ -241,13 +312,15 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
241 }); 312 });
242 } 313 }
243 314
244 - private void remove(Device device) {  
245 - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE,  
246 - Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM), 315 + private void remove(Device device, FirmwareType firmwareType) {
  316 + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType),
247 new FutureCallback<>() { 317 new FutureCallback<>() {
248 @Override 318 @Override
249 public void onSuccess(@Nullable Void tmp) { 319 public void onSuccess(@Nullable Void tmp) {
250 log.trace("[{}] Success remove target firmware attributes!", device.getId()); 320 log.trace("[{}] Success remove target firmware attributes!", device.getId());
  321 + Set<AttributeKey> keysToNotify = new HashSet<>();
  322 + FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key)));
  323 + tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null);
251 } 324 }
252 325
253 @Override 326 @Override
@@ -23,7 +23,7 @@ public interface FirmwareStateService { @@ -23,7 +23,7 @@ public interface FirmwareStateService {
23 23
24 void update(Device device, Device oldDevice); 24 void update(Device device, Device oldDevice);
25 25
26 - void update(DeviceProfile deviceProfile); 26 + void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged);
27 27
28 boolean process(ToFirmwareStateServiceMsg msg); 28 boolean process(ToFirmwareStateServiceMsg msg);
29 29
@@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
20 import org.springframework.beans.factory.annotation.Value; 20 import org.springframework.beans.factory.annotation.Value;
21 import org.springframework.scheduling.annotation.Scheduled; 21 import org.springframework.scheduling.annotation.Scheduled;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
  23 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 import org.thingsboard.rule.engine.api.RpcError; 24 import org.thingsboard.rule.engine.api.RpcError;
24 import org.thingsboard.server.actors.ActorSystemContext; 25 import org.thingsboard.server.actors.ActorSystemContext;
25 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
@@ -127,7 +128,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -127,7 +128,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
127 consumers.computeIfAbsent(configuration.getName(), queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration)); 128 consumers.computeIfAbsent(configuration.getName(), queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration));
128 consumerStats.put(configuration.getName(), new TbRuleEngineConsumerStats(configuration.getName(), statsFactory)); 129 consumerStats.put(configuration.getName(), new TbRuleEngineConsumerStats(configuration.getName(), statsFactory));
129 } 130 }
130 - submitExecutor = Executors.newSingleThreadExecutor(); 131 + submitExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-service-submit-executor"));
131 } 132 }
132 133
133 @PreDestroy 134 @PreDestroy
@@ -160,6 +161,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -160,6 +161,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
160 161
161 private void launchConsumer(TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer, TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats) { 162 private void launchConsumer(TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer, TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats) {
162 consumersExecutor.execute(() -> { 163 consumersExecutor.execute(() -> {
  164 + Thread.currentThread().setName("" + Thread.currentThread().getName() + "-" + configuration.getName());
163 while (!stopped) { 165 while (!stopped) {
164 try { 166 try {
165 List<TbProtoQueueMsg<ToRuleEngineMsg>> msgs = consumer.poll(pollDuration); 167 List<TbProtoQueueMsg<ToRuleEngineMsg>> msgs = consumer.poll(pollDuration);
@@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService { @@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService {
185 instance.setId(0); 185 instance.setId(0);
186 List<LwM2mResourceObserve> resources = new ArrayList<>(); 186 List<LwM2mResourceObserve> resources = new ArrayList<>();
187 obj.resources.forEach((k, v) -> { 187 obj.resources.forEach((k, v) -> {
188 - if (!v.operations.isExecutable()) { 188 + if (v.operations.isReadable()) {
189 LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false); 189 LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
190 resources.add(lwM2MResourceObserve); 190 resources.add(lwM2MResourceObserve);
191 } 191 }
@@ -25,6 +25,7 @@ import lombok.Getter; @@ -25,6 +25,7 @@ import lombok.Getter;
25 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
26 import org.springframework.beans.factory.annotation.Value; 26 import org.springframework.beans.factory.annotation.Value;
27 import org.springframework.scheduling.annotation.Scheduled; 27 import org.springframework.scheduling.annotation.Scheduled;
  28 +import org.thingsboard.common.util.ThingsBoardExecutors;
28 import org.thingsboard.server.queue.usagestats.TbApiUsageClient; 29 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
29 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 30 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
30 31
@@ -93,7 +94,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer @@ -93,7 +94,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
93 super.init(maxRequestsTimeout); 94 super.init(maxRequestsTimeout);
94 if (useJsSandbox()) { 95 if (useJsSandbox()) {
95 sandbox = NashornSandboxes.create(); 96 sandbox = NashornSandboxes.create();
96 - monitorExecutorService = Executors.newWorkStealingPool(getMonitorThreadPoolSize()); 97 + monitorExecutorService = ThingsBoardExecutors.newWorkStealingPool(getMonitorThreadPoolSize(), "nashorn-js-monitor");
97 sandbox.setExecutor(monitorExecutorService); 98 sandbox.setExecutor(monitorExecutorService);
98 sandbox.setMaxCPUTime(getMaxCpuTime()); 99 sandbox.setMaxCPUTime(getMaxCpuTime());
99 sandbox.allowNoBraces(false); 100 sandbox.allowNoBraces(false);
@@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.context.annotation.Lazy; 20 import org.springframework.context.annotation.Lazy;
21 import org.springframework.context.event.EventListener; 21 import org.springframework.context.event.EventListener;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
  23 +import org.thingsboard.common.util.ThingsBoardExecutors;
23 import org.thingsboard.server.gen.transport.TransportProtos; 24 import org.thingsboard.server.gen.transport.TransportProtos;
24 import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; 25 import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
25 import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; 26 import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
@@ -62,7 +63,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -62,7 +63,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
62 private SubscriptionManagerService subscriptionManagerService; 63 private SubscriptionManagerService subscriptionManagerService;
63 64
64 private ExecutorService subscriptionUpdateExecutor; 65 private ExecutorService subscriptionUpdateExecutor;
65 - 66 +
66 private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() { 67 private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() {
67 @Override 68 @Override
68 protected void onTbApplicationEvent(PartitionChangeEvent event) { 69 protected void onTbApplicationEvent(PartitionChangeEvent event) {
@@ -93,7 +94,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -93,7 +94,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
93 94
94 @PostConstruct 95 @PostConstruct
95 public void initExecutor() { 96 public void initExecutor() {
96 - subscriptionUpdateExecutor = Executors.newWorkStealingPool(20); 97 + subscriptionUpdateExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass());
97 } 98 }
98 99
99 @PreDestroy 100 @PreDestroy
@@ -28,6 +28,8 @@ import org.springframework.beans.factory.annotation.Value; @@ -28,6 +28,8 @@ import org.springframework.beans.factory.annotation.Value;
28 import org.springframework.stereotype.Service; 28 import org.springframework.stereotype.Service;
29 import org.springframework.util.StringUtils; 29 import org.springframework.util.StringUtils;
30 import org.springframework.web.socket.CloseStatus; 30 import org.springframework.web.socket.CloseStatus;
  31 +import org.thingsboard.common.util.ThingsBoardExecutors;
  32 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
31 import org.thingsboard.server.common.data.DataConstants; 33 import org.thingsboard.server.common.data.DataConstants;
32 import org.thingsboard.server.common.data.id.CustomerId; 34 import org.thingsboard.server.common.data.id.CustomerId;
33 import org.thingsboard.server.common.data.id.EntityId; 35 import org.thingsboard.server.common.data.id.EntityId;
@@ -157,9 +159,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -157,9 +159,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
157 @PostConstruct 159 @PostConstruct
158 public void initExecutor() { 160 public void initExecutor() {
159 serviceId = serviceInfoProvider.getServiceId(); 161 serviceId = serviceInfoProvider.getServiceId();
160 - executor = Executors.newWorkStealingPool(50); 162 + executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass());
161 163
162 - pingExecutor = Executors.newSingleThreadScheduledExecutor(); 164 + pingExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("telemetry-web-socket-ping"));
163 pingExecutor.scheduleWithFixedDelay(this::sendPing, 10000, 10000, TimeUnit.MILLISECONDS); 165 pingExecutor.scheduleWithFixedDelay(this::sendPing, 10000, 10000, TimeUnit.MILLISECONDS);
164 } 166 }
165 167
@@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.TenantProfile; @@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.TenantProfile;
41 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; 41 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
42 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; 42 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
43 import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; 43 import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
  44 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  45 +import org.thingsboard.server.common.data.firmware.FirmwareUtil;
44 import org.thingsboard.server.common.data.id.CustomerId; 46 import org.thingsboard.server.common.data.id.CustomerId;
45 import org.thingsboard.server.common.data.id.DeviceId; 47 import org.thingsboard.server.common.data.id.DeviceId;
46 import org.thingsboard.server.common.data.id.DeviceProfileId; 48 import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -512,16 +514,17 @@ public class DefaultTransportApiService implements TransportApiService { @@ -512,16 +514,17 @@ public class DefaultTransportApiService implements TransportApiService {
512 private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) { 514 private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) {
513 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); 515 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
514 DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); 516 DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
  517 + FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType());
515 Device device = deviceService.findDeviceById(tenantId, deviceId); 518 Device device = deviceService.findDeviceById(tenantId, deviceId);
516 519
517 if (device == null) { 520 if (device == null) {
518 return getEmptyTransportApiResponseFuture(); 521 return getEmptyTransportApiResponseFuture();
519 } 522 }
520 523
521 - FirmwareId firmwareId = device.getFirmwareId();  
522 - 524 + FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
523 if (firmwareId == null) { 525 if (firmwareId == null) {
524 - firmwareId = deviceProfileCache.find(device.getDeviceProfileId()).getFirmwareId(); 526 + DeviceProfile deviceProfile = deviceProfileCache.find(device.getDeviceProfileId());
  527 + firmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
525 } 528 }
526 529
527 TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder(); 530 TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder();
@@ -537,6 +540,7 @@ public class DefaultTransportApiService implements TransportApiService { @@ -537,6 +540,7 @@ public class DefaultTransportApiService implements TransportApiService {
537 builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS); 540 builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
538 builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()); 541 builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits());
539 builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()); 542 builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits());
  543 + builder.setType(firmwareInfo.getType().name());
540 builder.setTitle(firmwareInfo.getTitle()); 544 builder.setTitle(firmwareInfo.getTitle());
541 builder.setVersion(firmwareInfo.getVersion()); 545 builder.setVersion(firmwareInfo.getVersion());
542 builder.setFileName(firmwareInfo.getFileName()); 546 builder.setFileName(firmwareInfo.getFileName());
@@ -21,6 +21,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -21,6 +21,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
21 import org.springframework.context.event.EventListener; 21 import org.springframework.context.event.EventListener;
22 import org.springframework.core.annotation.Order; 22 import org.springframework.core.annotation.Order;
23 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
  24 +import org.thingsboard.common.util.ThingsBoardExecutors;
24 import org.thingsboard.server.common.stats.MessagesStats; 25 import org.thingsboard.server.common.stats.MessagesStats;
25 import org.thingsboard.server.common.stats.StatsFactory; 26 import org.thingsboard.server.common.stats.StatsFactory;
26 import org.thingsboard.server.common.stats.StatsType; 27 import org.thingsboard.server.common.stats.StatsType;
@@ -70,7 +71,7 @@ public class TbCoreTransportApiService { @@ -70,7 +71,7 @@ public class TbCoreTransportApiService {
70 71
71 @PostConstruct 72 @PostConstruct
72 public void init() { 73 public void init() {
73 - this.transportCallbackExecutor = Executors.newWorkStealingPool(maxCallbackThreads); 74 + this.transportCallbackExecutor = ThingsBoardExecutors.newWorkStealingPool(maxCallbackThreads, getClass());
74 TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> producer = tbCoreQueueFactory.createTransportApiResponseProducer(); 75 TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> producer = tbCoreQueueFactory.createTransportApiResponseProducer();
75 TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> consumer = tbCoreQueueFactory.createTransportApiRequestConsumer(); 76 TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> consumer = tbCoreQueueFactory.createTransportApiRequestConsumer();
76 77
@@ -24,10 +24,13 @@ import org.springframework.mock.web.MockMultipartFile; @@ -24,10 +24,13 @@ import org.springframework.mock.web.MockMultipartFile;
24 import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; 24 import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
25 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 25 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
26 import org.thingsboard.common.util.JacksonUtil; 26 import org.thingsboard.common.util.JacksonUtil;
  27 +import org.thingsboard.server.common.data.DeviceProfile;
27 import org.thingsboard.server.common.data.Firmware; 28 import org.thingsboard.server.common.data.Firmware;
28 import org.thingsboard.server.common.data.FirmwareInfo; 29 import org.thingsboard.server.common.data.FirmwareInfo;
29 import org.thingsboard.server.common.data.Tenant; 30 import org.thingsboard.server.common.data.Tenant;
30 import org.thingsboard.server.common.data.User; 31 import org.thingsboard.server.common.data.User;
  32 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  33 +import org.thingsboard.server.common.data.id.DeviceProfileId;
31 import org.thingsboard.server.common.data.page.PageData; 34 import org.thingsboard.server.common.data.page.PageData;
32 import org.thingsboard.server.common.data.page.PageLink; 35 import org.thingsboard.server.common.data.page.PageLink;
33 import org.thingsboard.server.common.data.security.Authority; 36 import org.thingsboard.server.common.data.security.Authority;
@@ -38,6 +41,7 @@ import java.util.Collections; @@ -38,6 +41,7 @@ import java.util.Collections;
38 import java.util.List; 41 import java.util.List;
39 42
40 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 43 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  44 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
41 45
42 public abstract class BaseFirmwareControllerTest extends AbstractControllerTest { 46 public abstract class BaseFirmwareControllerTest extends AbstractControllerTest {
43 47
@@ -53,6 +57,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -53,6 +57,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
53 57
54 private Tenant savedTenant; 58 private Tenant savedTenant;
55 private User tenantAdmin; 59 private User tenantAdmin;
  60 + private DeviceProfileId deviceProfileId;
56 61
57 @Before 62 @Before
58 public void beforeTest() throws Exception { 63 public void beforeTest() throws Exception {
@@ -71,6 +76,11 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -71,6 +76,11 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
71 tenantAdmin.setLastName("Downs"); 76 tenantAdmin.setLastName("Downs");
72 77
73 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); 78 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
  79 +
  80 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
  81 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  82 + Assert.assertNotNull(savedDeviceProfile);
  83 + deviceProfileId = savedDeviceProfile.getId();
74 } 84 }
75 85
76 @After 86 @After
@@ -84,6 +94,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -84,6 +94,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
84 @Test 94 @Test
85 public void testSaveFirmware() throws Exception { 95 public void testSaveFirmware() throws Exception {
86 FirmwareInfo firmwareInfo = new FirmwareInfo(); 96 FirmwareInfo firmwareInfo = new FirmwareInfo();
  97 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  98 + firmwareInfo.setType(FIRMWARE);
87 firmwareInfo.setTitle(TITLE); 99 firmwareInfo.setTitle(TITLE);
88 firmwareInfo.setVersion(VERSION); 100 firmwareInfo.setVersion(VERSION);
89 101
@@ -107,6 +119,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -107,6 +119,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
107 @Test 119 @Test
108 public void testSaveFirmwareData() throws Exception { 120 public void testSaveFirmwareData() throws Exception {
109 FirmwareInfo firmwareInfo = new FirmwareInfo(); 121 FirmwareInfo firmwareInfo = new FirmwareInfo();
  122 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  123 + firmwareInfo.setType(FIRMWARE);
110 firmwareInfo.setTitle(TITLE); 124 firmwareInfo.setTitle(TITLE);
111 firmwareInfo.setVersion(VERSION); 125 firmwareInfo.setVersion(VERSION);
112 126
@@ -137,6 +151,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -137,6 +151,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
137 @Test 151 @Test
138 public void testUpdateFirmwareFromDifferentTenant() throws Exception { 152 public void testUpdateFirmwareFromDifferentTenant() throws Exception {
139 FirmwareInfo firmwareInfo = new FirmwareInfo(); 153 FirmwareInfo firmwareInfo = new FirmwareInfo();
  154 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  155 + firmwareInfo.setType(FIRMWARE);
140 firmwareInfo.setTitle(TITLE); 156 firmwareInfo.setTitle(TITLE);
141 firmwareInfo.setVersion(VERSION); 157 firmwareInfo.setVersion(VERSION);
142 158
@@ -150,6 +166,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -150,6 +166,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
150 @Test 166 @Test
151 public void testFindFirmwareInfoById() throws Exception { 167 public void testFindFirmwareInfoById() throws Exception {
152 FirmwareInfo firmwareInfo = new FirmwareInfo(); 168 FirmwareInfo firmwareInfo = new FirmwareInfo();
  169 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  170 + firmwareInfo.setType(FIRMWARE);
153 firmwareInfo.setTitle(TITLE); 171 firmwareInfo.setTitle(TITLE);
154 firmwareInfo.setVersion(VERSION); 172 firmwareInfo.setVersion(VERSION);
155 173
@@ -163,6 +181,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -163,6 +181,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
163 @Test 181 @Test
164 public void testFindFirmwareById() throws Exception { 182 public void testFindFirmwareById() throws Exception {
165 FirmwareInfo firmwareInfo = new FirmwareInfo(); 183 FirmwareInfo firmwareInfo = new FirmwareInfo();
  184 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  185 + firmwareInfo.setType(FIRMWARE);
166 firmwareInfo.setTitle(TITLE); 186 firmwareInfo.setTitle(TITLE);
167 firmwareInfo.setVersion(VERSION); 187 firmwareInfo.setVersion(VERSION);
168 188
@@ -180,6 +200,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -180,6 +200,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
180 @Test 200 @Test
181 public void testDeleteFirmware() throws Exception { 201 public void testDeleteFirmware() throws Exception {
182 FirmwareInfo firmwareInfo = new FirmwareInfo(); 202 FirmwareInfo firmwareInfo = new FirmwareInfo();
  203 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  204 + firmwareInfo.setType(FIRMWARE);
183 firmwareInfo.setTitle(TITLE); 205 firmwareInfo.setTitle(TITLE);
184 firmwareInfo.setVersion(VERSION); 206 firmwareInfo.setVersion(VERSION);
185 207
@@ -197,6 +219,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -197,6 +219,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
197 List<FirmwareInfo> firmwares = new ArrayList<>(); 219 List<FirmwareInfo> firmwares = new ArrayList<>();
198 for (int i = 0; i < 165; i++) { 220 for (int i = 0; i < 165; i++) {
199 FirmwareInfo firmwareInfo = new FirmwareInfo(); 221 FirmwareInfo firmwareInfo = new FirmwareInfo();
  222 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  223 + firmwareInfo.setType(FIRMWARE);
200 firmwareInfo.setTitle(TITLE); 224 firmwareInfo.setTitle(TITLE);
201 firmwareInfo.setVersion(VERSION + i); 225 firmwareInfo.setVersion(VERSION + i);
202 226
@@ -238,6 +262,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -238,6 +262,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
238 262
239 for (int i = 0; i < 165; i++) { 263 for (int i = 0; i < 165; i++) {
240 FirmwareInfo firmwareInfo = new FirmwareInfo(); 264 FirmwareInfo firmwareInfo = new FirmwareInfo();
  265 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  266 + firmwareInfo.setType(FIRMWARE);
241 firmwareInfo.setTitle(TITLE); 267 firmwareInfo.setTitle(TITLE);
242 firmwareInfo.setVersion(VERSION + i); 268 firmwareInfo.setVersion(VERSION + i);
243 269
@@ -257,7 +283,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -257,7 +283,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
257 PageLink pageLink = new PageLink(24); 283 PageLink pageLink = new PageLink(24);
258 PageData<FirmwareInfo> pageData; 284 PageData<FirmwareInfo> pageData;
259 do { 285 do {
260 - pageData = doGetTypedWithPageLink("/api/firmwares/true?", 286 + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?",
261 new TypeReference<>() { 287 new TypeReference<>() {
262 }, pageLink); 288 }, pageLink);
263 loadedFirmwaresWithData.addAll(pageData.getData()); 289 loadedFirmwaresWithData.addAll(pageData.getData());
@@ -269,7 +295,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @@ -269,7 +295,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
269 List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>(); 295 List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>();
270 pageLink = new PageLink(24); 296 pageLink = new PageLink(24);
271 do { 297 do {
272 - pageData = doGetTypedWithPageLink("/api/firmwares/false?", 298 + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?",
273 new TypeReference<>() { 299 new TypeReference<>() {
274 }, pageLink); 300 }, pageLink);
275 loadedFirmwaresWithoutData.addAll(pageData.getData()); 301 loadedFirmwaresWithoutData.addAll(pageData.getData());
@@ -16,10 +16,10 @@ @@ -16,10 +16,10 @@
16 package org.thingsboard.server.service.queue; 16 package org.thingsboard.server.service.queue;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
19 import org.junit.Assert; 20 import org.junit.Assert;
20 import org.junit.Test; 21 import org.junit.Test;
21 import org.junit.runner.RunWith; 22 import org.junit.runner.RunWith;
22 -import org.mockito.Mockito;  
23 import org.mockito.junit.MockitoJUnitRunner; 23 import org.mockito.junit.MockitoJUnitRunner;
24 import org.thingsboard.server.gen.transport.TransportProtos; 24 import org.thingsboard.server.gen.transport.TransportProtos;
25 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 25 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
@@ -28,39 +28,74 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrateg @@ -28,39 +28,74 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrateg
28 import java.util.UUID; 28 import java.util.UUID;
29 import java.util.concurrent.ConcurrentHashMap; 29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ConcurrentMap; 30 import java.util.concurrent.ConcurrentMap;
  31 +import java.util.concurrent.CountDownLatch;
31 import java.util.concurrent.ExecutorService; 32 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.Executors; 33 import java.util.concurrent.Executors;
33 import java.util.concurrent.TimeUnit; 34 import java.util.concurrent.TimeUnit;
34 35
  36 +import static org.junit.Assert.assertTrue;
  37 +import static org.mockito.ArgumentMatchers.any;
35 import static org.mockito.Mockito.mock; 38 import static org.mockito.Mockito.mock;
  39 +import static org.mockito.Mockito.times;
  40 +import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when; 41 import static org.mockito.Mockito.when;
37 42
38 @Slf4j 43 @Slf4j
39 @RunWith(MockitoJUnitRunner.class) 44 @RunWith(MockitoJUnitRunner.class)
40 public class TbMsgPackProcessingContextTest { 45 public class TbMsgPackProcessingContextTest {
41 46
  47 + public static final int TIMEOUT = 10;
  48 + ExecutorService executorService;
  49 +
  50 + @After
  51 + public void tearDown() {
  52 + if (executorService != null) {
  53 + executorService.shutdownNow();
  54 + }
  55 + }
  56 +
42 @Test 57 @Test
43 public void testHighConcurrencyCase() throws InterruptedException { 58 public void testHighConcurrencyCase() throws InterruptedException {
44 - TbRuleEngineSubmitStrategy strategyMock = mock(TbRuleEngineSubmitStrategy.class); 59 + //log.warn("preparing the test...");
45 int msgCount = 1000; 60 int msgCount = 1000;
46 int parallelCount = 5; 61 int parallelCount = 5;
47 - ExecutorService executorService = Executors.newFixedThreadPool(parallelCount);  
48 - try {  
49 - ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> messages = new ConcurrentHashMap<>();  
50 - for (int i = 0; i < msgCount; i++) {  
51 - messages.put(UUID.randomUUID(), new TbProtoQueueMsg<>(UUID.randomUUID(), null));  
52 - }  
53 - when(strategyMock.getPendingMap()).thenReturn(messages);  
54 - TbMsgPackProcessingContext context = new TbMsgPackProcessingContext("Main", strategyMock);  
55 - for (UUID uuid : messages.keySet()) {  
56 - for (int i = 0; i < parallelCount; i++) {  
57 - executorService.submit(() -> context.onSuccess(uuid));  
58 - } 62 + executorService = Executors.newFixedThreadPool(parallelCount);
  63 +
  64 + ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> messages = new ConcurrentHashMap<>(msgCount);
  65 + for (int i = 0; i < msgCount; i++) {
  66 + messages.put(UUID.randomUUID(), new TbProtoQueueMsg<>(UUID.randomUUID(), null));
  67 + }
  68 + TbRuleEngineSubmitStrategy strategyMock = mock(TbRuleEngineSubmitStrategy.class);
  69 + when(strategyMock.getPendingMap()).thenReturn(messages);
  70 +
  71 + TbMsgPackProcessingContext context = new TbMsgPackProcessingContext("Main", strategyMock);
  72 + for (UUID uuid : messages.keySet()) {
  73 + final CountDownLatch readyLatch = new CountDownLatch(parallelCount);
  74 + final CountDownLatch startLatch = new CountDownLatch(1);
  75 + final CountDownLatch finishLatch = new CountDownLatch(parallelCount);
  76 + for (int i = 0; i < parallelCount; i++) {
  77 + //final String taskName = "" + uuid + " " + i;
  78 + executorService.submit(() -> {
  79 + //log.warn("ready {}", taskName);
  80 + readyLatch.countDown();
  81 + try {
  82 + startLatch.await();
  83 + } catch (InterruptedException e) {
  84 + Assert.fail("failed to await");
  85 + }
  86 + //log.warn("go {}", taskName);
  87 +
  88 + context.onSuccess(uuid);
  89 +
  90 + finishLatch.countDown();
  91 + });
59 } 92 }
60 - Assert.assertTrue(context.await(10, TimeUnit.SECONDS));  
61 - Mockito.verify(strategyMock, Mockito.times(msgCount)).onSuccess(Mockito.any(UUID.class));  
62 - } finally {  
63 - executorService.shutdownNow(); 93 + assertTrue(readyLatch.await(TIMEOUT, TimeUnit.SECONDS));
  94 + Thread.yield();
  95 + startLatch.countDown(); //run all-at-once submitted tasks
  96 + assertTrue(finishLatch.await(TIMEOUT, TimeUnit.SECONDS));
64 } 97 }
  98 + assertTrue(context.await(TIMEOUT, TimeUnit.SECONDS));
  99 + verify(strategyMock, times(msgCount)).onSuccess(any(UUID.class));
65 } 100 }
66 } 101 }
@@ -65,6 +65,8 @@ public interface DeviceService { @@ -65,6 +65,8 @@ public interface DeviceService {
65 65
66 PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink); 66 PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
67 67
  68 + PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink);
  69 +
68 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); 70 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
69 71
70 PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink); 72 PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink);
@@ -18,8 +18,9 @@ package org.thingsboard.server.dao.firmware; @@ -18,8 +18,9 @@ package org.thingsboard.server.dao.firmware;
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.server.common.data.Firmware; 19 import org.thingsboard.server.common.data.Firmware;
20 import org.thingsboard.server.common.data.FirmwareInfo; 20 import org.thingsboard.server.common.data.FirmwareInfo;
21 -import org.thingsboard.server.common.data.exception.ThingsboardException;  
22 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; 21 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
  22 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  23 +import org.thingsboard.server.common.data.id.DeviceProfileId;
23 import org.thingsboard.server.common.data.id.FirmwareId; 24 import org.thingsboard.server.common.data.id.FirmwareId;
24 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
25 import org.thingsboard.server.common.data.page.PageData; 26 import org.thingsboard.server.common.data.page.PageData;
@@ -33,7 +34,7 @@ public interface FirmwareService { @@ -33,7 +34,7 @@ public interface FirmwareService {
33 34
34 Firmware saveFirmware(Firmware firmware); 35 Firmware saveFirmware(Firmware firmware);
35 36
36 - String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) throws ThingsboardException; 37 + String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data);
37 38
38 Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId); 39 Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId);
39 40
@@ -43,7 +44,7 @@ public interface FirmwareService { @@ -43,7 +44,7 @@ public interface FirmwareService {
43 44
44 PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink); 45 PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink);
45 46
46 - PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); 47 + PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
47 48
48 void deleteFirmware(TenantId tenantId, FirmwareId firmwareId); 49 void deleteFirmware(TenantId tenantId, FirmwareId firmwareId);
49 50
@@ -93,22 +93,26 @@ public class DataConstants { @@ -93,22 +93,26 @@ public class DataConstants {
93 public static final String USERNAME = "username"; 93 public static final String USERNAME = "username";
94 public static final String PASSWORD = "password"; 94 public static final String PASSWORD = "password";
95 95
96 - //firmware  
97 - //telemetry  
98 - public static final String CURRENT_FIRMWARE_TITLE = "cur_fw_title";  
99 - public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version";  
100 - public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";  
101 - public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";  
102 - public static final String TARGET_FIRMWARE_TS = "target_fw_ts";  
103 - public static final String FIRMWARE_STATE = "fw_state";  
104 -  
105 - //attributes  
106 - //telemetry  
107 - public static final String FIRMWARE_TITLE = "fw_title";  
108 - public static final String FIRMWARE_VERSION = "fw_version";  
109 - public static final String FIRMWARE_SIZE = "fw_size";  
110 - public static final String FIRMWARE_CHECKSUM = "fw_checksum";  
111 - public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm"; 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
112 public static final String EDGE_MSG_SOURCE = "edge"; 115 public static final String EDGE_MSG_SOURCE = "edge";
113 public static final String MSG_SOURCE_KEY = "source"; 116 public static final String MSG_SOURCE_KEY = "source";
  117 +
114 } 118 }
@@ -32,7 +32,7 @@ import java.io.IOException; @@ -32,7 +32,7 @@ import java.io.IOException;
32 32
33 @EqualsAndHashCode(callSuper = true) 33 @EqualsAndHashCode(callSuper = true)
34 @Slf4j 34 @Slf4j
35 -public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId { 35 +public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId, HasFirmware {
36 36
37 private static final long serialVersionUID = 2807343040519543363L; 37 private static final long serialVersionUID = 2807343040519543363L;
38 38
@@ -50,6 +50,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -50,6 +50,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
50 private byte[] deviceDataBytes; 50 private byte[] deviceDataBytes;
51 51
52 private FirmwareId firmwareId; 52 private FirmwareId firmwareId;
  53 + private FirmwareId softwareId;
53 54
54 public Device() { 55 public Device() {
55 super(); 56 super();
@@ -69,6 +70,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -69,6 +70,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
69 this.deviceProfileId = device.getDeviceProfileId(); 70 this.deviceProfileId = device.getDeviceProfileId();
70 this.setDeviceData(device.getDeviceData()); 71 this.setDeviceData(device.getDeviceData());
71 this.firmwareId = device.getFirmwareId(); 72 this.firmwareId = device.getFirmwareId();
  73 + this.softwareId = device.getSoftwareId();
72 } 74 }
73 75
74 public Device updateDevice(Device device) { 76 public Device updateDevice(Device device) {
@@ -79,6 +81,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -79,6 +81,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
79 this.label = device.getLabel(); 81 this.label = device.getLabel();
80 this.deviceProfileId = device.getDeviceProfileId(); 82 this.deviceProfileId = device.getDeviceProfileId();
81 this.setDeviceData(device.getDeviceData()); 83 this.setDeviceData(device.getDeviceData());
  84 + this.setFirmwareId(device.getFirmwareId());
  85 + this.setSoftwareId(device.getSoftwareId());
82 return this; 86 return this;
83 } 87 }
84 88
@@ -171,6 +175,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -171,6 +175,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
171 this.firmwareId = firmwareId; 175 this.firmwareId = firmwareId;
172 } 176 }
173 177
  178 + public FirmwareId getSoftwareId() {
  179 + return softwareId;
  180 + }
  181 +
  182 + public void setSoftwareId(FirmwareId softwareId) {
  183 + this.softwareId = softwareId;
  184 + }
  185 +
174 @Override 186 @Override
175 public String toString() { 187 public String toString() {
176 StringBuilder builder = new StringBuilder(); 188 StringBuilder builder = new StringBuilder();
@@ -36,7 +36,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn @@ -36,7 +36,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
36 @Data 36 @Data
37 @EqualsAndHashCode(callSuper = true) 37 @EqualsAndHashCode(callSuper = true)
38 @Slf4j 38 @Slf4j
39 -public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId { 39 +public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasFirmware {
40 40
41 private TenantId tenantId; 41 private TenantId tenantId;
42 @NoXss 42 @NoXss
@@ -59,6 +59,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H @@ -59,6 +59,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
59 59
60 private FirmwareId firmwareId; 60 private FirmwareId firmwareId;
61 61
  62 + private FirmwareId softwareId;
  63 +
62 public DeviceProfile() { 64 public DeviceProfile() {
63 super(); 65 super();
64 } 66 }
@@ -77,6 +79,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H @@ -77,6 +79,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
77 this.defaultQueueName = deviceProfile.getDefaultQueueName(); 79 this.defaultQueueName = deviceProfile.getDefaultQueueName();
78 this.setProfileData(deviceProfile.getProfileData()); 80 this.setProfileData(deviceProfile.getProfileData());
79 this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); 81 this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
  82 + this.firmwareId = deviceProfile.getFirmwareId();
  83 + this.softwareId = deviceProfile.getSoftwareId();
80 } 84 }
81 85
82 @Override 86 @Override
@@ -20,6 +20,8 @@ import lombok.Data; @@ -20,6 +20,8 @@ import lombok.Data;
20 import lombok.EqualsAndHashCode; 20 import lombok.EqualsAndHashCode;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
22 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; 22 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
  23 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  24 +import org.thingsboard.server.common.data.id.DeviceProfileId;
23 import org.thingsboard.server.common.data.id.FirmwareId; 25 import org.thingsboard.server.common.data.id.FirmwareId;
24 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
25 27
@@ -31,6 +33,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> @@ -31,6 +33,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
31 private static final long serialVersionUID = 3168391583570815419L; 33 private static final long serialVersionUID = 3168391583570815419L;
32 34
33 private TenantId tenantId; 35 private TenantId tenantId;
  36 + private DeviceProfileId deviceProfileId;
  37 + private FirmwareType type;
34 private String title; 38 private String title;
35 private String version; 39 private String version;
36 private boolean hasData; 40 private boolean hasData;
@@ -52,6 +56,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> @@ -52,6 +56,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
52 public FirmwareInfo(FirmwareInfo firmwareInfo) { 56 public FirmwareInfo(FirmwareInfo firmwareInfo) {
53 super(firmwareInfo); 57 super(firmwareInfo);
54 this.tenantId = firmwareInfo.getTenantId(); 58 this.tenantId = firmwareInfo.getTenantId();
  59 + this.deviceProfileId = firmwareInfo.getDeviceProfileId();
  60 + this.type = firmwareInfo.getType();
55 this.title = firmwareInfo.getTitle(); 61 this.title = firmwareInfo.getTitle();
56 this.version = firmwareInfo.getVersion(); 62 this.version = firmwareInfo.getVersion();
57 this.hasData = firmwareInfo.isHasData(); 63 this.hasData = firmwareInfo.isHasData();
  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.common.data;
  17 +
  18 +import org.thingsboard.server.common.data.id.FirmwareId;
  19 +
  20 +public interface HasFirmware {
  21 +
  22 + FirmwareId getFirmwareId();
  23 +
  24 + FirmwareId getSoftwareId();
  25 +}
@@ -31,6 +31,7 @@ public class MqttTopics { @@ -31,6 +31,7 @@ public class MqttTopics {
31 private static final String SUB_TOPIC = "+"; 31 private static final String SUB_TOPIC = "+";
32 private static final String PROVISION = "/provision"; 32 private static final String PROVISION = "/provision";
33 private static final String FIRMWARE = "/fw"; 33 private static final String FIRMWARE = "/fw";
  34 + private static final String SOFTWARE = "/sw";
34 private static final String CHUNK = "/chunk/"; 35 private static final String CHUNK = "/chunk/";
35 private static final String ERROR = "/error"; 36 private static final String ERROR = "/error";
36 37
@@ -75,9 +76,17 @@ public class MqttTopics { @@ -75,9 +76,17 @@ public class MqttTopics {
75 // v2 topics 76 // v2 topics
76 public static final String BASE_DEVICE_API_TOPIC_V2 = "v2"; 77 public static final String BASE_DEVICE_API_TOPIC_V2 = "v2";
77 78
78 - public static final String DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/";  
79 - public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + SUB_TOPIC + CHUNK + SUB_TOPIC; 79 + public static final String REQUEST_ID_PATTERN = "(?<requestId>\\d+)";
  80 + public static final String CHUNK_PATTERN = "(?<chunk>\\d+)";
  81 +
  82 + public static final String DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
  83 + public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
80 public static final String DEVICE_FIRMWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + ERROR; 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";
  86 +
  87 + public static final String DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
  88 + public static final String DEVICE_SOFTWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
  89 + public static final String DEVICE_SOFTWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + ERROR;
81 90
82 private MqttTopics() { 91 private MqttTopics() {
83 } 92 }
  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.common.data.firmware;
  17 +
  18 +import lombok.Getter;
  19 +
  20 +public enum FirmwareKey {
  21 +
  22 + TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm");
  23 +
  24 + @Getter
  25 + private final String value;
  26 +
  27 + FirmwareKey(String value) {
  28 + this.value = value;
  29 + }
  30 +}
  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.common.data.firmware;
  17 +
  18 +import lombok.Getter;
  19 +
  20 +public enum FirmwareType {
  21 +
  22 + FIRMWARE("fw"), SOFTWARE("sw");
  23 +
  24 + @Getter
  25 + private final String keyPrefix;
  26 +
  27 + FirmwareType(String keyPrefix) {
  28 + this.keyPrefix = keyPrefix;
  29 + }
  30 +}
  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.common.data.firmware;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.server.common.data.HasFirmware;
  20 +import org.thingsboard.server.common.data.id.FirmwareId;
  21 +
  22 +import java.util.ArrayList;
  23 +import java.util.Collections;
  24 +import java.util.List;
  25 +
  26 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  27 +import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
  28 +
  29 +@Slf4j
  30 +public class FirmwareUtil {
  31 +
  32 + public static final List<String> ALL_FW_ATTRIBUTE_KEYS;
  33 +
  34 + public static final List<String> ALL_SW_ATTRIBUTE_KEYS;
  35 +
  36 + static {
  37 + ALL_FW_ATTRIBUTE_KEYS = new ArrayList<>();
  38 + for (FirmwareKey key : FirmwareKey.values()) {
  39 + ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(FIRMWARE, key));
  40 +
  41 + }
  42 +
  43 + ALL_SW_ATTRIBUTE_KEYS = new ArrayList<>();
  44 + for (FirmwareKey key : FirmwareKey.values()) {
  45 + ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(SOFTWARE, key));
  46 +
  47 + }
  48 + }
  49 +
  50 + public static List<String> getAttributeKeys(FirmwareType firmwareType) {
  51 + switch (firmwareType) {
  52 + case FIRMWARE:
  53 + return ALL_FW_ATTRIBUTE_KEYS;
  54 + case SOFTWARE:
  55 + return ALL_SW_ATTRIBUTE_KEYS;
  56 + }
  57 + return Collections.emptyList();
  58 + }
  59 +
  60 + public static String getAttributeKey(FirmwareType type, FirmwareKey key) {
  61 + return type.getKeyPrefix() + "_" + key.getValue();
  62 + }
  63 +
  64 + public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) {
  65 + return getTelemetryKey("target_", type, key);
  66 + }
  67 +
  68 + public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) {
  69 + return getTelemetryKey("current_", type, key);
  70 + }
  71 +
  72 + private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) {
  73 + return prefix + type.getKeyPrefix() + "_" + key.getValue();
  74 + }
  75 +
  76 + public static String getTelemetryKey(FirmwareType type, FirmwareKey key) {
  77 + return type.getKeyPrefix() + "_" + key.getValue();
  78 + }
  79 +
  80 + public static FirmwareId getFirmwareId(HasFirmware entity, FirmwareType firmwareType) {
  81 + switch (firmwareType) {
  82 + case FIRMWARE:
  83 + return entity.getFirmwareId();
  84 + case SOFTWARE:
  85 + return entity.getSoftwareId();
  86 + default:
  87 + log.warn("Unsupported firmware type: [{}]", firmwareType);
  88 + return null;
  89 + }
  90 + }
  91 +}
@@ -16,5 +16,5 @@ @@ -16,5 +16,5 @@
16 package org.thingsboard.server.common.msg.session; 16 package org.thingsboard.server.common.msg.session;
17 17
18 public enum FeatureType { 18 public enum FeatureType {
19 - ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE 19 + ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE, SOFTWARE
20 } 20 }
@@ -32,7 +32,8 @@ public enum SessionMsgType { @@ -32,7 +32,8 @@ public enum SessionMsgType {
32 32
33 CLAIM_REQUEST(), 33 CLAIM_REQUEST(),
34 34
35 - GET_FIRMWARE_REQUEST; 35 + GET_FIRMWARE_REQUEST,
  36 + GET_SOFTWARE_REQUEST;
36 37
37 private final boolean requiresRulesProcessing; 38 private final boolean requiresRulesProcessing;
38 39
@@ -38,6 +38,7 @@ import static java.util.Collections.emptyList; @@ -38,6 +38,7 @@ import static java.util.Collections.emptyList;
38 @Slf4j 38 @Slf4j
39 public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> implements TbQueueConsumer<T> { 39 public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> implements TbQueueConsumer<T> {
40 40
  41 + public static final long ONE_MILLISECOND_IN_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
41 private volatile boolean subscribed; 42 private volatile boolean subscribed;
42 protected volatile boolean stopped = false; 43 protected volatile boolean stopped = false;
43 protected volatile Set<TopicPartitionInfo> partitions; 44 protected volatile Set<TopicPartitionInfo> partitions;
@@ -83,7 +84,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i @@ -83,7 +84,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
83 } 84 }
84 85
85 if (consumerLock.isLocked()) { 86 if (consumerLock.isLocked()) {
86 - log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock", new RuntimeException("stacktrace")); 87 + log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace"));
87 } 88 }
88 89
89 consumerLock.lock(); 90 consumerLock.lock();
@@ -131,9 +132,12 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i @@ -131,9 +132,12 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
131 List<T> sleepAndReturnEmpty(final long startNanos, final long durationInMillis) { 132 List<T> sleepAndReturnEmpty(final long startNanos, final long durationInMillis) {
132 long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis); 133 long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis);
133 long spentNanos = System.nanoTime() - startNanos; 134 long spentNanos = System.nanoTime() - startNanos;
134 - if (spentNanos < durationNanos) { 135 + long nanosLeft = durationNanos - spentNanos;
  136 + if (nanosLeft >= ONE_MILLISECOND_IN_NANOS) {
135 try { 137 try {
136 - Thread.sleep(Math.max(TimeUnit.NANOSECONDS.toMillis(durationNanos - spentNanos), 1)); 138 + long sleepMs = TimeUnit.NANOSECONDS.toMillis(nanosLeft);
  139 + log.trace("Going to sleep after poll: topic {} for {}ms", topic, sleepMs);
  140 + Thread.sleep(sleepMs);
137 } catch (InterruptedException e) { 141 } catch (InterruptedException e) {
138 if (!stopped) { 142 if (!stopped) {
139 log.error("Failed to wait", e); 143 log.error("Failed to wait", e);
@@ -146,7 +150,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i @@ -146,7 +150,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
146 @Override 150 @Override
147 public void commit() { 151 public void commit() {
148 if (consumerLock.isLocked()) { 152 if (consumerLock.isLocked()) {
149 - log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock", new RuntimeException("stacktrace")); 153 + log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace"));
150 } 154 }
151 consumerLock.lock(); 155 consumerLock.lock();
152 try { 156 try {
@@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture;
20 import com.google.common.util.concurrent.SettableFuture; 20 import com.google.common.util.concurrent.SettableFuture;
21 import lombok.Builder; 21 import lombok.Builder;
22 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
  23 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 24 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
24 import org.thingsboard.server.queue.TbQueueAdmin; 25 import org.thingsboard.server.queue.TbQueueAdmin;
25 import org.thingsboard.server.queue.TbQueueCallback; 26 import org.thingsboard.server.queue.TbQueueCallback;
@@ -77,7 +78,7 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response @@ -77,7 +78,7 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
77 this.executor = executor; 78 this.executor = executor;
78 } else { 79 } else {
79 internalExecutor = true; 80 internalExecutor = true;
80 - this.executor = Executors.newSingleThreadExecutor(); 81 + this.executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-queue-request-template-" + responseTemplate.getTopic()));
81 } 82 }
82 } 83 }
83 84
@@ -17,6 +17,7 @@ package org.thingsboard.server.queue.common; @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.common;
17 17
18 import lombok.Builder; 18 import lombok.Builder;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
20 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
21 import org.thingsboard.server.queue.TbQueueConsumer; 22 import org.thingsboard.server.queue.TbQueueConsumer;
22 import org.thingsboard.server.queue.TbQueueHandler; 23 import org.thingsboard.server.queue.TbQueueHandler;
@@ -70,8 +71,8 @@ public class DefaultTbQueueResponseTemplate<Request extends TbQueueMsg, Response @@ -70,8 +71,8 @@ public class DefaultTbQueueResponseTemplate<Request extends TbQueueMsg, Response
70 this.requestTimeout = requestTimeout; 71 this.requestTimeout = requestTimeout;
71 this.callbackExecutor = executor; 72 this.callbackExecutor = executor;
72 this.stats = stats; 73 this.stats = stats;
73 - this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor();  
74 - this.loopExecutor = Executors.newSingleThreadExecutor(); 74 + this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("tb-queue-response-template-timeout-" + requestTemplate.getTopic()));
  75 + this.loopExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-queue-response-template-loop-" + requestTemplate.getTopic()));
75 } 76 }
76 77
77 @Override 78 @Override
@@ -388,16 +388,18 @@ message GetFirmwareRequestMsg { @@ -388,16 +388,18 @@ message GetFirmwareRequestMsg {
388 int64 deviceIdLSB = 2; 388 int64 deviceIdLSB = 2;
389 int64 tenantIdMSB = 3; 389 int64 tenantIdMSB = 3;
390 int64 tenantIdLSB = 4; 390 int64 tenantIdLSB = 4;
  391 + string type = 5;
391 } 392 }
392 393
393 message GetFirmwareResponseMsg { 394 message GetFirmwareResponseMsg {
394 ResponseStatus responseStatus = 1; 395 ResponseStatus responseStatus = 1;
395 int64 firmwareIdMSB = 2; 396 int64 firmwareIdMSB = 2;
396 int64 firmwareIdLSB = 3; 397 int64 firmwareIdLSB = 3;
397 - string title = 4;  
398 - string version = 5;  
399 - string contentType = 6;  
400 - string fileName = 7; 398 + string type = 4;
  399 + string title = 5;
  400 + string version = 6;
  401 + string contentType = 7;
  402 + string fileName = 8;
401 } 403 }
402 404
403 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. 405 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level.
@@ -711,4 +713,5 @@ message ToFirmwareStateServiceMsg { @@ -711,4 +713,5 @@ message ToFirmwareStateServiceMsg {
711 int64 deviceIdLSB = 5; 713 int64 deviceIdLSB = 5;
712 int64 firmwareIdMSB = 6; 714 int64 firmwareIdMSB = 6;
713 int64 firmwareIdLSB = 7; 715 int64 firmwareIdLSB = 7;
  716 + string type = 8;
714 } 717 }
@@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
43 import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; 43 import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
44 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; 44 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
45 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; 45 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
  46 +import org.thingsboard.server.common.data.firmware.FirmwareType;
46 import org.thingsboard.server.common.data.security.DeviceTokenCredentials; 47 import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
47 import org.thingsboard.server.common.msg.session.FeatureType; 48 import org.thingsboard.server.common.msg.session.FeatureType;
48 import org.thingsboard.server.common.msg.session.SessionMsgType; 49 import org.thingsboard.server.common.msg.session.SessionMsgType;
@@ -122,6 +123,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -122,6 +123,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
122 processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); 123 processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
123 } else if (featureType.get() == FeatureType.FIRMWARE) { 124 } else if (featureType.get() == FeatureType.FIRMWARE) {
124 processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST); 125 processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST);
  126 + } else if (featureType.get() == FeatureType.SOFTWARE) {
  127 + processRequest(exchange, SessionMsgType.GET_SOFTWARE_REQUEST);
125 } else { 128 } else {
126 log.trace("Invalid feature type parameter"); 129 log.trace("Invalid feature type parameter");
127 exchange.respond(CoAP.ResponseCode.BAD_REQUEST); 130 exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
@@ -326,12 +329,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -326,12 +329,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
326 new CoapNoOpCallback(exchange)); 329 new CoapNoOpCallback(exchange));
327 break; 330 break;
328 case GET_FIRMWARE_REQUEST: 331 case GET_FIRMWARE_REQUEST:
329 - TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()  
330 - .setTenantIdMSB(sessionInfo.getTenantIdMSB())  
331 - .setTenantIdLSB(sessionInfo.getTenantIdLSB())  
332 - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())  
333 - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();  
334 - transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange)); 332 + getFirmwareCallback(sessionInfo, exchange, FirmwareType.FIRMWARE);
  333 + break;
  334 + case GET_SOFTWARE_REQUEST:
  335 + getFirmwareCallback(sessionInfo, exchange, FirmwareType.SOFTWARE);
335 break; 336 break;
336 } 337 }
337 } catch (AdaptorException e) { 338 } catch (AdaptorException e) {
@@ -340,6 +341,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -340,6 +341,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
340 } 341 }
341 } 342 }
342 343
  344 + private void getFirmwareCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, FirmwareType firmwareType) {
  345 + TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
  346 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  347 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  348 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  349 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  350 + .setType(firmwareType.name()).build();
  351 + transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange));
  352 + }
  353 +
343 private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) { 354 private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) {
344 tokenToNotificationCounterMap.remove(token); 355 tokenToNotificationCounterMap.remove(token);
345 return tokenToSessionIdMap.remove(token); 356 return tokenToSessionIdMap.remove(token);
@@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam;
34 import org.springframework.web.bind.annotation.RestController; 34 import org.springframework.web.bind.annotation.RestController;
35 import org.springframework.web.context.request.async.DeferredResult; 35 import org.springframework.web.context.request.async.DeferredResult;
36 import org.thingsboard.server.common.data.DeviceTransportType; 36 import org.thingsboard.server.common.data.DeviceTransportType;
  37 +import org.thingsboard.server.common.data.firmware.FirmwareType;
37 import org.thingsboard.server.common.data.TbTransportService; 38 import org.thingsboard.server.common.data.TbTransportService;
38 import org.thingsboard.server.common.data.id.DeviceId; 39 import org.thingsboard.server.common.data.id.DeviceId;
39 import org.thingsboard.server.common.transport.SessionMsgListener; 40 import org.thingsboard.server.common.transport.SessionMsgListener;
@@ -210,8 +211,29 @@ public class DeviceApiController implements TbTransportService { @@ -210,8 +211,29 @@ public class DeviceApiController implements TbTransportService {
210 public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken, 211 public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken,
211 @RequestParam(value = "title") String title, 212 @RequestParam(value = "title") String title,
212 @RequestParam(value = "version") String version, 213 @RequestParam(value = "version") String version,
213 - @RequestParam(value = "chunkSize", required = false, defaultValue = "0") int chunkSize, 214 + @RequestParam(value = "size", required = false, defaultValue = "0") int size,
214 @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { 215 @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
  216 + return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.FIRMWARE);
  217 + }
  218 +
  219 + @RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET)
  220 + public DeferredResult<ResponseEntity> getSoftware(@PathVariable("deviceToken") String deviceToken,
  221 + @RequestParam(value = "title") String title,
  222 + @RequestParam(value = "version") String version,
  223 + @RequestParam(value = "size", required = false, defaultValue = "0") int size,
  224 + @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
  225 + return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.SOFTWARE);
  226 + }
  227 +
  228 + @RequestMapping(value = "/provision", method = RequestMethod.POST)
  229 + public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
  230 + DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
  231 + transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),
  232 + new DeviceProvisionCallback(responseWriter));
  233 + return responseWriter;
  234 + }
  235 +
  236 + private DeferredResult<ResponseEntity> getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) {
215 DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>(); 237 DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
216 transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), 238 transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
217 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { 239 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
@@ -219,20 +241,13 @@ public class DeviceApiController implements TbTransportService { @@ -219,20 +241,13 @@ public class DeviceApiController implements TbTransportService {
219 .setTenantIdMSB(sessionInfo.getTenantIdMSB()) 241 .setTenantIdMSB(sessionInfo.getTenantIdMSB())
220 .setTenantIdLSB(sessionInfo.getTenantIdLSB()) 242 .setTenantIdLSB(sessionInfo.getTenantIdLSB())
221 .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) 243 .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
222 - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();  
223 - transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, chunkSize, chunk)); 244 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  245 + .setType(firmwareType.name()).build();
  246 + transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, size, chunk));
224 })); 247 }));
225 return responseWriter; 248 return responseWriter;
226 } 249 }
227 250
228 - @RequestMapping(value = "/provision", method = RequestMethod.POST)  
229 - public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {  
230 - DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();  
231 - transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),  
232 - new DeviceProvisionCallback(responseWriter));  
233 - return responseWriter;  
234 - }  
235 -  
236 private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> { 251 private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
237 private final TransportContext transportContext; 252 private final TransportContext transportContext;
238 private final DeferredResult<ResponseEntity> responseWriter; 253 private final DeferredResult<ResponseEntity> responseWriter;
@@ -55,7 +55,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE @@ -55,7 +55,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
55 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; 55 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
56 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; 56 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
57 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; 57 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
58 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; 58 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
59 59
60 @Slf4j 60 @Slf4j
61 @Component 61 @Component
@@ -93,7 +93,6 @@ public class LwM2MTransportBootstrapServerConfiguration { @@ -93,7 +93,6 @@ public class LwM2MTransportBootstrapServerConfiguration {
93 builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort)); 93 builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort));
94 94
95 /** Define model provider (Create Models )*/ 95 /** Define model provider (Create Models )*/
96 -// builder.setModel(new StaticModel(contextS.getLwM2MTransportConfigServer().getModelsValueCommon()));  
97 96
98 /** Create credentials */ 97 /** Create credentials */
99 this.setServerWithCredentials(builder); 98 this.setServerWithCredentials(builder);
  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;
  17 +
  18 +import org.eclipse.californium.core.network.config.NetworkConfig;
  19 +
  20 +public class LwM2mNetworkConfig {
  21 +
  22 + public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {
  23 + NetworkConfig coapConfig = new NetworkConfig();
  24 + coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec);
  25 + coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort);
  26 + /**
  27 + * Example:Property for large packet:
  28 + * #NetworkConfig config = new NetworkConfig();
  29 + * #config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32);
  30 + * #config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32);
  31 + * #config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048);
  32 + * #config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3);
  33 + * #config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000);
  34 + */
  35 +
  36 + /**
  37 + * Property to indicate if the response should always include the Block2 option \
  38 + * when client request early blockwise negociation but the response can be sent on one packet.
  39 + * - value of false indicate that the server will respond without block2 option if no further blocks are required.
  40 + * - value of true indicate that the server will response with block2 option event if no further blocks are required.
  41 + * CoAP client will try to use block mode
  42 + * or adapt the block size when receiving a 4.13 Entity too large response code
  43 + */
  44 + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
  45 + /***
  46 + * Property to indicate if the response should always include the Block2 option \
  47 + * when client request early blockwise negociation but the response can be sent on one packet.
  48 + * - value of false indicate that the server will respond without block2 option if no further blocks are required.
  49 + * - value of true indicate that the server will response with block2 option event if no further blocks are required.
  50 + */
  51 + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
  52 +
  53 + coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000);
  54 + /**
  55 + * !!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
  56 + * The maximum size of a resource body (in bytes) that will be accepted
  57 + * as the payload of a POST/PUT or the response to a GET request in a
  58 + * transparent> blockwise transfer.
  59 + * This option serves as a safeguard against excessive memory
  60 + * consumption when many resources contain large bodies that cannot be
  61 + * transferred in a single CoAP message. This option has no impact on
  62 + * *manually* managed blockwise transfers in which the blocks are handled individually.
  63 + * Note that this option does not prevent local clients or resource
  64 + * implementations from sending large bodies as part of a request or response to a peer.
  65 + * The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192
  66 + * A value of {@code 0} turns off transparent handling of blockwise transfers altogether.
  67 + */
  68 +// coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 8192);
  69 + coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 16384);
  70 + /**
  71 + * The default DTLS response matcher.
  72 + * Supported values are STRICT, RELAXED, or PRINCIPAL.
  73 + * The default value is STRICT.
  74 + * Create new instance of udp endpoint context matcher.
  75 + * Params:
  76 + * checkAddress
  77 + * – true with address check, (STRICT, UDP)
  78 + * - false, without
  79 + */
  80 + coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT");
  81 + /**
  82 + * https://tools.ietf.org/html/rfc7959#section-2.9.3
  83 + * The block size (number of bytes) to use when doing a blockwise transfer. \
  84 + * This value serves as the upper limit for block size in blockwise transfers
  85 + */
  86 + coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 512);
  87 + /**
  88 + * The maximum payload size (in bytes) that can be transferred in a
  89 + * single message, i.e. without requiring a blockwise transfer.
  90 + * NB: this value MUST be adapted to the maximum message size supported by the transport layer.
  91 + * In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol
  92 + * DEFAULT_VALUE = 1024
  93 + */
  94 + coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 512);
  95 +
  96 + coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4);
  97 +
  98 + return coapConfig;
  99 + }
  100 +}
@@ -26,6 +26,7 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate; @@ -26,6 +26,7 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate;
26 26
27 import java.util.Collection; 27 import java.util.Collection;
28 28
  29 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
29 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; 30 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer;
30 31
31 @Slf4j 32 @Slf4j
@@ -85,17 +86,19 @@ public class LwM2mServerListener { @@ -85,17 +86,19 @@ public class LwM2mServerListener {
85 86
86 @Override 87 @Override
87 public void cancelled(Observation observation) { 88 public void cancelled(Observation observation) {
88 - log.info("Received notification cancelled from [{}] ", observation.getPath()); 89 + String msg = String.format("%s: Cancel Observation %s.", LOG_LW2M_INFO, observation.getPath());
  90 + service.sendLogsToThingsboard(msg, observation.getRegistrationId());
  91 + log.trace(msg);
89 } 92 }
90 93
91 @Override 94 @Override
92 public void onResponse(Observation observation, Registration registration, ObserveResponse response) { 95 public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
93 if (registration != null) { 96 if (registration != null) {
94 try { 97 try {
95 - service.onObservationResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), 98 + service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
96 registration), response, null); 99 registration), response, null);
97 } catch (Exception e) { 100 } catch (Exception e) {
98 - log.error("[{}] onResponse", e.toString()); 101 + log.error("Observation/Read onResponse", e);
99 102
100 } 103 }
101 } 104 }
@@ -108,7 +111,10 @@ public class LwM2mServerListener { @@ -108,7 +111,10 @@ public class LwM2mServerListener {
108 111
109 @Override 112 @Override
110 public void newObservation(Observation observation, Registration registration) { 113 public void newObservation(Observation observation, Registration registration) {
111 - log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint()); 114 + String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
  115 + observation.getPath());
  116 + service.sendLogsToThingsboard(msg, registration.getId());
  117 + log.trace(msg);
112 } 118 }
113 }; 119 };
114 120
@@ -25,7 +25,6 @@ import com.google.gson.JsonSyntaxException; @@ -25,7 +25,6 @@ import com.google.gson.JsonSyntaxException;
25 import com.google.gson.reflect.TypeToken; 25 import com.google.gson.reflect.TypeToken;
26 import lombok.extern.slf4j.Slf4j; 26 import lombok.extern.slf4j.Slf4j;
27 import org.apache.commons.lang3.StringUtils; 27 import org.apache.commons.lang3.StringUtils;
28 -import org.eclipse.californium.core.network.config.NetworkConfig;  
29 import org.eclipse.leshan.core.attributes.Attribute; 28 import org.eclipse.leshan.core.attributes.Attribute;
30 import org.eclipse.leshan.core.attributes.AttributeSet; 29 import org.eclipse.leshan.core.attributes.AttributeSet;
31 import org.eclipse.leshan.core.model.ObjectModel; 30 import org.eclipse.leshan.core.model.ObjectModel;
@@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException; @@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException;
40 import org.eclipse.leshan.core.request.DownlinkRequest; 39 import org.eclipse.leshan.core.request.DownlinkRequest;
41 import org.eclipse.leshan.core.request.WriteAttributesRequest; 40 import org.eclipse.leshan.core.request.WriteAttributesRequest;
42 import org.eclipse.leshan.core.util.Hex; 41 import org.eclipse.leshan.core.util.Hex;
43 -import org.eclipse.leshan.server.californium.LeshanServerBuilder;  
44 import org.eclipse.leshan.server.registration.Registration; 42 import org.eclipse.leshan.server.registration.Registration;
45 import org.nustaq.serialization.FSTConfiguration; 43 import org.nustaq.serialization.FSTConfiguration;
46 import org.thingsboard.server.common.data.DeviceProfile; 44 import org.thingsboard.server.common.data.DeviceProfile;
@@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; @@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
50 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; 48 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
51 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; 49 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
52 50
53 -import java.io.File;  
54 import java.io.IOException; 51 import java.io.IOException;
55 import java.util.ArrayList; 52 import java.util.ArrayList;
56 import java.util.Arrays; 53 import java.util.Arrays;
@@ -72,7 +69,6 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA @@ -72,7 +69,6 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA
72 @Slf4j 69 @Slf4j
73 public class LwM2mTransportHandler { 70 public class LwM2mTransportHandler {
74 71
75 - // public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";  
76 public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0"; 72 public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0";
77 public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; 73 public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
78 public static final String BOOTSTRAP = "bootstrap"; 74 public static final String BOOTSTRAP = "bootstrap";
@@ -85,19 +81,12 @@ public class LwM2mTransportHandler { @@ -85,19 +81,12 @@ public class LwM2mTransportHandler {
85 public static final String KEY_NAME = "keyName"; 81 public static final String KEY_NAME = "keyName";
86 public static final String OBSERVE_LWM2M = "observe"; 82 public static final String OBSERVE_LWM2M = "observe";
87 public static final String ATTRIBUTE_LWM2M = "attributeLwm2m"; 83 public static final String ATTRIBUTE_LWM2M = "attributeLwm2m";
88 -// public static final String RESOURCE_VALUE = "resValue";  
89 -// public static final String RESOURCE_TYPE = "resType";  
90 84
91 private static final String REQUEST = "/request"; 85 private static final String REQUEST = "/request";
92 - // private static final String RESPONSE = "/response";  
93 private static final String ATTRIBUTES = "/" + ATTRIBUTE; 86 private static final String ATTRIBUTES = "/" + ATTRIBUTE;
94 public static final String TELEMETRIES = "/" + TELEMETRY; 87 public static final String TELEMETRIES = "/" + TELEMETRY;
95 - // public static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE;  
96 public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST; 88 public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
97 - // public static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/";  
98 public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/"; 89 public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/";
99 -// public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES;  
100 -// public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES;  
101 90
102 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms 91 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
103 92
@@ -112,6 +101,11 @@ public class LwM2mTransportHandler { @@ -112,6 +101,11 @@ public class LwM2mTransportHandler {
112 101
113 public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; 102 public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";
114 103
  104 + public static final Integer FR_OBJECT_ID = 5;
  105 + public static final Integer FR_RESOURCE_VER_ID = 7;
  106 + public static final String FR_PATH_RESOURCE_VER_ID = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_PATH
  107 + + "0" + LWM2M_SEPARATOR_PATH + FR_RESOURCE_VER_ID;
  108 +
115 public enum LwM2mTypeServer { 109 public enum LwM2mTypeServer {
116 BOOTSTRAP(0, "bootstrap"), 110 BOOTSTRAP(0, "bootstrap"),
117 CLIENT(1, "client"); 111 CLIENT(1, "client");
@@ -168,6 +162,8 @@ public class LwM2mTransportHandler { @@ -168,6 +162,8 @@ public class LwM2mTransportHandler {
168 WRITE_ATTRIBUTES(8, "WriteAttributes"), 162 WRITE_ATTRIBUTES(8, "WriteAttributes"),
169 DELETE(9, "Delete"); 163 DELETE(9, "Delete");
170 164
  165 +// READ_INFO_FW(10, "ReadInfoFirmware");
  166 +
171 public int code; 167 public int code;
172 public String type; 168 public String type;
173 169
@@ -190,21 +186,6 @@ public class LwM2mTransportHandler { @@ -190,21 +186,6 @@ public class LwM2mTransportHandler {
190 public static final String SERVICE_CHANNEL = "SERVICE"; 186 public static final String SERVICE_CHANNEL = "SERVICE";
191 public static final String RESPONSE_CHANNEL = "RESP"; 187 public static final String RESPONSE_CHANNEL = "RESP";
192 188
193 - public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {  
194 - NetworkConfig coapConfig;  
195 - File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME);  
196 - if (configFile.isFile()) {  
197 - coapConfig = new NetworkConfig();  
198 - coapConfig.load(configFile);  
199 - } else {  
200 - coapConfig = LeshanServerBuilder.createDefaultNetworkConfig();  
201 - coapConfig.store(configFile);  
202 - }  
203 - coapConfig.setString("COAP_PORT", Integer.toString(serverPortNoSec));  
204 - coapConfig.setString("COAP_SECURE_PORT", Integer.toString(serverSecurePort));  
205 - return coapConfig;  
206 - }  
207 -  
208 public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException { 189 public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException {
209 switch (type) { 190 switch (type) {
210 case BOOLEAN: 191 case BOOLEAN:
@@ -67,6 +67,7 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; @@ -67,6 +67,7 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
67 import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; 67 import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST;
68 import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; 68 import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND;
69 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT; 69 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT;
  70 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID;
70 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; 71 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
71 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; 72 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
72 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; 73 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
@@ -125,7 +126,6 @@ public class LwM2mTransportRequest { @@ -125,7 +126,6 @@ public class LwM2mTransportRequest {
125 public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper, 126 public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
126 String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { 127 String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
127 try { 128 try {
128 -  
129 String target = convertPathFromIdVerToObjectId(targetIdVer); 129 String target = convertPathFromIdVerToObjectId(targetIdVer);
130 DownlinkRequest request = null; 130 DownlinkRequest request = null;
131 ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; 131 ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
@@ -145,11 +145,11 @@ public class LwM2mTransportRequest { @@ -145,11 +145,11 @@ public class LwM2mTransportRequest {
145 break; 145 break;
146 case OBSERVE: 146 case OBSERVE:
147 if (resultIds.isResource()) { 147 if (resultIds.isResource()) {
148 - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); 148 + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
149 } else if (resultIds.isObjectInstance()) { 149 } else if (resultIds.isObjectInstance()) {
150 - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId()); 150 + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId());
151 } else if (resultIds.getObjectId() >= 0) { 151 } else if (resultIds.getObjectId() >= 0) {
152 - request = new ObserveRequest(resultIds.getObjectId()); 152 + request = new ObserveRequest(contentFormat, resultIds.getObjectId());
153 } 153 }
154 break; 154 break;
155 case OBSERVE_CANCEL: 155 case OBSERVE_CANCEL:
@@ -171,8 +171,6 @@ public class LwM2mTransportRequest { @@ -171,8 +171,6 @@ public class LwM2mTransportRequest {
171 break; 171 break;
172 case WRITE_REPLACE: 172 case WRITE_REPLACE:
173 // Request to write a <b>String Single-Instance Resource</b> using the TLV content format. 173 // Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
174 -// resource = lwM2MClient.getResourceModel(targetIdVer);  
175 -// if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {  
176 resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() 174 resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
177 .getModelProvider()); 175 .getModelProvider());
178 if (contentFormat.equals(ContentFormat.TLV)) { 176 if (contentFormat.equals(ContentFormat.TLV)) {
@@ -181,7 +179,6 @@ public class LwM2mTransportRequest { @@ -181,7 +179,6 @@ public class LwM2mTransportRequest {
181 registration, rpcRequest); 179 registration, rpcRequest);
182 } 180 }
183 // Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON) 181 // Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON)
184 -// else if (!contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {  
185 else if (!contentFormat.equals(ContentFormat.TLV)) { 182 else if (!contentFormat.equals(ContentFormat.TLV)) {
186 request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), 183 request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
187 resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type, 184 resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type,
@@ -215,13 +212,16 @@ public class LwM2mTransportRequest { @@ -215,13 +212,16 @@ public class LwM2mTransportRequest {
215 long finalTimeoutInMs = timeoutInMs; 212 long finalTimeoutInMs = timeoutInMs;
216 lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest)); 213 lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest));
217 } catch (Exception e) { 214 } catch (Exception e) {
218 - log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper, e); 215 + log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
  216 + }
  217 + } else if (OBSERVE_CANCEL == typeOper) {
  218 + log.trace("[{}], [{}] - [{}] SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
  219 + if (rpcRequest != null) {
  220 + rpcRequest.setInfoMsg(null);
  221 + serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);
219 } 222 }
220 - } else if (OBSERVE_CANCEL == typeOper && rpcRequest != null) {  
221 - rpcRequest.setInfoMsg(null);  
222 - serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);  
223 } else { 223 } else {
224 - log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper, targetIdVer); 224 + log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
225 if (rpcRequest != null) { 225 if (rpcRequest != null) {
226 String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; 226 String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
227 serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); 227 serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
@@ -236,8 +236,8 @@ public class LwM2mTransportRequest { @@ -236,8 +236,8 @@ public class LwM2mTransportRequest {
236 Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); 236 Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
237 String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO, 237 String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO,
238 OBSERVE_READ_ALL.type, observationPaths); 238 OBSERVE_READ_ALL.type, observationPaths);
239 - serviceImpl.sendLogsToThingsboard(msg, registration);  
240 - log.info("[{}], [{}]", registration.getEndpoint(), msg); 239 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  240 + log.trace("[{}] [{}], [{}]", typeOper.name(), registration.getEndpoint(), msg);
241 if (rpcRequest != null) { 241 if (rpcRequest != null) {
242 String valueMsg = String.format("Observation paths - %s", observationPaths); 242 String valueMsg = String.format("Observation paths - %s", observationPaths);
243 serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); 243 serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
@@ -246,7 +246,7 @@ public class LwM2mTransportRequest { @@ -246,7 +246,7 @@ public class LwM2mTransportRequest {
246 } catch (Exception e) { 246 } catch (Exception e) {
247 String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, 247 String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
248 typeOper.name(), e.getMessage()); 248 typeOper.name(), e.getMessage());
249 - serviceImpl.sendLogsToThingsboard(msg, registration); 249 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
250 throw new Exception(e); 250 throw new Exception(e);
251 } 251 }
252 } 252 }
@@ -261,45 +261,53 @@ public class LwM2mTransportRequest { @@ -261,45 +261,53 @@ public class LwM2mTransportRequest {
261 private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { 261 private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
262 leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> { 262 leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
263 if (!lwM2MClient.isInit()) { 263 if (!lwM2MClient.isInit()) {
264 - lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); 264 + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
265 } 265 }
266 if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { 266 if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
267 this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest); 267 this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest);
268 - if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) {  
269 - LwM2mNode node = ((WriteRequest) request).getNode();  
270 - Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(),  
271 - ((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath());  
272 - String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",  
273 - LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(),  
274 - response.getCode().getName(), request.getPath().toString(), value);  
275 - serviceImpl.sendLogsToThingsboard(msg, registration);  
276 - log.info("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(),  
277 - ((Response) response.getCoapResponse()).getCode(), response.getCode(),  
278 - request.getPath().toString(), value);  
279 - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);  
280 - }  
281 } else { 268 } else {
282 - String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR, 269 + 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 ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); 270 ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
284 - serviceImpl.sendLogsToThingsboard(msg, registration);  
285 - log.error("[{}], [{}] - [{}] [{}] error SendRequest", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); 271 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  272 + log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
  273 + ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
  274 + if (!lwM2MClient.isInit()) {
  275 + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
  276 + }
286 if (rpcRequest != null) { 277 if (rpcRequest != null) {
287 serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); 278 serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
288 } 279 }
  280 + /** Not Found
  281 + * set setClient_fw_version = empty
  282 + **/
  283 + if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
  284 + lwM2MClient.setUpdateFw(false);
  285 + lwM2MClient.getFrUpdate().setClientFwVersion("");
  286 + log.warn("updateFirmwareClient1");
  287 + serviceImpl.updateFirmwareClient(lwM2MClient);
  288 + }
289 } 289 }
290 }, e -> { 290 }, e -> {
  291 + /** version == null
  292 + * set setClient_fw_version = empty
  293 + **/
  294 + if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
  295 + lwM2MClient.setUpdateFw(false);
  296 + lwM2MClient.getFrUpdate().setClientFwVersion("");
  297 + log.warn("updateFirmwareClient2");
  298 + serviceImpl.updateFirmwareClient(lwM2MClient);
  299 + }
291 if (!lwM2MClient.isInit()) { 300 if (!lwM2MClient.isInit()) {
292 - lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); 301 + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
293 } 302 }
294 - String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client",  
295 - LOG_LW2M_ERROR, request.getPath().toString(), e.getMessage());  
296 - serviceImpl.sendLogsToThingsboard(msg, registration);  
297 - log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); 303 + String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s",
  304 + LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage());
  305 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  306 + log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
298 if (rpcRequest != null) { 307 if (rpcRequest != null) {
299 serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); 308 serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
300 } 309 }
301 }); 310 });
302 -  
303 } 311 }
304 312
305 private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, 313 private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
@@ -323,7 +331,9 @@ public class LwM2mTransportRequest { @@ -323,7 +331,9 @@ public class LwM2mTransportRequest {
323 Date date = new Date(Long.decode(value.toString())); 331 Date date = new Date(Long.decode(value.toString()));
324 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date); 332 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date);
325 case OPAQUE: // byte[] value, base64 333 case OPAQUE: // byte[] value, base64
326 - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())); 334 + byte[] valueRequest = value instanceof byte[] ? (byte[]) value : Hex.decodeHex(value.toString().toCharArray());
  335 + return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueRequest) :
  336 + new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest);
327 default: 337 default:
328 } 338 }
329 } 339 }
@@ -337,7 +347,7 @@ public class LwM2mTransportRequest { @@ -337,7 +347,7 @@ public class LwM2mTransportRequest {
337 String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; 347 String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
338 String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", 348 String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
339 patn, type, value, e.toString()); 349 patn, type, value, e.toString());
340 - serviceImpl.sendLogsToThingsboard(msg, registration); 350 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
341 log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); 351 log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
342 if (rpcRequest != null) { 352 if (rpcRequest != null) {
343 String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); 353 String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
@@ -369,7 +379,7 @@ public class LwM2mTransportRequest { @@ -369,7 +379,7 @@ public class LwM2mTransportRequest {
369 DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { 379 DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
370 String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); 380 String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
371 if (response instanceof ReadResponse) { 381 if (response instanceof ReadResponse) {
372 - serviceImpl.onObservationResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); 382 + serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
373 } else if (response instanceof CancelObservationResponse) { 383 } else if (response instanceof CancelObservationResponse) {
374 log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response); 384 log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response);
375 385
@@ -389,14 +399,33 @@ public class LwM2mTransportRequest { @@ -389,14 +399,33 @@ public class LwM2mTransportRequest {
389 } else if (response instanceof WriteAttributesResponse) { 399 } else if (response instanceof WriteAttributesResponse) {
390 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response); 400 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
391 } else if (response instanceof WriteResponse) { 401 } else if (response instanceof WriteResponse) {
392 - log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", pathIdVer, response); 402 + log.info("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
  403 + this.infoWriteResponse(registration, response, request);
393 serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); 404 serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
394 } 405 }
395 - if (rpcRequest != null && (response instanceof ExecuteResponse  
396 - || response instanceof WriteAttributesResponse  
397 - || response instanceof DeleteResponse)) {  
398 - rpcRequest.setInfoMsg(null);  
399 - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null); 406 + if (rpcRequest != null) {
  407 + if (response instanceof ExecuteResponse
  408 + || response instanceof WriteAttributesResponse
  409 + || response instanceof DeleteResponse) {
  410 + rpcRequest.setInfoMsg(null);
  411 + serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
  412 + } else if (response instanceof WriteResponse) {
  413 + serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
  414 + }
400 } 415 }
401 } 416 }
  417 +
  418 + private void infoWriteResponse(Registration registration, LwM2mResponse response,
  419 + DownlinkRequest request) {
  420 + LwM2mNode node = ((WriteRequest) request).getNode();
  421 + Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(),
  422 + ((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath());
  423 + String msg = String.format("%s: Update finished successfully: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s",
  424 + LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(),
  425 + response.getCode().getName(), request.getPath().toString(), value);
  426 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  427 + log.warn("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName().toString(), registration.getEndpoint(),
  428 + ((Response) response.getCoapResponse()).getCode(), response.getCode(),
  429 + request.getPath().toString(), value);
  430 + }
402 } 431 }
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 package org.thingsboard.server.transport.lwm2m.server; 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.californium.core.network.config.NetworkConfig;
  20 +import org.eclipse.californium.core.network.stack.BlockwiseLayer;
19 import org.eclipse.californium.scandium.config.DtlsConnectorConfig; 21 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; 22 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
21 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; 23 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
@@ -57,7 +59,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE @@ -57,7 +59,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
57 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; 59 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
58 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; 60 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
59 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; 61 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
60 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; 62 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
61 63
62 @Slf4j 64 @Slf4j
63 @Component 65 @Component
@@ -92,7 +94,10 @@ public class LwM2mTransportServerConfiguration { @@ -92,7 +94,10 @@ public class LwM2mTransportServerConfiguration {
92 /** Use a magic converter to support bad type send by the UI. */ 94 /** Use a magic converter to support bad type send by the UI. */
93 builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); 95 builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
94 96
  97 +
95 /** Create CoAP Config */ 98 /** Create CoAP Config */
  99 + NetworkConfig networkConfig = getCoapConfig(serverPortNoSec, serverSecurePort);
  100 + BlockwiseLayer blockwiseLayer = new BlockwiseLayer(networkConfig);
96 builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort)); 101 builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort));
97 102
98 /** Define model provider (Create Models )*/ 103 /** Define model provider (Create Models )*/
@@ -110,6 +115,7 @@ public class LwM2mTransportServerConfiguration { @@ -110,6 +115,7 @@ public class LwM2mTransportServerConfiguration {
110 115
111 /** Create DTLS Config */ 116 /** Create DTLS Config */
112 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); 117 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
  118 + dtlsConfig.setServerOnly(true);
113 dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups()); 119 dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups());
114 dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers()); 120 dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers());
115 if (this.pskMode) { 121 if (this.pskMode) {
@@ -39,7 +39,7 @@ public interface LwM2mTransportService extends TbTransportService { @@ -39,7 +39,7 @@ public interface LwM2mTransportService extends TbTransportService {
39 39
40 void setCancelObservations(Registration registration); 40 void setCancelObservations(Registration registration);
41 41
42 - void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest); 42 + void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest);
43 43
44 void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); 44 void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
45 45
@@ -60,6 +60,4 @@ public interface LwM2mTransportService extends TbTransportService { @@ -60,6 +60,4 @@ public interface LwM2mTransportService extends TbTransportService {
60 void doTrigger(Registration registration, String path); 60 void doTrigger(Registration registration, String path);
61 61
62 void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); 62 void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
63 -  
64 -  
65 } 63 }
@@ -38,10 +38,15 @@ import org.eclipse.leshan.server.registration.Registration; @@ -38,10 +38,15 @@ import org.eclipse.leshan.server.registration.Registration;
38 import org.springframework.context.annotation.Lazy; 38 import org.springframework.context.annotation.Lazy;
39 import org.springframework.stereotype.Service; 39 import org.springframework.stereotype.Service;
40 import org.thingsboard.common.util.JacksonUtil; 40 import org.thingsboard.common.util.JacksonUtil;
  41 +import org.thingsboard.server.cache.firmware.FirmwareDataCache;
41 import org.thingsboard.server.common.data.Device; 42 import org.thingsboard.server.common.data.Device;
42 import org.thingsboard.server.common.data.DeviceProfile; 43 import org.thingsboard.server.common.data.DeviceProfile;
43 -import org.thingsboard.server.common.data.DeviceTransportType; 44 +import org.thingsboard.server.common.data.firmware.FirmwareKey;
  45 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  46 +import org.thingsboard.server.common.data.firmware.FirmwareUtil;
  47 +import org.thingsboard.server.common.data.id.FirmwareId;
44 import org.thingsboard.server.common.transport.TransportService; 48 import org.thingsboard.server.common.transport.TransportService;
  49 +import org.thingsboard.server.common.transport.TransportServiceCallback;
45 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 50 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
46 import org.thingsboard.server.common.transport.service.DefaultTransportService; 51 import org.thingsboard.server.common.transport.service.DefaultTransportService;
47 import org.thingsboard.server.gen.transport.TransportProtos; 52 import org.thingsboard.server.gen.transport.TransportProtos;
@@ -77,9 +82,11 @@ import java.util.stream.Collectors; @@ -77,9 +82,11 @@ import java.util.stream.Collectors;
77 82
78 import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; 83 import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
79 import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; 84 import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
  85 +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
80 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; 86 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
81 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED; 87 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED;
82 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST; 88 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST;
  89 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID;
83 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; 90 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
84 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; 91 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
85 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; 92 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
@@ -110,6 +117,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -110,6 +117,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
110 private ExecutorService executorUpdateRegistered; 117 private ExecutorService executorUpdateRegistered;
111 private ExecutorService executorUnRegistered; 118 private ExecutorService executorUnRegistered;
112 private LwM2mValueConverterImpl converter; 119 private LwM2mValueConverterImpl converter;
  120 + private FirmwareDataCache firmwareDataCache;
  121 +
113 122
114 private final TransportService transportService; 123 private final TransportService transportService;
115 124
@@ -121,12 +130,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -121,12 +130,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
121 130
122 private final LwM2mTransportRequest lwM2mTransportRequest; 131 private final LwM2mTransportRequest lwM2mTransportRequest;
123 132
124 - public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, @Lazy LwM2mTransportRequest lwM2mTransportRequest) { 133 + public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer,
  134 + LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer,
  135 + @Lazy LwM2mTransportRequest lwM2mTransportRequest, FirmwareDataCache firmwareDataCache) {
125 this.transportService = transportService; 136 this.transportService = transportService;
126 this.lwM2mTransportContextServer = lwM2mTransportContextServer; 137 this.lwM2mTransportContextServer = lwM2mTransportContextServer;
127 this.lwM2mClientContext = lwM2mClientContext; 138 this.lwM2mClientContext = lwM2mClientContext;
128 this.leshanServer = leshanServer; 139 this.leshanServer = leshanServer;
129 this.lwM2mTransportRequest = lwM2mTransportRequest; 140 this.lwM2mTransportRequest = lwM2mTransportRequest;
  141 + this.firmwareDataCache = firmwareDataCache;
130 } 142 }
131 143
132 @PostConstruct 144 @PostConstruct
@@ -168,8 +180,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -168,8 +180,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
168 transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); 180 transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
169 transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); 181 transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
170 transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null); 182 transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null);
  183 + this.getInfoFirmwareUpdate(lwM2MClient);
171 this.initLwM2mFromClientValue(registration, lwM2MClient); 184 this.initLwM2mFromClientValue(registration, lwM2MClient);
172 - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); 185 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId());
173 } else { 186 } else {
174 log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); 187 log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
175 } 188 }
@@ -224,7 +237,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -224,7 +237,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
224 executorUnRegistered.submit(() -> { 237 executorUnRegistered.submit(() -> {
225 try { 238 try {
226 this.setCancelObservations(registration); 239 this.setCancelObservations(registration);
227 - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); 240 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
228 this.closeClientSession(registration); 241 this.closeClientSession(registration);
229 } catch (Throwable t) { 242 } catch (Throwable t) {
230 log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); 243 log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
@@ -257,7 +270,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -257,7 +270,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
257 @Override 270 @Override
258 public void onSleepingDev(Registration registration) { 271 public void onSleepingDev(Registration registration) {
259 log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); 272 log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
260 - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration); 273 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration.getId());
261 274
262 //TODO: associate endpointId with device information. 275 //TODO: associate endpointId with device information.
263 } 276 }
@@ -280,7 +293,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -280,7 +293,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
280 * @param response - observe 293 * @param response - observe
281 */ 294 */
282 @Override 295 @Override
283 - public void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) { 296 + public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) {
284 if (response.getContent() != null) { 297 if (response.getContent() != null) {
285 if (response.getContent() instanceof LwM2mObject) { 298 if (response.getContent() instanceof LwM2mObject) {
286 LwM2mObject lwM2mObject = (LwM2mObject) response.getContent(); 299 LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
@@ -304,20 +317,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -304,20 +317,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
304 317
305 /** 318 /**
306 * Update - send request in change value resources in Client 319 * Update - send request in change value resources in Client
307 - * Path to resources from profile equal keyName or from ModelObject equal name  
308 - * Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)  
309 - * Delete - nothing * 320 + * 1. FirmwareUpdate:
  321 + * - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0
  322 + * 2. Shared Other AttributeUpdate
  323 + * -- Path to resources from profile equal keyName or from ModelObject equal name
  324 + * -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
  325 + * 3. Delete - nothing
310 * 326 *
311 * @param msg - 327 * @param msg -
312 */ 328 */
313 @Override 329 @Override
314 public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { 330 public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
  331 + LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
315 if (msg.getSharedUpdatedCount() > 0) { 332 if (msg.getSharedUpdatedCount() > 0) {
316 msg.getSharedUpdatedList().forEach(tsKvProto -> { 333 msg.getSharedUpdatedList().forEach(tsKvProto -> {
317 String pathName = tsKvProto.getKv().getKey(); 334 String pathName = tsKvProto.getKv().getKey();
318 String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); 335 String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
319 Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); 336 Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv());
320 - LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); 337 + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
  338 + this.getInfoFirmwareUpdate(lwM2MClient);
  339 + }
321 if (pathIdVer != null) { 340 if (pathIdVer != null) {
322 ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() 341 ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
323 .getModelProvider()); 342 .getModelProvider());
@@ -327,19 +346,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -327,19 +346,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
327 log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); 346 log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew);
328 String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", 347 String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
329 LOG_LW2M_ERROR, pathIdVer, valueNew); 348 LOG_LW2M_ERROR, pathIdVer, valueNew);
330 - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); 349 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
331 } 350 }
332 } else { 351 } else {
333 log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); 352 log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
334 String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", 353 String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
335 LOG_LW2M_ERROR, pathName, valueNew); 354 LOG_LW2M_ERROR, pathName, valueNew);
336 - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); 355 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
337 } 356 }
  357 +
338 }); 358 });
339 } else if (msg.getSharedDeletedCount() > 0) { 359 } else if (msg.getSharedDeletedCount() > 0) {
  360 + msg.getSharedUpdatedList().forEach(tsKvProto -> {
  361 + String pathName = tsKvProto.getKv().getKey();
  362 + Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv());
  363 + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
  364 + lwM2MClient.getFrUpdate().setCurrentFwVersion((String) valueNew);
  365 + }
  366 + });
340 log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); 367 log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
341 } 368 }
342 -  
343 } 369 }
344 370
345 /** 371 /**
@@ -455,23 +481,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -455,23 +481,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
455 } 481 }
456 if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) { 482 if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) {
457 lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey) 483 lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
458 - .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {  
459 - }.getType())); 484 + .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
  485 + }.getType()));
460 } 486 }
461 lwm2mClientRpcRequest.setSessionInfo(sessionInfo); 487 lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
462 if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) { 488 if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) {
463 lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " + 489 lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " +
464 lwm2mClientRpcRequest.keyNameKey + " is null or bad format"); 490 lwm2mClientRpcRequest.keyNameKey + " is null or bad format");
465 - }  
466 - else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper() 491 + } else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
467 || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper()) 492 || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper())
468 - && lwm2mClientRpcRequest.getTargetIdVer() !=null 493 + && lwm2mClientRpcRequest.getTargetIdVer() != null
469 && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource() 494 && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource()
470 || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) { 495 || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) {
471 - lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey 496 + lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
472 + ". Only Resource or ResourceInstance can be this operation"); 497 + ". Only Resource or ResourceInstance can be this operation");
473 - }  
474 - else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()){ 498 + } else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) {
475 lwm2mClientRpcRequest.setErrorMsg("Procedures In Development..."); 499 lwm2mClientRpcRequest.setErrorMsg("Procedures In Development...");
476 } 500 }
477 } else { 501 } else {
@@ -483,23 +507,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -483,23 +507,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
483 return lwm2mClientRpcRequest; 507 return lwm2mClientRpcRequest;
484 } 508 }
485 509
486 - public void sentRpcRequest (Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { 510 + public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
487 rpcRequest.setResponseCode(requestCode); 511 rpcRequest.setResponseCode(requestCode);
488 - if (LOG_LW2M_ERROR.equals(typeMsg)) {  
489 - rpcRequest.setInfoMsg(null);  
490 - rpcRequest.setValueMsg(null);  
491 - if (rpcRequest.getErrorMsg() == null) {  
492 - msg = msg.isEmpty() ? null : msg;  
493 - rpcRequest.setErrorMsg(msg);  
494 - }  
495 - } else if (LOG_LW2M_INFO.equals(typeMsg)) {  
496 - if (rpcRequest.getInfoMsg() == null) {  
497 - rpcRequest.setInfoMsg(msg);  
498 - }  
499 - } else if (LOG_LW2M_VALUE.equals(typeMsg)) {  
500 - if (rpcRequest.getValueMsg() == null) {  
501 - rpcRequest.setValueMsg(msg);  
502 - } 512 + if (LOG_LW2M_ERROR.equals(typeMsg)) {
  513 + rpcRequest.setInfoMsg(null);
  514 + rpcRequest.setValueMsg(null);
  515 + if (rpcRequest.getErrorMsg() == null) {
  516 + msg = msg.isEmpty() ? null : msg;
  517 + rpcRequest.setErrorMsg(msg);
  518 + }
  519 + } else if (LOG_LW2M_INFO.equals(typeMsg)) {
  520 + if (rpcRequest.getInfoMsg() == null) {
  521 + rpcRequest.setInfoMsg(msg);
  522 + }
  523 + } else if (LOG_LW2M_VALUE.equals(typeMsg)) {
  524 + if (rpcRequest.getValueMsg() == null) {
  525 + rpcRequest.setValueMsg(msg);
  526 + }
503 } 527 }
504 this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo()); 528 this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo());
505 } 529 }
@@ -556,7 +580,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -556,7 +580,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
556 */ 580 */
557 protected void onAwakeDev(Registration registration) { 581 protected void onAwakeDev(Registration registration) {
558 log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); 582 log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
559 - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration); 583 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId());
560 //TODO: associate endpointId with device information. 584 //TODO: associate endpointId with device information.
561 } 585 }
562 586
@@ -583,10 +607,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -583,10 +607,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
583 607
584 /** 608 /**
585 * @param logMsg - text msg 609 * @param logMsg - text msg
586 - * @param registration - Id of Registration LwM2M Client 610 + * @param registrationId - Id of Registration LwM2M Client
587 */ 611 */
588 - public void sendLogsToThingsboard(String logMsg, Registration registration) {  
589 - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); 612 + public void sendLogsToThingsboard(String logMsg, String registrationId) {
  613 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId);
590 if (logMsg != null && sessionInfo != null) { 614 if (logMsg != null && sessionInfo != null) {
591 this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo); 615 this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo);
592 } 616 }
@@ -610,7 +634,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -610,7 +634,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
610 if (clientObjects != null && clientObjects.size() > 0) { 634 if (clientObjects != null && clientObjects.size() > 0) {
611 if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { 635 if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
612 // #2 636 // #2
613 - lwM2MClient.getPendingRequests().addAll(clientObjects); 637 + lwM2MClient.getPendingReadRequests().addAll(clientObjects);
614 clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), 638 clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(),
615 null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); 639 null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null));
616 } 640 }
@@ -652,6 +676,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -652,6 +676,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
652 * Sending observe value of resources to thingsboard 676 * Sending observe value of resources to thingsboard
653 * #1 Return old Value Resource from LwM2MClient 677 * #1 Return old Value Resource from LwM2MClient
654 * #2 Update new Resources (replace old Resource Value on new Resource Value) 678 * #2 Update new Resources (replace old Resource Value on new Resource Value)
  679 + * #3 If fr_update -> UpdateFirmware
  680 + * #4 updateAttrTelemetry
655 * 681 *
656 * @param registration - Registration LwM2M Client 682 * @param registration - Registration LwM2M Client
657 * @param lwM2mResource - LwM2mSingleResource response.getContent() 683 * @param lwM2mResource - LwM2mSingleResource response.getContent()
@@ -661,6 +687,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -661,6 +687,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
661 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); 687 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
662 if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() 688 if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
663 .getModelProvider())) { 689 .getModelProvider())) {
  690 + if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) &&
  691 + lwM2MClient.getFrUpdate().getCurrentFwVersion() != null
  692 + && !lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())
  693 + && lwM2MClient.isUpdateFw()) {
  694 +
  695 + /** version != null
  696 + * set setClient_fw_version = value
  697 + **/
  698 + lwM2MClient.setUpdateFw(false);
  699 + lwM2MClient.getFrUpdate().setClientFwVersion(lwM2mResource.getValue().toString());
  700 + log.warn("updateFirmwareClient3");
  701 + this.updateFirmwareClient(lwM2MClient);
  702 + }
664 Set<String> paths = new HashSet<>(); 703 Set<String> paths = new HashSet<>();
665 paths.add(path); 704 paths.add(path);
666 this.updateAttrTelemetry(registration, paths); 705 this.updateAttrTelemetry(registration, paths);
@@ -669,6 +708,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -669,6 +708,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
669 } 708 }
670 } 709 }
671 710
  711 +
672 /** 712 /**
673 * send Attribute and Telemetry to Thingsboard 713 * send Attribute and Telemetry to Thingsboard
674 * #1 - get AttrName/TelemetryName with value from LwM2MClient: 714 * #1 - get AttrName/TelemetryName with value from LwM2MClient:
@@ -723,22 +763,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -723,22 +763,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
723 params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); 763 params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
724 result = params.keySet(); 764 result = params.keySet();
725 } 765 }
726 - if (!result.isEmpty()) { 766 + if (result != null && !result.isEmpty()) {
727 // #1 767 // #1
728 Set<String> pathSend = result.stream().filter(target -> { 768 Set<String> pathSend = result.stream().filter(target -> {
729 return target.split(LWM2M_SEPARATOR_PATH).length < 3 ? 769 return target.split(LWM2M_SEPARATOR_PATH).length < 3 ?
730 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) : 770 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) :
731 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]); 771 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]);
732 } 772 }
733 - )  
734 - .collect(Collectors.toUnmodifiableSet()); 773 + ).collect(Collectors.toUnmodifiableSet());
735 if (!pathSend.isEmpty()) { 774 if (!pathSend.isEmpty()) {
736 - lwM2MClient.getPendingRequests().addAll(pathSend); 775 + lwM2MClient.getPendingReadRequests().addAll(pathSend);
737 ConcurrentHashMap<String, Object> finalParams = params; 776 ConcurrentHashMap<String, Object> finalParams = params;
738 - pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),  
739 - finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); 777 + pathSend.forEach(target -> {
  778 + lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
  779 + finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
  780 + });
740 if (OBSERVE.equals(typeOper)) { 781 if (OBSERVE.equals(typeOper)) {
741 - lwM2MClient.initValue(this, null); 782 + lwM2MClient.initReadValue(this, null);
742 } 783 }
743 } 784 }
744 } 785 }
@@ -969,7 +1010,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -969,7 +1010,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
969 // update value in Resources 1010 // update value in Resources
970 registrationIds.forEach(registrationId -> { 1011 registrationIds.forEach(registrationId -> {
971 Registration registration = lwM2mClientContext.getRegistration(registrationId); 1012 Registration registration = lwM2mClientContext.getRegistration(registrationId);
972 - this.readResourceValueObserve(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ); 1013 + this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
973 // send attr/telemetry to tingsboard for new path 1014 // send attr/telemetry to tingsboard for new path
974 this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd()); 1015 this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd());
975 }); 1016 });
@@ -998,12 +1039,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -998,12 +1039,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
998 registrationIds.forEach(registrationId -> { 1039 registrationIds.forEach(registrationId -> {
999 Registration registration = lwM2mClientContext.getRegistration(registrationId); 1040 Registration registration = lwM2mClientContext.getRegistration(registrationId);
1000 if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) { 1041 if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
1001 - this.readResourceValueObserve(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); 1042 + this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
1002 } 1043 }
1003 // 5.3 del 1044 // 5.3 del
1004 // send Request cancel observe to Client 1045 // send Request cancel observe to Client
1005 if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) { 1046 if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) {
1006 - this.cancelObserveIsValue(registration, postObserveAnalyzer.getPathPostParametersDel()); 1047 + this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel());
1007 } 1048 }
1008 }); 1049 });
1009 } 1050 }
@@ -1043,7 +1084,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1043,7 +1084,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1043 * @param registration - Registration LwM2M Client 1084 * @param registration - Registration LwM2M Client
1044 * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] 1085 * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
1045 */ 1086 */
1046 - private void readResourceValueObserve(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) { 1087 + private void readObserveFromProfile(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) {
1047 targets.forEach(target -> { 1088 targets.forEach(target -> {
1048 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target)); 1089 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
1049 if (pathIds.isResource()) { 1090 if (pathIds.isResource()) {
@@ -1133,7 +1174,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1133,7 +1174,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1133 1174
1134 } 1175 }
1135 1176
1136 - private void cancelObserveIsValue(Registration registration, Set<String> paramAnallyzer) { 1177 + private void cancelObserveFromProfile(Registration registration, Set<String> paramAnallyzer) {
1137 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); 1178 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
1138 paramAnallyzer.forEach(pathIdVer -> { 1179 paramAnallyzer.forEach(pathIdVer -> {
1139 if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) { 1180 if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) {
@@ -1153,7 +1194,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1153,7 +1194,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1153 log.error("Failed update resource [{}] [{}]", path, valueNew); 1194 log.error("Failed update resource [{}] [{}]", path, valueNew);
1154 String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", 1195 String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad",
1155 LOG_LW2M_ERROR, path, valueNew); 1196 LOG_LW2M_ERROR, path, valueNew);
1156 - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); 1197 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
1157 log.info("Failed update resource [{}] [{}]", path, valueNew); 1198 log.info("Failed update resource [{}] [{}]", path, valueNew);
1158 } 1199 }
1159 } 1200 }
@@ -1182,8 +1223,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1182,8 +1223,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1182 } 1223 }
1183 1224
1184 /** 1225 /**
1185 - * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values  
1186 - * #1 Get path resource by result attributesResponse 1226 + * 1. FirmwareUpdate:
  1227 + * - msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0
  1228 + * 2. Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
  1229 + * - Get path resource by result attributesResponse
1187 * 1230 *
1188 * @param attributesResponse - 1231 * @param attributesResponse -
1189 * @param sessionInfo - 1232 * @param sessionInfo -
@@ -1191,6 +1234,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1191,6 +1234,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1191 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { 1234 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
1192 try { 1235 try {
1193 List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList(); 1236 List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList();
  1237 +
1194 this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo); 1238 this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo);
1195 } catch (Exception e) { 1239 } catch (Exception e) {
1196 log.error(String.valueOf(e)); 1240 log.error(String.valueOf(e));
@@ -1274,7 +1318,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1274,7 +1318,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1274 */ 1318 */
1275 private SessionInfoProto getValidateSessionInfo(String registrationId) { 1319 private SessionInfoProto getValidateSessionInfo(String registrationId) {
1276 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(null, registrationId); 1320 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(null, registrationId);
1277 - return getNewSessionInfoProto(lwM2MClient); 1321 + return lwM2MClient != null ? this.getNewSessionInfoProto(lwM2MClient) : null;
1278 } 1322 }
1279 1323
1280 /** 1324 /**
@@ -1293,28 +1337,88 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1293,28 +1337,88 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1293 } 1337 }
1294 1338
1295 /** 1339 /**
1296 - * !!! sharedAttr === profileAttr !!!  
1297 - * If there is a difference in values between the current resource values and the shared attribute values  
1298 - * when the client connects to the server  
1299 - * #1 get attributes name from profile include name resources in ModelObject if resource isWritable  
1300 - * #2.1 #1 size > 0 => send Request getAttributes to thingsboard 1340 + * #1. !!! sharedAttr === profileAttr !!!
  1341 + * - If there is a difference in values between the current resource values and the shared attribute values
  1342 + * - when the client connects to the server
  1343 + * #1.1 get attributes name from profile include name resources in ModelObject if resource isWritable
  1344 + * #1.2 #1 size > 0 => send Request getAttributes to thingsboard
  1345 + * #2. FirmwareAttribute subscribe:
1301 * 1346 *
1302 * @param lwM2MClient - LwM2M Client 1347 * @param lwM2MClient - LwM2M Client
1303 */ 1348 */
1304 public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) { 1349 public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) {
1305 SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); 1350 SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
1306 if (sessionInfo != null) { 1351 if (sessionInfo != null) {
1307 - //#1.1 + #1.2  
1308 - List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);  
1309 - if (attrSharedNames.size() > 0) {  
1310 - //#2.1 1352 + //#1.1
  1353 + ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient);
  1354 + if (keyNamesMap.values().size() > 0) {
1311 try { 1355 try {
1312 - TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, attrSharedNames); 1356 + //#1.2
  1357 + TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, keyNamesMap.values());
1313 transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); 1358 transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
1314 } catch (AdaptorException e) { 1359 } catch (AdaptorException e) {
1315 log.warn("Failed to decode get attributes request", e); 1360 log.warn("Failed to decode get attributes request", e);
1316 } 1361 }
1317 } 1362 }
  1363 +
  1364 + }
  1365 + }
  1366 +
  1367 + public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) {
  1368 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
  1369 + if (sessionInfo != null) {
  1370 + TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
  1371 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  1372 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  1373 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  1374 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  1375 + .build();
  1376 + transportService.process(sessionInfo, getFirmwareRequestMsg,
  1377 + new TransportServiceCallback<>() {
  1378 + @Override
  1379 + public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
  1380 + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
  1381 + lwM2MClient.getFrUpdate().setCurrentFwVersion(response.getVersion());
  1382 + lwM2MClient.getFrUpdate().setCurrentFwId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
  1383 + lwM2MClient.setUpdateFw(true);
  1384 + readRequestToClientFirmwareVer(lwM2MClient.getRegistration());
  1385 + } else {
  1386 + log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
  1387 + }
  1388 + }
  1389 +
  1390 + @Override
  1391 + public void onError(Throwable e) {
  1392 + log.trace("Failed to process credentials ", e);
  1393 + }
  1394 + });
  1395 + }
  1396 + }
  1397 +
  1398 + /**
  1399 + * @param registration
  1400 + */
  1401 + public void readRequestToClientFirmwareVer(Registration registration) {
  1402 + String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration);
  1403 + lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(),
  1404 + null, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
  1405 + }
  1406 +
  1407 + /**
  1408 + *
  1409 + * @param lwM2MClient -
  1410 + */
  1411 + public void updateFirmwareClient(LwM2mClient lwM2MClient) {
  1412 + if (!lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())) {
  1413 + int chunkSize = 0;
  1414 + int chunk = 0;
  1415 + byte[] firmwareChunk = firmwareDataCache.get(lwM2MClient.getFrUpdate().getCurrentFwId().toString(), chunkSize, chunk);
  1416 + Integer objectId = 5;
  1417 + String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(objectId);
  1418 + String targetIdVer = LWM2M_SEPARATOR_PATH + objectId + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0;
  1419 + lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.TLV.getName(),
  1420 + firmwareChunk, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
  1421 + log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion());
1318 } 1422 }
1319 } 1423 }
1320 1424
@@ -1326,23 +1430,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1326,23 +1430,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1326 * @param lwM2MClient - 1430 * @param lwM2MClient -
1327 * @return ArrayList keyNames from profile profileAttr && IsWritable 1431 * @return ArrayList keyNames from profile profileAttr && IsWritable
1328 */ 1432 */
1329 - private List<String> getNamesAttrFromProfileIsWritable(LwM2mClient lwM2MClient) { 1433 + private ConcurrentMap<String, String> getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) {
  1434 +
1330 LwM2mClientProfile profile = lwM2mClientContext.getProfile(lwM2MClient.getProfileId()); 1435 LwM2mClientProfile profile = lwM2mClientContext.getProfile(lwM2MClient.getProfileId());
1331 - Set<String> attrSet = new Gson().fromJson(profile.getPostAttributeProfile(),  
1332 - new TypeToken<HashSet<String>>() {  
1333 - }.getType());  
1334 - ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), 1436 + return new Gson().fromJson(profile.getPostKeyNameProfile().toString(),
1335 new TypeToken<ConcurrentHashMap<String, String>>() { 1437 new TypeToken<ConcurrentHashMap<String, String>>() {
1336 }.getType()); 1438 }.getType());
1337 -  
1338 - ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()  
1339 - .stream()  
1340 - .filter(e -> (attrSet.contains(e.getKey()) && validateResourceInModel(lwM2MClient, e.getKey(), true)))  
1341 - .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));  
1342 -  
1343 - Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();  
1344 - namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));  
1345 - return new ArrayList<>(namesIsWritable);  
1346 } 1439 }
1347 1440
1348 private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { 1441 private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) {
@@ -24,11 +24,8 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -24,11 +24,8 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException;
24 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 24 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
25 import org.thingsboard.server.gen.transport.TransportProtos; 25 import org.thingsboard.server.gen.transport.TransportProtos;
26 26
27 -import java.util.Arrays;  
28 -import java.util.HashSet;  
29 -import java.util.List; 27 +import java.util.Collection;
30 import java.util.Random; 28 import java.util.Random;
31 -import java.util.Set;  
32 29
33 @Slf4j 30 @Slf4j
34 @Component("LwM2MJsonAdaptor") 31 @Component("LwM2MJsonAdaptor")
@@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor { @@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
54 } 51 }
55 52
56 @Override 53 @Override
57 - public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException {  
58 - return processGetAttributeRequestMsg(clientKeys, sharedKeys);  
59 - }  
60 -  
61 - protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException { 54 + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException {
62 try { 55 try {
63 TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); 56 TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
64 Random random = new Random(); 57 Random random = new Random();
@@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor { @@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
75 throw new AdaptorException(e); 68 throw new AdaptorException(e);
76 } 69 }
77 } 70 }
78 -  
79 - private Set<String> toStringSet(JsonElement requestBody, String name) {  
80 - JsonElement element = requestBody.getAsJsonObject().get(name);  
81 - if (element != null) {  
82 - return new HashSet<>(Arrays.asList(element.getAsString().split(",")));  
83 - } else {  
84 - return null;  
85 - }  
86 - }  
87 -  
88 } 71 }
@@ -19,7 +19,7 @@ import com.google.gson.JsonElement; @@ -19,7 +19,7 @@ import com.google.gson.JsonElement;
19 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 19 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
20 import org.thingsboard.server.gen.transport.TransportProtos; 20 import org.thingsboard.server.gen.transport.TransportProtos;
21 21
22 -import java.util.List; 22 +import java.util.Collection;
23 23
24 public interface LwM2MTransportAdaptor { 24 public interface LwM2MTransportAdaptor {
25 25
@@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor { @@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor {
27 27
28 TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException; 28 TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException;
29 29
30 - TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException; 30 + TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException;
31 } 31 }
@@ -57,13 +57,15 @@ public class LwM2mClient implements Cloneable { @@ -57,13 +57,15 @@ public class LwM2mClient implements Cloneable {
57 private UUID deviceId; 57 private UUID deviceId;
58 private UUID sessionId; 58 private UUID sessionId;
59 private UUID profileId; 59 private UUID profileId;
  60 + private volatile LwM2mFirmwareUpdate frUpdate;
60 private Registration registration; 61 private Registration registration;
61 private ValidateDeviceCredentialsResponseMsg credentialsResponse; 62 private ValidateDeviceCredentialsResponseMsg credentialsResponse;
62 private final Map<String, ResourceValue> resources; 63 private final Map<String, ResourceValue> resources;
63 private final Map<String, TransportProtos.TsKvProto> delayedRequests; 64 private final Map<String, TransportProtos.TsKvProto> delayedRequests;
64 - private final List<String> pendingRequests; 65 + private final List<String> pendingReadRequests;
65 private final Queue<LwM2mQueuedRequest> queuedRequests; 66 private final Queue<LwM2mQueuedRequest> queuedRequests;
66 private boolean init; 67 private boolean init;
  68 + private volatile boolean updateFw;
67 69
68 public Object clone() throws CloneNotSupportedException { 70 public Object clone() throws CloneNotSupportedException {
69 return super.clone(); 71 return super.clone();
@@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable { @@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable {
75 this.securityInfo = securityInfo; 77 this.securityInfo = securityInfo;
76 this.credentialsResponse = credentialsResponse; 78 this.credentialsResponse = credentialsResponse;
77 this.delayedRequests = new ConcurrentHashMap<>(); 79 this.delayedRequests = new ConcurrentHashMap<>();
78 - this.pendingRequests = new CopyOnWriteArrayList<>(); 80 + this.pendingReadRequests = new CopyOnWriteArrayList<>();
79 this.resources = new ConcurrentHashMap<>(); 81 this.resources = new ConcurrentHashMap<>();
80 this.profileId = profileId; 82 this.profileId = profileId;
81 this.sessionId = sessionId; 83 this.sessionId = sessionId;
82 this.init = false; 84 this.init = false;
  85 + this.updateFw = false;
83 this.queuedRequests = new ConcurrentLinkedQueue<>(); 86 this.queuedRequests = new ConcurrentLinkedQueue<>();
  87 + this.frUpdate = new LwM2mFirmwareUpdate();
84 } 88 }
85 89
86 public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) { 90 public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) {
@@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable { @@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable {
103 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez)); 107 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez));
104 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); 108 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
105 String verRez = getVerFromPathIdVerOrId(pathRez); 109 String verRez = getVerFromPathIdVerOrId(pathRez);
106 - return (verRez == null || verSupportedObject.equals(verRez)) ? modelProvider.getObjectModel(registration) 110 + return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
107 .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null; 111 .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
108 } 112 }
109 113
110 public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider, 114 public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider,
111 LwM2mValueConverterImpl converter) { 115 LwM2mValueConverterImpl converter) {
112 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); 116 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer));
113 - String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());  
114 - String verRez = getVerFromPathIdVerOrId(pathRezIdVer);  
115 Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet(); 117 Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
116 Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration) 118 Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
117 .getObjectModel(pathIds.getObjectId()).resources; 119 .getObjectModel(pathIds.getObjectId()).resources;
@@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable { @@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable {
170 .collect(Collectors.toSet()); 172 .collect(Collectors.toSet());
171 } 173 }
172 174
173 - public void initValue(LwM2mTransportServiceImpl serviceImpl, String path) { 175 + public void initReadValue(LwM2mTransportServiceImpl serviceImpl, String path) {
174 if (path != null) { 176 if (path != null) {
175 - this.pendingRequests.remove(path); 177 + this.pendingReadRequests.remove(path);
176 } 178 }
177 - if (this.pendingRequests.size() == 0) { 179 + if (this.pendingReadRequests.size() == 0) {
178 this.init = true; 180 this.init = true;
179 serviceImpl.putDelayedUpdateResourcesThingsboard(this); 181 serviceImpl.putDelayedUpdateResourcesThingsboard(this);
180 } 182 }
@@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
82 82
83 @Override 83 @Override
84 public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) { 84 public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) {
85 - LwM2mClient client = registrationId != null ? 85 + LwM2mClient client = registrationId != null && this.lwM2mClients.containsKey(registrationId) ?
86 this.lwM2mClients.get(registrationId) : 86 this.lwM2mClients.get(registrationId) :
87 - this.lwM2mClients.containsKey(registration.getId()) ?  
88 - this.lwM2mClients.get(registration.getId()) :  
89 - this.lwM2mClients.get(registration.getEndpoint());  
90 - return client != null ? client : updateInSessionsLwM2MClient(registration); 87 + registration !=null && this.lwM2mClients.containsKey(registration.getId()) ?
  88 + this.lwM2mClients.get(registration.getId()) : registration !=null && this.lwM2mClients.containsKey(registration) ?
  89 + this.lwM2mClients.get(registration.getEndpoint()) : null;
  90 + return client != null ? client : registration!= null ? updateInSessionsLwM2MClient(registration) : null;
91 } 91 }
92 92
93 @Override 93 @Override
  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 +}
@@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
47 import org.thingsboard.server.common.data.DeviceTransportType; 47 import org.thingsboard.server.common.data.DeviceTransportType;
48 import org.thingsboard.server.common.data.TransportPayloadType; 48 import org.thingsboard.server.common.data.TransportPayloadType;
49 import org.thingsboard.server.common.data.device.profile.MqttTopics; 49 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  50 +import org.thingsboard.server.common.data.firmware.FirmwareType;
50 import org.thingsboard.server.common.data.id.FirmwareId; 51 import org.thingsboard.server.common.data.id.FirmwareId;
51 import org.thingsboard.server.common.msg.EncryptionUtil; 52 import org.thingsboard.server.common.msg.EncryptionUtil;
52 import org.thingsboard.server.common.msg.tools.TbRateLimitsException; 53 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
@@ -59,6 +60,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; @@ -59,6 +60,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
59 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; 60 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
60 import org.thingsboard.server.common.transport.service.DefaultTransportService; 61 import org.thingsboard.server.common.transport.service.DefaultTransportService;
61 import org.thingsboard.server.common.transport.service.SessionMetaData; 62 import org.thingsboard.server.common.transport.service.SessionMetaData;
  63 +import org.thingsboard.server.common.transport.util.SslUtil;
62 import org.thingsboard.server.gen.transport.TransportProtos; 64 import org.thingsboard.server.gen.transport.TransportProtos;
63 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; 65 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
64 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; 66 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
@@ -68,7 +70,6 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; @@ -68,7 +70,6 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
68 import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; 70 import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
69 import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler; 71 import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler;
70 import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher; 72 import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher;
71 -import org.thingsboard.server.common.transport.util.SslUtil;  
72 73
73 import javax.net.ssl.SSLPeerUnverifiedException; 74 import javax.net.ssl.SSLPeerUnverifiedException;
74 import java.io.IOException; 75 import java.io.IOException;
@@ -97,6 +98,8 @@ import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK; @@ -97,6 +98,8 @@ import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK;
97 import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; 98 import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
98 import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; 99 import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
99 import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; 100 import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
  101 +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN;
  102 +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN;
100 103
101 /** 104 /**
102 * @author Andrew Shvayka 105 * @author Andrew Shvayka
@@ -104,7 +107,9 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; @@ -104,7 +107,9 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
104 @Slf4j 107 @Slf4j
105 public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener<Future<? super Void>>, SessionMsgListener { 108 public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
106 109
107 - private static final Pattern FW_PATTERN = Pattern.compile("v2/fw/request/(?<requestId>\\d+)/chunk/(?<chunk>\\d+)"); 110 + private static final Pattern FW_REQUEST_PATTERN = Pattern.compile(DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN);
  111 + private static final Pattern SW_REQUEST_PATTERN = Pattern.compile(DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN);
  112 +
108 113
109 private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE"; 114 private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE";
110 115
@@ -314,38 +319,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -314,38 +319,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
314 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { 319 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) {
315 TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); 320 TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg);
316 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); 321 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg));
317 - } else if ((fwMatcher = FW_PATTERN.matcher(topicName)).find()) {  
318 - String payload = mqttMsg.content().toString(UTF8);  
319 - int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0;  
320 - String requestId = fwMatcher.group("requestId");  
321 - int chunk = Integer.parseInt(fwMatcher.group("chunk"));  
322 -  
323 - if (chunkSize > 0) {  
324 - this.fwChunkSizes.put(requestId, chunkSize);  
325 - } else {  
326 - chunkSize = fwChunkSizes.getOrDefault(requestId, 0);  
327 - }  
328 -  
329 - if (chunkSize > context.getMaxPayloadSize()) {  
330 - sendFirmwareError(ctx, PAYLOAD_TOO_LARGE);  
331 - return;  
332 - }  
333 -  
334 - String firmwareId = fwSessions.get(requestId);  
335 -  
336 - if (firmwareId != null) {  
337 - sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk);  
338 - } else {  
339 - TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo();  
340 - TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()  
341 - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())  
342 - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())  
343 - .setTenantIdMSB(sessionInfo.getTenantIdMSB())  
344 - .setTenantIdLSB(sessionInfo.getTenantIdLSB())  
345 - .build();  
346 - transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg,  
347 - new FirmwareCallback(ctx, mqttMsg.variableHeader().packetId(), getFirmwareRequestMsg, requestId, chunkSize, chunk));  
348 - } 322 + } else if ((fwMatcher = FW_REQUEST_PATTERN.matcher(topicName)).find()) {
  323 + getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.FIRMWARE);
  324 + } else if ((fwMatcher = SW_REQUEST_PATTERN.matcher(topicName)).find()) {
  325 + getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.SOFTWARE);
349 } else { 326 } else {
350 transportService.reportActivity(deviceSessionCtx.getSessionInfo()); 327 transportService.reportActivity(deviceSessionCtx.getSessionInfo());
351 ack(ctx, msgId); 328 ack(ctx, msgId);
@@ -357,6 +334,41 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -357,6 +334,41 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
357 } 334 }
358 } 335 }
359 336
  337 + private void getFirmwareCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, FirmwareType type) {
  338 + String payload = mqttMsg.content().toString(UTF8);
  339 + int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0;
  340 + String requestId = fwMatcher.group("requestId");
  341 + int chunk = Integer.parseInt(fwMatcher.group("chunk"));
  342 +
  343 + if (chunkSize > 0) {
  344 + this.fwChunkSizes.put(requestId, chunkSize);
  345 + } else {
  346 + chunkSize = fwChunkSizes.getOrDefault(requestId, 0);
  347 + }
  348 +
  349 + if (chunkSize > context.getMaxPayloadSize()) {
  350 + sendFirmwareError(ctx, PAYLOAD_TOO_LARGE);
  351 + return;
  352 + }
  353 +
  354 + String firmwareId = fwSessions.get(requestId);
  355 +
  356 + if (firmwareId != null) {
  357 + sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk, type);
  358 + } else {
  359 + TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo();
  360 + TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
  361 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  362 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  363 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  364 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  365 + .setType(type.name())
  366 + .build();
  367 + transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg,
  368 + new FirmwareCallback(ctx, msgId, getFirmwareRequestMsg, requestId, chunkSize, chunk));
  369 + }
  370 + }
  371 +
360 private void ack(ChannelHandlerContext ctx, int msgId) { 372 private void ack(ChannelHandlerContext ctx, int msgId) {
361 if (msgId > 0) { 373 if (msgId > 0) {
362 ctx.writeAndFlush(createMqttPubAckMsg(msgId)); 374 ctx.writeAndFlush(createMqttPubAckMsg(msgId));
@@ -435,7 +447,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -435,7 +447,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
435 if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { 447 if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
436 FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())); 448 FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB()));
437 fwSessions.put(requestId, firmwareId.toString()); 449 fwSessions.put(requestId, firmwareId.toString());
438 - sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk); 450 + sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, FirmwareType.valueOf(response.getType()));
439 } else { 451 } else {
440 sendFirmwareError(ctx, response.getResponseStatus().toString()); 452 sendFirmwareError(ctx, response.getResponseStatus().toString());
441 } 453 }
@@ -448,13 +460,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -448,13 +460,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
448 } 460 }
449 } 461 }
450 462
451 - private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk) { 463 + private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, FirmwareType type) {
452 log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); 464 log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId);
453 ack(ctx, msgId); 465 ack(ctx, msgId);
454 try { 466 try {
455 byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk); 467 byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk);
456 deviceSessionCtx.getPayloadAdaptor() 468 deviceSessionCtx.getPayloadAdaptor()
457 - .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk) 469 + .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type)
458 .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 470 .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
459 } catch (Exception e) { 471 } catch (Exception e) {
460 log.trace("[{}] Failed to send firmware response!", sessionId, e); 472 log.trace("[{}] Failed to send firmware response!", sessionId, e);
@@ -501,6 +513,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -501,6 +513,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
501 case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC: 513 case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC:
502 case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC: 514 case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC:
503 case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC: 515 case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC:
  516 + case MqttTopics.DEVICE_SOFTWARE_RESPONSES_TOPIC:
  517 + case MqttTopics.DEVICE_SOFTWARE_ERROR_TOPIC:
504 registerSubQoS(topic, grantedQoSList, reqQoS); 518 registerSubQoS(topic, grantedQoSList, reqQoS);
505 break; 519 break;
506 default: 520 default:
@@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j; @@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
30 import org.springframework.stereotype.Component; 30 import org.springframework.stereotype.Component;
31 import org.springframework.util.StringUtils; 31 import org.springframework.util.StringUtils;
32 import org.thingsboard.server.common.data.device.profile.MqttTopics; 32 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  33 +import org.thingsboard.server.common.data.firmware.FirmwareType;
33 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 34 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
34 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 35 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
35 import org.thingsboard.server.gen.transport.TransportProtos; 36 import org.thingsboard.server.gen.transport.TransportProtos;
@@ -43,6 +44,9 @@ import java.util.Optional; @@ -43,6 +44,9 @@ import java.util.Optional;
43 import java.util.Set; 44 import java.util.Set;
44 import java.util.UUID; 45 import java.util.UUID;
45 46
  47 +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT;
  48 +
  49 +
46 /** 50 /**
47 * @author Andrew Shvayka 51 * @author Andrew Shvayka
48 */ 52 */
@@ -151,8 +155,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -151,8 +155,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
151 } 155 }
152 156
153 @Override 157 @Override
154 - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) {  
155 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/chunk/" + chunk, firmwareChunk)); 158 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) {
  159 + return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
156 } 160 }
157 161
158 public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { 162 public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
@@ -23,6 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage; @@ -23,6 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage;
23 import io.netty.handler.codec.mqtt.MqttMessageType; 23 import io.netty.handler.codec.mqtt.MqttMessageType;
24 import io.netty.handler.codec.mqtt.MqttPublishMessage; 24 import io.netty.handler.codec.mqtt.MqttPublishMessage;
25 import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; 25 import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
  26 +import org.thingsboard.server.common.data.firmware.FirmwareType;
26 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 27 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
27 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
@@ -77,7 +78,7 @@ public interface MqttTransportAdaptor { @@ -77,7 +78,7 @@ public interface MqttTransportAdaptor {
77 78
78 Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; 79 Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException;
79 80
80 - Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException; 81 + Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException;
81 82
82 default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) { 83 default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) {
83 MqttFixedHeader mqttFixedHeader = 84 MqttFixedHeader mqttFixedHeader =
@@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j; @@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
28 import org.springframework.stereotype.Component; 28 import org.springframework.stereotype.Component;
29 import org.springframework.util.StringUtils; 29 import org.springframework.util.StringUtils;
30 import org.thingsboard.server.common.data.device.profile.MqttTopics; 30 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  31 +import org.thingsboard.server.common.data.firmware.FirmwareType;
31 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 32 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
32 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 33 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
33 import org.thingsboard.server.common.transport.adaptor.ProtoConverter; 34 import org.thingsboard.server.common.transport.adaptor.ProtoConverter;
@@ -38,6 +39,8 @@ import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionConte @@ -38,6 +39,8 @@ import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionConte
38 39
39 import java.util.Optional; 40 import java.util.Optional;
40 41
  42 +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT;
  43 +
41 @Component 44 @Component
42 @Slf4j 45 @Slf4j
43 public class ProtoMqttAdaptor implements MqttTransportAdaptor { 46 public class ProtoMqttAdaptor implements MqttTransportAdaptor {
@@ -165,8 +168,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { @@ -165,8 +168,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
165 } 168 }
166 169
167 @Override 170 @Override
168 - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException {  
169 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/" + chunk, firmwareChunk)); 171 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException {
  172 + return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
170 } 173 }
171 174
172 @Override 175 @Override
@@ -55,7 +55,6 @@ import java.util.Optional; @@ -55,7 +55,6 @@ import java.util.Optional;
55 import java.util.UUID; 55 import java.util.UUID;
56 import java.util.concurrent.ConcurrentHashMap; 56 import java.util.concurrent.ConcurrentHashMap;
57 import java.util.concurrent.ConcurrentLinkedDeque; 57 import java.util.concurrent.ConcurrentLinkedDeque;
58 -import java.util.stream.Collectors;  
59 58
60 @TbSnmpTransportComponent 59 @TbSnmpTransportComponent
61 @Component 60 @Component
@@ -72,25 +71,30 @@ public class SnmpTransportContext extends TransportContext { @@ -72,25 +71,30 @@ public class SnmpTransportContext extends TransportContext {
72 private final SnmpAuthService snmpAuthService; 71 private final SnmpAuthService snmpAuthService;
73 72
74 private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>(); 73 private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>();
75 - private Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>(); 74 + private final Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
76 75
77 @AfterStartUp(order = 2) 76 @AfterStartUp(order = 2)
78 - public void initDevicesSessions() { 77 + public void fetchDevicesAndEstablishSessions() {
79 log.info("Initializing SNMP devices sessions"); 78 log.info("Initializing SNMP devices sessions");
80 - allSnmpDevicesIds = protoEntityService.getAllSnmpDevicesIds().stream()  
81 - .map(DeviceId::new)  
82 - .collect(Collectors.toList());  
83 - log.trace("Found all SNMP devices ids: {}", allSnmpDevicesIds);  
84 79
85 - List<DeviceId> managedDevicesIds = allSnmpDevicesIds.stream()  
86 - .filter(deviceId -> balancingService.isManagedByCurrentTransport(deviceId.getId()))  
87 - .collect(Collectors.toList());  
88 - log.info("SNMP devices managed by current SNMP transport: {}", managedDevicesIds); 80 + int batchIndex = 0;
  81 + int batchSize = 512;
  82 + boolean nextBatchExists = true;
89 83
90 - managedDevicesIds.stream()  
91 - .map(protoEntityService::getDeviceById)  
92 - .collect(Collectors.toList())  
93 - .forEach(this::establishDeviceSession); 84 + while (nextBatchExists) {
  85 + TransportProtos.GetSnmpDevicesResponseMsg snmpDevicesResponse = protoEntityService.getSnmpDevicesIds(batchIndex, batchSize);
  86 + snmpDevicesResponse.getIdsList().stream()
  87 + .map(id -> new DeviceId(UUID.fromString(id)))
  88 + .peek(allSnmpDevicesIds::add)
  89 + .filter(deviceId -> balancingService.isManagedByCurrentTransport(deviceId.getId()))
  90 + .map(protoEntityService::getDeviceById)
  91 + .forEach(device -> getExecutor().execute(() -> establishDeviceSession(device)));
  92 +
  93 + nextBatchExists = snmpDevicesResponse.getHasNextPage();
  94 + batchIndex++;
  95 + }
  96 +
  97 + log.debug("Found all SNMP devices ids: {}", allSnmpDevicesIds);
94 } 98 }
95 99
96 private void establishDeviceSession(Device device) { 100 private void establishDeviceSession(Device device) {
@@ -81,26 +81,7 @@ public class ProtoTransportEntityService { @@ -81,26 +81,7 @@ public class ProtoTransportEntityService {
81 .orElseThrow(() -> new IllegalArgumentException("Device credentials not found")); 81 .orElseThrow(() -> new IllegalArgumentException("Device credentials not found"));
82 } 82 }
83 83
84 - public List<UUID> getAllSnmpDevicesIds() {  
85 - List<UUID> result = new ArrayList<>();  
86 -  
87 - int page = 0;  
88 - int pageSize = 512;  
89 - boolean hasNextPage = true;  
90 -  
91 - while (hasNextPage) {  
92 - TransportProtos.GetSnmpDevicesResponseMsg responseMsg = requestSnmpDevicesIds(page, pageSize);  
93 - result.addAll(responseMsg.getIdsList().stream()  
94 - .map(UUID::fromString)  
95 - .collect(Collectors.toList()));  
96 - hasNextPage = responseMsg.getHasNextPage();  
97 - page++;  
98 - }  
99 -  
100 - return result;  
101 - }  
102 -  
103 - private TransportProtos.GetSnmpDevicesResponseMsg requestSnmpDevicesIds(int page, int pageSize) { 84 + public TransportProtos.GetSnmpDevicesResponseMsg getSnmpDevicesIds(int page, int pageSize) {
104 TransportProtos.GetSnmpDevicesRequestMsg requestMsg = TransportProtos.GetSnmpDevicesRequestMsg.newBuilder() 85 TransportProtos.GetSnmpDevicesRequestMsg requestMsg = TransportProtos.GetSnmpDevicesRequestMsg.newBuilder()
105 .setPage(page) 86 .setPage(page)
106 .setPageSize(pageSize) 87 .setPageSize(pageSize)
@@ -20,6 +20,7 @@ import lombok.Data; @@ -20,6 +20,7 @@ import lombok.Data;
20 import lombok.Getter; 20 import lombok.Getter;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.thingsboard.common.util.ThingsBoardExecutors;
23 import org.thingsboard.server.cache.firmware.FirmwareDataCache; 24 import org.thingsboard.server.cache.firmware.FirmwareDataCache;
24 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 25 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
25 import org.thingsboard.server.queue.scheduler.SchedulerComponent; 26 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
@@ -57,7 +58,7 @@ public abstract class TransportContext { @@ -57,7 +58,7 @@ public abstract class TransportContext {
57 58
58 @PostConstruct 59 @PostConstruct
59 public void init() { 60 public void init() {
60 - executor = Executors.newWorkStealingPool(50); 61 + executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass());
61 } 62 }
62 63
63 @PreDestroy 64 @PreDestroy
@@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
25 import org.springframework.beans.factory.annotation.Value; 25 import org.springframework.beans.factory.annotation.Value;
26 import org.springframework.context.ApplicationEventPublisher; 26 import org.springframework.context.ApplicationEventPublisher;
27 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
  28 +import org.thingsboard.common.util.ThingsBoardExecutors;
28 import org.thingsboard.common.util.ThingsBoardThreadFactory; 29 import org.thingsboard.common.util.ThingsBoardThreadFactory;
29 import org.thingsboard.server.common.data.ApiUsageRecordKey; 30 import org.thingsboard.server.common.data.ApiUsageRecordKey;
30 import org.thingsboard.server.common.data.ApiUsageState; 31 import org.thingsboard.server.common.data.ApiUsageState;
@@ -186,7 +187,7 @@ public class DefaultTransportService implements TransportService { @@ -186,7 +187,7 @@ public class DefaultTransportService implements TransportService {
186 this.ruleEngineProducerStats = statsFactory.createMessagesStats(StatsType.RULE_ENGINE.getName() + ".producer"); 187 this.ruleEngineProducerStats = statsFactory.createMessagesStats(StatsType.RULE_ENGINE.getName() + ".producer");
187 this.tbCoreProducerStats = statsFactory.createMessagesStats(StatsType.CORE.getName() + ".producer"); 188 this.tbCoreProducerStats = statsFactory.createMessagesStats(StatsType.CORE.getName() + ".producer");
188 this.transportApiStats = statsFactory.createMessagesStats(StatsType.TRANSPORT.getName() + ".producer"); 189 this.transportApiStats = statsFactory.createMessagesStats(StatsType.TRANSPORT.getName() + ".producer");
189 - this.transportCallbackExecutor = Executors.newWorkStealingPool(20); 190 + this.transportCallbackExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass());
190 this.scheduler.scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS); 191 this.scheduler.scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS);
191 transportApiRequestTemplate = queueProvider.createTransportApiRequestTemplate(); 192 transportApiRequestTemplate = queueProvider.createTransportApiRequestTemplate();
192 transportApiRequestTemplate.setMessagesStats(transportApiStats); 193 transportApiRequestTemplate.setMessagesStats(transportApiStats);
@@ -34,7 +34,7 @@ public abstract class AbstractListeningExecutor implements ListeningExecutor { @@ -34,7 +34,7 @@ public abstract class AbstractListeningExecutor implements ListeningExecutor {
34 34
35 @PostConstruct 35 @PostConstruct
36 public void init() { 36 public void init() {
37 - this.service = MoreExecutors.listeningDecorator(Executors.newWorkStealingPool(getThreadPollSize())); 37 + this.service = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(getThreadPollSize(), getClass()));
38 } 38 }
39 39
40 @PreDestroy 40 @PreDestroy
  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.common.util;
  17 +
  18 +import java.util.concurrent.ExecutorService;
  19 +import java.util.concurrent.ForkJoinPool;
  20 +
  21 +public class ThingsBoardExecutors {
  22 +
  23 + /**
  24 + * Method forked from ExecutorService to provide thread poll name
  25 + *
  26 + * Creates a thread pool that maintains enough threads to support
  27 + * the given parallelism level, and may use multiple queues to
  28 + * reduce contention. The parallelism level corresponds to the
  29 + * maximum number of threads actively engaged in, or available to
  30 + * engage in, task processing. The actual number of threads may
  31 + * grow and shrink dynamically. A work-stealing pool makes no
  32 + * guarantees about the order in which submitted tasks are
  33 + * executed.
  34 + *
  35 + * @param parallelism the targeted parallelism level
  36 + * @param namePrefix used to define thread name
  37 + * @return the newly created thread pool
  38 + * @throws IllegalArgumentException if {@code parallelism <= 0}
  39 + * @since 1.8
  40 + */
  41 + public static ExecutorService newWorkStealingPool(int parallelism, String namePrefix) {
  42 + return new ForkJoinPool(parallelism,
  43 + new ThingsBoardForkJoinWorkerThreadFactory(namePrefix),
  44 + null, true);
  45 + }
  46 +
  47 + public static ExecutorService newWorkStealingPool(int parallelism, Class clazz) {
  48 + return newWorkStealingPool(parallelism, clazz.getSimpleName());
  49 + }
  50 +}
  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.common.util;
  17 +
  18 +import lombok.NonNull;
  19 +import lombok.ToString;
  20 +
  21 +import java.util.concurrent.ForkJoinPool;
  22 +import java.util.concurrent.ForkJoinWorkerThread;
  23 +import java.util.concurrent.atomic.AtomicLong;
  24 +
  25 +@ToString
  26 +public class ThingsBoardForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory {
  27 + private final String namePrefix;
  28 + private final AtomicLong threadNumber = new AtomicLong(1);
  29 +
  30 + public ThingsBoardForkJoinWorkerThreadFactory(@NonNull String namePrefix) {
  31 + this.namePrefix = namePrefix;
  32 + }
  33 +
  34 + @Override
  35 + public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
  36 + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
  37 + thread.setName(namePrefix +"-"+thread.getPoolIndex()+"-"+threadNumber.getAndIncrement());
  38 + return thread;
  39 + }
  40 +
  41 +}
@@ -83,6 +83,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao { @@ -83,6 +83,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao {
83 83
84 PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink); 84 PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink);
85 85
  86 + PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink);
  87 +
86 /** 88 /**
87 * Find device infos by tenantId, type and page link. 89 * Find device infos by tenantId, type and page link.
88 * 90 *
@@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr @@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr
56 import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; 56 import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
57 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; 57 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
58 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; 58 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
  59 +import org.thingsboard.server.common.data.firmware.FirmwareType;
59 import org.thingsboard.server.common.data.id.DeviceProfileId; 60 import org.thingsboard.server.common.data.id.DeviceProfileId;
60 import org.thingsboard.server.common.data.id.TenantId; 61 import org.thingsboard.server.common.data.id.TenantId;
61 import org.thingsboard.server.common.data.page.PageData; 62 import org.thingsboard.server.common.data.page.PageData;
@@ -406,9 +407,31 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -406,9 +407,31 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
406 if (firmware == null) { 407 if (firmware == null) {
407 throw new DataValidationException("Can't assign non-existent firmware!"); 408 throw new DataValidationException("Can't assign non-existent firmware!");
408 } 409 }
  410 + if (!firmware.getType().equals(FirmwareType.FIRMWARE)) {
  411 + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
  412 + }
409 if (firmware.getData() == null) { 413 if (firmware.getData() == null) {
410 throw new DataValidationException("Can't assign firmware with empty data!"); 414 throw new DataValidationException("Can't assign firmware with empty data!");
411 } 415 }
  416 + if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) {
  417 + throw new DataValidationException("Can't assign firmware with different deviceProfile!");
  418 + }
  419 + }
  420 +
  421 + if (deviceProfile.getSoftwareId() != null) {
  422 + Firmware software = firmwareService.findFirmwareById(tenantId, deviceProfile.getSoftwareId());
  423 + if (software == null) {
  424 + throw new DataValidationException("Can't assign non-existent software!");
  425 + }
  426 + if (!software.getType().equals(FirmwareType.SOFTWARE)) {
  427 + throw new DataValidationException("Can't assign software with type: " + software.getType());
  428 + }
  429 + if (software.getData() == null) {
  430 + throw new DataValidationException("Can't assign software with empty data!");
  431 + }
  432 + if (!software.getDeviceProfileId().equals(deviceProfile.getId())) {
  433 + throw new DataValidationException("Can't assign firmware with different deviceProfile!");
  434 + }
412 } 435 }
413 } 436 }
414 437
@@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfig @@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfig
53 import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration; 53 import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
54 import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; 54 import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
55 import org.thingsboard.server.common.data.edge.Edge; 55 import org.thingsboard.server.common.data.edge.Edge;
  56 +import org.thingsboard.server.common.data.firmware.FirmwareType;
56 import org.thingsboard.server.common.data.id.CustomerId; 57 import org.thingsboard.server.common.data.id.CustomerId;
57 import org.thingsboard.server.common.data.id.DeviceId; 58 import org.thingsboard.server.common.data.id.DeviceId;
58 import org.thingsboard.server.common.data.id.DeviceProfileId; 59 import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -361,7 +362,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -361,7 +362,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
361 362
362 @Override 363 @Override
363 public PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) { 364 public PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) {
364 - log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); 365 + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyFirmware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
365 validateId(tenantId, INCORRECT_TENANT_ID + tenantId); 366 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
366 validateString(type, "Incorrect type " + type); 367 validateString(type, "Incorrect type " + type);
367 validatePageLink(pageLink); 368 validatePageLink(pageLink);
@@ -369,6 +370,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -369,6 +370,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
369 } 370 }
370 371
371 @Override 372 @Override
  373 + public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink) {
  374 + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptySoftware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
  375 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  376 + validateString(type, "Incorrect type " + type);
  377 + validatePageLink(pageLink);
  378 + return deviceDao.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId.getId(), type, pageLink);
  379 + }
  380 +
  381 + @Override
372 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { 382 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
373 log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); 383 log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
374 validateId(tenantId, INCORRECT_TENANT_ID + tenantId); 384 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
@@ -696,9 +706,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -696,9 +706,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
696 if (firmware == null) { 706 if (firmware == null) {
697 throw new DataValidationException("Can't assign non-existent firmware!"); 707 throw new DataValidationException("Can't assign non-existent firmware!");
698 } 708 }
  709 + if (!firmware.getType().equals(FirmwareType.FIRMWARE)) {
  710 + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
  711 + }
699 if (firmware.getData() == null) { 712 if (firmware.getData() == null) {
700 throw new DataValidationException("Can't assign firmware with empty data!"); 713 throw new DataValidationException("Can't assign firmware with empty data!");
701 } 714 }
  715 + if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) {
  716 + throw new DataValidationException("Can't assign firmware with different deviceProfile!");
  717 + }
  718 + }
  719 +
  720 + if (device.getSoftwareId() != null) {
  721 + Firmware software = firmwareService.findFirmwareById(tenantId, device.getSoftwareId());
  722 + if (software == null) {
  723 + throw new DataValidationException("Can't assign non-existent software!");
  724 + }
  725 + if (!software.getType().equals(FirmwareType.SOFTWARE)) {
  726 + throw new DataValidationException("Can't assign software with type: " + software.getType());
  727 + }
  728 + if (software.getData() == null) {
  729 + throw new DataValidationException("Can't assign software with empty data!");
  730 + }
  731 + if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) {
  732 + throw new DataValidationException("Can't assign firmware with different deviceProfile!");
  733 + }
702 } 734 }
703 } 735 }
704 }; 736 };
@@ -27,16 +27,20 @@ import org.springframework.cache.CacheManager; @@ -27,16 +27,20 @@ import org.springframework.cache.CacheManager;
27 import org.springframework.cache.annotation.Cacheable; 27 import org.springframework.cache.annotation.Cacheable;
28 import org.springframework.stereotype.Service; 28 import org.springframework.stereotype.Service;
29 import org.thingsboard.server.cache.firmware.FirmwareDataCache; 29 import org.thingsboard.server.cache.firmware.FirmwareDataCache;
  30 +import org.thingsboard.server.common.data.DeviceProfile;
30 import org.thingsboard.server.common.data.Firmware; 31 import org.thingsboard.server.common.data.Firmware;
31 import org.thingsboard.server.common.data.FirmwareInfo; 32 import org.thingsboard.server.common.data.FirmwareInfo;
32 import org.thingsboard.server.common.data.Tenant; 33 import org.thingsboard.server.common.data.Tenant;
33 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 34 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
34 import org.thingsboard.server.common.data.exception.ThingsboardException; 35 import org.thingsboard.server.common.data.exception.ThingsboardException;
35 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; 36 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
  37 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  38 +import org.thingsboard.server.common.data.id.DeviceProfileId;
36 import org.thingsboard.server.common.data.id.FirmwareId; 39 import org.thingsboard.server.common.data.id.FirmwareId;
37 import org.thingsboard.server.common.data.id.TenantId; 40 import org.thingsboard.server.common.data.id.TenantId;
38 import org.thingsboard.server.common.data.page.PageData; 41 import org.thingsboard.server.common.data.page.PageData;
39 import org.thingsboard.server.common.data.page.PageLink; 42 import org.thingsboard.server.common.data.page.PageLink;
  43 +import org.thingsboard.server.dao.device.DeviceProfileDao;
40 import org.thingsboard.server.dao.exception.DataValidationException; 44 import org.thingsboard.server.dao.exception.DataValidationException;
41 import org.thingsboard.server.dao.service.DataValidator; 45 import org.thingsboard.server.dao.service.DataValidator;
42 import org.thingsboard.server.dao.service.PaginatedRemover; 46 import org.thingsboard.server.dao.service.PaginatedRemover;
@@ -63,6 +67,7 @@ public class BaseFirmwareService implements FirmwareService { @@ -63,6 +67,7 @@ public class BaseFirmwareService implements FirmwareService {
63 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; 67 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
64 68
65 private final TenantDao tenantDao; 69 private final TenantDao tenantDao;
  70 + private final DeviceProfileDao deviceProfileDao;
66 private final FirmwareDao firmwareDao; 71 private final FirmwareDao firmwareDao;
67 private final FirmwareInfoDao firmwareInfoDao; 72 private final FirmwareInfoDao firmwareInfoDao;
68 private final CacheManager cacheManager; 73 private final CacheManager cacheManager;
@@ -113,7 +118,7 @@ public class BaseFirmwareService implements FirmwareService { @@ -113,7 +118,7 @@ public class BaseFirmwareService implements FirmwareService {
113 } 118 }
114 119
115 @Override 120 @Override
116 - public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) throws ThingsboardException { 121 + public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) {
117 122
118 if (data == null || !data.hasArray() || data.array().length == 0) { 123 if (data == null || !data.hasArray() || data.array().length == 0) {
119 throw new DataValidationException("Firmware data should be specified!"); 124 throw new DataValidationException("Firmware data should be specified!");
@@ -167,7 +172,8 @@ public class BaseFirmwareService implements FirmwareService { @@ -167,7 +172,8 @@ public class BaseFirmwareService implements FirmwareService {
167 public ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) { 172 public ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) {
168 log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId); 173 log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId);
169 validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); 174 validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId);
170 - return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); } 175 + return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId());
  176 + }
171 177
172 @Override 178 @Override
173 public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) { 179 public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) {
@@ -178,12 +184,11 @@ public class BaseFirmwareService implements FirmwareService { @@ -178,12 +184,11 @@ public class BaseFirmwareService implements FirmwareService {
178 } 184 }
179 185
180 @Override 186 @Override
181 - public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId,  
182 - boolean hasData, PageLink pageLink) { 187 + public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) {
183 log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); 188 log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink);
184 validateId(tenantId, INCORRECT_TENANT_ID + tenantId); 189 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
185 validatePageLink(pageLink); 190 validatePageLink(pageLink);
186 - return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink); 191 + return firmwareInfoDao.findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, firmwareType, hasData, pageLink);
187 } 192 }
188 193
189 @Override 194 @Override
@@ -201,6 +206,10 @@ public class BaseFirmwareService implements FirmwareService { @@ -201,6 +206,10 @@ public class BaseFirmwareService implements FirmwareService {
201 throw new DataValidationException("The firmware referenced by the devices cannot be deleted!"); 206 throw new DataValidationException("The firmware referenced by the devices cannot be deleted!");
202 } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) { 207 } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) {
203 throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!"); 208 throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!");
  209 + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) {
  210 + throw new DataValidationException("The software referenced by the devices cannot be deleted!");
  211 + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) {
  212 + throw new DataValidationException("The software referenced by the device profile cannot be deleted!");
204 } else { 213 } else {
205 throw t; 214 throw t;
206 } 215 }
@@ -217,29 +226,15 @@ public class BaseFirmwareService implements FirmwareService { @@ -217,29 +226,15 @@ public class BaseFirmwareService implements FirmwareService {
217 private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() { 226 private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() {
218 227
219 @Override 228 @Override
220 - protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmware) {  
221 - if (firmware.getTenantId() == null) {  
222 - throw new DataValidationException("Firmware should be assigned to tenant!");  
223 - } else {  
224 - Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());  
225 - if (tenant == null) {  
226 - throw new DataValidationException("Firmware is referencing to non-existent tenant!");  
227 - }  
228 - }  
229 -  
230 - if (StringUtils.isEmpty(firmware.getTitle())) {  
231 - throw new DataValidationException("Firmware title should be specified!");  
232 - }  
233 -  
234 - if (StringUtils.isEmpty(firmware.getVersion())) {  
235 - throw new DataValidationException("Firmware version should be specified!");  
236 - } 229 + protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmwareInfo) {
  230 + validateImpl(firmwareInfo);
237 } 231 }
238 232
239 @Override 233 @Override
240 protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) { 234 protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) {
241 FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId()); 235 FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId());
242 236
  237 + validateUpdateDeviceProfile(firmware, firmwareOld);
243 BaseFirmwareService.validateUpdate(firmware, firmwareOld); 238 BaseFirmwareService.validateUpdate(firmware, firmwareOld);
244 } 239 }
245 }; 240 };
@@ -248,22 +243,7 @@ public class BaseFirmwareService implements FirmwareService { @@ -248,22 +243,7 @@ public class BaseFirmwareService implements FirmwareService {
248 243
249 @Override 244 @Override
250 protected void validateDataImpl(TenantId tenantId, Firmware firmware) { 245 protected void validateDataImpl(TenantId tenantId, Firmware firmware) {
251 - if (firmware.getTenantId() == null) {  
252 - throw new DataValidationException("Firmware should be assigned to tenant!");  
253 - } else {  
254 - Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());  
255 - if (tenant == null) {  
256 - throw new DataValidationException("Firmware is referencing to non-existent tenant!");  
257 - }  
258 - }  
259 -  
260 - if (StringUtils.isEmpty(firmware.getTitle())) {  
261 - throw new DataValidationException("Firmware title should be specified!");  
262 - }  
263 -  
264 - if (StringUtils.isEmpty(firmware.getVersion())) {  
265 - throw new DataValidationException("Firmware version should be specified!");  
266 - } 246 + validateImpl(firmware);
267 247
268 if (StringUtils.isEmpty(firmware.getFileName())) { 248 if (StringUtils.isEmpty(firmware.getFileName())) {
269 throw new DataValidationException("Firmware file name should be specified!"); 249 throw new DataValidationException("Firmware file name should be specified!");
@@ -282,11 +262,7 @@ public class BaseFirmwareService implements FirmwareService { @@ -282,11 +262,7 @@ public class BaseFirmwareService implements FirmwareService {
282 262
283 String currentChecksum; 263 String currentChecksum;
284 264
285 - try {  
286 - currentChecksum = generateChecksum(firmware.getChecksumAlgorithm(), firmware.getData());  
287 - } catch (ThingsboardException e) {  
288 - throw new DataValidationException(e.getMessage());  
289 - } 265 + currentChecksum = generateChecksum(firmware.getChecksumAlgorithm(), firmware.getData());
290 266
291 if (!currentChecksum.equals(firmware.getChecksum())) { 267 if (!currentChecksum.equals(firmware.getChecksum())) {
292 throw new DataValidationException("Wrong firmware file!"); 268 throw new DataValidationException("Wrong firmware file!");
@@ -297,6 +273,7 @@ public class BaseFirmwareService implements FirmwareService { @@ -297,6 +273,7 @@ public class BaseFirmwareService implements FirmwareService {
297 protected void validateUpdate(TenantId tenantId, Firmware firmware) { 273 protected void validateUpdate(TenantId tenantId, Firmware firmware) {
298 Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId()); 274 Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId());
299 275
  276 + validateUpdateDeviceProfile(firmware, firmwareOld);
300 BaseFirmwareService.validateUpdate(firmware, firmwareOld); 277 BaseFirmwareService.validateUpdate(firmware, firmwareOld);
301 278
302 if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) { 279 if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) {
@@ -305,7 +282,19 @@ public class BaseFirmwareService implements FirmwareService { @@ -305,7 +282,19 @@ public class BaseFirmwareService implements FirmwareService {
305 } 282 }
306 }; 283 };
307 284
  285 + private void validateUpdateDeviceProfile(FirmwareInfo firmware, FirmwareInfo firmwareOld) {
  286 + if (firmwareOld.getDeviceProfileId() != null && !firmwareOld.getDeviceProfileId().equals(firmware.getDeviceProfileId())) {
  287 + if (firmwareInfoDao.isFirmwareUsed(firmwareOld.getId(), firmware.getType(), firmwareOld.getDeviceProfileId())) {
  288 + throw new DataValidationException("Can`t update deviceProfileId because firmware is already in use!");
  289 + }
  290 + }
  291 + }
  292 +
308 private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) { 293 private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) {
  294 + if (!firmwareOld.getType().equals(firmware.getType())) {
  295 + throw new DataValidationException("Updating type is prohibited!");
  296 + }
  297 +
309 if (!firmwareOld.getTitle().equals(firmware.getTitle())) { 298 if (!firmwareOld.getTitle().equals(firmware.getTitle())) {
310 throw new DataValidationException("Updating firmware title is prohibited!"); 299 throw new DataValidationException("Updating firmware title is prohibited!");
311 } 300 }
@@ -335,6 +324,36 @@ public class BaseFirmwareService implements FirmwareService { @@ -335,6 +324,36 @@ public class BaseFirmwareService implements FirmwareService {
335 } 324 }
336 } 325 }
337 326
  327 + private void validateImpl(FirmwareInfo firmwareInfo) {
  328 + if (firmwareInfo.getTenantId() == null) {
  329 + throw new DataValidationException("Firmware should be assigned to tenant!");
  330 + } else {
  331 + Tenant tenant = tenantDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getTenantId().getId());
  332 + if (tenant == null) {
  333 + throw new DataValidationException("Firmware is referencing to non-existent tenant!");
  334 + }
  335 + }
  336 +
  337 + if (firmwareInfo.getDeviceProfileId() != null) {
  338 + DeviceProfile deviceProfile = deviceProfileDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getDeviceProfileId().getId());
  339 + if (deviceProfile == null) {
  340 + throw new DataValidationException("Firmware is referencing to non-existent device profile!");
  341 + }
  342 + }
  343 +
  344 + if (firmwareInfo.getType() == null) {
  345 + throw new DataValidationException("Type should be specified!");
  346 + }
  347 +
  348 + if (StringUtils.isEmpty(firmwareInfo.getTitle())) {
  349 + throw new DataValidationException("Firmware title should be specified!");
  350 + }
  351 +
  352 + if (StringUtils.isEmpty(firmwareInfo.getVersion())) {
  353 + throw new DataValidationException("Firmware version should be specified!");
  354 + }
  355 + }
  356 +
338 private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover = 357 private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover =
339 new PaginatedRemover<>() { 358 new PaginatedRemover<>() {
340 359
@@ -16,6 +16,9 @@ @@ -16,6 +16,9 @@
16 package org.thingsboard.server.dao.firmware; 16 package org.thingsboard.server.dao.firmware;
17 17
18 import org.thingsboard.server.common.data.FirmwareInfo; 18 import org.thingsboard.server.common.data.FirmwareInfo;
  19 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  20 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  21 +import org.thingsboard.server.common.data.id.FirmwareId;
19 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.common.data.page.PageData; 23 import org.thingsboard.server.common.data.page.PageData;
21 import org.thingsboard.server.common.data.page.PageLink; 24 import org.thingsboard.server.common.data.page.PageLink;
@@ -27,6 +30,8 @@ public interface FirmwareInfoDao extends Dao<FirmwareInfo> { @@ -27,6 +30,8 @@ public interface FirmwareInfoDao extends Dao<FirmwareInfo> {
27 30
28 PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink); 31 PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink);
29 32
30 - PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); 33 + PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
  34 +
  35 + boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId);
31 36
32 } 37 }
@@ -154,6 +154,7 @@ public class ModelConstants { @@ -154,6 +154,7 @@ public class ModelConstants {
154 public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id"; 154 public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id";
155 public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data"; 155 public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data";
156 public static final String DEVICE_FIRMWARE_ID_PROPERTY = "firmware_id"; 156 public static final String DEVICE_FIRMWARE_ID_PROPERTY = "firmware_id";
  157 + public static final String DEVICE_SOFTWARE_ID_PROPERTY = "software_id";
157 158
158 public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; 159 public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
159 public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text"; 160 public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
@@ -178,6 +179,7 @@ public class ModelConstants { @@ -178,6 +179,7 @@ public class ModelConstants {
178 public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name"; 179 public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name";
179 public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key"; 180 public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key";
180 public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id"; 181 public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id";
  182 + public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id";
181 183
182 /** 184 /**
183 * Cassandra entityView constants. 185 * Cassandra entityView constants.
@@ -476,6 +478,8 @@ public class ModelConstants { @@ -476,6 +478,8 @@ public class ModelConstants {
476 */ 478 */
477 public static final String FIRMWARE_TABLE_NAME = "firmware"; 479 public static final String FIRMWARE_TABLE_NAME = "firmware";
478 public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; 480 public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
  481 + public static final String FIRMWARE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id";
  482 + public static final String FIRMWARE_TYPE_COLUMN = "type";
479 public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY; 483 public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY;
480 public static final String FIRMWARE_VERSION_COLUMN = "version"; 484 public static final String FIRMWARE_VERSION_COLUMN = "version";
481 public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name"; 485 public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name";
@@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode; @@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode;
22 import org.hibernate.annotations.Type; 22 import org.hibernate.annotations.Type;
23 import org.hibernate.annotations.TypeDef; 23 import org.hibernate.annotations.TypeDef;
24 import org.hibernate.annotations.TypeDefs; 24 import org.hibernate.annotations.TypeDefs;
  25 +import org.thingsboard.common.util.JacksonUtil;
25 import org.thingsboard.server.common.data.Device; 26 import org.thingsboard.server.common.data.Device;
26 import org.thingsboard.server.common.data.device.data.DeviceData; 27 import org.thingsboard.server.common.data.device.data.DeviceData;
27 import org.thingsboard.server.common.data.id.CustomerId; 28 import org.thingsboard.server.common.data.id.CustomerId;
@@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.id.TenantId;
32 import org.thingsboard.server.dao.model.BaseSqlEntity; 33 import org.thingsboard.server.dao.model.BaseSqlEntity;
33 import org.thingsboard.server.dao.model.ModelConstants; 34 import org.thingsboard.server.dao.model.ModelConstants;
34 import org.thingsboard.server.dao.model.SearchTextEntity; 35 import org.thingsboard.server.dao.model.SearchTextEntity;
35 -import org.thingsboard.common.util.JacksonUtil;  
36 import org.thingsboard.server.dao.util.mapping.JsonBinaryType; 36 import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
37 import org.thingsboard.server.dao.util.mapping.JsonStringType; 37 import org.thingsboard.server.dao.util.mapping.JsonStringType;
38 38
@@ -43,8 +43,8 @@ import java.util.UUID; @@ -43,8 +43,8 @@ import java.util.UUID;
43 @Data 43 @Data
44 @EqualsAndHashCode(callSuper = true) 44 @EqualsAndHashCode(callSuper = true)
45 @TypeDefs({ 45 @TypeDefs({
46 - @TypeDef(name = "json", typeClass = JsonStringType.class),  
47 - @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) 46 + @TypeDef(name = "json", typeClass = JsonStringType.class),
  47 + @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
48 }) 48 })
49 @MappedSuperclass 49 @MappedSuperclass
50 public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEntity<T> implements SearchTextEntity<T> { 50 public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEntity<T> implements SearchTextEntity<T> {
@@ -77,6 +77,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti @@ -77,6 +77,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
77 @Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid") 77 @Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid")
78 private UUID firmwareId; 78 private UUID firmwareId;
79 79
  80 + @Column(name = ModelConstants.DEVICE_SOFTWARE_ID_PROPERTY, columnDefinition = "uuid")
  81 + private UUID softwareId;
  82 +
80 @Type(type = "jsonb") 83 @Type(type = "jsonb")
81 @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb") 84 @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb")
82 private JsonNode deviceData; 85 private JsonNode deviceData;
@@ -102,6 +105,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti @@ -102,6 +105,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
102 if (device.getFirmwareId() != null) { 105 if (device.getFirmwareId() != null) {
103 this.firmwareId = device.getFirmwareId().getId(); 106 this.firmwareId = device.getFirmwareId().getId();
104 } 107 }
  108 + if (device.getSoftwareId() != null) {
  109 + this.softwareId = device.getSoftwareId().getId();
  110 + }
105 this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class); 111 this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class);
106 this.name = device.getName(); 112 this.name = device.getName();
107 this.type = device.getType(); 113 this.type = device.getType();
@@ -122,6 +128,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti @@ -122,6 +128,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
122 this.searchText = deviceEntity.getSearchText(); 128 this.searchText = deviceEntity.getSearchText();
123 this.additionalInfo = deviceEntity.getAdditionalInfo(); 129 this.additionalInfo = deviceEntity.getAdditionalInfo();
124 this.firmwareId = deviceEntity.getFirmwareId(); 130 this.firmwareId = deviceEntity.getFirmwareId();
  131 + this.softwareId = deviceEntity.getSoftwareId();
125 } 132 }
126 133
127 @Override 134 @Override
@@ -149,6 +156,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti @@ -149,6 +156,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
149 if (firmwareId != null) { 156 if (firmwareId != null) {
150 device.setFirmwareId(new FirmwareId(firmwareId)); 157 device.setFirmwareId(new FirmwareId(firmwareId));
151 } 158 }
  159 + if (softwareId != null) {
  160 + device.setSoftwareId(new FirmwareId(softwareId));
  161 + }
152 device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class)); 162 device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class));
153 device.setName(name); 163 device.setName(name);
154 device.setType(type); 164 device.setType(type);
@@ -21,9 +21,10 @@ import lombok.Data; @@ -21,9 +21,10 @@ import lombok.Data;
21 import lombok.EqualsAndHashCode; 21 import lombok.EqualsAndHashCode;
22 import org.hibernate.annotations.Type; 22 import org.hibernate.annotations.Type;
23 import org.hibernate.annotations.TypeDef; 23 import org.hibernate.annotations.TypeDef;
  24 +import org.thingsboard.common.util.JacksonUtil;
24 import org.thingsboard.server.common.data.DeviceProfile; 25 import org.thingsboard.server.common.data.DeviceProfile;
25 -import org.thingsboard.server.common.data.DeviceProfileType;  
26 import org.thingsboard.server.common.data.DeviceProfileProvisionType; 26 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
  27 +import org.thingsboard.server.common.data.DeviceProfileType;
27 import org.thingsboard.server.common.data.DeviceTransportType; 28 import org.thingsboard.server.common.data.DeviceTransportType;
28 import org.thingsboard.server.common.data.device.profile.DeviceProfileData; 29 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
29 import org.thingsboard.server.common.data.id.DeviceProfileId; 30 import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantId;
33 import org.thingsboard.server.dao.model.BaseSqlEntity; 34 import org.thingsboard.server.dao.model.BaseSqlEntity;
34 import org.thingsboard.server.dao.model.ModelConstants; 35 import org.thingsboard.server.dao.model.ModelConstants;
35 import org.thingsboard.server.dao.model.SearchTextEntity; 36 import org.thingsboard.server.dao.model.SearchTextEntity;
36 -import org.thingsboard.common.util.JacksonUtil;  
37 import org.thingsboard.server.dao.util.mapping.JsonBinaryType; 37 import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
38 38
39 import javax.persistence.Column; 39 import javax.persistence.Column;
@@ -87,12 +87,15 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl @@ -87,12 +87,15 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
87 @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb") 87 @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb")
88 private JsonNode profileData; 88 private JsonNode profileData;
89 89
90 - @Column(name=ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY) 90 + @Column(name = ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY)
91 private String provisionDeviceKey; 91 private String provisionDeviceKey;
92 92
93 - @Column(name=ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY) 93 + @Column(name = ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY)
94 private UUID firmwareId; 94 private UUID firmwareId;
95 95
  96 + @Column(name = ModelConstants.DEVICE_PROFILE_SOFTWARE_ID_PROPERTY)
  97 + private UUID softwareId;
  98 +
96 public DeviceProfileEntity() { 99 public DeviceProfileEntity() {
97 super(); 100 super();
98 } 101 }
@@ -120,6 +123,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl @@ -120,6 +123,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
120 if (deviceProfile.getFirmwareId() != null) { 123 if (deviceProfile.getFirmwareId() != null) {
121 this.firmwareId = deviceProfile.getFirmwareId().getId(); 124 this.firmwareId = deviceProfile.getFirmwareId().getId();
122 } 125 }
  126 + if (deviceProfile.getSoftwareId() != null) {
  127 + this.firmwareId = deviceProfile.getSoftwareId().getId();
  128 + }
123 } 129 }
124 130
125 @Override 131 @Override
@@ -160,6 +166,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl @@ -160,6 +166,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
160 deviceProfile.setFirmwareId(new FirmwareId(firmwareId)); 166 deviceProfile.setFirmwareId(new FirmwareId(firmwareId));
161 } 167 }
162 168
  169 + if (softwareId != null) {
  170 + deviceProfile.setSoftwareId(new FirmwareId(softwareId));
  171 + }
  172 +
163 return deviceProfile; 173 return deviceProfile;
164 } 174 }
165 } 175 }
@@ -22,6 +22,8 @@ import org.hibernate.annotations.Type; @@ -22,6 +22,8 @@ import org.hibernate.annotations.Type;
22 import org.hibernate.annotations.TypeDef; 22 import org.hibernate.annotations.TypeDef;
23 import org.thingsboard.server.common.data.Firmware; 23 import org.thingsboard.server.common.data.Firmware;
24 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; 24 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
  25 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  26 +import org.thingsboard.server.common.data.id.DeviceProfileId;
25 import org.thingsboard.server.common.data.id.FirmwareId; 27 import org.thingsboard.server.common.data.id.FirmwareId;
26 import org.thingsboard.server.common.data.id.TenantId; 28 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.dao.model.BaseSqlEntity; 29 import org.thingsboard.server.dao.model.BaseSqlEntity;
@@ -43,10 +45,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ @@ -43,10 +45,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_
43 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; 45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
44 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN; 46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; 47 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
  48 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN;
46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; 49 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
47 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; 50 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
48 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; 51 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
49 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; 52 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
  53 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
50 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; 54 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
51 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; 55 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
52 56
@@ -60,6 +64,13 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex @@ -60,6 +64,13 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
60 @Column(name = FIRMWARE_TENANT_ID_COLUMN) 64 @Column(name = FIRMWARE_TENANT_ID_COLUMN)
61 private UUID tenantId; 65 private UUID tenantId;
62 66
  67 + @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN)
  68 + private UUID deviceProfileId;
  69 +
  70 + @Enumerated(EnumType.STRING)
  71 + @Column(name = FIRMWARE_TYPE_COLUMN)
  72 + private FirmwareType type;
  73 +
63 @Column(name = FIRMWARE_TITLE_COLUMN) 74 @Column(name = FIRMWARE_TITLE_COLUMN)
64 private String title; 75 private String title;
65 76
@@ -101,6 +112,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex @@ -101,6 +112,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
101 this.createdTime = firmware.getCreatedTime(); 112 this.createdTime = firmware.getCreatedTime();
102 this.setUuid(firmware.getUuidId()); 113 this.setUuid(firmware.getUuidId());
103 this.tenantId = firmware.getTenantId().getId(); 114 this.tenantId = firmware.getTenantId().getId();
  115 + if (firmware.getDeviceProfileId() != null) {
  116 + this.deviceProfileId = firmware.getDeviceProfileId().getId();
  117 + }
  118 + this.type = firmware.getType();
104 this.title = firmware.getTitle(); 119 this.title = firmware.getTitle();
105 this.version = firmware.getVersion(); 120 this.version = firmware.getVersion();
106 this.fileName = firmware.getFileName(); 121 this.fileName = firmware.getFileName();
@@ -127,6 +142,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex @@ -127,6 +142,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
127 Firmware firmware = new Firmware(new FirmwareId(id)); 142 Firmware firmware = new Firmware(new FirmwareId(id));
128 firmware.setCreatedTime(createdTime); 143 firmware.setCreatedTime(createdTime);
129 firmware.setTenantId(new TenantId(tenantId)); 144 firmware.setTenantId(new TenantId(tenantId));
  145 + if (deviceProfileId != null) {
  146 + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
  147 + }
  148 + firmware.setType(type);
130 firmware.setTitle(title); 149 firmware.setTitle(title);
131 firmware.setVersion(version); 150 firmware.setVersion(version);
132 firmware.setFileName(fileName); 151 firmware.setFileName(fileName);
@@ -23,6 +23,8 @@ import org.hibernate.annotations.TypeDef; @@ -23,6 +23,8 @@ import org.hibernate.annotations.TypeDef;
23 import org.thingsboard.common.util.JacksonUtil; 23 import org.thingsboard.common.util.JacksonUtil;
24 import org.thingsboard.server.common.data.FirmwareInfo; 24 import org.thingsboard.server.common.data.FirmwareInfo;
25 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; 25 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
  26 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  27 +import org.thingsboard.server.common.data.id.DeviceProfileId;
26 import org.thingsboard.server.common.data.id.FirmwareId; 28 import org.thingsboard.server.common.data.id.FirmwareId;
27 import org.thingsboard.server.common.data.id.TenantId; 29 import org.thingsboard.server.common.data.id.TenantId;
28 import org.thingsboard.server.dao.model.BaseSqlEntity; 30 import org.thingsboard.server.dao.model.BaseSqlEntity;
@@ -41,13 +43,13 @@ import java.util.UUID; @@ -41,13 +43,13 @@ import java.util.UUID;
41 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; 43 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN;
42 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; 44 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN;
43 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; 45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
44 -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;  
45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; 46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
  47 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN;
46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; 48 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
47 -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY;  
48 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; 49 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
49 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; 50 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
50 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; 51 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
  52 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
51 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; 53 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
52 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; 54 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
53 55
@@ -61,6 +63,13 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S @@ -61,6 +63,13 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
61 @Column(name = FIRMWARE_TENANT_ID_COLUMN) 63 @Column(name = FIRMWARE_TENANT_ID_COLUMN)
62 private UUID tenantId; 64 private UUID tenantId;
63 65
  66 + @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN)
  67 + private UUID deviceProfileId;
  68 +
  69 + @Enumerated(EnumType.STRING)
  70 + @Column(name = FIRMWARE_TYPE_COLUMN)
  71 + private FirmwareType type;
  72 +
64 @Column(name = FIRMWARE_TITLE_COLUMN) 73 @Column(name = FIRMWARE_TITLE_COLUMN)
65 private String title; 74 private String title;
66 75
@@ -101,6 +110,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S @@ -101,6 +110,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
101 this.createdTime = firmware.getCreatedTime(); 110 this.createdTime = firmware.getCreatedTime();
102 this.setUuid(firmware.getUuidId()); 111 this.setUuid(firmware.getUuidId());
103 this.tenantId = firmware.getTenantId().getId(); 112 this.tenantId = firmware.getTenantId().getId();
  113 + this.type = firmware.getType();
  114 + if (firmware.getDeviceProfileId() != null) {
  115 + this.deviceProfileId = firmware.getDeviceProfileId().getId();
  116 + }
104 this.title = firmware.getTitle(); 117 this.title = firmware.getTitle();
105 this.version = firmware.getVersion(); 118 this.version = firmware.getVersion();
106 this.fileName = firmware.getFileName(); 119 this.fileName = firmware.getFileName();
@@ -111,12 +124,14 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S @@ -111,12 +124,14 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
111 this.additionalInfo = firmware.getAdditionalInfo(); 124 this.additionalInfo = firmware.getAdditionalInfo();
112 } 125 }
113 126
114 - public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version, 127 + public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, FirmwareType type, String title, String version,
115 String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, 128 String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize,
116 Object additionalInfo, boolean hasData) { 129 Object additionalInfo, boolean hasData) {
117 this.id = id; 130 this.id = id;
118 this.createdTime = createdTime; 131 this.createdTime = createdTime;
119 this.tenantId = tenantId; 132 this.tenantId = tenantId;
  133 + this.deviceProfileId = deviceProfileId;
  134 + this.type = type;
120 this.title = title; 135 this.title = title;
121 this.version = version; 136 this.version = version;
122 this.fileName = fileName; 137 this.fileName = fileName;
@@ -143,6 +158,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S @@ -143,6 +158,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
143 FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id)); 158 FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id));
144 firmware.setCreatedTime(createdTime); 159 firmware.setCreatedTime(createdTime);
145 firmware.setTenantId(new TenantId(tenantId)); 160 firmware.setTenantId(new TenantId(tenantId));
  161 + if (deviceProfileId != null) {
  162 + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
  163 + }
  164 + firmware.setType(type);
146 firmware.setTitle(title); 165 firmware.setTitle(title);
147 firmware.setVersion(version); 166 firmware.setVersion(version);
148 firmware.setFileName(fileName); 167 firmware.setFileName(fileName);
@@ -104,6 +104,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit @@ -104,6 +104,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
104 @Param("textSearch") String textSearch, 104 @Param("textSearch") String textSearch,
105 Pageable pageable); 105 Pageable pageable);
106 106
  107 + @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " +
  108 + "AND d.type = :type " +
  109 + "AND d.softwareId = null " +
  110 + "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
  111 + Page<DeviceEntity> findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId,
  112 + @Param("type") String type,
  113 + @Param("textSearch") String textSearch,
  114 + Pageable pageable);
  115 +
107 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + 116 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
108 "FROM DeviceEntity d " + 117 "FROM DeviceEntity d " +
109 "LEFT JOIN CustomerEntity c on c.id = d.customerId " + 118 "LEFT JOIN CustomerEntity c on c.id = d.customerId " +
@@ -165,6 +165,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> @@ -165,6 +165,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
165 } 165 }
166 166
167 @Override 167 @Override
  168 + public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink) {
  169 + return DaoUtil.toPageData(
  170 + deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(
  171 + tenantId,
  172 + type,
  173 + Objects.toString(pageLink.getTextSearch(), ""),
  174 + DaoUtil.toPageable(pageLink)));
  175 + }
  176 +
  177 + @Override
168 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) { 178 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) {
169 return DaoUtil.toPageData( 179 return DaoUtil.toPageData(
170 deviceRepository.findDeviceInfosByTenantIdAndType( 180 deviceRepository.findDeviceInfosByTenantIdAndType(
@@ -20,27 +20,41 @@ import org.springframework.data.domain.Pageable; @@ -20,27 +20,41 @@ import org.springframework.data.domain.Pageable;
20 import org.springframework.data.jpa.repository.Query; 20 import org.springframework.data.jpa.repository.Query;
21 import org.springframework.data.repository.CrudRepository; 21 import org.springframework.data.repository.CrudRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
  23 +import org.thingsboard.server.common.data.firmware.FirmwareType;
23 import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; 24 import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity;
24 25
25 import java.util.UUID; 26 import java.util.UUID;
26 27
27 public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> { 28 public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> {
28 - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + 29 + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
29 "f.tenantId = :tenantId " + 30 "f.tenantId = :tenantId " +
30 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") 31 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
31 Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId, 32 Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
32 @Param("searchText") String searchText, 33 @Param("searchText") String searchText,
33 Pageable pageable); 34 Pageable pageable);
34 35
35 - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + 36 + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
36 "f.tenantId = :tenantId " + 37 "f.tenantId = :tenantId " +
  38 + "AND f.deviceProfileId = :deviceProfileId " +
  39 + "AND f.type = :type " +
37 "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + 40 "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" +
38 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") 41 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
39 - Page<FirmwareInfoEntity> findAllByTenantIdAndHasData(@Param("tenantId") UUID tenantId,  
40 - @Param("hasData") boolean hasData,  
41 - @Param("searchText") String searchText,  
42 - Pageable pageable); 42 + Page<FirmwareInfoEntity> findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId,
  43 + @Param("deviceProfileId") UUID deviceProfileId,
  44 + @Param("type") FirmwareType type,
  45 + @Param("hasData") boolean hasData,
  46 + @Param("searchText") String searchText,
  47 + Pageable pageable);
43 48
44 - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id") 49 + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id")
45 FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id); 50 FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id);
  51 +
  52 + @Query(value = "SELECT exists(SELECT * " +
  53 + "FROM device_profile AS dp " +
  54 + "LEFT JOIN device AS d ON dp.id = d.device_profile_id " +
  55 + "WHERE dp.id = :deviceProfileId AND " +
  56 + "(('FIRMWARE' = :type AND (dp.firmware_id = :firmwareId OR d.firmware_id = :firmwareId)) " +
  57 + "OR ('SOFTWARE' = :type AND (dp.software_id = :firmwareId or d.software_id = :firmwareId))))", nativeQuery = true)
  58 + boolean isFirmwareUsed(@Param("firmwareId") UUID firmwareId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type);
  59 +
46 } 60 }
@@ -20,6 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.data.repository.CrudRepository; 20 import org.springframework.data.repository.CrudRepository;
21 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
22 import org.thingsboard.server.common.data.FirmwareInfo; 22 import org.thingsboard.server.common.data.FirmwareInfo;
  23 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  24 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  25 +import org.thingsboard.server.common.data.id.FirmwareId;
23 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.data.page.PageData; 27 import org.thingsboard.server.common.data.page.PageData;
25 import org.thingsboard.server.common.data.page.PageLink; 28 import org.thingsboard.server.common.data.page.PageLink;
@@ -73,12 +76,19 @@ public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao<FirmwareInfoEnt @@ -73,12 +76,19 @@ public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao<FirmwareInfoEnt
73 } 76 }
74 77
75 @Override 78 @Override
76 - public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) { 79 + public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) {
77 return DaoUtil.toPageData(firmwareInfoRepository 80 return DaoUtil.toPageData(firmwareInfoRepository
78 - .findAllByTenantIdAndHasData( 81 + .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(
79 tenantId.getId(), 82 tenantId.getId(),
  83 + deviceProfileId.getId(),
  84 + firmwareType,
80 hasData, 85 hasData,
81 Objects.toString(pageLink.getTextSearch(), ""), 86 Objects.toString(pageLink.getTextSearch(), ""),
82 DaoUtil.toPageable(pageLink))); 87 DaoUtil.toPageable(pageLink)));
83 } 88 }
  89 +
  90 + @Override
  91 + public boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId) {
  92 + return firmwareInfoRepository.isFirmwareUsed(firmwareId.getId(), deviceProfileId.getId(), type.name());
  93 + }
84 } 94 }
@@ -17,12 +17,8 @@ package org.thingsboard.server.dao.sql.query; @@ -17,12 +17,8 @@ package org.thingsboard.server.dao.sql.query;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.apache.commons.lang3.StringUtils; 19 import org.apache.commons.lang3.StringUtils;
20 -import org.springframework.beans.factory.annotation.Autowired;  
21 -import org.springframework.beans.factory.annotation.Value;  
22 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 20 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
23 import org.springframework.stereotype.Repository; 21 import org.springframework.stereotype.Repository;
24 -import org.springframework.transaction.TransactionStatus;  
25 -import org.springframework.transaction.support.TransactionCallback;  
26 import org.springframework.transaction.support.TransactionTemplate; 22 import org.springframework.transaction.support.TransactionTemplate;
27 import org.thingsboard.server.common.data.EntityType; 23 import org.thingsboard.server.common.data.EntityType;
28 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; 24 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
@@ -40,8 +36,6 @@ import org.thingsboard.server.common.data.query.EntityKey; @@ -40,8 +36,6 @@ import org.thingsboard.server.common.data.query.EntityKey;
40 import org.thingsboard.server.common.data.query.EntityKeyType; 36 import org.thingsboard.server.common.data.query.EntityKeyType;
41 import org.thingsboard.server.dao.model.ModelConstants; 37 import org.thingsboard.server.dao.model.ModelConstants;
42 38
43 -import java.util.ArrayList;  
44 -import java.util.Arrays;  
45 import java.util.Collection; 39 import java.util.Collection;
46 import java.util.Collections; 40 import java.util.Collections;
47 import java.util.HashMap; 41 import java.util.HashMap;
@@ -134,6 +128,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { @@ -134,6 +128,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
134 StringBuilder fromPart = new StringBuilder(" from alarm a "); 128 StringBuilder fromPart = new StringBuilder(" from alarm a ");
135 StringBuilder wherePart = new StringBuilder(" where "); 129 StringBuilder wherePart = new StringBuilder(" where ");
136 StringBuilder sortPart = new StringBuilder(" order by "); 130 StringBuilder sortPart = new StringBuilder(" order by ");
  131 + StringBuilder joinPart = new StringBuilder();
137 boolean addAnd = false; 132 boolean addAnd = false;
138 if (pageLink.isSearchPropagatedAlarms()) { 133 if (pageLink.isSearchPropagatedAlarms()) {
139 selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id "); 134 selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id ");
@@ -156,23 +151,23 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { @@ -156,23 +151,23 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
156 wherePart.append(" a.originator_id in (:entity_ids)"); 151 wherePart.append(" a.originator_id in (:entity_ids)");
157 } 152 }
158 } else { 153 } else {
159 - fromPart.append(" inner join (select * from (VALUES"); 154 + joinPart.append(" inner join (select * from (VALUES");
160 int entityIdIdx = 0; 155 int entityIdIdx = 0;
161 int lastEntityIdIdx = orderedEntityIds.size() - 1; 156 int lastEntityIdIdx = orderedEntityIds.size() - 1;
162 for (EntityId entityId : orderedEntityIds) { 157 for (EntityId entityId : orderedEntityIds) {
163 - fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")"); 158 + joinPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")");
164 if (entityIdIdx != lastEntityIdIdx) { 159 if (entityIdIdx != lastEntityIdIdx) {
165 - fromPart.append(","); 160 + joinPart.append(",");
166 } else { 161 } else {
167 - fromPart.append(")"); 162 + joinPart.append(")");
168 } 163 }
169 entityIdIdx++; 164 entityIdIdx++;
170 } 165 }
171 - fromPart.append(" as e(id, priority)) e "); 166 + joinPart.append(" as e(id, priority)) e ");
172 if (pageLink.isSearchPropagatedAlarms()) { 167 if (pageLink.isSearchPropagatedAlarms()) {
173 - fromPart.append("on (r.from_id IS NULL and a.originator_id = e.id) or (r.from_id IS NOT NULL and r.from_id = e.id)"); 168 + joinPart.append("on (r.from_id IS NULL and a.originator_id = e.id) or (r.from_id IS NOT NULL and r.from_id = e.id)");
174 } else { 169 } else {
175 - fromPart.append("on a.originator_id = e.id"); 170 + joinPart.append("on a.originator_id = e.id");
176 } 171 }
177 sortPart.append("e.priority"); 172 sortPart.append("e.priority");
178 } 173 }
@@ -226,9 +221,12 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { @@ -226,9 +221,12 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
226 } 221 }
227 222
228 String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch()); 223 String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch());
229 - String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString(); 224 + String mainQuery;
230 if (!textSearchQuery.isEmpty()) { 225 if (!textSearchQuery.isEmpty()) {
231 - mainQuery = String.format("select * from (%s) a WHERE %s", mainQuery, textSearchQuery); 226 + mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString();
  227 + mainQuery = String.format("select * from (%s) a %s WHERE %s", mainQuery, joinPart, textSearchQuery);
  228 + } else {
  229 + mainQuery = selectPart.toString() + fromPart.toString() + joinPart.toString() + wherePart.toString();
232 } 230 }
233 String countQuery = String.format("select count(*) from (%s) result", mainQuery); 231 String countQuery = String.format("select count(*) from (%s) result", mainQuery);
234 long queryTs = System.currentTimeMillis(); 232 long queryTs = System.currentTimeMillis();
@@ -243,23 +243,21 @@ public class EntityKeyMapping { @@ -243,23 +243,21 @@ public class EntityKeyMapping {
243 } else { 243 } else {
244 entityTypeStr = "'" + entityType.name() + "'"; 244 entityTypeStr = "'" + entityType.name() + "'";
245 } 245 }
246 - ctx.addStringParameter(alias + "_key_id", entityKey.getKey()); 246 + ctx.addStringParameter(getKeyId(), entityKey.getKey());
247 String filterQuery = toQueries(ctx, entityFilter.getType()) 247 String filterQuery = toQueries(ctx, entityFilter.getType())
248 .filter(StringUtils::isNotEmpty) 248 .filter(StringUtils::isNotEmpty)
249 .collect(Collectors.joining(" and ")); 249 .collect(Collectors.joining(" and "));
250 - if (StringUtils.isEmpty(filterQuery)) {  
251 - filterQuery = "";  
252 - } else { 250 + if (StringUtils.isNotEmpty(filterQuery)) {
253 filterQuery = " AND (" + filterQuery + ")"; 251 filterQuery = " AND (" + filterQuery + ")";
254 } 252 }
255 if (entityKey.getType().equals(EntityKeyType.TIME_SERIES)) { 253 if (entityKey.getType().equals(EntityKeyType.TIME_SERIES)) {
256 - String join = hasFilter() ? "inner join" : "left join"; 254 + String join = (hasFilter() && hasFilterValues(ctx)) ? "inner join" : "left join";
257 return String.format("%s ts_kv_latest %s ON %s.entity_id=entities.id AND %s.key = (select key_id from ts_kv_dictionary where key = :%s_key_id) %s", 255 return String.format("%s ts_kv_latest %s ON %s.entity_id=entities.id AND %s.key = (select key_id from ts_kv_dictionary where key = :%s_key_id) %s",
258 join, alias, alias, alias, alias, filterQuery); 256 join, alias, alias, alias, alias, filterQuery);
259 } else { 257 } else {
260 String query; 258 String query;
261 if (!entityKey.getType().equals(EntityKeyType.ATTRIBUTE)) { 259 if (!entityKey.getType().equals(EntityKeyType.ATTRIBUTE)) {
262 - String join = hasFilter() ? "inner join" : "left join"; 260 + String join = (hasFilter() && hasFilterValues(ctx)) ? "inner join" : "left join";
263 query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type=%s AND %s.attribute_key=:%s_key_id ", 261 query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type=%s AND %s.attribute_key=:%s_key_id ",
264 join, alias, alias, alias, entityTypeStr, alias, alias); 262 join, alias, alias, alias, entityTypeStr, alias, alias);
265 String scope; 263 String scope;
@@ -272,7 +270,7 @@ public class EntityKeyMapping { @@ -272,7 +270,7 @@ public class EntityKeyMapping {
272 } 270 }
273 query = String.format("%s AND %s.attribute_type='%s' %s", query, alias, scope, filterQuery); 271 query = String.format("%s AND %s.attribute_type='%s' %s", query, alias, scope, filterQuery);
274 } else { 272 } else {
275 - String join = hasFilter() ? "join LATERAL" : "left join LATERAL"; 273 + String join = (hasFilter() && hasFilterValues(ctx)) ? "join LATERAL" : "left join LATERAL";
276 query = String.format("%s (select * from attribute_kv %s WHERE %s.entity_id=entities.id AND %s.entity_type=%s AND %s.attribute_key=:%s_key_id %s " + 274 query = String.format("%s (select * from attribute_kv %s WHERE %s.entity_id=entities.id AND %s.entity_type=%s AND %s.attribute_key=:%s_key_id %s " +
277 "ORDER BY %s.last_update_ts DESC limit 1) as %s ON true", 275 "ORDER BY %s.last_update_ts DESC limit 1) as %s ON true",
278 join, alias, alias, alias, entityTypeStr, alias, alias, filterQuery, alias, alias); 276 join, alias, alias, alias, entityTypeStr, alias, alias, filterQuery, alias, alias);
@@ -281,15 +279,26 @@ public class EntityKeyMapping { @@ -281,15 +279,26 @@ public class EntityKeyMapping {
281 } 279 }
282 } 280 }
283 281
  282 + private boolean hasFilterValues(QueryContext ctx) {
  283 + return Arrays.stream(ctx.getParameterNames()).anyMatch(parameterName -> {
  284 + return !parameterName.equals(getKeyId()) && parameterName.startsWith(alias);
  285 + });
  286 + }
  287 +
  288 + private String getKeyId() {
  289 + return alias + "_key_id";
  290 + }
  291 +
284 public static String buildSelections(List<EntityKeyMapping> mappings, EntityFilterType filterType, EntityType entityType) { 292 public static String buildSelections(List<EntityKeyMapping> mappings, EntityFilterType filterType, EntityType entityType) {
285 return mappings.stream().map(mapping -> mapping.toSelection(filterType, entityType)).collect( 293 return mappings.stream().map(mapping -> mapping.toSelection(filterType, entityType)).collect(
286 Collectors.joining(", ")); 294 Collectors.joining(", "));
287 } 295 }
288 296
289 public static String buildLatestJoins(QueryContext ctx, EntityFilter entityFilter, EntityType entityType, List<EntityKeyMapping> latestMappings, boolean countQuery) { 297 public static String buildLatestJoins(QueryContext ctx, EntityFilter entityFilter, EntityType entityType, List<EntityKeyMapping> latestMappings, boolean countQuery) {
290 - return latestMappings.stream().filter(mapping -> !countQuery || mapping.hasFilter())  
291 - .map(mapping -> mapping.toLatestJoin(ctx, entityFilter, entityType)).collect(  
292 - Collectors.joining(" ")); 298 + return latestMappings.stream()
  299 + .filter(mapping -> !countQuery || mapping.hasFilter())
  300 + .map(mapping -> mapping.toLatestJoin(ctx, entityFilter, entityType))
  301 + .collect(Collectors.joining(" "));
293 } 302 }
294 303
295 public static String buildQuery(QueryContext ctx, List<EntityKeyMapping> mappings, EntityFilterType filterType) { 304 public static String buildQuery(QueryContext ctx, List<EntityKeyMapping> mappings, EntityFilterType filterType) {
@@ -28,6 +28,7 @@ import com.google.common.util.concurrent.Futures; @@ -28,6 +28,7 @@ import com.google.common.util.concurrent.Futures;
28 import com.google.common.util.concurrent.ListenableFuture; 28 import com.google.common.util.concurrent.ListenableFuture;
29 import com.google.common.util.concurrent.SettableFuture; 29 import com.google.common.util.concurrent.SettableFuture;
30 import lombok.extern.slf4j.Slf4j; 30 import lombok.extern.slf4j.Slf4j;
  31 +import org.thingsboard.common.util.ThingsBoardExecutors;
31 import org.thingsboard.common.util.ThingsBoardThreadFactory; 32 import org.thingsboard.common.util.ThingsBoardThreadFactory;
32 import org.thingsboard.server.common.data.id.TenantId; 33 import org.thingsboard.server.common.data.id.TenantId;
33 import org.thingsboard.server.common.stats.StatsFactory; 34 import org.thingsboard.server.common.stats.StatsFactory;
@@ -82,7 +83,7 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend @@ -82,7 +83,7 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
82 this.printQueriesFreq = printQueriesFreq; 83 this.printQueriesFreq = printQueriesFreq;
83 this.queue = new LinkedBlockingDeque<>(queueLimit); 84 this.queue = new LinkedBlockingDeque<>(queueLimit);
84 this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-dispatcher")); 85 this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-dispatcher"));
85 - this.callbackExecutor = Executors.newWorkStealingPool(callbackThreads); 86 + this.callbackExecutor = ThingsBoardExecutors.newWorkStealingPool(callbackThreads, getClass());
86 this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nosql-timeout")); 87 this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nosql-timeout"));
87 this.perTenantLimitsEnabled = perTenantLimitsEnabled; 88 this.perTenantLimitsEnabled = perTenantLimitsEnabled;
88 this.perTenantLimitsConfiguration = perTenantLimitsConfiguration; 89 this.perTenantLimitsConfiguration = perTenantLimitsConfiguration;
@@ -162,6 +162,8 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -162,6 +162,8 @@ CREATE TABLE IF NOT EXISTS firmware (
162 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, 162 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
163 created_time bigint NOT NULL, 163 created_time bigint NOT NULL,
164 tenant_id uuid NOT NULL, 164 tenant_id uuid NOT NULL,
  165 + device_profile_id uuid ,
  166 + type varchar(32) NOT NULL,
165 title varchar(255) NOT NULL, 167 title varchar(255) NOT NULL,
166 version varchar(255) NOT NULL, 168 version varchar(255) NOT NULL,
167 file_name varchar(255), 169 file_name varchar(255),
@@ -188,13 +190,15 @@ CREATE TABLE IF NOT EXISTS device_profile ( @@ -188,13 +190,15 @@ CREATE TABLE IF NOT EXISTS device_profile (
188 is_default boolean, 190 is_default boolean,
189 tenant_id uuid, 191 tenant_id uuid,
190 firmware_id uuid, 192 firmware_id uuid,
  193 + software_id uuid,
191 default_rule_chain_id uuid, 194 default_rule_chain_id uuid,
192 default_queue_name varchar(255), 195 default_queue_name varchar(255),
193 provision_device_key varchar, 196 provision_device_key varchar,
194 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), 197 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
195 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), 198 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
196 CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), 199 CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
197 - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id) 200 + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id),
  201 + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id)
198 ); 202 );
199 203
200 CREATE TABLE IF NOT EXISTS device ( 204 CREATE TABLE IF NOT EXISTS device (
@@ -210,9 +214,11 @@ CREATE TABLE IF NOT EXISTS device ( @@ -210,9 +214,11 @@ CREATE TABLE IF NOT EXISTS device (
210 search_text varchar(255), 214 search_text varchar(255),
211 tenant_id uuid, 215 tenant_id uuid,
212 firmware_id uuid, 216 firmware_id uuid,
  217 + software_id uuid,
213 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), 218 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
214 CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), 219 CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
215 - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id) 220 + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id),
  221 + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id)
216 ); 222 );
217 223
218 CREATE TABLE IF NOT EXISTS device_credentials ( 224 CREATE TABLE IF NOT EXISTS device_credentials (
@@ -45,3 +45,4 @@ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type); @@ -45,3 +45,4 @@ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type);
45 CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc); 45 CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
46 46
47 CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time); 47 CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time);
  48 +
@@ -180,6 +180,8 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -180,6 +180,8 @@ CREATE TABLE IF NOT EXISTS firmware (
180 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, 180 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
181 created_time bigint NOT NULL, 181 created_time bigint NOT NULL,
182 tenant_id uuid NOT NULL, 182 tenant_id uuid NOT NULL,
  183 + device_profile_id uuid ,
  184 + type varchar(32) NOT NULL,
183 title varchar(255) NOT NULL, 185 title varchar(255) NOT NULL,
184 version varchar(255) NOT NULL, 186 version varchar(255) NOT NULL,
185 file_name varchar(255), 187 file_name varchar(255),
@@ -191,6 +193,7 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -191,6 +193,7 @@ CREATE TABLE IF NOT EXISTS firmware (
191 additional_info varchar, 193 additional_info varchar,
192 search_text varchar(255), 194 search_text varchar(255),
193 CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) 195 CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
  196 +-- CONSTRAINT fk_device_profile_firmware FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE
194 ); 197 );
195 198
196 CREATE TABLE IF NOT EXISTS device_profile ( 199 CREATE TABLE IF NOT EXISTS device_profile (
@@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile ( @@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile (
206 is_default boolean, 209 is_default boolean,
207 tenant_id uuid, 210 tenant_id uuid,
208 firmware_id uuid, 211 firmware_id uuid,
  212 + software_id uuid,
209 default_rule_chain_id uuid, 213 default_rule_chain_id uuid,
210 default_queue_name varchar(255), 214 default_queue_name varchar(255),
211 provision_device_key varchar, 215 provision_device_key varchar,
212 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), 216 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
213 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), 217 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
214 CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), 218 CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
215 - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id) 219 + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id),
  220 + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id)
216 ); 221 );
217 222
  223 +-- We will use one-to-many relation in the first release and extend this feature in case of user requests
  224 +-- CREATE TABLE IF NOT EXISTS device_profile_firmware (
  225 +-- device_profile_id uuid NOT NULL,
  226 +-- firmware_id uuid NOT NULL,
  227 +-- CONSTRAINT device_profile_firmware_unq_key UNIQUE (device_profile_id, firmware_id),
  228 +-- CONSTRAINT fk_device_profile_firmware_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE,
  229 +-- CONSTRAINT fk_device_profile_firmware_firmware FOREIGN KEY (firmware_id) REFERENCES firmware(id) ON DELETE CASCADE,
  230 +-- );
  231 +
218 CREATE TABLE IF NOT EXISTS device ( 232 CREATE TABLE IF NOT EXISTS device (
219 id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, 233 id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
220 created_time bigint NOT NULL, 234 created_time bigint NOT NULL,
@@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device ( @@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device (
228 search_text varchar(255), 242 search_text varchar(255),
229 tenant_id uuid, 243 tenant_id uuid,
230 firmware_id uuid, 244 firmware_id uuid,
  245 + software_id uuid,
231 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), 246 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
232 CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), 247 CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
233 - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id) 248 + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id),
  249 + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id)
234 ); 250 );
235 251
236 CREATE TABLE IF NOT EXISTS device_credentials ( 252 CREATE TABLE IF NOT EXISTS device_credentials (
@@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo;
30 import org.thingsboard.server.common.data.DeviceTransportType; 30 import org.thingsboard.server.common.data.DeviceTransportType;
31 import org.thingsboard.server.common.data.Firmware; 31 import org.thingsboard.server.common.data.Firmware;
32 import org.thingsboard.server.common.data.Tenant; 32 import org.thingsboard.server.common.data.Tenant;
  33 +import org.thingsboard.server.common.data.firmware.FirmwareType;
33 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; 34 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
34 import org.thingsboard.server.common.data.id.TenantId; 35 import org.thingsboard.server.common.data.id.TenantId;
35 import org.thingsboard.server.common.data.page.PageData; 36 import org.thingsboard.server.common.data.page.PageData;
@@ -44,6 +45,8 @@ import java.util.concurrent.ExecutionException; @@ -44,6 +45,8 @@ import java.util.concurrent.ExecutionException;
44 import java.util.concurrent.Executors; 45 import java.util.concurrent.Executors;
45 import java.util.stream.Collectors; 46 import java.util.stream.Collectors;
46 47
  48 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  49 +
47 public class BaseDeviceProfileServiceTest extends AbstractServiceTest { 50 public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
48 51
49 private IdComparator<DeviceProfile> idComparator = new IdComparator<>(); 52 private IdComparator<DeviceProfile> idComparator = new IdComparator<>();
@@ -98,6 +101,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { @@ -98,6 +101,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
98 101
99 Firmware firmware = new Firmware(); 102 Firmware firmware = new Firmware();
100 firmware.setTenantId(tenantId); 103 firmware.setTenantId(tenantId);
  104 + firmware.setDeviceProfileId(savedDeviceProfile.getId());
  105 + firmware.setType(FIRMWARE);
101 firmware.setTitle("my firmware"); 106 firmware.setTitle("my firmware");
102 firmware.setVersion("v1.0"); 107 firmware.setVersion("v1.0");
103 firmware.setFileName("test.txt"); 108 firmware.setFileName("test.txt");
@@ -20,10 +20,13 @@ import org.apache.commons.lang3.RandomStringUtils; @@ -20,10 +20,13 @@ import org.apache.commons.lang3.RandomStringUtils;
20 import org.junit.After; 20 import org.junit.After;
21 import org.junit.Assert; 21 import org.junit.Assert;
22 import org.junit.Before; 22 import org.junit.Before;
  23 +import org.junit.Rule;
23 import org.junit.Test; 24 import org.junit.Test;
  25 +import org.junit.rules.ExpectedException;
24 import org.thingsboard.server.common.data.Customer; 26 import org.thingsboard.server.common.data.Customer;
25 import org.thingsboard.server.common.data.Device; 27 import org.thingsboard.server.common.data.Device;
26 import org.thingsboard.server.common.data.DeviceInfo; 28 import org.thingsboard.server.common.data.DeviceInfo;
  29 +import org.thingsboard.server.common.data.DeviceProfile;
27 import org.thingsboard.server.common.data.EntitySubtype; 30 import org.thingsboard.server.common.data.EntitySubtype;
28 import org.thingsboard.server.common.data.Firmware; 31 import org.thingsboard.server.common.data.Firmware;
29 import org.thingsboard.server.common.data.Tenant; 32 import org.thingsboard.server.common.data.Tenant;
@@ -43,6 +46,7 @@ import java.util.ArrayList; @@ -43,6 +46,7 @@ import java.util.ArrayList;
43 import java.util.Collections; 46 import java.util.Collections;
44 import java.util.List; 47 import java.util.List;
45 48
  49 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
46 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; 50 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
47 51
48 public abstract class BaseDeviceServiceTest extends AbstractServiceTest { 52 public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
@@ -67,6 +71,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { @@ -67,6 +71,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
67 tenantProfileService.deleteTenantProfiles(anotherTenantId); 71 tenantProfileService.deleteTenantProfiles(anotherTenantId);
68 } 72 }
69 73
  74 + @Rule
  75 + public ExpectedException thrown = ExpectedException.none();
  76 +
70 @Test 77 @Test
71 public void testSaveDevicesWithoutMaxDeviceLimit() { 78 public void testSaveDevicesWithoutMaxDeviceLimit() {
72 Device device = this.saveDevice(tenantId, "My device"); 79 Device device = this.saveDevice(tenantId, "My device");
@@ -184,6 +191,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { @@ -184,6 +191,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
184 191
185 Firmware firmware = new Firmware(); 192 Firmware firmware = new Firmware();
186 firmware.setTenantId(tenantId); 193 firmware.setTenantId(tenantId);
  194 + firmware.setDeviceProfileId(device.getDeviceProfileId());
  195 + firmware.setType(FIRMWARE);
187 firmware.setTitle("my firmware"); 196 firmware.setTitle("my firmware");
188 firmware.setVersion("v1.0"); 197 firmware.setVersion("v1.0");
189 firmware.setFileName("test.txt"); 198 firmware.setFileName("test.txt");
@@ -199,6 +208,40 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { @@ -199,6 +208,40 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
199 Device foundDevice = deviceService.findDeviceById(tenantId, savedDevice.getId()); 208 Device foundDevice = deviceService.findDeviceById(tenantId, savedDevice.getId());
200 Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); 209 Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
201 } 210 }
  211 +
  212 + @Test
  213 + public void testAssignFirmwareToDeviceWithDifferentDeviceProfile() {
  214 + Device device = new Device();
  215 + device.setTenantId(tenantId);
  216 + device.setName("My device");
  217 + device.setType("default");
  218 + Device savedDevice = deviceService.saveDevice(device);
  219 +
  220 + Assert.assertNotNull(savedDevice);
  221 +
  222 + DeviceProfile deviceProfile = createDeviceProfile(tenantId, "New device Profile");
  223 + DeviceProfile savedProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
  224 + Assert.assertNotNull(savedProfile);
  225 +
  226 + Firmware firmware = new Firmware();
  227 + firmware.setTenantId(tenantId);
  228 + firmware.setDeviceProfileId(savedProfile.getId());
  229 + firmware.setType(FIRMWARE);
  230 + firmware.setTitle("my firmware");
  231 + firmware.setVersion("v1.0");
  232 + firmware.setFileName("test.txt");
  233 + firmware.setContentType("text/plain");
  234 + firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256);
  235 + firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a");
  236 + firmware.setData(ByteBuffer.wrap(new byte[]{1}));
  237 + Firmware savedFirmware = firmwareService.saveFirmware(firmware);
  238 +
  239 + savedDevice.setFirmwareId(savedFirmware.getId());
  240 +
  241 + thrown.expect(DataValidationException.class);
  242 + thrown.expectMessage("Can't assign firmware with different deviceProfile!");
  243 + deviceService.saveDevice(savedDevice);
  244 + }
202 245
203 @Test(expected = DataValidationException.class) 246 @Test(expected = DataValidationException.class)
204 public void testSaveDeviceWithEmptyName() { 247 public void testSaveDeviceWithEmptyName() {
@@ -19,7 +19,9 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; @@ -19,7 +19,9 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
19 import org.junit.After; 19 import org.junit.After;
20 import org.junit.Assert; 20 import org.junit.Assert;
21 import org.junit.Before; 21 import org.junit.Before;
  22 +import org.junit.Rule;
22 import org.junit.Test; 23 import org.junit.Test;
  24 +import org.junit.rules.ExpectedException;
23 import org.thingsboard.common.util.JacksonUtil; 25 import org.thingsboard.common.util.JacksonUtil;
24 import org.thingsboard.server.common.data.Device; 26 import org.thingsboard.server.common.data.Device;
25 import org.thingsboard.server.common.data.DeviceProfile; 27 import org.thingsboard.server.common.data.DeviceProfile;
@@ -27,6 +29,7 @@ import org.thingsboard.server.common.data.Firmware; @@ -27,6 +29,7 @@ import org.thingsboard.server.common.data.Firmware;
27 import org.thingsboard.server.common.data.FirmwareInfo; 29 import org.thingsboard.server.common.data.FirmwareInfo;
28 import org.thingsboard.server.common.data.Tenant; 30 import org.thingsboard.server.common.data.Tenant;
29 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; 31 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
  32 +import org.thingsboard.server.common.data.id.DeviceProfileId;
30 import org.thingsboard.server.common.data.id.TenantId; 33 import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.common.data.page.PageData; 34 import org.thingsboard.server.common.data.page.PageData;
32 import org.thingsboard.server.common.data.page.PageLink; 35 import org.thingsboard.server.common.data.page.PageLink;
@@ -37,6 +40,8 @@ import java.util.ArrayList; @@ -37,6 +40,8 @@ import java.util.ArrayList;
37 import java.util.Collections; 40 import java.util.Collections;
38 import java.util.List; 41 import java.util.List;
39 42
  43 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  44 +
40 public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { 45 public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
41 46
42 public static final String TITLE = "My firmware"; 47 public static final String TITLE = "My firmware";
@@ -51,6 +56,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -51,6 +56,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
51 56
52 private TenantId tenantId; 57 private TenantId tenantId;
53 58
  59 + private DeviceProfileId deviceProfileId;
  60 +
54 @Before 61 @Before
55 public void before() { 62 public void before() {
56 Tenant tenant = new Tenant(); 63 Tenant tenant = new Tenant();
@@ -58,8 +65,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -58,8 +65,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
58 Tenant savedTenant = tenantService.saveTenant(tenant); 65 Tenant savedTenant = tenantService.saveTenant(tenant);
59 Assert.assertNotNull(savedTenant); 66 Assert.assertNotNull(savedTenant);
60 tenantId = savedTenant.getId(); 67 tenantId = savedTenant.getId();
  68 +
  69 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile");
  70 + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
  71 + Assert.assertNotNull(savedDeviceProfile);
  72 + deviceProfileId = savedDeviceProfile.getId();
61 } 73 }
62 74
  75 + @Rule
  76 + public ExpectedException thrown = ExpectedException.none();
  77 +
63 @After 78 @After
64 public void after() { 79 public void after() {
65 tenantService.deleteTenant(tenantId); 80 tenantService.deleteTenant(tenantId);
@@ -69,6 +84,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -69,6 +84,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
69 public void testSaveFirmware() { 84 public void testSaveFirmware() {
70 Firmware firmware = new Firmware(); 85 Firmware firmware = new Firmware();
71 firmware.setTenantId(tenantId); 86 firmware.setTenantId(tenantId);
  87 + firmware.setDeviceProfileId(deviceProfileId);
  88 + firmware.setType(FIRMWARE);
72 firmware.setTitle(TITLE); 89 firmware.setTitle(TITLE);
73 firmware.setVersion(VERSION); 90 firmware.setVersion(VERSION);
74 firmware.setFileName(FILE_NAME); 91 firmware.setFileName(FILE_NAME);
@@ -100,6 +117,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -100,6 +117,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
100 public void testSaveFirmwareInfoAndUpdateWithData() { 117 public void testSaveFirmwareInfoAndUpdateWithData() {
101 FirmwareInfo firmwareInfo = new FirmwareInfo(); 118 FirmwareInfo firmwareInfo = new FirmwareInfo();
102 firmwareInfo.setTenantId(tenantId); 119 firmwareInfo.setTenantId(tenantId);
  120 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  121 + firmwareInfo.setType(FIRMWARE);
103 firmwareInfo.setTitle(TITLE); 122 firmwareInfo.setTitle(TITLE);
104 firmwareInfo.setVersion(VERSION); 123 firmwareInfo.setVersion(VERSION);
105 FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); 124 FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo);
@@ -113,6 +132,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -113,6 +132,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
113 Firmware firmware = new Firmware(savedFirmwareInfo.getId()); 132 Firmware firmware = new Firmware(savedFirmwareInfo.getId());
114 firmware.setCreatedTime(firmwareInfo.getCreatedTime()); 133 firmware.setCreatedTime(firmwareInfo.getCreatedTime());
115 firmware.setTenantId(tenantId); 134 firmware.setTenantId(tenantId);
  135 + firmware.setDeviceProfileId(deviceProfileId);
  136 + firmware.setType(FIRMWARE);
116 firmware.setTitle(TITLE); 137 firmware.setTitle(TITLE);
117 firmware.setVersion(VERSION); 138 firmware.setVersion(VERSION);
118 firmware.setFileName(FILE_NAME); 139 firmware.setFileName(FILE_NAME);
@@ -136,9 +157,11 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -136,9 +157,11 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
136 firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId()); 157 firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId());
137 } 158 }
138 159
139 - @Test(expected = DataValidationException.class) 160 + @Test
140 public void testSaveFirmwareWithEmptyTenant() { 161 public void testSaveFirmwareWithEmptyTenant() {
141 Firmware firmware = new Firmware(); 162 Firmware firmware = new Firmware();
  163 + firmware.setDeviceProfileId(deviceProfileId);
  164 + firmware.setType(FIRMWARE);
142 firmware.setTitle(TITLE); 165 firmware.setTitle(TITLE);
143 firmware.setVersion(VERSION); 166 firmware.setVersion(VERSION);
144 firmware.setFileName(FILE_NAME); 167 firmware.setFileName(FILE_NAME);
@@ -146,65 +169,108 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -146,65 +169,108 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
146 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 169 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
147 firmware.setChecksum(CHECKSUM); 170 firmware.setChecksum(CHECKSUM);
148 firmware.setData(DATA); 171 firmware.setData(DATA);
  172 +
  173 + thrown.expect(DataValidationException.class);
  174 + thrown.expectMessage("Firmware should be assigned to tenant!");
149 firmwareService.saveFirmware(firmware); 175 firmwareService.saveFirmware(firmware);
150 } 176 }
151 177
152 - @Test(expected = DataValidationException.class) 178 + @Test
  179 + public void testSaveFirmwareWithEmptyType() {
  180 + Firmware firmware = new Firmware();
  181 + firmware.setTenantId(tenantId);
  182 + firmware.setDeviceProfileId(deviceProfileId);
  183 + firmware.setTitle(TITLE);
  184 + firmware.setVersion(VERSION);
  185 + firmware.setFileName(FILE_NAME);
  186 + firmware.setContentType(CONTENT_TYPE);
  187 + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  188 + firmware.setChecksum(CHECKSUM);
  189 + firmware.setData(DATA);
  190 +
  191 + thrown.expect(DataValidationException.class);
  192 + thrown.expectMessage("Type should be specified!");
  193 + firmwareService.saveFirmware(firmware);
  194 + }
  195 +
  196 + @Test
153 public void testSaveFirmwareWithEmptyTitle() { 197 public void testSaveFirmwareWithEmptyTitle() {
154 Firmware firmware = new Firmware(); 198 Firmware firmware = new Firmware();
155 firmware.setTenantId(tenantId); 199 firmware.setTenantId(tenantId);
  200 + firmware.setDeviceProfileId(deviceProfileId);
  201 + firmware.setType(FIRMWARE);
156 firmware.setVersion(VERSION); 202 firmware.setVersion(VERSION);
157 firmware.setFileName(FILE_NAME); 203 firmware.setFileName(FILE_NAME);
158 firmware.setContentType(CONTENT_TYPE); 204 firmware.setContentType(CONTENT_TYPE);
159 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 205 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
160 firmware.setChecksum(CHECKSUM); 206 firmware.setChecksum(CHECKSUM);
161 firmware.setData(DATA); 207 firmware.setData(DATA);
  208 +
  209 + thrown.expect(DataValidationException.class);
  210 + thrown.expectMessage("Firmware title should be specified!");
162 firmwareService.saveFirmware(firmware); 211 firmwareService.saveFirmware(firmware);
163 } 212 }
164 213
165 - @Test(expected = DataValidationException.class) 214 + @Test
166 public void testSaveFirmwareWithEmptyFileName() { 215 public void testSaveFirmwareWithEmptyFileName() {
167 Firmware firmware = new Firmware(); 216 Firmware firmware = new Firmware();
168 firmware.setTenantId(tenantId); 217 firmware.setTenantId(tenantId);
  218 + firmware.setDeviceProfileId(deviceProfileId);
  219 + firmware.setType(FIRMWARE);
169 firmware.setTitle(TITLE); 220 firmware.setTitle(TITLE);
170 firmware.setVersion(VERSION); 221 firmware.setVersion(VERSION);
171 firmware.setContentType(CONTENT_TYPE); 222 firmware.setContentType(CONTENT_TYPE);
172 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 223 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
173 firmware.setChecksum(CHECKSUM); 224 firmware.setChecksum(CHECKSUM);
174 firmware.setData(DATA); 225 firmware.setData(DATA);
  226 +
  227 + thrown.expect(DataValidationException.class);
  228 + thrown.expectMessage("Firmware file name should be specified!");
175 firmwareService.saveFirmware(firmware); 229 firmwareService.saveFirmware(firmware);
176 } 230 }
177 231
178 - @Test(expected = DataValidationException.class) 232 + @Test
179 public void testSaveFirmwareWithEmptyContentType() { 233 public void testSaveFirmwareWithEmptyContentType() {
180 Firmware firmware = new Firmware(); 234 Firmware firmware = new Firmware();
181 firmware.setTenantId(tenantId); 235 firmware.setTenantId(tenantId);
  236 + firmware.setDeviceProfileId(deviceProfileId);
  237 + firmware.setType(FIRMWARE);
182 firmware.setTitle(TITLE); 238 firmware.setTitle(TITLE);
183 firmware.setVersion(VERSION); 239 firmware.setVersion(VERSION);
184 firmware.setFileName(FILE_NAME); 240 firmware.setFileName(FILE_NAME);
185 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 241 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
186 firmware.setChecksum(CHECKSUM); 242 firmware.setChecksum(CHECKSUM);
187 firmware.setData(DATA); 243 firmware.setData(DATA);
  244 +
  245 + thrown.expect(DataValidationException.class);
  246 + thrown.expectMessage("Firmware content type should be specified!");
188 firmwareService.saveFirmware(firmware); 247 firmwareService.saveFirmware(firmware);
189 } 248 }
190 249
191 - @Test(expected = DataValidationException.class) 250 + @Test
192 public void testSaveFirmwareWithEmptyData() { 251 public void testSaveFirmwareWithEmptyData() {
193 Firmware firmware = new Firmware(); 252 Firmware firmware = new Firmware();
194 firmware.setTenantId(tenantId); 253 firmware.setTenantId(tenantId);
  254 + firmware.setDeviceProfileId(deviceProfileId);
  255 + firmware.setType(FIRMWARE);
195 firmware.setTitle(TITLE); 256 firmware.setTitle(TITLE);
196 firmware.setVersion(VERSION); 257 firmware.setVersion(VERSION);
197 firmware.setFileName(FILE_NAME); 258 firmware.setFileName(FILE_NAME);
198 firmware.setContentType(CONTENT_TYPE); 259 firmware.setContentType(CONTENT_TYPE);
199 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 260 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
200 firmware.setChecksum(CHECKSUM); 261 firmware.setChecksum(CHECKSUM);
  262 +
  263 + thrown.expect(DataValidationException.class);
  264 + thrown.expectMessage("Firmware data should be specified!");
201 firmwareService.saveFirmware(firmware); 265 firmwareService.saveFirmware(firmware);
202 } 266 }
203 267
204 - @Test(expected = DataValidationException.class) 268 + @Test
205 public void testSaveFirmwareWithInvalidTenant() { 269 public void testSaveFirmwareWithInvalidTenant() {
206 Firmware firmware = new Firmware(); 270 Firmware firmware = new Firmware();
207 firmware.setTenantId(new TenantId(Uuids.timeBased())); 271 firmware.setTenantId(new TenantId(Uuids.timeBased()));
  272 + firmware.setDeviceProfileId(deviceProfileId);
  273 + firmware.setType(FIRMWARE);
208 firmware.setTitle(TITLE); 274 firmware.setTitle(TITLE);
209 firmware.setVersion(VERSION); 275 firmware.setVersion(VERSION);
210 firmware.setFileName(FILE_NAME); 276 firmware.setFileName(FILE_NAME);
@@ -212,41 +278,77 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -212,41 +278,77 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
212 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 278 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
213 firmware.setChecksum(CHECKSUM); 279 firmware.setChecksum(CHECKSUM);
214 firmware.setData(DATA); 280 firmware.setData(DATA);
  281 +
  282 + thrown.expect(DataValidationException.class);
  283 + thrown.expectMessage("Firmware is referencing to non-existent tenant!");
215 firmwareService.saveFirmware(firmware); 284 firmwareService.saveFirmware(firmware);
216 } 285 }
217 286
218 - @Test(expected = DataValidationException.class) 287 + @Test
  288 + public void testSaveFirmwareWithInvalidDeviceProfileId() {
  289 + Firmware firmware = new Firmware();
  290 + firmware.setTenantId(tenantId);
  291 + firmware.setDeviceProfileId(new DeviceProfileId(Uuids.timeBased()));
  292 + firmware.setType(FIRMWARE);
  293 + firmware.setTitle(TITLE);
  294 + firmware.setVersion(VERSION);
  295 + firmware.setFileName(FILE_NAME);
  296 + firmware.setContentType(CONTENT_TYPE);
  297 + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  298 + firmware.setChecksum(CHECKSUM);
  299 + firmware.setData(DATA);
  300 +
  301 + thrown.expect(DataValidationException.class);
  302 + thrown.expectMessage("Firmware is referencing to non-existent device profile!");
  303 + firmwareService.saveFirmware(firmware);
  304 + }
  305 +
  306 + @Test
219 public void testSaveFirmwareWithEmptyChecksum() { 307 public void testSaveFirmwareWithEmptyChecksum() {
220 Firmware firmware = new Firmware(); 308 Firmware firmware = new Firmware();
221 - firmware.setTenantId(new TenantId(Uuids.timeBased())); 309 + firmware.setTenantId(tenantId);
  310 + firmware.setDeviceProfileId(deviceProfileId);
  311 + firmware.setType(FIRMWARE);
222 firmware.setTitle(TITLE); 312 firmware.setTitle(TITLE);
223 firmware.setVersion(VERSION); 313 firmware.setVersion(VERSION);
224 firmware.setFileName(FILE_NAME); 314 firmware.setFileName(FILE_NAME);
225 firmware.setContentType(CONTENT_TYPE); 315 firmware.setContentType(CONTENT_TYPE);
226 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 316 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
227 firmware.setData(DATA); 317 firmware.setData(DATA);
  318 +
  319 + thrown.expect(DataValidationException.class);
  320 + thrown.expectMessage("Firmware checksum should be specified!");
228 firmwareService.saveFirmware(firmware); 321 firmwareService.saveFirmware(firmware);
229 } 322 }
230 323
231 - @Test(expected = DataValidationException.class) 324 + @Test
232 public void testSaveFirmwareInfoWithExistingTitleAndVersion() { 325 public void testSaveFirmwareInfoWithExistingTitleAndVersion() {
233 FirmwareInfo firmwareInfo = new FirmwareInfo(); 326 FirmwareInfo firmwareInfo = new FirmwareInfo();
234 firmwareInfo.setTenantId(tenantId); 327 firmwareInfo.setTenantId(tenantId);
  328 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  329 + firmwareInfo.setType(FIRMWARE);
235 firmwareInfo.setTitle(TITLE); 330 firmwareInfo.setTitle(TITLE);
236 firmwareInfo.setVersion(VERSION); 331 firmwareInfo.setVersion(VERSION);
237 firmwareService.saveFirmwareInfo(firmwareInfo); 332 firmwareService.saveFirmwareInfo(firmwareInfo);
238 333
239 FirmwareInfo newFirmwareInfo = new FirmwareInfo(); 334 FirmwareInfo newFirmwareInfo = new FirmwareInfo();
240 newFirmwareInfo.setTenantId(tenantId); 335 newFirmwareInfo.setTenantId(tenantId);
  336 + newFirmwareInfo.setDeviceProfileId(deviceProfileId);
  337 + newFirmwareInfo.setType(FIRMWARE);
241 newFirmwareInfo.setTitle(TITLE); 338 newFirmwareInfo.setTitle(TITLE);
242 newFirmwareInfo.setVersion(VERSION); 339 newFirmwareInfo.setVersion(VERSION);
  340 +
  341 + thrown.expect(DataValidationException.class);
  342 + thrown.expectMessage("Firmware with such title and version already exists!");
243 firmwareService.saveFirmwareInfo(newFirmwareInfo); 343 firmwareService.saveFirmwareInfo(newFirmwareInfo);
244 } 344 }
245 345
246 - @Test(expected = DataValidationException.class) 346 + @Test
247 public void testSaveFirmwareWithExistingTitleAndVersion() { 347 public void testSaveFirmwareWithExistingTitleAndVersion() {
248 Firmware firmware = new Firmware(); 348 Firmware firmware = new Firmware();
249 firmware.setTenantId(tenantId); 349 firmware.setTenantId(tenantId);
  350 + firmware.setDeviceProfileId(deviceProfileId);
  351 + firmware.setType(FIRMWARE);
250 firmware.setTitle(TITLE); 352 firmware.setTitle(TITLE);
251 firmware.setVersion(VERSION); 353 firmware.setVersion(VERSION);
252 firmware.setFileName(FILE_NAME); 354 firmware.setFileName(FILE_NAME);
@@ -258,18 +360,27 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -258,18 +360,27 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
258 360
259 Firmware newFirmware = new Firmware(); 361 Firmware newFirmware = new Firmware();
260 newFirmware.setTenantId(tenantId); 362 newFirmware.setTenantId(tenantId);
  363 + newFirmware.setDeviceProfileId(deviceProfileId);
  364 + newFirmware.setType(FIRMWARE);
261 newFirmware.setTitle(TITLE); 365 newFirmware.setTitle(TITLE);
262 newFirmware.setVersion(VERSION); 366 newFirmware.setVersion(VERSION);
263 newFirmware.setFileName(FILE_NAME); 367 newFirmware.setFileName(FILE_NAME);
264 newFirmware.setContentType(CONTENT_TYPE); 368 newFirmware.setContentType(CONTENT_TYPE);
  369 + newFirmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  370 + newFirmware.setChecksum(CHECKSUM);
265 newFirmware.setData(DATA); 371 newFirmware.setData(DATA);
  372 +
  373 + thrown.expect(DataValidationException.class);
  374 + thrown.expectMessage("Firmware with such title and version already exists!");
266 firmwareService.saveFirmware(newFirmware); 375 firmwareService.saveFirmware(newFirmware);
267 } 376 }
268 377
269 - @Test(expected = DataValidationException.class) 378 + @Test
270 public void testDeleteFirmwareWithReferenceByDevice() { 379 public void testDeleteFirmwareWithReferenceByDevice() {
271 Firmware firmware = new Firmware(); 380 Firmware firmware = new Firmware();
272 firmware.setTenantId(tenantId); 381 firmware.setTenantId(tenantId);
  382 + firmware.setDeviceProfileId(deviceProfileId);
  383 + firmware.setType(FIRMWARE);
273 firmware.setTitle(TITLE); 384 firmware.setTitle(TITLE);
274 firmware.setVersion(VERSION); 385 firmware.setVersion(VERSION);
275 firmware.setFileName(FILE_NAME); 386 firmware.setFileName(FILE_NAME);
@@ -282,11 +393,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -282,11 +393,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
282 Device device = new Device(); 393 Device device = new Device();
283 device.setTenantId(tenantId); 394 device.setTenantId(tenantId);
284 device.setName("My device"); 395 device.setName("My device");
285 - device.setType("default"); 396 + device.setDeviceProfileId(deviceProfileId);
286 device.setFirmwareId(savedFirmware.getId()); 397 device.setFirmwareId(savedFirmware.getId());
287 Device savedDevice = deviceService.saveDevice(device); 398 Device savedDevice = deviceService.saveDevice(device);
288 399
289 try { 400 try {
  401 + thrown.expect(DataValidationException.class);
  402 + thrown.expectMessage("The firmware referenced by the devices cannot be deleted!");
290 firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); 403 firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
291 } finally { 404 } finally {
292 deviceService.deleteDevice(tenantId, savedDevice.getId()); 405 deviceService.deleteDevice(tenantId, savedDevice.getId());
@@ -294,10 +407,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -294,10 +407,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
294 } 407 }
295 } 408 }
296 409
297 - @Test(expected = DataValidationException.class)  
298 - public void testDeleteFirmwareWithReferenceByDeviceProfile() { 410 + @Test
  411 + public void testUpdateDeviceProfileIdWithReferenceByDevice() {
299 Firmware firmware = new Firmware(); 412 Firmware firmware = new Firmware();
300 firmware.setTenantId(tenantId); 413 firmware.setTenantId(tenantId);
  414 + firmware.setDeviceProfileId(deviceProfileId);
  415 + firmware.setType(FIRMWARE);
301 firmware.setTitle(TITLE); 416 firmware.setTitle(TITLE);
302 firmware.setVersion(VERSION); 417 firmware.setVersion(VERSION);
303 firmware.setFileName(FILE_NAME); 418 firmware.setFileName(FILE_NAME);
@@ -307,12 +422,81 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -307,12 +422,81 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
307 firmware.setData(DATA); 422 firmware.setData(DATA);
308 Firmware savedFirmware = firmwareService.saveFirmware(firmware); 423 Firmware savedFirmware = firmwareService.saveFirmware(firmware);
309 424
310 - DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile");  
311 - deviceProfile.setFirmwareId(savedFirmware.getId()); 425 + Device device = new Device();
  426 + device.setTenantId(tenantId);
  427 + device.setName("My device");
  428 + device.setDeviceProfileId(deviceProfileId);
  429 + device.setFirmwareId(savedFirmware.getId());
  430 + Device savedDevice = deviceService.saveDevice(device);
  431 +
  432 + try {
  433 + thrown.expect(DataValidationException.class);
  434 + thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!");
  435 + savedFirmware.setDeviceProfileId(null);
  436 + firmwareService.saveFirmware(savedFirmware);
  437 + } finally {
  438 + deviceService.deleteDevice(tenantId, savedDevice.getId());
  439 + firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
  440 + }
  441 + }
  442 +
  443 + @Test
  444 + public void testDeleteFirmwareWithReferenceByDeviceProfile() {
  445 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile");
312 DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); 446 DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
313 447
  448 + Firmware firmware = new Firmware();
  449 + firmware.setTenantId(tenantId);
  450 + firmware.setDeviceProfileId(savedDeviceProfile.getId());
  451 + firmware.setType(FIRMWARE);
  452 + firmware.setTitle(TITLE);
  453 + firmware.setVersion(VERSION);
  454 + firmware.setFileName(FILE_NAME);
  455 + firmware.setContentType(CONTENT_TYPE);
  456 + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  457 + firmware.setChecksum(CHECKSUM);
  458 + firmware.setData(DATA);
  459 + Firmware savedFirmware = firmwareService.saveFirmware(firmware);
  460 +
  461 + savedDeviceProfile.setFirmwareId(savedFirmware.getId());
  462 + deviceProfileService.saveDeviceProfile(savedDeviceProfile);
  463 +
314 try { 464 try {
  465 + thrown.expect(DataValidationException.class);
  466 + thrown.expectMessage("The firmware referenced by the device profile cannot be deleted!");
  467 + firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
  468 + } finally {
  469 + deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
315 firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); 470 firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
  471 + }
  472 + }
  473 +
  474 + @Test
  475 + public void testUpdateDeviceProfileIdWithReferenceByDeviceProfile() {
  476 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile");
  477 + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
  478 +
  479 + Firmware firmware = new Firmware();
  480 + firmware.setTenantId(tenantId);
  481 + firmware.setDeviceProfileId(savedDeviceProfile.getId());
  482 + firmware.setType(FIRMWARE);
  483 + firmware.setTitle(TITLE);
  484 + firmware.setVersion(VERSION);
  485 + firmware.setFileName(FILE_NAME);
  486 + firmware.setContentType(CONTENT_TYPE);
  487 + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  488 + firmware.setChecksum(CHECKSUM);
  489 + firmware.setData(DATA);
  490 + Firmware savedFirmware = firmwareService.saveFirmware(firmware);
  491 +
  492 + savedDeviceProfile.setFirmwareId(savedFirmware.getId());
  493 + deviceProfileService.saveDeviceProfile(savedDeviceProfile);
  494 +
  495 + try {
  496 + thrown.expect(DataValidationException.class);
  497 + thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!");
  498 + savedFirmware.setDeviceProfileId(null);
  499 + firmwareService.saveFirmware(savedFirmware);
316 } finally { 500 } finally {
317 deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); 501 deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
318 firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); 502 firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
@@ -323,6 +507,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -323,6 +507,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
323 public void testFindFirmwareById() { 507 public void testFindFirmwareById() {
324 Firmware firmware = new Firmware(); 508 Firmware firmware = new Firmware();
325 firmware.setTenantId(tenantId); 509 firmware.setTenantId(tenantId);
  510 + firmware.setDeviceProfileId(deviceProfileId);
  511 + firmware.setType(FIRMWARE);
326 firmware.setTitle(TITLE); 512 firmware.setTitle(TITLE);
327 firmware.setVersion(VERSION); 513 firmware.setVersion(VERSION);
328 firmware.setFileName(FILE_NAME); 514 firmware.setFileName(FILE_NAME);
@@ -342,6 +528,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -342,6 +528,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
342 public void testFindFirmwareInfoById() { 528 public void testFindFirmwareInfoById() {
343 FirmwareInfo firmware = new FirmwareInfo(); 529 FirmwareInfo firmware = new FirmwareInfo();
344 firmware.setTenantId(tenantId); 530 firmware.setTenantId(tenantId);
  531 + firmware.setDeviceProfileId(deviceProfileId);
  532 + firmware.setType(FIRMWARE);
345 firmware.setTitle(TITLE); 533 firmware.setTitle(TITLE);
346 firmware.setVersion(VERSION); 534 firmware.setVersion(VERSION);
347 FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware); 535 FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware);
@@ -356,6 +544,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -356,6 +544,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
356 public void testDeleteFirmware() { 544 public void testDeleteFirmware() {
357 Firmware firmware = new Firmware(); 545 Firmware firmware = new Firmware();
358 firmware.setTenantId(tenantId); 546 firmware.setTenantId(tenantId);
  547 + firmware.setDeviceProfileId(deviceProfileId);
  548 + firmware.setType(FIRMWARE);
359 firmware.setTitle(TITLE); 549 firmware.setTitle(TITLE);
360 firmware.setVersion(VERSION); 550 firmware.setVersion(VERSION);
361 firmware.setFileName(FILE_NAME); 551 firmware.setFileName(FILE_NAME);
@@ -378,6 +568,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -378,6 +568,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
378 for (int i = 0; i < 165; i++) { 568 for (int i = 0; i < 165; i++) {
379 Firmware firmware = new Firmware(); 569 Firmware firmware = new Firmware();
380 firmware.setTenantId(tenantId); 570 firmware.setTenantId(tenantId);
  571 + firmware.setDeviceProfileId(deviceProfileId);
  572 + firmware.setType(FIRMWARE);
381 firmware.setTitle(TITLE); 573 firmware.setTitle(TITLE);
382 firmware.setVersion(VERSION + i); 574 firmware.setVersion(VERSION + i);
383 firmware.setFileName(FILE_NAME); 575 firmware.setFileName(FILE_NAME);
@@ -421,6 +613,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -421,6 +613,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
421 for (int i = 0; i < 165; i++) { 613 for (int i = 0; i < 165; i++) {
422 FirmwareInfo firmwareInfo = new FirmwareInfo(); 614 FirmwareInfo firmwareInfo = new FirmwareInfo();
423 firmwareInfo.setTenantId(tenantId); 615 firmwareInfo.setTenantId(tenantId);
  616 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  617 + firmwareInfo.setType(FIRMWARE);
424 firmwareInfo.setTitle(TITLE); 618 firmwareInfo.setTitle(TITLE);
425 firmwareInfo.setVersion(VERSION + i); 619 firmwareInfo.setVersion(VERSION + i);
426 firmwareInfo.setFileName(FILE_NAME); 620 firmwareInfo.setFileName(FILE_NAME);
@@ -435,7 +629,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -435,7 +629,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
435 PageLink pageLink = new PageLink(16); 629 PageLink pageLink = new PageLink(16);
436 PageData<FirmwareInfo> pageData; 630 PageData<FirmwareInfo> pageData;
437 do { 631 do {
438 - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, false, pageLink); 632 + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink);
439 loadedFirmwares.addAll(pageData.getData()); 633 loadedFirmwares.addAll(pageData.getData());
440 if (pageData.hasNext()) { 634 if (pageData.hasNext()) {
441 pageLink = pageLink.nextPageLink(); 635 pageLink = pageLink.nextPageLink();
@@ -451,6 +645,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -451,6 +645,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
451 Firmware firmware = new Firmware(f.getId()); 645 Firmware firmware = new Firmware(f.getId());
452 firmware.setCreatedTime(f.getCreatedTime()); 646 firmware.setCreatedTime(f.getCreatedTime());
453 firmware.setTenantId(f.getTenantId()); 647 firmware.setTenantId(f.getTenantId());
  648 + firmware.setDeviceProfileId(deviceProfileId);
  649 + firmware.setType(FIRMWARE);
454 firmware.setTitle(f.getTitle()); 650 firmware.setTitle(f.getTitle());
455 firmware.setVersion(f.getVersion()); 651 firmware.setVersion(f.getVersion());
456 firmware.setFileName(FILE_NAME); 652 firmware.setFileName(FILE_NAME);
@@ -466,7 +662,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @@ -466,7 +662,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
466 loadedFirmwares = new ArrayList<>(); 662 loadedFirmwares = new ArrayList<>();
467 pageLink = new PageLink(16); 663 pageLink = new PageLink(16);
468 do { 664 do {
469 - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, true, pageLink); 665 + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink);
470 loadedFirmwares.addAll(pageData.getData()); 666 loadedFirmwares.addAll(pageData.getData());
471 if (pageData.hasNext()) { 667 if (pageData.hasNext()) {
472 pageLink = pageLink.nextPageLink(); 668 pageLink = pageLink.nextPageLink();
@@ -19,3 +19,6 @@ TB_VERSION=latest @@ -19,3 +19,6 @@ TB_VERSION=latest
19 DATABASE=postgres 19 DATABASE=postgres
20 20
21 LOAD_BALANCER_NAME=haproxy-certbot 21 LOAD_BALANCER_NAME=haproxy-certbot
  22 +
  23 +# If enabled Prometheus and Grafana containers are deployed along with other containers
  24 +MONITORING_ENABLED=false