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 63 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
64 64 created_time bigint NOT NULL,
65 65 tenant_id uuid NOT NULL,
  66 + device_profile_id uuid,
  67 + type varchar(32) NOT NULL,
66 68 title varchar(255) NOT NULL,
67 69 version varchar(255) NOT NULL,
68 70 file_name varchar(255),
... ... @@ -77,10 +79,12 @@ CREATE TABLE IF NOT EXISTS firmware (
77 79 );
78 80
79 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 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 89 DO $$
86 90 BEGIN
... ... @@ -90,11 +94,23 @@ DO $$
90 94 FOREIGN KEY (firmware_id) REFERENCES firmware(id);
91 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 103 IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN
94 104 ALTER TABLE device
95 105 ADD CONSTRAINT fk_firmware_device
96 106 FOREIGN KEY (firmware_id) REFERENCES firmware(id);
97 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 114 END;
99 115 $$;
100 116
... ...
... ... @@ -22,6 +22,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
22 22 import org.springframework.context.event.EventListener;
23 23 import org.springframework.core.annotation.Order;
24 24 import org.springframework.stereotype.Service;
  25 +import org.thingsboard.common.util.ThingsBoardExecutors;
25 26 import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 27 import org.thingsboard.server.actors.ActorSystemContext;
27 28 import org.thingsboard.server.actors.DefaultTbActorSystem;
... ... @@ -108,7 +109,7 @@ public class DefaultActorService extends TbApplicationEventListener<PartitionCha
108 109 if (poolSize == 1) {
109 110 return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName));
110 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 146 checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
147 147
148 148 boolean isFirmwareChanged = false;
  149 + boolean isSoftwareChanged = false;
149 150
150 151 if (!created) {
151 152 DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId());
152 153 if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
153 154 isFirmwareChanged = true;
154 155 }
  156 + if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) {
  157 + isSoftwareChanged = true;
  158 + }
155 159 }
156 160
157 161 DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
... ... @@ -164,9 +168,8 @@ public class DeviceProfileController extends BaseController {
164 168 null,
165 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 173 sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
171 174 deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
172 175 return savedDeviceProfile;
... ...
... ... @@ -35,6 +35,8 @@ import org.thingsboard.server.common.data.FirmwareInfo;
35 35 import org.thingsboard.server.common.data.audit.ActionType;
36 36 import org.thingsboard.server.common.data.exception.ThingsboardException;
37 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 40 import org.thingsboard.server.common.data.id.FirmwareId;
39 41 import org.thingsboard.server.common.data.page.PageData;
40 42 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -135,6 +137,8 @@ public class FirmwareController extends BaseController {
135 137 Firmware firmware = new Firmware(firmwareId);
136 138 firmware.setCreatedTime(info.getCreatedTime());
137 139 firmware.setTenantId(getTenantId());
  140 + firmware.setDeviceProfileId(info.getDeviceProfileId());
  141 + firmware.setType(info.getType());
138 142 firmware.setTitle(info.getTitle());
139 143 firmware.setVersion(info.getVersion());
140 144 firmware.setAdditionalInfo(info.getAdditionalInfo());
... ... @@ -178,17 +182,22 @@ public class FirmwareController extends BaseController {
178 182 }
179 183
180 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 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 190 @RequestParam int pageSize,
185 191 @RequestParam int page,
186 192 @RequestParam(required = false) String textSearch,
187 193 @RequestParam(required = false) String sortProperty,
188 194 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  195 + checkParameter("deviceProfileId", strDeviceProfileId);
  196 + checkParameter("type", strType);
189 197 try {
190 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 201 } catch (Exception e) {
193 202 throw handleException(e);
194 203 }
... ...
... ... @@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestParam;
32 32 import org.springframework.web.bind.annotation.ResponseBody;
33 33 import org.springframework.web.bind.annotation.ResponseStatus;
34 34 import org.springframework.web.bind.annotation.RestController;
  35 +import org.thingsboard.common.util.JacksonUtil;
35 36 import org.thingsboard.rule.engine.api.MailService;
36 37 import org.thingsboard.server.common.data.EntityType;
37 38 import org.thingsboard.server.common.data.User;
... ... @@ -89,13 +90,14 @@ public class UserController extends BaseController {
89 90 try {
90 91 UserId userId = new UserId(toUUID(strUserId));
91 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 102 return user;
101 103 } catch (Exception e) {
... ... @@ -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 108 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
115 109 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET)
116 110 @ResponseBody
... ...
... ... @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.beans.factory.annotation.Value;
24 24 import org.springframework.context.annotation.Lazy;
25 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 27 import org.thingsboard.rule.engine.api.MailService;
27 28 import org.thingsboard.server.common.data.ApiFeature;
28 29 import org.thingsboard.server.common.data.ApiUsageRecordKey;
... ... @@ -146,7 +147,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
146 147 this.scheduler = scheduler;
147 148 this.tenantProfileCache = tenantProfileCache;
148 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 153 @PostConstruct
... ...
... ... @@ -19,13 +19,17 @@ import com.google.common.util.concurrent.FutureCallback;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.springframework.stereotype.Service;
21 21 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
  22 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
22 23 import org.thingsboard.server.common.data.DataConstants;
23 24 import org.thingsboard.server.common.data.Device;
24 25 import org.thingsboard.server.common.data.DeviceProfile;
25 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 29 import org.thingsboard.server.common.data.id.DeviceId;
27 30 import org.thingsboard.server.common.data.id.FirmwareId;
28 31 import org.thingsboard.server.common.data.id.TenantId;
  32 +import org.thingsboard.server.common.data.kv.AttributeKey;
29 33 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
30 34 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
31 35 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
... ... @@ -43,37 +47,49 @@ import org.thingsboard.server.queue.TbQueueProducer;
43 47 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
44 48 import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
45 49 import org.thingsboard.server.queue.util.TbCoreComponent;
  50 +import org.thingsboard.server.service.queue.TbClusterService;
46 51
47 52 import javax.annotation.Nullable;
48 53 import java.util.ArrayList;
49   -import java.util.Arrays;
50 54 import java.util.Collections;
  55 +import java.util.HashSet;
51 56 import java.util.List;
  57 +import java.util.Set;
52 58 import java.util.UUID;
53 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 75 @Slf4j
62 76 @Service
63 77 @TbCoreComponent
64 78 public class DefaultFirmwareStateService implements FirmwareStateService {
65 79
  80 + private final TbClusterService tbClusterService;
66 81 private final FirmwareService firmwareService;
67 82 private final DeviceService deviceService;
68 83 private final DeviceProfileService deviceProfileService;
69 84 private final RuleEngineTelemetryService telemetryService;
70 85 private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer;
71 86
72   - public DefaultFirmwareStateService(FirmwareService firmwareService,
  87 + public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService,
73 88 DeviceService deviceService,
74 89 DeviceProfileService deviceProfileService,
75 90 RuleEngineTelemetryService telemetryService,
76 91 TbCoreQueueFactory coreQueueFactory) {
  92 + this.tbClusterService = tbClusterService;
77 93 this.firmwareService = firmwareService;
78 94 this.deviceService = deviceService;
79 95 this.deviceProfileService = deviceProfileService;
... ... @@ -83,6 +99,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
83 99
84 100 @Override
85 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 107 FirmwareId newFirmwareId = device.getFirmwareId();
87 108 if (newFirmwareId == null) {
88 109 DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
... ... @@ -97,35 +118,84 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
97 118 }
98 119 if (!newFirmwareId.equals(oldFirmwareId)) {
99 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 123 } else {
103 124 // Device was updated and new firmware is not set.
104   - remove(device);
  125 + remove(device, FIRMWARE);
105 126 }
106 127 } else if (newFirmwareId != null) {
107 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 160 @Override
113   - public void update(DeviceProfile deviceProfile) {
  161 + public void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged) {
114 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 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 188 if (deviceProfile.getFirmwareId() != null) {
118 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 191 } else {
121   - updateConsumer = this::remove;
  192 + updateConsumer = d -> remove(d, firmwareType);
122 193 }
123 194
124 195 PageLink pageLink = new PageLink(100);
125 196 PageData<Device> pageData;
126 197 do {
127   - pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pageLink);
128   -
  198 + pageData = getDevicesFunction.apply(pageLink);
129 199 pageData.getData().forEach(updateConsumer);
130 200
131 201 if (pageData.hasNext()) {
... ... @@ -140,16 +210,17 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
140 210 FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()));
141 211 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
142 212 TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB()));
  213 + FirmwareType firmwareType = FirmwareType.valueOf(msg.getType());
143 214 long ts = msg.getTs();
144 215
145 216 Device device = deviceService.findDeviceById(tenantId, deviceId);
146 217 if (device == null) {
147 218 log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId);
148 219 } else {
149   - FirmwareId currentFirmwareId = device.getFirmwareId();
150   -
  220 + FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
151 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 226 if (targetFirmwareId.equals(currentFirmwareId)) {
... ... @@ -162,7 +233,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
162 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 237 ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder()
167 238 .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
168 239 .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
... ... @@ -170,6 +241,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
170 241 .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
171 242 .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits())
172 243 .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits())
  244 + .setType(firmwareType.name())
173 245 .setTs(ts)
174 246 .build();
175 247
... ... @@ -183,10 +255,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
183 255 fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null);
184 256
185 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 263 telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
192 264 @Override
... ... @@ -206,7 +278,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
206 278 TenantId tenantId = device.getTenantId();
207 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 283 telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() {
212 284 @Override
... ... @@ -221,13 +293,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
221 293 });
222 294
223 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 302 telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() {
232 303 @Override
233 304 public void onSuccess(@Nullable Void tmp) {
... ... @@ -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 317 new FutureCallback<>() {
248 318 @Override
249 319 public void onSuccess(@Nullable Void tmp) {
250 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 326 @Override
... ...
... ... @@ -23,7 +23,7 @@ public interface FirmwareStateService {
23 23
24 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 28 boolean process(ToFirmwareStateServiceMsg msg);
29 29
... ...
... ... @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
20 20 import org.springframework.beans.factory.annotation.Value;
21 21 import org.springframework.scheduling.annotation.Scheduled;
22 22 import org.springframework.stereotype.Service;
  23 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 24 import org.thingsboard.rule.engine.api.RpcError;
24 25 import org.thingsboard.server.actors.ActorSystemContext;
25 26 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -127,7 +128,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
127 128 consumers.computeIfAbsent(configuration.getName(), queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration));
128 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 134 @PreDestroy
... ... @@ -160,6 +161,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
160 161
161 162 private void launchConsumer(TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer, TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats) {
162 163 consumersExecutor.execute(() -> {
  164 + Thread.currentThread().setName("" + Thread.currentThread().getName() + "-" + configuration.getName());
163 165 while (!stopped) {
164 166 try {
165 167 List<TbProtoQueueMsg<ToRuleEngineMsg>> msgs = consumer.poll(pollDuration);
... ...
... ... @@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService {
185 185 instance.setId(0);
186 186 List<LwM2mResourceObserve> resources = new ArrayList<>();
187 187 obj.resources.forEach((k, v) -> {
188   - if (!v.operations.isExecutable()) {
  188 + if (v.operations.isReadable()) {
189 189 LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
190 190 resources.add(lwM2MResourceObserve);
191 191 }
... ...
... ... @@ -25,6 +25,7 @@ import lombok.Getter;
25 25 import lombok.extern.slf4j.Slf4j;
26 26 import org.springframework.beans.factory.annotation.Value;
27 27 import org.springframework.scheduling.annotation.Scheduled;
  28 +import org.thingsboard.common.util.ThingsBoardExecutors;
28 29 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
29 30 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
30 31
... ... @@ -93,7 +94,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
93 94 super.init(maxRequestsTimeout);
94 95 if (useJsSandbox()) {
95 96 sandbox = NashornSandboxes.create();
96   - monitorExecutorService = Executors.newWorkStealingPool(getMonitorThreadPoolSize());
  97 + monitorExecutorService = ThingsBoardExecutors.newWorkStealingPool(getMonitorThreadPoolSize(), "nashorn-js-monitor");
97 98 sandbox.setExecutor(monitorExecutorService);
98 99 sandbox.setMaxCPUTime(getMaxCpuTime());
99 100 sandbox.allowNoBraces(false);
... ...
... ... @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
20 20 import org.springframework.context.annotation.Lazy;
21 21 import org.springframework.context.event.EventListener;
22 22 import org.springframework.stereotype.Service;
  23 +import org.thingsboard.common.util.ThingsBoardExecutors;
23 24 import org.thingsboard.server.gen.transport.TransportProtos;
24 25 import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
25 26 import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
... ... @@ -62,7 +63,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
62 63 private SubscriptionManagerService subscriptionManagerService;
63 64
64 65 private ExecutorService subscriptionUpdateExecutor;
65   -
  66 +
66 67 private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() {
67 68 @Override
68 69 protected void onTbApplicationEvent(PartitionChangeEvent event) {
... ... @@ -93,7 +94,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
93 94
94 95 @PostConstruct
95 96 public void initExecutor() {
96   - subscriptionUpdateExecutor = Executors.newWorkStealingPool(20);
  97 + subscriptionUpdateExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass());
97 98 }
98 99
99 100 @PreDestroy
... ...
... ... @@ -28,6 +28,8 @@ import org.springframework.beans.factory.annotation.Value;
28 28 import org.springframework.stereotype.Service;
29 29 import org.springframework.util.StringUtils;
30 30 import org.springframework.web.socket.CloseStatus;
  31 +import org.thingsboard.common.util.ThingsBoardExecutors;
  32 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
31 33 import org.thingsboard.server.common.data.DataConstants;
32 34 import org.thingsboard.server.common.data.id.CustomerId;
33 35 import org.thingsboard.server.common.data.id.EntityId;
... ... @@ -157,9 +159,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
157 159 @PostConstruct
158 160 public void initExecutor() {
159 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 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 41 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
42 42 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
43 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 46 import org.thingsboard.server.common.data.id.CustomerId;
45 47 import org.thingsboard.server.common.data.id.DeviceId;
46 48 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -512,16 +514,17 @@ public class DefaultTransportApiService implements TransportApiService {
512 514 private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) {
513 515 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
514 516 DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
  517 + FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType());
515 518 Device device = deviceService.findDeviceById(tenantId, deviceId);
516 519
517 520 if (device == null) {
518 521 return getEmptyTransportApiResponseFuture();
519 522 }
520 523
521   - FirmwareId firmwareId = device.getFirmwareId();
522   -
  524 + FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
523 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 530 TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder();
... ... @@ -537,6 +540,7 @@ public class DefaultTransportApiService implements TransportApiService {
537 540 builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
538 541 builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits());
539 542 builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits());
  543 + builder.setType(firmwareInfo.getType().name());
540 544 builder.setTitle(firmwareInfo.getTitle());
541 545 builder.setVersion(firmwareInfo.getVersion());
542 546 builder.setFileName(firmwareInfo.getFileName());
... ...
... ... @@ -21,6 +21,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
21 21 import org.springframework.context.event.EventListener;
22 22 import org.springframework.core.annotation.Order;
23 23 import org.springframework.stereotype.Service;
  24 +import org.thingsboard.common.util.ThingsBoardExecutors;
24 25 import org.thingsboard.server.common.stats.MessagesStats;
25 26 import org.thingsboard.server.common.stats.StatsFactory;
26 27 import org.thingsboard.server.common.stats.StatsType;
... ... @@ -70,7 +71,7 @@ public class TbCoreTransportApiService {
70 71
71 72 @PostConstruct
72 73 public void init() {
73   - this.transportCallbackExecutor = Executors.newWorkStealingPool(maxCallbackThreads);
  74 + this.transportCallbackExecutor = ThingsBoardExecutors.newWorkStealingPool(maxCallbackThreads, getClass());
74 75 TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> producer = tbCoreQueueFactory.createTransportApiResponseProducer();
75 76 TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> consumer = tbCoreQueueFactory.createTransportApiRequestConsumer();
76 77
... ...
... ... @@ -24,10 +24,13 @@ import org.springframework.mock.web.MockMultipartFile;
24 24 import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
25 25 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
26 26 import org.thingsboard.common.util.JacksonUtil;
  27 +import org.thingsboard.server.common.data.DeviceProfile;
27 28 import org.thingsboard.server.common.data.Firmware;
28 29 import org.thingsboard.server.common.data.FirmwareInfo;
29 30 import org.thingsboard.server.common.data.Tenant;
30 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 34 import org.thingsboard.server.common.data.page.PageData;
32 35 import org.thingsboard.server.common.data.page.PageLink;
33 36 import org.thingsboard.server.common.data.security.Authority;
... ... @@ -38,6 +41,7 @@ import java.util.Collections;
38 41 import java.util.List;
39 42
40 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 46 public abstract class BaseFirmwareControllerTest extends AbstractControllerTest {
43 47
... ... @@ -53,6 +57,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
53 57
54 58 private Tenant savedTenant;
55 59 private User tenantAdmin;
  60 + private DeviceProfileId deviceProfileId;
56 61
57 62 @Before
58 63 public void beforeTest() throws Exception {
... ... @@ -71,6 +76,11 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
71 76 tenantAdmin.setLastName("Downs");
72 77
73 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 86 @After
... ... @@ -84,6 +94,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
84 94 @Test
85 95 public void testSaveFirmware() throws Exception {
86 96 FirmwareInfo firmwareInfo = new FirmwareInfo();
  97 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  98 + firmwareInfo.setType(FIRMWARE);
87 99 firmwareInfo.setTitle(TITLE);
88 100 firmwareInfo.setVersion(VERSION);
89 101
... ... @@ -107,6 +119,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
107 119 @Test
108 120 public void testSaveFirmwareData() throws Exception {
109 121 FirmwareInfo firmwareInfo = new FirmwareInfo();
  122 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  123 + firmwareInfo.setType(FIRMWARE);
110 124 firmwareInfo.setTitle(TITLE);
111 125 firmwareInfo.setVersion(VERSION);
112 126
... ... @@ -137,6 +151,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
137 151 @Test
138 152 public void testUpdateFirmwareFromDifferentTenant() throws Exception {
139 153 FirmwareInfo firmwareInfo = new FirmwareInfo();
  154 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  155 + firmwareInfo.setType(FIRMWARE);
140 156 firmwareInfo.setTitle(TITLE);
141 157 firmwareInfo.setVersion(VERSION);
142 158
... ... @@ -150,6 +166,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
150 166 @Test
151 167 public void testFindFirmwareInfoById() throws Exception {
152 168 FirmwareInfo firmwareInfo = new FirmwareInfo();
  169 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  170 + firmwareInfo.setType(FIRMWARE);
153 171 firmwareInfo.setTitle(TITLE);
154 172 firmwareInfo.setVersion(VERSION);
155 173
... ... @@ -163,6 +181,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
163 181 @Test
164 182 public void testFindFirmwareById() throws Exception {
165 183 FirmwareInfo firmwareInfo = new FirmwareInfo();
  184 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  185 + firmwareInfo.setType(FIRMWARE);
166 186 firmwareInfo.setTitle(TITLE);
167 187 firmwareInfo.setVersion(VERSION);
168 188
... ... @@ -180,6 +200,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
180 200 @Test
181 201 public void testDeleteFirmware() throws Exception {
182 202 FirmwareInfo firmwareInfo = new FirmwareInfo();
  203 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  204 + firmwareInfo.setType(FIRMWARE);
183 205 firmwareInfo.setTitle(TITLE);
184 206 firmwareInfo.setVersion(VERSION);
185 207
... ... @@ -197,6 +219,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
197 219 List<FirmwareInfo> firmwares = new ArrayList<>();
198 220 for (int i = 0; i < 165; i++) {
199 221 FirmwareInfo firmwareInfo = new FirmwareInfo();
  222 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  223 + firmwareInfo.setType(FIRMWARE);
200 224 firmwareInfo.setTitle(TITLE);
201 225 firmwareInfo.setVersion(VERSION + i);
202 226
... ... @@ -238,6 +262,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
238 262
239 263 for (int i = 0; i < 165; i++) {
240 264 FirmwareInfo firmwareInfo = new FirmwareInfo();
  265 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  266 + firmwareInfo.setType(FIRMWARE);
241 267 firmwareInfo.setTitle(TITLE);
242 268 firmwareInfo.setVersion(VERSION + i);
243 269
... ... @@ -257,7 +283,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
257 283 PageLink pageLink = new PageLink(24);
258 284 PageData<FirmwareInfo> pageData;
259 285 do {
260   - pageData = doGetTypedWithPageLink("/api/firmwares/true?",
  286 + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?",
261 287 new TypeReference<>() {
262 288 }, pageLink);
263 289 loadedFirmwaresWithData.addAll(pageData.getData());
... ... @@ -269,7 +295,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
269 295 List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>();
270 296 pageLink = new PageLink(24);
271 297 do {
272   - pageData = doGetTypedWithPageLink("/api/firmwares/false?",
  298 + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?",
273 299 new TypeReference<>() {
274 300 }, pageLink);
275 301 loadedFirmwaresWithoutData.addAll(pageData.getData());
... ...
... ... @@ -16,10 +16,10 @@
16 16 package org.thingsboard.server.service.queue;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
19 20 import org.junit.Assert;
20 21 import org.junit.Test;
21 22 import org.junit.runner.RunWith;
22   -import org.mockito.Mockito;
23 23 import org.mockito.junit.MockitoJUnitRunner;
24 24 import org.thingsboard.server.gen.transport.TransportProtos;
25 25 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
... ... @@ -28,39 +28,74 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrateg
28 28 import java.util.UUID;
29 29 import java.util.concurrent.ConcurrentHashMap;
30 30 import java.util.concurrent.ConcurrentMap;
  31 +import java.util.concurrent.CountDownLatch;
31 32 import java.util.concurrent.ExecutorService;
32 33 import java.util.concurrent.Executors;
33 34 import java.util.concurrent.TimeUnit;
34 35
  36 +import static org.junit.Assert.assertTrue;
  37 +import static org.mockito.ArgumentMatchers.any;
35 38 import static org.mockito.Mockito.mock;
  39 +import static org.mockito.Mockito.times;
  40 +import static org.mockito.Mockito.verify;
36 41 import static org.mockito.Mockito.when;
37 42
38 43 @Slf4j
39 44 @RunWith(MockitoJUnitRunner.class)
40 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 57 @Test
43 58 public void testHighConcurrencyCase() throws InterruptedException {
44   - TbRuleEngineSubmitStrategy strategyMock = mock(TbRuleEngineSubmitStrategy.class);
  59 + //log.warn("preparing the test...");
45 60 int msgCount = 1000;
46 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 65
66 66 PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
67 67
  68 + PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink);
  69 +
68 70 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
69 71
70 72 PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink);
... ...
... ... @@ -18,8 +18,9 @@ package org.thingsboard.server.dao.firmware;
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.thingsboard.server.common.data.Firmware;
20 20 import org.thingsboard.server.common.data.FirmwareInfo;
21   -import org.thingsboard.server.common.data.exception.ThingsboardException;
22 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 24 import org.thingsboard.server.common.data.id.FirmwareId;
24 25 import org.thingsboard.server.common.data.id.TenantId;
25 26 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -33,7 +34,7 @@ public interface FirmwareService {
33 34
34 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 39 Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId);
39 40
... ... @@ -43,7 +44,7 @@ public interface FirmwareService {
43 44
44 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 49 void deleteFirmware(TenantId tenantId, FirmwareId firmwareId);
49 50
... ...
... ... @@ -93,22 +93,26 @@ public class DataConstants {
93 93 public static final String USERNAME = "username";
94 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 115 public static final String EDGE_MSG_SOURCE = "edge";
113 116 public static final String MSG_SOURCE_KEY = "source";
  117 +
114 118 }
... ...
... ... @@ -32,7 +32,7 @@ import java.io.IOException;
32 32
33 33 @EqualsAndHashCode(callSuper = true)
34 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 37 private static final long serialVersionUID = 2807343040519543363L;
38 38
... ... @@ -50,6 +50,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
50 50 private byte[] deviceDataBytes;
51 51
52 52 private FirmwareId firmwareId;
  53 + private FirmwareId softwareId;
53 54
54 55 public Device() {
55 56 super();
... ... @@ -69,6 +70,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
69 70 this.deviceProfileId = device.getDeviceProfileId();
70 71 this.setDeviceData(device.getDeviceData());
71 72 this.firmwareId = device.getFirmwareId();
  73 + this.softwareId = device.getSoftwareId();
72 74 }
73 75
74 76 public Device updateDevice(Device device) {
... ... @@ -79,6 +81,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
79 81 this.label = device.getLabel();
80 82 this.deviceProfileId = device.getDeviceProfileId();
81 83 this.setDeviceData(device.getDeviceData());
  84 + this.setFirmwareId(device.getFirmwareId());
  85 + this.setSoftwareId(device.getSoftwareId());
82 86 return this;
83 87 }
84 88
... ... @@ -171,6 +175,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
171 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 186 @Override
175 187 public String toString() {
176 188 StringBuilder builder = new StringBuilder();
... ...
... ... @@ -36,7 +36,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
36 36 @Data
37 37 @EqualsAndHashCode(callSuper = true)
38 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 41 private TenantId tenantId;
42 42 @NoXss
... ... @@ -59,6 +59,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
59 59
60 60 private FirmwareId firmwareId;
61 61
  62 + private FirmwareId softwareId;
  63 +
62 64 public DeviceProfile() {
63 65 super();
64 66 }
... ... @@ -77,6 +79,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
77 79 this.defaultQueueName = deviceProfile.getDefaultQueueName();
78 80 this.setProfileData(deviceProfile.getProfileData());
79 81 this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
  82 + this.firmwareId = deviceProfile.getFirmwareId();
  83 + this.softwareId = deviceProfile.getSoftwareId();
80 84 }
81 85
82 86 @Override
... ...
... ... @@ -20,6 +20,8 @@ import lombok.Data;
20 20 import lombok.EqualsAndHashCode;
21 21 import lombok.extern.slf4j.Slf4j;
22 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 25 import org.thingsboard.server.common.data.id.FirmwareId;
24 26 import org.thingsboard.server.common.data.id.TenantId;
25 27
... ... @@ -31,6 +33,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
31 33 private static final long serialVersionUID = 3168391583570815419L;
32 34
33 35 private TenantId tenantId;
  36 + private DeviceProfileId deviceProfileId;
  37 + private FirmwareType type;
34 38 private String title;
35 39 private String version;
36 40 private boolean hasData;
... ... @@ -52,6 +56,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
52 56 public FirmwareInfo(FirmwareInfo firmwareInfo) {
53 57 super(firmwareInfo);
54 58 this.tenantId = firmwareInfo.getTenantId();
  59 + this.deviceProfileId = firmwareInfo.getDeviceProfileId();
  60 + this.type = firmwareInfo.getType();
55 61 this.title = firmwareInfo.getTitle();
56 62 this.version = firmwareInfo.getVersion();
57 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 31 private static final String SUB_TOPIC = "+";
32 32 private static final String PROVISION = "/provision";
33 33 private static final String FIRMWARE = "/fw";
  34 + private static final String SOFTWARE = "/sw";
34 35 private static final String CHUNK = "/chunk/";
35 36 private static final String ERROR = "/error";
36 37
... ... @@ -75,9 +76,17 @@ public class MqttTopics {
75 76 // v2 topics
76 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 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 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 16 package org.thingsboard.server.common.msg.session;
17 17
18 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 32
33 33 CLAIM_REQUEST(),
34 34
35   - GET_FIRMWARE_REQUEST;
  35 + GET_FIRMWARE_REQUEST,
  36 + GET_SOFTWARE_REQUEST;
36 37
37 38 private final boolean requiresRulesProcessing;
38 39
... ...
... ... @@ -38,6 +38,7 @@ import static java.util.Collections.emptyList;
38 38 @Slf4j
39 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 42 private volatile boolean subscribed;
42 43 protected volatile boolean stopped = false;
43 44 protected volatile Set<TopicPartitionInfo> partitions;
... ... @@ -83,7 +84,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
83 84 }
84 85
85 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 90 consumerLock.lock();
... ... @@ -131,9 +132,12 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
131 132 List<T> sleepAndReturnEmpty(final long startNanos, final long durationInMillis) {
132 133 long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis);
133 134 long spentNanos = System.nanoTime() - startNanos;
134   - if (spentNanos < durationNanos) {
  135 + long nanosLeft = durationNanos - spentNanos;
  136 + if (nanosLeft >= ONE_MILLISECOND_IN_NANOS) {
135 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 141 } catch (InterruptedException e) {
138 142 if (!stopped) {
139 143 log.error("Failed to wait", e);
... ... @@ -146,7 +150,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
146 150 @Override
147 151 public void commit() {
148 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 155 consumerLock.lock();
152 156 try {
... ...
... ... @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture;
20 20 import com.google.common.util.concurrent.SettableFuture;
21 21 import lombok.Builder;
22 22 import lombok.extern.slf4j.Slf4j;
  23 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 24 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
24 25 import org.thingsboard.server.queue.TbQueueAdmin;
25 26 import org.thingsboard.server.queue.TbQueueCallback;
... ... @@ -77,7 +78,7 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
77 78 this.executor = executor;
78 79 } else {
79 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 17
18 18 import lombok.Builder;
19 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
20 21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
21 22 import org.thingsboard.server.queue.TbQueueConsumer;
22 23 import org.thingsboard.server.queue.TbQueueHandler;
... ... @@ -70,8 +71,8 @@ public class DefaultTbQueueResponseTemplate<Request extends TbQueueMsg, Response
70 71 this.requestTimeout = requestTimeout;
71 72 this.callbackExecutor = executor;
72 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 78 @Override
... ...
... ... @@ -388,16 +388,18 @@ message GetFirmwareRequestMsg {
388 388 int64 deviceIdLSB = 2;
389 389 int64 tenantIdMSB = 3;
390 390 int64 tenantIdLSB = 4;
  391 + string type = 5;
391 392 }
392 393
393 394 message GetFirmwareResponseMsg {
394 395 ResponseStatus responseStatus = 1;
395 396 int64 firmwareIdMSB = 2;
396 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 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 713 int64 deviceIdLSB = 5;
712 714 int64 firmwareIdMSB = 6;
713 715 int64 firmwareIdLSB = 7;
  716 + string type = 8;
714 717 }
... ...
... ... @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
43 43 import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
44 44 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
45 45 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
  46 +import org.thingsboard.server.common.data.firmware.FirmwareType;
46 47 import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
47 48 import org.thingsboard.server.common.msg.session.FeatureType;
48 49 import org.thingsboard.server.common.msg.session.SessionMsgType;
... ... @@ -122,6 +123,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
122 123 processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
123 124 } else if (featureType.get() == FeatureType.FIRMWARE) {
124 125 processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST);
  126 + } else if (featureType.get() == FeatureType.SOFTWARE) {
  127 + processRequest(exchange, SessionMsgType.GET_SOFTWARE_REQUEST);
125 128 } else {
126 129 log.trace("Invalid feature type parameter");
127 130 exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
... ... @@ -326,12 +329,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
326 329 new CoapNoOpCallback(exchange));
327 330 break;
328 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 336 break;
336 337 }
337 338 } catch (AdaptorException e) {
... ... @@ -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 354 private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) {
344 355 tokenToNotificationCounterMap.remove(token);
345 356 return tokenToSessionIdMap.remove(token);
... ...
... ... @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam;
34 34 import org.springframework.web.bind.annotation.RestController;
35 35 import org.springframework.web.context.request.async.DeferredResult;
36 36 import org.thingsboard.server.common.data.DeviceTransportType;
  37 +import org.thingsboard.server.common.data.firmware.FirmwareType;
37 38 import org.thingsboard.server.common.data.TbTransportService;
38 39 import org.thingsboard.server.common.data.id.DeviceId;
39 40 import org.thingsboard.server.common.transport.SessionMsgListener;
... ... @@ -210,8 +211,29 @@ public class DeviceApiController implements TbTransportService {
210 211 public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken,
211 212 @RequestParam(value = "title") String title,
212 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 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 237 DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
216 238 transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
217 239 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
... ... @@ -219,20 +241,13 @@ public class DeviceApiController implements TbTransportService {
219 241 .setTenantIdMSB(sessionInfo.getTenantIdMSB())
220 242 .setTenantIdLSB(sessionInfo.getTenantIdLSB())
221 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 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 251 private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
237 252 private final TransportContext transportContext;
238 253 private final DeferredResult<ResponseEntity> responseWriter;
... ...
... ... @@ -55,7 +55,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
55 55 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
56 56 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
57 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 60 @Slf4j
61 61 @Component
... ... @@ -93,7 +93,6 @@ public class LwM2MTransportBootstrapServerConfiguration {
93 93 builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort));
94 94
95 95 /** Define model provider (Create Models )*/
96   -// builder.setModel(new StaticModel(contextS.getLwM2MTransportConfigServer().getModelsValueCommon()));
97 96
98 97 /** Create credentials */
99 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 26
27 27 import java.util.Collection;
28 28
  29 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
29 30 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer;
30 31
31 32 @Slf4j
... ... @@ -85,17 +86,19 @@ public class LwM2mServerListener {
85 86
86 87 @Override
87 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 94 @Override
92 95 public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
93 96 if (registration != null) {
94 97 try {
95   - service.onObservationResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
  98 + service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
96 99 registration), response, null);
97 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 111
109 112 @Override
110 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 25 import com.google.gson.reflect.TypeToken;
26 26 import lombok.extern.slf4j.Slf4j;
27 27 import org.apache.commons.lang3.StringUtils;
28   -import org.eclipse.californium.core.network.config.NetworkConfig;
29 28 import org.eclipse.leshan.core.attributes.Attribute;
30 29 import org.eclipse.leshan.core.attributes.AttributeSet;
31 30 import org.eclipse.leshan.core.model.ObjectModel;
... ... @@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException;
40 39 import org.eclipse.leshan.core.request.DownlinkRequest;
41 40 import org.eclipse.leshan.core.request.WriteAttributesRequest;
42 41 import org.eclipse.leshan.core.util.Hex;
43   -import org.eclipse.leshan.server.californium.LeshanServerBuilder;
44 42 import org.eclipse.leshan.server.registration.Registration;
45 43 import org.nustaq.serialization.FSTConfiguration;
46 44 import org.thingsboard.server.common.data.DeviceProfile;
... ... @@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
50 48 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
51 49 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
52 50
53   -import java.io.File;
54 51 import java.io.IOException;
55 52 import java.util.ArrayList;
56 53 import java.util.Arrays;
... ... @@ -72,7 +69,6 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA
72 69 @Slf4j
73 70 public class LwM2mTransportHandler {
74 71
75   - // public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";
76 72 public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0";
77 73 public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
78 74 public static final String BOOTSTRAP = "bootstrap";
... ... @@ -85,19 +81,12 @@ public class LwM2mTransportHandler {
85 81 public static final String KEY_NAME = "keyName";
86 82 public static final String OBSERVE_LWM2M = "observe";
87 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 85 private static final String REQUEST = "/request";
92   - // private static final String RESPONSE = "/response";
93 86 private static final String ATTRIBUTES = "/" + ATTRIBUTE;
94 87 public static final String TELEMETRIES = "/" + TELEMETRY;
95   - // public static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE;
96 88 public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
97   - // public static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/";
98 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 91 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
103 92
... ... @@ -112,6 +101,11 @@ public class LwM2mTransportHandler {
112 101
113 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 109 public enum LwM2mTypeServer {
116 110 BOOTSTRAP(0, "bootstrap"),
117 111 CLIENT(1, "client");
... ... @@ -168,6 +162,8 @@ public class LwM2mTransportHandler {
168 162 WRITE_ATTRIBUTES(8, "WriteAttributes"),
169 163 DELETE(9, "Delete");
170 164
  165 +// READ_INFO_FW(10, "ReadInfoFirmware");
  166 +
171 167 public int code;
172 168 public String type;
173 169
... ... @@ -190,21 +186,6 @@ public class LwM2mTransportHandler {
190 186 public static final String SERVICE_CHANNEL = "SERVICE";
191 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 189 public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException {
209 190 switch (type) {
210 191 case BOOLEAN:
... ...
... ... @@ -67,6 +67,7 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
67 67 import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST;
68 68 import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND;
69 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 71 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
71 72 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
72 73 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
... ... @@ -125,7 +126,6 @@ public class LwM2mTransportRequest {
125 126 public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
126 127 String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
127 128 try {
128   -
129 129 String target = convertPathFromIdVerToObjectId(targetIdVer);
130 130 DownlinkRequest request = null;
131 131 ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
... ... @@ -145,11 +145,11 @@ public class LwM2mTransportRequest {
145 145 break;
146 146 case OBSERVE:
147 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 149 } else if (resultIds.isObjectInstance()) {
150   - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId());
  150 + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId());
151 151 } else if (resultIds.getObjectId() >= 0) {
152   - request = new ObserveRequest(resultIds.getObjectId());
  152 + request = new ObserveRequest(contentFormat, resultIds.getObjectId());
153 153 }
154 154 break;
155 155 case OBSERVE_CANCEL:
... ... @@ -171,8 +171,6 @@ public class LwM2mTransportRequest {
171 171 break;
172 172 case WRITE_REPLACE:
173 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 174 resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
177 175 .getModelProvider());
178 176 if (contentFormat.equals(ContentFormat.TLV)) {
... ... @@ -181,7 +179,6 @@ public class LwM2mTransportRequest {
181 179 registration, rpcRequest);
182 180 }
183 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 182 else if (!contentFormat.equals(ContentFormat.TLV)) {
186 183 request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
187 184 resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type,
... ... @@ -215,13 +212,16 @@ public class LwM2mTransportRequest {
215 212 long finalTimeoutInMs = timeoutInMs;
216 213 lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest));
217 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 223 } else {
224   - log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper, targetIdVer);
  224 + log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
225 225 if (rpcRequest != null) {
226 226 String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
227 227 serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
... ... @@ -236,8 +236,8 @@ public class LwM2mTransportRequest {
236 236 Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
237 237 String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO,
238 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 241 if (rpcRequest != null) {
242 242 String valueMsg = String.format("Observation paths - %s", observationPaths);
243 243 serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
... ... @@ -246,7 +246,7 @@ public class LwM2mTransportRequest {
246 246 } catch (Exception e) {
247 247 String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
248 248 typeOper.name(), e.getMessage());
249   - serviceImpl.sendLogsToThingsboard(msg, registration);
  249 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
250 250 throw new Exception(e);
251 251 }
252 252 }
... ... @@ -261,45 +261,53 @@ public class LwM2mTransportRequest {
261 261 private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
262 262 leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
263 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 266 if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
267 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 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 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 277 if (rpcRequest != null) {
287 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 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 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 307 if (rpcRequest != null) {
299 308 serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
300 309 }
301 310 });
302   -
303 311 }
304 312
305 313 private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
... ... @@ -323,7 +331,9 @@ public class LwM2mTransportRequest {
323 331 Date date = new Date(Long.decode(value.toString()));
324 332 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date);
325 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 337 default:
328 338 }
329 339 }
... ... @@ -337,7 +347,7 @@ public class LwM2mTransportRequest {
337 347 String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
338 348 String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
339 349 patn, type, value, e.toString());
340   - serviceImpl.sendLogsToThingsboard(msg, registration);
  350 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
341 351 log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
342 352 if (rpcRequest != null) {
343 353 String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
... ... @@ -369,7 +379,7 @@ public class LwM2mTransportRequest {
369 379 DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
370 380 String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
371 381 if (response instanceof ReadResponse) {
372   - serviceImpl.onObservationResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
  382 + serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
373 383 } else if (response instanceof CancelObservationResponse) {
374 384 log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response);
375 385
... ... @@ -389,14 +399,33 @@ public class LwM2mTransportRequest {
389 399 } else if (response instanceof WriteAttributesResponse) {
390 400 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
391 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 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 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
18 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 21 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 22 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
21 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 59 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
58 60 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
59 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 64 @Slf4j
63 65 @Component
... ... @@ -92,7 +94,10 @@ public class LwM2mTransportServerConfiguration {
92 94 /** Use a magic converter to support bad type send by the UI. */
93 95 builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
94 96
  97 +
95 98 /** Create CoAP Config */
  99 + NetworkConfig networkConfig = getCoapConfig(serverPortNoSec, serverSecurePort);
  100 + BlockwiseLayer blockwiseLayer = new BlockwiseLayer(networkConfig);
96 101 builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort));
97 102
98 103 /** Define model provider (Create Models )*/
... ... @@ -110,6 +115,7 @@ public class LwM2mTransportServerConfiguration {
110 115
111 116 /** Create DTLS Config */
112 117 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
  118 + dtlsConfig.setServerOnly(true);
113 119 dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups());
114 120 dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers());
115 121 if (this.pskMode) {
... ...
... ... @@ -39,7 +39,7 @@ public interface LwM2mTransportService extends TbTransportService {
39 39
40 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 44 void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
45 45
... ... @@ -60,6 +60,4 @@ public interface LwM2mTransportService extends TbTransportService {
60 60 void doTrigger(Registration registration, String path);
61 61
62 62 void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
63   -
64   -
65 63 }
... ...
... ... @@ -38,10 +38,15 @@ import org.eclipse.leshan.server.registration.Registration;
38 38 import org.springframework.context.annotation.Lazy;
39 39 import org.springframework.stereotype.Service;
40 40 import org.thingsboard.common.util.JacksonUtil;
  41 +import org.thingsboard.server.cache.firmware.FirmwareDataCache;
41 42 import org.thingsboard.server.common.data.Device;
42 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 48 import org.thingsboard.server.common.transport.TransportService;
  49 +import org.thingsboard.server.common.transport.TransportServiceCallback;
45 50 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
46 51 import org.thingsboard.server.common.transport.service.DefaultTransportService;
47 52 import org.thingsboard.server.gen.transport.TransportProtos;
... ... @@ -77,9 +82,11 @@ import java.util.stream.Collectors;
77 82
78 83 import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
79 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 86 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
81 87 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED;
82 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 90 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
84 91 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
85 92 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
... ... @@ -110,6 +117,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
110 117 private ExecutorService executorUpdateRegistered;
111 118 private ExecutorService executorUnRegistered;
112 119 private LwM2mValueConverterImpl converter;
  120 + private FirmwareDataCache firmwareDataCache;
  121 +
113 122
114 123 private final TransportService transportService;
115 124
... ... @@ -121,12 +130,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
121 130
122 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 136 this.transportService = transportService;
126 137 this.lwM2mTransportContextServer = lwM2mTransportContextServer;
127 138 this.lwM2mClientContext = lwM2mClientContext;
128 139 this.leshanServer = leshanServer;
129 140 this.lwM2mTransportRequest = lwM2mTransportRequest;
  141 + this.firmwareDataCache = firmwareDataCache;
130 142 }
131 143
132 144 @PostConstruct
... ... @@ -168,8 +180,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
168 180 transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
169 181 transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
170 182 transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null);
  183 + this.getInfoFirmwareUpdate(lwM2MClient);
171 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 186 } else {
174 187 log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
175 188 }
... ... @@ -224,7 +237,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
224 237 executorUnRegistered.submit(() -> {
225 238 try {
226 239 this.setCancelObservations(registration);
227   - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
  240 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
228 241 this.closeClientSession(registration);
229 242 } catch (Throwable t) {
230 243 log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
... ... @@ -257,7 +270,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
257 270 @Override
258 271 public void onSleepingDev(Registration registration) {
259 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 275 //TODO: associate endpointId with device information.
263 276 }
... ... @@ -280,7 +293,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
280 293 * @param response - observe
281 294 */
282 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 297 if (response.getContent() != null) {
285 298 if (response.getContent() instanceof LwM2mObject) {
286 299 LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
... ... @@ -304,20 +317,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
304 317
305 318 /**
306 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 327 * @param msg -
312 328 */
313 329 @Override
314 330 public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
  331 + LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
315 332 if (msg.getSharedUpdatedCount() > 0) {
316 333 msg.getSharedUpdatedList().forEach(tsKvProto -> {
317 334 String pathName = tsKvProto.getKv().getKey();
318 335 String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
319 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 340 if (pathIdVer != null) {
322 341 ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
323 342 .getModelProvider());
... ... @@ -327,19 +346,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
327 346 log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew);
328 347 String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
329 348 LOG_LW2M_ERROR, pathIdVer, valueNew);
330   - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  349 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
331 350 }
332 351 } else {
333 352 log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
334 353 String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
335 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 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 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 481 }
456 482 if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) {
457 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 487 lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
462 488 if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) {
463 489 lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " +
464 490 lwm2mClientRpcRequest.keyNameKey + " is null or bad format");
465   - }
466   - else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
  491 + } else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
467 492 || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper())
468   - && lwm2mClientRpcRequest.getTargetIdVer() !=null
  493 + && lwm2mClientRpcRequest.getTargetIdVer() != null
469 494 && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource()
470 495 || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) {
471   - lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
  496 + lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
472 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 499 lwm2mClientRpcRequest.setErrorMsg("Procedures In Development...");
476 500 }
477 501 } else {
... ... @@ -483,23 +507,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
483 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 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 528 this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo());
505 529 }
... ... @@ -556,7 +580,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
556 580 */
557 581 protected void onAwakeDev(Registration registration) {
558 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 584 //TODO: associate endpointId with device information.
561 585 }
562 586
... ... @@ -583,10 +607,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
583 607
584 608 /**
585 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 614 if (logMsg != null && sessionInfo != null) {
591 615 this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo);
592 616 }
... ... @@ -610,7 +634,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
610 634 if (clientObjects != null && clientObjects.size() > 0) {
611 635 if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
612 636 // #2
613   - lwM2MClient.getPendingRequests().addAll(clientObjects);
  637 + lwM2MClient.getPendingReadRequests().addAll(clientObjects);
614 638 clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(),
615 639 null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null));
616 640 }
... ... @@ -652,6 +676,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
652 676 * Sending observe value of resources to thingsboard
653 677 * #1 Return old Value Resource from LwM2MClient
654 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 682 * @param registration - Registration LwM2M Client
657 683 * @param lwM2mResource - LwM2mSingleResource response.getContent()
... ... @@ -661,6 +687,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
661 687 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
662 688 if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
663 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 703 Set<String> paths = new HashSet<>();
665 704 paths.add(path);
666 705 this.updateAttrTelemetry(registration, paths);
... ... @@ -669,6 +708,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
669 708 }
670 709 }
671 710
  711 +
672 712 /**
673 713 * send Attribute and Telemetry to Thingsboard
674 714 * #1 - get AttrName/TelemetryName with value from LwM2MClient:
... ... @@ -723,22 +763,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
723 763 params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
724 764 result = params.keySet();
725 765 }
726   - if (!result.isEmpty()) {
  766 + if (result != null && !result.isEmpty()) {
727 767 // #1
728 768 Set<String> pathSend = result.stream().filter(target -> {
729 769 return target.split(LWM2M_SEPARATOR_PATH).length < 3 ?
730 770 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) :
731 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 774 if (!pathSend.isEmpty()) {
736   - lwM2MClient.getPendingRequests().addAll(pathSend);
  775 + lwM2MClient.getPendingReadRequests().addAll(pathSend);
737 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 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 1010 // update value in Resources
970 1011 registrationIds.forEach(registrationId -> {
971 1012 Registration registration = lwM2mClientContext.getRegistration(registrationId);
972   - this.readResourceValueObserve(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
  1013 + this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
973 1014 // send attr/telemetry to tingsboard for new path
974 1015 this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd());
975 1016 });
... ... @@ -998,12 +1039,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
998 1039 registrationIds.forEach(registrationId -> {
999 1040 Registration registration = lwM2mClientContext.getRegistration(registrationId);
1000 1041 if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
1001   - this.readResourceValueObserve(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
  1042 + this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
1002 1043 }
1003 1044 // 5.3 del
1004 1045 // send Request cancel observe to Client
1005 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 1084 * @param registration - Registration LwM2M Client
1044 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 1088 targets.forEach(target -> {
1048 1089 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
1049 1090 if (pathIds.isResource()) {
... ... @@ -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 1178 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
1138 1179 paramAnallyzer.forEach(pathIdVer -> {
1139 1180 if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) {
... ... @@ -1153,7 +1194,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1153 1194 log.error("Failed update resource [{}] [{}]", path, valueNew);
1154 1195 String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad",
1155 1196 LOG_LW2M_ERROR, path, valueNew);
1156   - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  1197 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
1157 1198 log.info("Failed update resource [{}] [{}]", path, valueNew);
1158 1199 }
1159 1200 }
... ... @@ -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 1231 * @param attributesResponse -
1189 1232 * @param sessionInfo -
... ... @@ -1191,6 +1234,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1191 1234 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
1192 1235 try {
1193 1236 List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList();
  1237 +
1194 1238 this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo);
1195 1239 } catch (Exception e) {
1196 1240 log.error(String.valueOf(e));
... ... @@ -1274,7 +1318,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1274 1318 */
1275 1319 private SessionInfoProto getValidateSessionInfo(String registrationId) {
1276 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 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 1347 * @param lwM2MClient - LwM2M Client
1303 1348 */
1304 1349 public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) {
1305 1350 SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
1306 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 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 1358 transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
1314 1359 } catch (AdaptorException e) {
1315 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 1430 * @param lwM2MClient -
1327 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 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 1437 new TypeToken<ConcurrentHashMap<String, String>>() {
1336 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 1441 private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) {
... ...
... ... @@ -24,11 +24,8 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException;
24 24 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
25 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 28 import java.util.Random;
31   -import java.util.Set;
32 29
33 30 @Slf4j
34 31 @Component("LwM2MJsonAdaptor")
... ... @@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
54 51 }
55 52
56 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 55 try {
63 56 TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
64 57 Random random = new Random();
... ... @@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
75 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 19 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
20 20 import org.thingsboard.server.gen.transport.TransportProtos;
21 21
22   -import java.util.List;
  22 +import java.util.Collection;
23 23
24 24 public interface LwM2MTransportAdaptor {
25 25
... ... @@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor {
27 27
28 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 57 private UUID deviceId;
58 58 private UUID sessionId;
59 59 private UUID profileId;
  60 + private volatile LwM2mFirmwareUpdate frUpdate;
60 61 private Registration registration;
61 62 private ValidateDeviceCredentialsResponseMsg credentialsResponse;
62 63 private final Map<String, ResourceValue> resources;
63 64 private final Map<String, TransportProtos.TsKvProto> delayedRequests;
64   - private final List<String> pendingRequests;
  65 + private final List<String> pendingReadRequests;
65 66 private final Queue<LwM2mQueuedRequest> queuedRequests;
66 67 private boolean init;
  68 + private volatile boolean updateFw;
67 69
68 70 public Object clone() throws CloneNotSupportedException {
69 71 return super.clone();
... ... @@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable {
75 77 this.securityInfo = securityInfo;
76 78 this.credentialsResponse = credentialsResponse;
77 79 this.delayedRequests = new ConcurrentHashMap<>();
78   - this.pendingRequests = new CopyOnWriteArrayList<>();
  80 + this.pendingReadRequests = new CopyOnWriteArrayList<>();
79 81 this.resources = new ConcurrentHashMap<>();
80 82 this.profileId = profileId;
81 83 this.sessionId = sessionId;
82 84 this.init = false;
  85 + this.updateFw = false;
83 86 this.queuedRequests = new ConcurrentLinkedQueue<>();
  87 + this.frUpdate = new LwM2mFirmwareUpdate();
84 88 }
85 89
86 90 public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) {
... ... @@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable {
103 107 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez));
104 108 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
105 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 111 .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
108 112 }
109 113
110 114 public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider,
111 115 LwM2mValueConverterImpl converter) {
112 116 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer));
113   - String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
114   - String verRez = getVerFromPathIdVerOrId(pathRezIdVer);
115 117 Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
116 118 Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
117 119 .getObjectModel(pathIds.getObjectId()).resources;
... ... @@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable {
170 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 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 180 this.init = true;
179 181 serviceImpl.putDelayedUpdateResourcesThingsboard(this);
180 182 }
... ...
... ... @@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
82 82
83 83 @Override
84 84 public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) {
85   - LwM2mClient client = registrationId != null ?
  85 + LwM2mClient client = registrationId != null && this.lwM2mClients.containsKey(registrationId) ?
86 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 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 47 import org.thingsboard.server.common.data.DeviceTransportType;
48 48 import org.thingsboard.server.common.data.TransportPayloadType;
49 49 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  50 +import org.thingsboard.server.common.data.firmware.FirmwareType;
50 51 import org.thingsboard.server.common.data.id.FirmwareId;
51 52 import org.thingsboard.server.common.msg.EncryptionUtil;
52 53 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
... ... @@ -59,6 +60,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
59 60 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
60 61 import org.thingsboard.server.common.transport.service.DefaultTransportService;
61 62 import org.thingsboard.server.common.transport.service.SessionMetaData;
  63 +import org.thingsboard.server.common.transport.util.SslUtil;
62 64 import org.thingsboard.server.gen.transport.TransportProtos;
63 65 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
64 66 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
... ... @@ -68,7 +70,6 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
68 70 import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
69 71 import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler;
70 72 import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher;
71   -import org.thingsboard.server.common.transport.util.SslUtil;
72 73
73 74 import javax.net.ssl.SSLPeerUnverifiedException;
74 75 import java.io.IOException;
... ... @@ -97,6 +98,8 @@ import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK;
97 98 import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
98 99 import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
99 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 105 * @author Andrew Shvayka
... ... @@ -104,7 +107,9 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
104 107 @Slf4j
105 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 114 private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE";
110 115
... ... @@ -314,38 +319,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
314 319 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) {
315 320 TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg);
316 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 326 } else {
350 327 transportService.reportActivity(deviceSessionCtx.getSessionInfo());
351 328 ack(ctx, msgId);
... ... @@ -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 372 private void ack(ChannelHandlerContext ctx, int msgId) {
361 373 if (msgId > 0) {
362 374 ctx.writeAndFlush(createMqttPubAckMsg(msgId));
... ... @@ -435,7 +447,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
435 447 if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
436 448 FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB()));
437 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 451 } else {
440 452 sendFirmwareError(ctx, response.getResponseStatus().toString());
441 453 }
... ... @@ -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 464 log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId);
453 465 ack(ctx, msgId);
454 466 try {
455 467 byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk);
456 468 deviceSessionCtx.getPayloadAdaptor()
457   - .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk)
  469 + .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type)
458 470 .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
459 471 } catch (Exception e) {
460 472 log.trace("[{}] Failed to send firmware response!", sessionId, e);
... ... @@ -501,6 +513,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
501 513 case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC:
502 514 case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC:
503 515 case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC:
  516 + case MqttTopics.DEVICE_SOFTWARE_RESPONSES_TOPIC:
  517 + case MqttTopics.DEVICE_SOFTWARE_ERROR_TOPIC:
504 518 registerSubQoS(topic, grantedQoSList, reqQoS);
505 519 break;
506 520 default:
... ...
... ... @@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
30 30 import org.springframework.stereotype.Component;
31 31 import org.springframework.util.StringUtils;
32 32 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  33 +import org.thingsboard.server.common.data.firmware.FirmwareType;
33 34 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
34 35 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
35 36 import org.thingsboard.server.gen.transport.TransportProtos;
... ... @@ -43,6 +44,9 @@ import java.util.Optional;
43 44 import java.util.Set;
44 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 51 * @author Andrew Shvayka
48 52 */
... ... @@ -151,8 +155,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
151 155 }
152 156
153 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 162 public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
... ...
... ... @@ -23,6 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage;
23 23 import io.netty.handler.codec.mqtt.MqttMessageType;
24 24 import io.netty.handler.codec.mqtt.MqttPublishMessage;
25 25 import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
  26 +import org.thingsboard.server.common.data.firmware.FirmwareType;
26 27 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
28 29 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
... ... @@ -77,7 +78,7 @@ public interface MqttTransportAdaptor {
77 78
78 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 83 default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) {
83 84 MqttFixedHeader mqttFixedHeader =
... ...
... ... @@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
28 28 import org.springframework.stereotype.Component;
29 29 import org.springframework.util.StringUtils;
30 30 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  31 +import org.thingsboard.server.common.data.firmware.FirmwareType;
31 32 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
32 33 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
33 34 import org.thingsboard.server.common.transport.adaptor.ProtoConverter;
... ... @@ -38,6 +39,8 @@ import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionConte
38 39
39 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 44 @Component
42 45 @Slf4j
43 46 public class ProtoMqttAdaptor implements MqttTransportAdaptor {
... ... @@ -165,8 +168,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
165 168 }
166 169
167 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 175 @Override
... ...
... ... @@ -55,7 +55,6 @@ import java.util.Optional;
55 55 import java.util.UUID;
56 56 import java.util.concurrent.ConcurrentHashMap;
57 57 import java.util.concurrent.ConcurrentLinkedDeque;
58   -import java.util.stream.Collectors;
59 58
60 59 @TbSnmpTransportComponent
61 60 @Component
... ... @@ -72,25 +71,30 @@ public class SnmpTransportContext extends TransportContext {
72 71 private final SnmpAuthService snmpAuthService;
73 72
74 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 76 @AfterStartUp(order = 2)
78   - public void initDevicesSessions() {
  77 + public void fetchDevicesAndEstablishSessions() {
79 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 100 private void establishDeviceSession(Device device) {
... ...
... ... @@ -81,26 +81,7 @@ public class ProtoTransportEntityService {
81 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 85 TransportProtos.GetSnmpDevicesRequestMsg requestMsg = TransportProtos.GetSnmpDevicesRequestMsg.newBuilder()
105 86 .setPage(page)
106 87 .setPageSize(pageSize)
... ...
... ... @@ -20,6 +20,7 @@ import lombok.Data;
20 20 import lombok.Getter;
21 21 import lombok.extern.slf4j.Slf4j;
22 22 import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.thingsboard.common.util.ThingsBoardExecutors;
23 24 import org.thingsboard.server.cache.firmware.FirmwareDataCache;
24 25 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
25 26 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
... ... @@ -57,7 +58,7 @@ public abstract class TransportContext {
57 58
58 59 @PostConstruct
59 60 public void init() {
60   - executor = Executors.newWorkStealingPool(50);
  61 + executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass());
61 62 }
62 63
63 64 @PreDestroy
... ...
... ... @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
25 25 import org.springframework.beans.factory.annotation.Value;
26 26 import org.springframework.context.ApplicationEventPublisher;
27 27 import org.springframework.stereotype.Service;
  28 +import org.thingsboard.common.util.ThingsBoardExecutors;
28 29 import org.thingsboard.common.util.ThingsBoardThreadFactory;
29 30 import org.thingsboard.server.common.data.ApiUsageRecordKey;
30 31 import org.thingsboard.server.common.data.ApiUsageState;
... ... @@ -186,7 +187,7 @@ public class DefaultTransportService implements TransportService {
186 187 this.ruleEngineProducerStats = statsFactory.createMessagesStats(StatsType.RULE_ENGINE.getName() + ".producer");
187 188 this.tbCoreProducerStats = statsFactory.createMessagesStats(StatsType.CORE.getName() + ".producer");
188 189 this.transportApiStats = statsFactory.createMessagesStats(StatsType.TRANSPORT.getName() + ".producer");
189   - this.transportCallbackExecutor = Executors.newWorkStealingPool(20);
  190 + this.transportCallbackExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass());
190 191 this.scheduler.scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS);
191 192 transportApiRequestTemplate = queueProvider.createTransportApiRequestTemplate();
192 193 transportApiRequestTemplate.setMessagesStats(transportApiStats);
... ...
... ... @@ -34,7 +34,7 @@ public abstract class AbstractListeningExecutor implements ListeningExecutor {
34 34
35 35 @PostConstruct
36 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 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 83
84 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 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 56 import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
57 57 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
58 58 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
  59 +import org.thingsboard.server.common.data.firmware.FirmwareType;
59 60 import org.thingsboard.server.common.data.id.DeviceProfileId;
60 61 import org.thingsboard.server.common.data.id.TenantId;
61 62 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -406,9 +407,31 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
406 407 if (firmware == null) {
407 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 413 if (firmware.getData() == null) {
410 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 53 import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
54 54 import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
55 55 import org.thingsboard.server.common.data.edge.Edge;
  56 +import org.thingsboard.server.common.data.firmware.FirmwareType;
56 57 import org.thingsboard.server.common.data.id.CustomerId;
57 58 import org.thingsboard.server.common.data.id.DeviceId;
58 59 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -361,7 +362,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
361 362
362 363 @Override
363 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 366 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
366 367 validateString(type, "Incorrect type " + type);
367 368 validatePageLink(pageLink);
... ... @@ -369,6 +370,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
369 370 }
370 371
371 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 382 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
373 383 log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
374 384 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
... ... @@ -696,9 +706,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
696 706 if (firmware == null) {
697 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 712 if (firmware.getData() == null) {
700 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 27 import org.springframework.cache.annotation.Cacheable;
28 28 import org.springframework.stereotype.Service;
29 29 import org.thingsboard.server.cache.firmware.FirmwareDataCache;
  30 +import org.thingsboard.server.common.data.DeviceProfile;
30 31 import org.thingsboard.server.common.data.Firmware;
31 32 import org.thingsboard.server.common.data.FirmwareInfo;
32 33 import org.thingsboard.server.common.data.Tenant;
33 34 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
34 35 import org.thingsboard.server.common.data.exception.ThingsboardException;
35 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 39 import org.thingsboard.server.common.data.id.FirmwareId;
37 40 import org.thingsboard.server.common.data.id.TenantId;
38 41 import org.thingsboard.server.common.data.page.PageData;
39 42 import org.thingsboard.server.common.data.page.PageLink;
  43 +import org.thingsboard.server.dao.device.DeviceProfileDao;
40 44 import org.thingsboard.server.dao.exception.DataValidationException;
41 45 import org.thingsboard.server.dao.service.DataValidator;
42 46 import org.thingsboard.server.dao.service.PaginatedRemover;
... ... @@ -63,6 +67,7 @@ public class BaseFirmwareService implements FirmwareService {
63 67 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
64 68
65 69 private final TenantDao tenantDao;
  70 + private final DeviceProfileDao deviceProfileDao;
66 71 private final FirmwareDao firmwareDao;
67 72 private final FirmwareInfoDao firmwareInfoDao;
68 73 private final CacheManager cacheManager;
... ... @@ -113,7 +118,7 @@ public class BaseFirmwareService implements FirmwareService {
113 118 }
114 119
115 120 @Override
116   - public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) throws ThingsboardException {
  121 + public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) {
117 122
118 123 if (data == null || !data.hasArray() || data.array().length == 0) {
119 124 throw new DataValidationException("Firmware data should be specified!");
... ... @@ -167,7 +172,8 @@ public class BaseFirmwareService implements FirmwareService {
167 172 public ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) {
168 173 log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId);
169 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 178 @Override
173 179 public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) {
... ... @@ -178,12 +184,11 @@ public class BaseFirmwareService implements FirmwareService {
178 184 }
179 185
180 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 188 log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink);
184 189 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
185 190 validatePageLink(pageLink);
186   - return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink);
  191 + return firmwareInfoDao.findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, firmwareType, hasData, pageLink);
187 192 }
188 193
189 194 @Override
... ... @@ -201,6 +206,10 @@ public class BaseFirmwareService implements FirmwareService {
201 206 throw new DataValidationException("The firmware referenced by the devices cannot be deleted!");
202 207 } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) {
203 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 213 } else {
205 214 throw t;
206 215 }
... ... @@ -217,29 +226,15 @@ public class BaseFirmwareService implements FirmwareService {
217 226 private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() {
218 227
219 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 233 @Override
240 234 protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) {
241 235 FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId());
242 236
  237 + validateUpdateDeviceProfile(firmware, firmwareOld);
243 238 BaseFirmwareService.validateUpdate(firmware, firmwareOld);
244 239 }
245 240 };
... ... @@ -248,22 +243,7 @@ public class BaseFirmwareService implements FirmwareService {
248 243
249 244 @Override
250 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 248 if (StringUtils.isEmpty(firmware.getFileName())) {
269 249 throw new DataValidationException("Firmware file name should be specified!");
... ... @@ -282,11 +262,7 @@ public class BaseFirmwareService implements FirmwareService {
282 262
283 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 267 if (!currentChecksum.equals(firmware.getChecksum())) {
292 268 throw new DataValidationException("Wrong firmware file!");
... ... @@ -297,6 +273,7 @@ public class BaseFirmwareService implements FirmwareService {
297 273 protected void validateUpdate(TenantId tenantId, Firmware firmware) {
298 274 Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId());
299 275
  276 + validateUpdateDeviceProfile(firmware, firmwareOld);
300 277 BaseFirmwareService.validateUpdate(firmware, firmwareOld);
301 278
302 279 if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) {
... ... @@ -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 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 298 if (!firmwareOld.getTitle().equals(firmware.getTitle())) {
310 299 throw new DataValidationException("Updating firmware title is prohibited!");
311 300 }
... ... @@ -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 357 private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover =
339 358 new PaginatedRemover<>() {
340 359
... ...
... ... @@ -16,6 +16,9 @@
16 16 package org.thingsboard.server.dao.firmware;
17 17
18 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 22 import org.thingsboard.server.common.data.id.TenantId;
20 23 import org.thingsboard.server.common.data.page.PageData;
21 24 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -27,6 +30,8 @@ public interface FirmwareInfoDao extends Dao<FirmwareInfo> {
27 30
28 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 154 public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id";
155 155 public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data";
156 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 159 public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
159 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 179 public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name";
179 180 public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key";
180 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 185 * Cassandra entityView constants.
... ... @@ -476,6 +478,8 @@ public class ModelConstants {
476 478 */
477 479 public static final String FIRMWARE_TABLE_NAME = "firmware";
478 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 483 public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY;
480 484 public static final String FIRMWARE_VERSION_COLUMN = "version";
481 485 public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name";
... ...
... ... @@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode;
22 22 import org.hibernate.annotations.Type;
23 23 import org.hibernate.annotations.TypeDef;
24 24 import org.hibernate.annotations.TypeDefs;
  25 +import org.thingsboard.common.util.JacksonUtil;
25 26 import org.thingsboard.server.common.data.Device;
26 27 import org.thingsboard.server.common.data.device.data.DeviceData;
27 28 import org.thingsboard.server.common.data.id.CustomerId;
... ... @@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.id.TenantId;
32 33 import org.thingsboard.server.dao.model.BaseSqlEntity;
33 34 import org.thingsboard.server.dao.model.ModelConstants;
34 35 import org.thingsboard.server.dao.model.SearchTextEntity;
35   -import org.thingsboard.common.util.JacksonUtil;
36 36 import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
37 37 import org.thingsboard.server.dao.util.mapping.JsonStringType;
38 38
... ... @@ -43,8 +43,8 @@ import java.util.UUID;
43 43 @Data
44 44 @EqualsAndHashCode(callSuper = true)
45 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 49 @MappedSuperclass
50 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 77 @Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid")
78 78 private UUID firmwareId;
79 79
  80 + @Column(name = ModelConstants.DEVICE_SOFTWARE_ID_PROPERTY, columnDefinition = "uuid")
  81 + private UUID softwareId;
  82 +
80 83 @Type(type = "jsonb")
81 84 @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb")
82 85 private JsonNode deviceData;
... ... @@ -102,6 +105,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
102 105 if (device.getFirmwareId() != null) {
103 106 this.firmwareId = device.getFirmwareId().getId();
104 107 }
  108 + if (device.getSoftwareId() != null) {
  109 + this.softwareId = device.getSoftwareId().getId();
  110 + }
105 111 this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class);
106 112 this.name = device.getName();
107 113 this.type = device.getType();
... ... @@ -122,6 +128,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
122 128 this.searchText = deviceEntity.getSearchText();
123 129 this.additionalInfo = deviceEntity.getAdditionalInfo();
124 130 this.firmwareId = deviceEntity.getFirmwareId();
  131 + this.softwareId = deviceEntity.getSoftwareId();
125 132 }
126 133
127 134 @Override
... ... @@ -149,6 +156,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
149 156 if (firmwareId != null) {
150 157 device.setFirmwareId(new FirmwareId(firmwareId));
151 158 }
  159 + if (softwareId != null) {
  160 + device.setSoftwareId(new FirmwareId(softwareId));
  161 + }
152 162 device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class));
153 163 device.setName(name);
154 164 device.setType(type);
... ...
... ... @@ -21,9 +21,10 @@ import lombok.Data;
21 21 import lombok.EqualsAndHashCode;
22 22 import org.hibernate.annotations.Type;
23 23 import org.hibernate.annotations.TypeDef;
  24 +import org.thingsboard.common.util.JacksonUtil;
24 25 import org.thingsboard.server.common.data.DeviceProfile;
25   -import org.thingsboard.server.common.data.DeviceProfileType;
26 26 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
  27 +import org.thingsboard.server.common.data.DeviceProfileType;
27 28 import org.thingsboard.server.common.data.DeviceTransportType;
28 29 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
29 30 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantId;
33 34 import org.thingsboard.server.dao.model.BaseSqlEntity;
34 35 import org.thingsboard.server.dao.model.ModelConstants;
35 36 import org.thingsboard.server.dao.model.SearchTextEntity;
36   -import org.thingsboard.common.util.JacksonUtil;
37 37 import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
38 38
39 39 import javax.persistence.Column;
... ... @@ -87,12 +87,15 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
87 87 @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb")
88 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 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 94 private UUID firmwareId;
95 95
  96 + @Column(name = ModelConstants.DEVICE_PROFILE_SOFTWARE_ID_PROPERTY)
  97 + private UUID softwareId;
  98 +
96 99 public DeviceProfileEntity() {
97 100 super();
98 101 }
... ... @@ -120,6 +123,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
120 123 if (deviceProfile.getFirmwareId() != null) {
121 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 131 @Override
... ... @@ -160,6 +166,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
160 166 deviceProfile.setFirmwareId(new FirmwareId(firmwareId));
161 167 }
162 168
  169 + if (softwareId != null) {
  170 + deviceProfile.setSoftwareId(new FirmwareId(softwareId));
  171 + }
  172 +
163 173 return deviceProfile;
164 174 }
165 175 }
... ...
... ... @@ -22,6 +22,8 @@ import org.hibernate.annotations.Type;
22 22 import org.hibernate.annotations.TypeDef;
23 23 import org.thingsboard.server.common.data.Firmware;
24 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 27 import org.thingsboard.server.common.data.id.FirmwareId;
26 28 import org.thingsboard.server.common.data.id.TenantId;
27 29 import org.thingsboard.server.dao.model.BaseSqlEntity;
... ... @@ -43,10 +45,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_
43 45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
44 46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
45 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 49 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
47 50 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
48 51 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
49 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 54 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
51 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 64 @Column(name = FIRMWARE_TENANT_ID_COLUMN)
61 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 74 @Column(name = FIRMWARE_TITLE_COLUMN)
64 75 private String title;
65 76
... ... @@ -101,6 +112,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
101 112 this.createdTime = firmware.getCreatedTime();
102 113 this.setUuid(firmware.getUuidId());
103 114 this.tenantId = firmware.getTenantId().getId();
  115 + if (firmware.getDeviceProfileId() != null) {
  116 + this.deviceProfileId = firmware.getDeviceProfileId().getId();
  117 + }
  118 + this.type = firmware.getType();
104 119 this.title = firmware.getTitle();
105 120 this.version = firmware.getVersion();
106 121 this.fileName = firmware.getFileName();
... ... @@ -127,6 +142,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
127 142 Firmware firmware = new Firmware(new FirmwareId(id));
128 143 firmware.setCreatedTime(createdTime);
129 144 firmware.setTenantId(new TenantId(tenantId));
  145 + if (deviceProfileId != null) {
  146 + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
  147 + }
  148 + firmware.setType(type);
130 149 firmware.setTitle(title);
131 150 firmware.setVersion(version);
132 151 firmware.setFileName(fileName);
... ...
... ... @@ -23,6 +23,8 @@ import org.hibernate.annotations.TypeDef;
23 23 import org.thingsboard.common.util.JacksonUtil;
24 24 import org.thingsboard.server.common.data.FirmwareInfo;
25 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 28 import org.thingsboard.server.common.data.id.FirmwareId;
27 29 import org.thingsboard.server.common.data.id.TenantId;
28 30 import org.thingsboard.server.dao.model.BaseSqlEntity;
... ... @@ -41,13 +43,13 @@ import java.util.UUID;
41 43 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN;
42 44 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN;
43 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 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 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 49 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
49 50 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
50 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 53 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
52 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 63 @Column(name = FIRMWARE_TENANT_ID_COLUMN)
62 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 73 @Column(name = FIRMWARE_TITLE_COLUMN)
65 74 private String title;
66 75
... ... @@ -101,6 +110,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
101 110 this.createdTime = firmware.getCreatedTime();
102 111 this.setUuid(firmware.getUuidId());
103 112 this.tenantId = firmware.getTenantId().getId();
  113 + this.type = firmware.getType();
  114 + if (firmware.getDeviceProfileId() != null) {
  115 + this.deviceProfileId = firmware.getDeviceProfileId().getId();
  116 + }
104 117 this.title = firmware.getTitle();
105 118 this.version = firmware.getVersion();
106 119 this.fileName = firmware.getFileName();
... ... @@ -111,12 +124,14 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
111 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 128 String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize,
116 129 Object additionalInfo, boolean hasData) {
117 130 this.id = id;
118 131 this.createdTime = createdTime;
119 132 this.tenantId = tenantId;
  133 + this.deviceProfileId = deviceProfileId;
  134 + this.type = type;
120 135 this.title = title;
121 136 this.version = version;
122 137 this.fileName = fileName;
... ... @@ -143,6 +158,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
143 158 FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id));
144 159 firmware.setCreatedTime(createdTime);
145 160 firmware.setTenantId(new TenantId(tenantId));
  161 + if (deviceProfileId != null) {
  162 + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
  163 + }
  164 + firmware.setType(type);
146 165 firmware.setTitle(title);
147 166 firmware.setVersion(version);
148 167 firmware.setFileName(fileName);
... ...
... ... @@ -104,6 +104,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
104 104 @Param("textSearch") String textSearch,
105 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 116 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
108 117 "FROM DeviceEntity d " +
109 118 "LEFT JOIN CustomerEntity c on c.id = d.customerId " +
... ...
... ... @@ -165,6 +165,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
165 165 }
166 166
167 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 178 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) {
169 179 return DaoUtil.toPageData(
170 180 deviceRepository.findDeviceInfosByTenantIdAndType(
... ...
... ... @@ -20,27 +20,41 @@ import org.springframework.data.domain.Pageable;
20 20 import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.CrudRepository;
22 22 import org.springframework.data.repository.query.Param;
  23 +import org.thingsboard.server.common.data.firmware.FirmwareType;
23 24 import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity;
24 25
25 26 import java.util.UUID;
26 27
27 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 30 "f.tenantId = :tenantId " +
30 31 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
31 32 Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
32 33 @Param("searchText") String searchText,
33 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 37 "f.tenantId = :tenantId " +
  38 + "AND f.deviceProfileId = :deviceProfileId " +
  39 + "AND f.type = :type " +
37 40 "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" +
38 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 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 20 import org.springframework.data.repository.CrudRepository;
21 21 import org.springframework.stereotype.Component;
22 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 26 import org.thingsboard.server.common.data.id.TenantId;
24 27 import org.thingsboard.server.common.data.page.PageData;
25 28 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -73,12 +76,19 @@ public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao<FirmwareInfoEnt
73 76 }
74 77
75 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 80 return DaoUtil.toPageData(firmwareInfoRepository
78   - .findAllByTenantIdAndHasData(
  81 + .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(
79 82 tenantId.getId(),
  83 + deviceProfileId.getId(),
  84 + firmwareType,
80 85 hasData,
81 86 Objects.toString(pageLink.getTextSearch(), ""),
82 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 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.apache.commons.lang3.StringUtils;
20   -import org.springframework.beans.factory.annotation.Autowired;
21   -import org.springframework.beans.factory.annotation.Value;
22 20 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
23 21 import org.springframework.stereotype.Repository;
24   -import org.springframework.transaction.TransactionStatus;
25   -import org.springframework.transaction.support.TransactionCallback;
26 22 import org.springframework.transaction.support.TransactionTemplate;
27 23 import org.thingsboard.server.common.data.EntityType;
28 24 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
... ... @@ -40,8 +36,6 @@ import org.thingsboard.server.common.data.query.EntityKey;
40 36 import org.thingsboard.server.common.data.query.EntityKeyType;
41 37 import org.thingsboard.server.dao.model.ModelConstants;
42 38
43   -import java.util.ArrayList;
44   -import java.util.Arrays;
45 39 import java.util.Collection;
46 40 import java.util.Collections;
47 41 import java.util.HashMap;
... ... @@ -134,6 +128,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
134 128 StringBuilder fromPart = new StringBuilder(" from alarm a ");
135 129 StringBuilder wherePart = new StringBuilder(" where ");
136 130 StringBuilder sortPart = new StringBuilder(" order by ");
  131 + StringBuilder joinPart = new StringBuilder();
137 132 boolean addAnd = false;
138 133 if (pageLink.isSearchPropagatedAlarms()) {
139 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 151 wherePart.append(" a.originator_id in (:entity_ids)");
157 152 }
158 153 } else {
159   - fromPart.append(" inner join (select * from (VALUES");
  154 + joinPart.append(" inner join (select * from (VALUES");
160 155 int entityIdIdx = 0;
161 156 int lastEntityIdIdx = orderedEntityIds.size() - 1;
162 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 159 if (entityIdIdx != lastEntityIdIdx) {
165   - fromPart.append(",");
  160 + joinPart.append(",");
166 161 } else {
167   - fromPart.append(")");
  162 + joinPart.append(")");
168 163 }
169 164 entityIdIdx++;
170 165 }
171   - fromPart.append(" as e(id, priority)) e ");
  166 + joinPart.append(" as e(id, priority)) e ");
172 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 169 } else {
175   - fromPart.append("on a.originator_id = e.id");
  170 + joinPart.append("on a.originator_id = e.id");
176 171 }
177 172 sortPart.append("e.priority");
178 173 }
... ... @@ -226,9 +221,12 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
226 221 }
227 222
228 223 String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch());
229   - String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString();
  224 + String mainQuery;
230 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 231 String countQuery = String.format("select count(*) from (%s) result", mainQuery);
234 232 long queryTs = System.currentTimeMillis();
... ...
... ... @@ -243,23 +243,21 @@ public class EntityKeyMapping {
243 243 } else {
244 244 entityTypeStr = "'" + entityType.name() + "'";
245 245 }
246   - ctx.addStringParameter(alias + "_key_id", entityKey.getKey());
  246 + ctx.addStringParameter(getKeyId(), entityKey.getKey());
247 247 String filterQuery = toQueries(ctx, entityFilter.getType())
248 248 .filter(StringUtils::isNotEmpty)
249 249 .collect(Collectors.joining(" and "));
250   - if (StringUtils.isEmpty(filterQuery)) {
251   - filterQuery = "";
252   - } else {
  250 + if (StringUtils.isNotEmpty(filterQuery)) {
253 251 filterQuery = " AND (" + filterQuery + ")";
254 252 }
255 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 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 256 join, alias, alias, alias, alias, filterQuery);
259 257 } else {
260 258 String query;
261 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 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 262 join, alias, alias, alias, entityTypeStr, alias, alias);
265 263 String scope;
... ... @@ -272,7 +270,7 @@ public class EntityKeyMapping {
272 270 }
273 271 query = String.format("%s AND %s.attribute_type='%s' %s", query, alias, scope, filterQuery);
274 272 } else {
275   - String join = hasFilter() ? "join LATERAL" : "left join LATERAL";
  273 + String join = (hasFilter() && hasFilterValues(ctx)) ? "join LATERAL" : "left join LATERAL";
276 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 275 "ORDER BY %s.last_update_ts DESC limit 1) as %s ON true",
278 276 join, alias, alias, alias, entityTypeStr, alias, alias, filterQuery, alias, alias);
... ... @@ -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 292 public static String buildSelections(List<EntityKeyMapping> mappings, EntityFilterType filterType, EntityType entityType) {
285 293 return mappings.stream().map(mapping -> mapping.toSelection(filterType, entityType)).collect(
286 294 Collectors.joining(", "));
287 295 }
288 296
289 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 304 public static String buildQuery(QueryContext ctx, List<EntityKeyMapping> mappings, EntityFilterType filterType) {
... ...
... ... @@ -28,6 +28,7 @@ import com.google.common.util.concurrent.Futures;
28 28 import com.google.common.util.concurrent.ListenableFuture;
29 29 import com.google.common.util.concurrent.SettableFuture;
30 30 import lombok.extern.slf4j.Slf4j;
  31 +import org.thingsboard.common.util.ThingsBoardExecutors;
31 32 import org.thingsboard.common.util.ThingsBoardThreadFactory;
32 33 import org.thingsboard.server.common.data.id.TenantId;
33 34 import org.thingsboard.server.common.stats.StatsFactory;
... ... @@ -82,7 +83,7 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
82 83 this.printQueriesFreq = printQueriesFreq;
83 84 this.queue = new LinkedBlockingDeque<>(queueLimit);
84 85 this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-dispatcher"));
85   - this.callbackExecutor = Executors.newWorkStealingPool(callbackThreads);
  86 + this.callbackExecutor = ThingsBoardExecutors.newWorkStealingPool(callbackThreads, getClass());
86 87 this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nosql-timeout"));
87 88 this.perTenantLimitsEnabled = perTenantLimitsEnabled;
88 89 this.perTenantLimitsConfiguration = perTenantLimitsConfiguration;
... ...
... ... @@ -162,6 +162,8 @@ CREATE TABLE IF NOT EXISTS firmware (
162 162 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
163 163 created_time bigint NOT NULL,
164 164 tenant_id uuid NOT NULL,
  165 + device_profile_id uuid ,
  166 + type varchar(32) NOT NULL,
165 167 title varchar(255) NOT NULL,
166 168 version varchar(255) NOT NULL,
167 169 file_name varchar(255),
... ... @@ -188,13 +190,15 @@ CREATE TABLE IF NOT EXISTS device_profile (
188 190 is_default boolean,
189 191 tenant_id uuid,
190 192 firmware_id uuid,
  193 + software_id uuid,
191 194 default_rule_chain_id uuid,
192 195 default_queue_name varchar(255),
193 196 provision_device_key varchar,
194 197 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
195 198 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
196 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 204 CREATE TABLE IF NOT EXISTS device (
... ... @@ -210,9 +214,11 @@ CREATE TABLE IF NOT EXISTS device (
210 214 search_text varchar(255),
211 215 tenant_id uuid,
212 216 firmware_id uuid,
  217 + software_id uuid,
213 218 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
214 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 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 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 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 180 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
181 181 created_time bigint NOT NULL,
182 182 tenant_id uuid NOT NULL,
  183 + device_profile_id uuid ,
  184 + type varchar(32) NOT NULL,
183 185 title varchar(255) NOT NULL,
184 186 version varchar(255) NOT NULL,
185 187 file_name varchar(255),
... ... @@ -191,6 +193,7 @@ CREATE TABLE IF NOT EXISTS firmware (
191 193 additional_info varchar,
192 194 search_text varchar(255),
193 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 199 CREATE TABLE IF NOT EXISTS device_profile (
... ... @@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile (
206 209 is_default boolean,
207 210 tenant_id uuid,
208 211 firmware_id uuid,
  212 + software_id uuid,
209 213 default_rule_chain_id uuid,
210 214 default_queue_name varchar(255),
211 215 provision_device_key varchar,
212 216 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
213 217 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
214 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 232 CREATE TABLE IF NOT EXISTS device (
219 233 id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
220 234 created_time bigint NOT NULL,
... ... @@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device (
228 242 search_text varchar(255),
229 243 tenant_id uuid,
230 244 firmware_id uuid,
  245 + software_id uuid,
231 246 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
232 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 252 CREATE TABLE IF NOT EXISTS device_credentials (
... ...
... ... @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo;
30 30 import org.thingsboard.server.common.data.DeviceTransportType;
31 31 import org.thingsboard.server.common.data.Firmware;
32 32 import org.thingsboard.server.common.data.Tenant;
  33 +import org.thingsboard.server.common.data.firmware.FirmwareType;
33 34 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
34 35 import org.thingsboard.server.common.data.id.TenantId;
35 36 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -44,6 +45,8 @@ import java.util.concurrent.ExecutionException;
44 45 import java.util.concurrent.Executors;
45 46 import java.util.stream.Collectors;
46 47
  48 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  49 +
47 50 public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
48 51
49 52 private IdComparator<DeviceProfile> idComparator = new IdComparator<>();
... ... @@ -98,6 +101,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
98 101
99 102 Firmware firmware = new Firmware();
100 103 firmware.setTenantId(tenantId);
  104 + firmware.setDeviceProfileId(savedDeviceProfile.getId());
  105 + firmware.setType(FIRMWARE);
101 106 firmware.setTitle("my firmware");
102 107 firmware.setVersion("v1.0");
103 108 firmware.setFileName("test.txt");
... ...
... ... @@ -20,10 +20,13 @@ import org.apache.commons.lang3.RandomStringUtils;
20 20 import org.junit.After;
21 21 import org.junit.Assert;
22 22 import org.junit.Before;
  23 +import org.junit.Rule;
23 24 import org.junit.Test;
  25 +import org.junit.rules.ExpectedException;
24 26 import org.thingsboard.server.common.data.Customer;
25 27 import org.thingsboard.server.common.data.Device;
26 28 import org.thingsboard.server.common.data.DeviceInfo;
  29 +import org.thingsboard.server.common.data.DeviceProfile;
27 30 import org.thingsboard.server.common.data.EntitySubtype;
28 31 import org.thingsboard.server.common.data.Firmware;
29 32 import org.thingsboard.server.common.data.Tenant;
... ... @@ -43,6 +46,7 @@ import java.util.ArrayList;
43 46 import java.util.Collections;
44 47 import java.util.List;
45 48
  49 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
46 50 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
47 51
48 52 public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
... ... @@ -67,6 +71,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
67 71 tenantProfileService.deleteTenantProfiles(anotherTenantId);
68 72 }
69 73
  74 + @Rule
  75 + public ExpectedException thrown = ExpectedException.none();
  76 +
70 77 @Test
71 78 public void testSaveDevicesWithoutMaxDeviceLimit() {
72 79 Device device = this.saveDevice(tenantId, "My device");
... ... @@ -184,6 +191,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
184 191
185 192 Firmware firmware = new Firmware();
186 193 firmware.setTenantId(tenantId);
  194 + firmware.setDeviceProfileId(device.getDeviceProfileId());
  195 + firmware.setType(FIRMWARE);
187 196 firmware.setTitle("my firmware");
188 197 firmware.setVersion("v1.0");
189 198 firmware.setFileName("test.txt");
... ... @@ -199,6 +208,40 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
199 208 Device foundDevice = deviceService.findDeviceById(tenantId, savedDevice.getId());
200 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 246 @Test(expected = DataValidationException.class)
204 247 public void testSaveDeviceWithEmptyName() {
... ...
... ... @@ -19,7 +19,9 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
19 19 import org.junit.After;
20 20 import org.junit.Assert;
21 21 import org.junit.Before;
  22 +import org.junit.Rule;
22 23 import org.junit.Test;
  24 +import org.junit.rules.ExpectedException;
23 25 import org.thingsboard.common.util.JacksonUtil;
24 26 import org.thingsboard.server.common.data.Device;
25 27 import org.thingsboard.server.common.data.DeviceProfile;
... ... @@ -27,6 +29,7 @@ import org.thingsboard.server.common.data.Firmware;
27 29 import org.thingsboard.server.common.data.FirmwareInfo;
28 30 import org.thingsboard.server.common.data.Tenant;
29 31 import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
  32 +import org.thingsboard.server.common.data.id.DeviceProfileId;
30 33 import org.thingsboard.server.common.data.id.TenantId;
31 34 import org.thingsboard.server.common.data.page.PageData;
32 35 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -37,6 +40,8 @@ import java.util.ArrayList;
37 40 import java.util.Collections;
38 41 import java.util.List;
39 42
  43 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  44 +
40 45 public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
41 46
42 47 public static final String TITLE = "My firmware";
... ... @@ -51,6 +56,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
51 56
52 57 private TenantId tenantId;
53 58
  59 + private DeviceProfileId deviceProfileId;
  60 +
54 61 @Before
55 62 public void before() {
56 63 Tenant tenant = new Tenant();
... ... @@ -58,8 +65,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
58 65 Tenant savedTenant = tenantService.saveTenant(tenant);
59 66 Assert.assertNotNull(savedTenant);
60 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 78 @After
64 79 public void after() {
65 80 tenantService.deleteTenant(tenantId);
... ... @@ -69,6 +84,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
69 84 public void testSaveFirmware() {
70 85 Firmware firmware = new Firmware();
71 86 firmware.setTenantId(tenantId);
  87 + firmware.setDeviceProfileId(deviceProfileId);
  88 + firmware.setType(FIRMWARE);
72 89 firmware.setTitle(TITLE);
73 90 firmware.setVersion(VERSION);
74 91 firmware.setFileName(FILE_NAME);
... ... @@ -100,6 +117,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
100 117 public void testSaveFirmwareInfoAndUpdateWithData() {
101 118 FirmwareInfo firmwareInfo = new FirmwareInfo();
102 119 firmwareInfo.setTenantId(tenantId);
  120 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  121 + firmwareInfo.setType(FIRMWARE);
103 122 firmwareInfo.setTitle(TITLE);
104 123 firmwareInfo.setVersion(VERSION);
105 124 FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo);
... ... @@ -113,6 +132,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
113 132 Firmware firmware = new Firmware(savedFirmwareInfo.getId());
114 133 firmware.setCreatedTime(firmwareInfo.getCreatedTime());
115 134 firmware.setTenantId(tenantId);
  135 + firmware.setDeviceProfileId(deviceProfileId);
  136 + firmware.setType(FIRMWARE);
116 137 firmware.setTitle(TITLE);
117 138 firmware.setVersion(VERSION);
118 139 firmware.setFileName(FILE_NAME);
... ... @@ -136,9 +157,11 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
136 157 firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId());
137 158 }
138 159
139   - @Test(expected = DataValidationException.class)
  160 + @Test
140 161 public void testSaveFirmwareWithEmptyTenant() {
141 162 Firmware firmware = new Firmware();
  163 + firmware.setDeviceProfileId(deviceProfileId);
  164 + firmware.setType(FIRMWARE);
142 165 firmware.setTitle(TITLE);
143 166 firmware.setVersion(VERSION);
144 167 firmware.setFileName(FILE_NAME);
... ... @@ -146,65 +169,108 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
146 169 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
147 170 firmware.setChecksum(CHECKSUM);
148 171 firmware.setData(DATA);
  172 +
  173 + thrown.expect(DataValidationException.class);
  174 + thrown.expectMessage("Firmware should be assigned to tenant!");
149 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 197 public void testSaveFirmwareWithEmptyTitle() {
154 198 Firmware firmware = new Firmware();
155 199 firmware.setTenantId(tenantId);
  200 + firmware.setDeviceProfileId(deviceProfileId);
  201 + firmware.setType(FIRMWARE);
156 202 firmware.setVersion(VERSION);
157 203 firmware.setFileName(FILE_NAME);
158 204 firmware.setContentType(CONTENT_TYPE);
159 205 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
160 206 firmware.setChecksum(CHECKSUM);
161 207 firmware.setData(DATA);
  208 +
  209 + thrown.expect(DataValidationException.class);
  210 + thrown.expectMessage("Firmware title should be specified!");
162 211 firmwareService.saveFirmware(firmware);
163 212 }
164 213
165   - @Test(expected = DataValidationException.class)
  214 + @Test
166 215 public void testSaveFirmwareWithEmptyFileName() {
167 216 Firmware firmware = new Firmware();
168 217 firmware.setTenantId(tenantId);
  218 + firmware.setDeviceProfileId(deviceProfileId);
  219 + firmware.setType(FIRMWARE);
169 220 firmware.setTitle(TITLE);
170 221 firmware.setVersion(VERSION);
171 222 firmware.setContentType(CONTENT_TYPE);
172 223 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
173 224 firmware.setChecksum(CHECKSUM);
174 225 firmware.setData(DATA);
  226 +
  227 + thrown.expect(DataValidationException.class);
  228 + thrown.expectMessage("Firmware file name should be specified!");
175 229 firmwareService.saveFirmware(firmware);
176 230 }
177 231
178   - @Test(expected = DataValidationException.class)
  232 + @Test
179 233 public void testSaveFirmwareWithEmptyContentType() {
180 234 Firmware firmware = new Firmware();
181 235 firmware.setTenantId(tenantId);
  236 + firmware.setDeviceProfileId(deviceProfileId);
  237 + firmware.setType(FIRMWARE);
182 238 firmware.setTitle(TITLE);
183 239 firmware.setVersion(VERSION);
184 240 firmware.setFileName(FILE_NAME);
185 241 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
186 242 firmware.setChecksum(CHECKSUM);
187 243 firmware.setData(DATA);
  244 +
  245 + thrown.expect(DataValidationException.class);
  246 + thrown.expectMessage("Firmware content type should be specified!");
188 247 firmwareService.saveFirmware(firmware);
189 248 }
190 249
191   - @Test(expected = DataValidationException.class)
  250 + @Test
192 251 public void testSaveFirmwareWithEmptyData() {
193 252 Firmware firmware = new Firmware();
194 253 firmware.setTenantId(tenantId);
  254 + firmware.setDeviceProfileId(deviceProfileId);
  255 + firmware.setType(FIRMWARE);
195 256 firmware.setTitle(TITLE);
196 257 firmware.setVersion(VERSION);
197 258 firmware.setFileName(FILE_NAME);
198 259 firmware.setContentType(CONTENT_TYPE);
199 260 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
200 261 firmware.setChecksum(CHECKSUM);
  262 +
  263 + thrown.expect(DataValidationException.class);
  264 + thrown.expectMessage("Firmware data should be specified!");
201 265 firmwareService.saveFirmware(firmware);
202 266 }
203 267
204   - @Test(expected = DataValidationException.class)
  268 + @Test
205 269 public void testSaveFirmwareWithInvalidTenant() {
206 270 Firmware firmware = new Firmware();
207 271 firmware.setTenantId(new TenantId(Uuids.timeBased()));
  272 + firmware.setDeviceProfileId(deviceProfileId);
  273 + firmware.setType(FIRMWARE);
208 274 firmware.setTitle(TITLE);
209 275 firmware.setVersion(VERSION);
210 276 firmware.setFileName(FILE_NAME);
... ... @@ -212,41 +278,77 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
212 278 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
213 279 firmware.setChecksum(CHECKSUM);
214 280 firmware.setData(DATA);
  281 +
  282 + thrown.expect(DataValidationException.class);
  283 + thrown.expectMessage("Firmware is referencing to non-existent tenant!");
215 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 307 public void testSaveFirmwareWithEmptyChecksum() {
220 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 312 firmware.setTitle(TITLE);
223 313 firmware.setVersion(VERSION);
224 314 firmware.setFileName(FILE_NAME);
225 315 firmware.setContentType(CONTENT_TYPE);
226 316 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
227 317 firmware.setData(DATA);
  318 +
  319 + thrown.expect(DataValidationException.class);
  320 + thrown.expectMessage("Firmware checksum should be specified!");
228 321 firmwareService.saveFirmware(firmware);
229 322 }
230 323
231   - @Test(expected = DataValidationException.class)
  324 + @Test
232 325 public void testSaveFirmwareInfoWithExistingTitleAndVersion() {
233 326 FirmwareInfo firmwareInfo = new FirmwareInfo();
234 327 firmwareInfo.setTenantId(tenantId);
  328 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  329 + firmwareInfo.setType(FIRMWARE);
235 330 firmwareInfo.setTitle(TITLE);
236 331 firmwareInfo.setVersion(VERSION);
237 332 firmwareService.saveFirmwareInfo(firmwareInfo);
238 333
239 334 FirmwareInfo newFirmwareInfo = new FirmwareInfo();
240 335 newFirmwareInfo.setTenantId(tenantId);
  336 + newFirmwareInfo.setDeviceProfileId(deviceProfileId);
  337 + newFirmwareInfo.setType(FIRMWARE);
241 338 newFirmwareInfo.setTitle(TITLE);
242 339 newFirmwareInfo.setVersion(VERSION);
  340 +
  341 + thrown.expect(DataValidationException.class);
  342 + thrown.expectMessage("Firmware with such title and version already exists!");
243 343 firmwareService.saveFirmwareInfo(newFirmwareInfo);
244 344 }
245 345
246   - @Test(expected = DataValidationException.class)
  346 + @Test
247 347 public void testSaveFirmwareWithExistingTitleAndVersion() {
248 348 Firmware firmware = new Firmware();
249 349 firmware.setTenantId(tenantId);
  350 + firmware.setDeviceProfileId(deviceProfileId);
  351 + firmware.setType(FIRMWARE);
250 352 firmware.setTitle(TITLE);
251 353 firmware.setVersion(VERSION);
252 354 firmware.setFileName(FILE_NAME);
... ... @@ -258,18 +360,27 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
258 360
259 361 Firmware newFirmware = new Firmware();
260 362 newFirmware.setTenantId(tenantId);
  363 + newFirmware.setDeviceProfileId(deviceProfileId);
  364 + newFirmware.setType(FIRMWARE);
261 365 newFirmware.setTitle(TITLE);
262 366 newFirmware.setVersion(VERSION);
263 367 newFirmware.setFileName(FILE_NAME);
264 368 newFirmware.setContentType(CONTENT_TYPE);
  369 + newFirmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  370 + newFirmware.setChecksum(CHECKSUM);
265 371 newFirmware.setData(DATA);
  372 +
  373 + thrown.expect(DataValidationException.class);
  374 + thrown.expectMessage("Firmware with such title and version already exists!");
266 375 firmwareService.saveFirmware(newFirmware);
267 376 }
268 377
269   - @Test(expected = DataValidationException.class)
  378 + @Test
270 379 public void testDeleteFirmwareWithReferenceByDevice() {
271 380 Firmware firmware = new Firmware();
272 381 firmware.setTenantId(tenantId);
  382 + firmware.setDeviceProfileId(deviceProfileId);
  383 + firmware.setType(FIRMWARE);
273 384 firmware.setTitle(TITLE);
274 385 firmware.setVersion(VERSION);
275 386 firmware.setFileName(FILE_NAME);
... ... @@ -282,11 +393,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
282 393 Device device = new Device();
283 394 device.setTenantId(tenantId);
284 395 device.setName("My device");
285   - device.setType("default");
  396 + device.setDeviceProfileId(deviceProfileId);
286 397 device.setFirmwareId(savedFirmware.getId());
287 398 Device savedDevice = deviceService.saveDevice(device);
288 399
289 400 try {
  401 + thrown.expect(DataValidationException.class);
  402 + thrown.expectMessage("The firmware referenced by the devices cannot be deleted!");
290 403 firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
291 404 } finally {
292 405 deviceService.deleteDevice(tenantId, savedDevice.getId());
... ... @@ -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 412 Firmware firmware = new Firmware();
300 413 firmware.setTenantId(tenantId);
  414 + firmware.setDeviceProfileId(deviceProfileId);
  415 + firmware.setType(FIRMWARE);
301 416 firmware.setTitle(TITLE);
302 417 firmware.setVersion(VERSION);
303 418 firmware.setFileName(FILE_NAME);
... ... @@ -307,12 +422,81 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
307 422 firmware.setData(DATA);
308 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 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 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 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 500 } finally {
317 501 deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
318 502 firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
... ... @@ -323,6 +507,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
323 507 public void testFindFirmwareById() {
324 508 Firmware firmware = new Firmware();
325 509 firmware.setTenantId(tenantId);
  510 + firmware.setDeviceProfileId(deviceProfileId);
  511 + firmware.setType(FIRMWARE);
326 512 firmware.setTitle(TITLE);
327 513 firmware.setVersion(VERSION);
328 514 firmware.setFileName(FILE_NAME);
... ... @@ -342,6 +528,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
342 528 public void testFindFirmwareInfoById() {
343 529 FirmwareInfo firmware = new FirmwareInfo();
344 530 firmware.setTenantId(tenantId);
  531 + firmware.setDeviceProfileId(deviceProfileId);
  532 + firmware.setType(FIRMWARE);
345 533 firmware.setTitle(TITLE);
346 534 firmware.setVersion(VERSION);
347 535 FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware);
... ... @@ -356,6 +544,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
356 544 public void testDeleteFirmware() {
357 545 Firmware firmware = new Firmware();
358 546 firmware.setTenantId(tenantId);
  547 + firmware.setDeviceProfileId(deviceProfileId);
  548 + firmware.setType(FIRMWARE);
359 549 firmware.setTitle(TITLE);
360 550 firmware.setVersion(VERSION);
361 551 firmware.setFileName(FILE_NAME);
... ... @@ -378,6 +568,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
378 568 for (int i = 0; i < 165; i++) {
379 569 Firmware firmware = new Firmware();
380 570 firmware.setTenantId(tenantId);
  571 + firmware.setDeviceProfileId(deviceProfileId);
  572 + firmware.setType(FIRMWARE);
381 573 firmware.setTitle(TITLE);
382 574 firmware.setVersion(VERSION + i);
383 575 firmware.setFileName(FILE_NAME);
... ... @@ -421,6 +613,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
421 613 for (int i = 0; i < 165; i++) {
422 614 FirmwareInfo firmwareInfo = new FirmwareInfo();
423 615 firmwareInfo.setTenantId(tenantId);
  616 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  617 + firmwareInfo.setType(FIRMWARE);
424 618 firmwareInfo.setTitle(TITLE);
425 619 firmwareInfo.setVersion(VERSION + i);
426 620 firmwareInfo.setFileName(FILE_NAME);
... ... @@ -435,7 +629,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
435 629 PageLink pageLink = new PageLink(16);
436 630 PageData<FirmwareInfo> pageData;
437 631 do {
438   - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, false, pageLink);
  632 + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink);
439 633 loadedFirmwares.addAll(pageData.getData());
440 634 if (pageData.hasNext()) {
441 635 pageLink = pageLink.nextPageLink();
... ... @@ -451,6 +645,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
451 645 Firmware firmware = new Firmware(f.getId());
452 646 firmware.setCreatedTime(f.getCreatedTime());
453 647 firmware.setTenantId(f.getTenantId());
  648 + firmware.setDeviceProfileId(deviceProfileId);
  649 + firmware.setType(FIRMWARE);
454 650 firmware.setTitle(f.getTitle());
455 651 firmware.setVersion(f.getVersion());
456 652 firmware.setFileName(FILE_NAME);
... ... @@ -466,7 +662,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
466 662 loadedFirmwares = new ArrayList<>();
467 663 pageLink = new PageLink(16);
468 664 do {
469   - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, true, pageLink);
  665 + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink);
470 666 loadedFirmwares.addAll(pageData.getData());
471 667 if (pageData.hasNext()) {
472 668 pageLink = pageLink.nextPageLink();
... ...
... ... @@ -19,3 +19,6 @@ TB_VERSION=latest
19 19 DATABASE=postgres
20 20
21 21 LOAD_BALANCER_NAME=haproxy-certbot
  22 +
  23 +# If enabled Prometheus and Grafana containers are deployed along with other containers
  24 +MONITORING_ENABLED=false
\ No newline at end of file
... ...