Commit 820fc4a9cd6ddcba817e8368d8d66835f269b7e2

Authored by Andrii Shvaika
2 parents ec788903 b0a27954

Merge branch 'YevhenBondarenko-feature/firmware' into feature/firmware

Showing 54 changed files with 703 additions and 123 deletions
... ... @@ -70,6 +70,7 @@ CREATE TABLE IF NOT EXISTS firmware (
70 70 checksum_algorithm varchar(32),
71 71 checksum varchar(1020),
72 72 data bytea,
  73 + data_size bigint,
73 74 additional_info varchar,
74 75 search_text varchar(255),
75 76 CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
... ...
... ... @@ -129,16 +129,18 @@ public class FirmwareController extends BaseController {
129 129 firmware.setVersion(info.getVersion());
130 130 firmware.setAdditionalInfo(info.getAdditionalInfo());
131 131
  132 + byte[] data = file.getBytes();
132 133 if (StringUtils.isEmpty(checksumAlgorithm)) {
133 134 checksumAlgorithm = "sha256";
134   - checksum = Hashing.sha256().hashBytes(file.getBytes()).toString();
  135 + checksum = Hashing.sha256().hashBytes(data).toString();
135 136 }
136 137
137 138 firmware.setChecksumAlgorithm(checksumAlgorithm);
138 139 firmware.setChecksum(checksum);
139 140 firmware.setFileName(file.getOriginalFilename());
140 141 firmware.setContentType(file.getContentType());
141   - firmware.setData(ByteBuffer.wrap(file.getBytes()));
  142 + firmware.setData(ByteBuffer.wrap(data));
  143 + firmware.setDataSize((long) data.length);
142 144 return firmwareService.saveFirmware(firmware);
143 145 } catch (Exception e) {
144 146 throw handleException(e);
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.DataConstants;
23 23 import org.thingsboard.server.common.data.Device;
24 24 import org.thingsboard.server.common.data.DeviceProfile;
25 25 import org.thingsboard.server.common.data.Firmware;
  26 +import org.thingsboard.server.common.data.FirmwareInfo;
26 27 import org.thingsboard.server.common.data.id.DeviceId;
27 28 import org.thingsboard.server.common.data.id.FirmwareId;
28 29 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -34,16 +35,23 @@ import org.thingsboard.server.common.data.kv.StringDataEntry;
34 35 import org.thingsboard.server.common.data.kv.TsKvEntry;
35 36 import org.thingsboard.server.common.data.page.PageData;
36 37 import org.thingsboard.server.common.data.page.PageLink;
  38 +import org.thingsboard.server.common.msg.queue.TbCallback;
  39 +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
37 40 import org.thingsboard.server.dao.device.DeviceProfileService;
38 41 import org.thingsboard.server.dao.device.DeviceService;
39 42 import org.thingsboard.server.dao.firmware.FirmwareService;
  43 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
  44 +import org.thingsboard.server.queue.TbQueueProducer;
  45 +import org.thingsboard.server.queue.common.TbProtoQueueMsg;
  46 +import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
40 47 import org.thingsboard.server.queue.util.TbCoreComponent;
41 48
42 49 import javax.annotation.Nullable;
43 50 import java.util.ArrayList;
44 51 import java.util.Arrays;
  52 +import java.util.Collections;
45 53 import java.util.List;
46   -import java.util.Objects;
  54 +import java.util.UUID;
47 55 import java.util.function.Consumer;
48 56
49 57 import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM;
... ... @@ -61,12 +69,18 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
61 69 private final DeviceService deviceService;
62 70 private final DeviceProfileService deviceProfileService;
63 71 private final RuleEngineTelemetryService telemetryService;
  72 + private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer;
64 73
65   - public DefaultFirmwareStateService(FirmwareService firmwareService, DeviceService deviceService, DeviceProfileService deviceProfileService, RuleEngineTelemetryService telemetryService) {
  74 + public DefaultFirmwareStateService(FirmwareService firmwareService,
  75 + DeviceService deviceService,
  76 + DeviceProfileService deviceProfileService,
  77 + RuleEngineTelemetryService telemetryService,
  78 + TbCoreQueueFactory coreQueueFactory) {
66 79 this.firmwareService = firmwareService;
67 80 this.deviceService = deviceService;
68 81 this.deviceProfileService = deviceProfileService;
69 82 this.telemetryService = telemetryService;
  83 + this.fwStateMsgProducer = coreQueueFactory.createToFirmwareStateServiceMsgProducer();
70 84 }
71 85
72 86 @Override
... ... @@ -85,7 +99,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
85 99 }
86 100 if (!newFirmwareId.equals(oldFirmwareId)) {
87 101 // Device was updated and new firmware is different from previous firmware.
88   - update(device, firmwareService.findFirmwareById(device.getTenantId(), newFirmwareId), System.currentTimeMillis());
  102 + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis());
89 103 }
90 104 } else {
91 105 // Device was updated and new firmware is not set.
... ... @@ -93,7 +107,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
93 107 }
94 108 } else if (newFirmwareId != null) {
95 109 // Device was created and firmware is defined.
96   - update(device, firmwareService.findFirmwareById(device.getTenantId(), newFirmwareId), System.currentTimeMillis());
  110 + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis());
97 111 }
98 112 }
99 113
... ... @@ -103,9 +117,8 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
103 117
104 118 Consumer<Device> updateConsumer;
105 119 if (deviceProfile.getFirmwareId() != null) {
106   - Firmware firmware = firmwareService.findFirmwareById(tenantId, deviceProfile.getFirmwareId());
107 120 long ts = System.currentTimeMillis();
108   - updateConsumer = d -> update(d, firmware, ts);
  121 + updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts);
109 122 } else {
110 123 updateConsumer = this::remove;
111 124 }
... ... @@ -113,10 +126,9 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
113 126 PageLink pageLink = new PageLink(100);
114 127 PageData<Device> pageData;
115 128 do {
116   - //TODO: create a query which will return devices without firmware
117   - pageData = deviceService.findDevicesByTenantIdAndType(tenantId, deviceProfile.getName(), pageLink);
  129 + pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pageLink);
118 130
119   - pageData.getData().stream().filter(d -> d.getFirmwareId() == null).forEach(updateConsumer);
  131 + pageData.getData().forEach(updateConsumer);
120 132
121 133 if (pageData.hasNext()) {
122 134 pageLink = pageLink.nextPageLink();
... ... @@ -124,17 +136,82 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
124 136 } while (pageData.hasNext());
125 137 }
126 138
127   - private void update(Device device, Firmware firmware, long ts) {
128   - TenantId tenantId = device.getTenantId();
129   - DeviceId deviceId = device.getId();
  139 + @Override
  140 + public boolean process(ToFirmwareStateServiceMsg msg) {
  141 + boolean isSuccess = false;
  142 + FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()));
  143 + DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
  144 + TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB()));
  145 + long ts = msg.getTs();
  146 +
  147 + Device device = deviceService.findDeviceById(tenantId, deviceId);
  148 + if (device == null) {
  149 + log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId);
  150 + } else {
  151 + FirmwareId currentFirmwareId = device.getFirmwareId();
  152 +
  153 + if (currentFirmwareId == null) {
  154 + currentFirmwareId = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()).getFirmwareId();
  155 + }
  156 +
  157 + if (targetFirmwareId.equals(currentFirmwareId)) {
  158 + update(device, firmwareService.findFirmwareById(device.getTenantId(), targetFirmwareId), ts);
  159 + isSuccess = true;
  160 + } else {
  161 + log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId);
  162 + }
  163 + }
  164 + return isSuccess;
  165 + }
  166 +
  167 + private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts) {
  168 + ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder()
  169 + .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
  170 + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
  171 + .setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
  172 + .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
  173 + .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits())
  174 + .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits())
  175 + .setTs(ts)
  176 + .build();
  177 +
  178 + FirmwareInfo firmware = firmwareService.findFirmwareInfoById(tenantId, firmwareId);
  179 + if (firmware == null) {
  180 + log.warn("[{}] Failed to send firmware update because firmware was already deleted", firmwareId);
  181 + return;
  182 + }
  183 +
  184 + TopicPartitionInfo tpi = new TopicPartitionInfo(fwStateMsgProducer.getDefaultTopic(), null, null, false);
  185 + fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null);
130 186
131 187 List<TsKvEntry> telemetry = new ArrayList<>();
132 188 telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle())));
133 189 telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion())));
  190 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name())));
134 191
135 192 telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
136 193 @Override
137 194 public void onSuccess(@Nullable Void tmp) {
  195 + log.trace("[{}] Success save firmware status!", deviceId);
  196 + }
  197 +
  198 + @Override
  199 + public void onFailure(Throwable t) {
  200 + log.error("[{}] Failed to save firmware status!", deviceId, t);
  201 + }
  202 + });
  203 + }
  204 +
  205 +
  206 + private void update(Device device, FirmwareInfo firmware, long ts) {
  207 + TenantId tenantId = device.getTenantId();
  208 + DeviceId deviceId = device.getId();
  209 +
  210 + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.INITIATED.name()));
  211 +
  212 + telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() {
  213 + @Override
  214 + public void onSuccess(@Nullable Void tmp) {
138 215 log.trace("[{}] Success save telemetry with target firmware for device!", deviceId);
139 216 }
140 217
... ... @@ -149,7 +226,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
149 226 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle())));
150 227 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion())));
151 228
152   - attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, (long) firmware.getData().array().length)));
  229 + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, firmware.getDataSize())));
153 230 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm())));
154 231 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum())));
155 232 telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() {
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.service.firmware;
17 17
18 18 import org.thingsboard.server.common.data.Device;
19 19 import org.thingsboard.server.common.data.DeviceProfile;
  20 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
20 21
21 22 public interface FirmwareStateService {
22 23
... ... @@ -24,4 +25,6 @@ public interface FirmwareStateService {
24 25
25 26 void update(DeviceProfile deviceProfile);
26 27
  28 + boolean process(ToFirmwareStateServiceMsg msg);
  29 +
27 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.service.firmware;
  17 +
  18 +public enum FirmwareUpdateStatus {
  19 + QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
  20 +}
... ...
... ... @@ -24,6 +24,7 @@ import org.springframework.context.event.EventListener;
24 24 import org.springframework.core.annotation.Order;
25 25 import org.springframework.scheduling.annotation.Scheduled;
26 26 import org.springframework.stereotype.Service;
  27 +import org.thingsboard.common.util.JacksonUtil;
27 28 import org.thingsboard.common.util.ThingsBoardThreadFactory;
28 29 import org.thingsboard.rule.engine.api.RpcError;
29 30 import org.thingsboard.server.actors.ActorSystemContext;
... ... @@ -35,7 +36,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
35 36 import org.thingsboard.server.common.msg.queue.TbCallback;
36 37 import org.thingsboard.server.common.stats.StatsFactory;
37 38 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
38   -import org.thingsboard.common.util.JacksonUtil;
  39 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
39 40 import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
40 41 import org.thingsboard.server.gen.transport.TransportProtos.EdgeNotificationMsgProto;
41 42 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
... ... @@ -49,6 +50,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseP
49 50 import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto;
50 51 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
51 52 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  53 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
52 54 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
53 55 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
54 56 import org.thingsboard.server.queue.TbQueueConsumer;
... ... @@ -58,8 +60,8 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
58 60 import org.thingsboard.server.queue.util.TbCoreComponent;
59 61 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
60 62 import org.thingsboard.server.service.edge.EdgeNotificationService;
  63 +import org.thingsboard.server.service.firmware.FirmwareStateService;
61 64 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
62   -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
63 65 import org.thingsboard.server.service.queue.processing.AbstractConsumerService;
64 66 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
65 67 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
... ... @@ -97,6 +99,11 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
97 99 @Value("${queue.core.stats.enabled:false}")
98 100 private boolean statsEnabled;
99 101
  102 + @Value("${queue.core.firmware.pack-interval-ms:60000}")
  103 + private long firmwarePackInterval;
  104 + @Value("${queue.core.firmware.pack-size:100}")
  105 + private int firmwarePackSize;
  106 +
100 107 private final TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> mainConsumer;
101 108 private final DeviceStateService stateService;
102 109 private final TbApiUsageStateService statsService;
... ... @@ -104,11 +111,15 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
104 111 private final SubscriptionManagerService subscriptionManagerService;
105 112 private final TbCoreDeviceRpcService tbCoreDeviceRpcService;
106 113 private final EdgeNotificationService edgeNotificationService;
  114 + private final FirmwareStateService firmwareStateService;
107 115 private final TbCoreConsumerStats stats;
108 116 protected final TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> usageStatsConsumer;
  117 + private final TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> firmwareStatesConsumer;
109 118
110 119 protected volatile ExecutorService usageStatsExecutor;
111 120
  121 + private volatile ExecutorService firmwareStatesExecutor;
  122 +
112 123 public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory,
113 124 ActorSystemContext actorContext,
114 125 DeviceStateService stateService,
... ... @@ -121,10 +132,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
121 132 TbApiUsageStateService statsService,
122 133 TbTenantProfileCache tenantProfileCache,
123 134 TbApiUsageStateService apiUsageStateService,
124   - EdgeNotificationService edgeNotificationService) {
  135 + EdgeNotificationService edgeNotificationService,
  136 + FirmwareStateService firmwareStateService) {
125 137 super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer());
126 138 this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer();
127 139 this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer();
  140 + this.firmwareStatesConsumer = tbCoreQueueFactory.createToFirmwareStateServiceMsgConsumer();
128 141 this.stateService = stateService;
129 142 this.localSubscriptionService = localSubscriptionService;
130 143 this.subscriptionManagerService = subscriptionManagerService;
... ... @@ -132,12 +145,14 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
132 145 this.edgeNotificationService = edgeNotificationService;
133 146 this.stats = new TbCoreConsumerStats(statsFactory);
134 147 this.statsService = statsService;
  148 + this.firmwareStateService = firmwareStateService;
135 149 }
136 150
137 151 @PostConstruct
138 152 public void init() {
139 153 super.init("tb-core-consumer", "tb-core-notifications-consumer");
140   - this.usageStatsExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("tb-core-usage-stats-consumer"));
  154 + this.usageStatsExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-core-usage-stats-consumer"));
  155 + this.firmwareStatesExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-core-firmware-notifications-consumer"));
141 156 }
142 157
143 158 @PreDestroy
... ... @@ -146,6 +161,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
146 161 if (usageStatsExecutor != null) {
147 162 usageStatsExecutor.shutdownNow();
148 163 }
  164 + if (firmwareStatesExecutor != null) {
  165 + firmwareStatesExecutor.shutdownNow();
  166 + }
149 167 }
150 168
151 169 @EventListener(ApplicationReadyEvent.class)
... ... @@ -153,6 +171,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
153 171 public void onApplicationEvent(ApplicationReadyEvent event) {
154 172 super.onApplicationEvent(event);
155 173 launchUsageStatsConsumer();
  174 + launchFirmwareUpdateNotificationConsumer();
156 175 }
157 176
158 177 @Override
... ... @@ -167,6 +186,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
167 186 .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic()))
168 187 .collect(Collectors.toSet()));
169 188 }
  189 + this.firmwareStatesConsumer.subscribe();
170 190 }
171 191
172 192 @Override
... ... @@ -336,10 +356,59 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
336 356 });
337 357 }
338 358
  359 + private void launchFirmwareUpdateNotificationConsumer() {
  360 + long maxProcessingTimeoutPerRecord = firmwarePackInterval / firmwarePackSize;
  361 + firmwareStatesExecutor.submit(() -> {
  362 + while (!stopped) {
  363 + try {
  364 + List<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> msgs = firmwareStatesConsumer.poll(getNotificationPollDuration());
  365 + if (msgs.isEmpty()) {
  366 + continue;
  367 + }
  368 + long timeToSleep = maxProcessingTimeoutPerRecord;
  369 + for (TbProtoQueueMsg<ToFirmwareStateServiceMsg> msg : msgs) {
  370 + try {
  371 + long startTime = System.currentTimeMillis();
  372 + boolean isSuccessUpdate = handleFirmwareUpdates(msg);
  373 + long endTime = System.currentTimeMillis();
  374 + long spentTime = endTime - startTime;
  375 + timeToSleep = timeToSleep - spentTime;
  376 + if (isSuccessUpdate) {
  377 + if (timeToSleep > 0) {
  378 + log.debug("Spent time per record is: [{}]!", spentTime);
  379 + Thread.sleep(timeToSleep);
  380 + timeToSleep = 0;
  381 + }
  382 + timeToSleep += maxProcessingTimeoutPerRecord;
  383 + }
  384 + } catch (Throwable e) {
  385 + log.warn("Failed to process firmware update msg: {}", msg, e);
  386 + }
  387 + }
  388 + firmwareStatesConsumer.commit();
  389 + } catch (Exception e) {
  390 + if (!stopped) {
  391 + log.warn("Failed to obtain usage stats from queue.", e);
  392 + try {
  393 + Thread.sleep(getNotificationPollDuration());
  394 + } catch (InterruptedException e2) {
  395 + log.trace("Failed to wait until the server has capacity to handle new firmware updates", e2);
  396 + }
  397 + }
  398 + }
  399 + }
  400 + log.info("TB Firmware States Consumer stopped.");
  401 + });
  402 + }
  403 +
339 404 private void handleUsageStats(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) {
340 405 statsService.process(msg, callback);
341 406 }
342 407
  408 + private boolean handleFirmwareUpdates(TbProtoQueueMsg<ToFirmwareStateServiceMsg> msg) {
  409 + return firmwareStateService.process(msg.getValue());
  410 + }
  411 +
343 412 private void forwardToCoreRpcService(FromDeviceRPCResponseProto proto, TbCallback callback) {
344 413 RpcError error = proto.getError() > 0 ? RpcError.values()[proto.getError()] : null;
345 414 FromDeviceRpcResponse response = new FromDeviceRpcResponse(new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB())
... ... @@ -448,6 +517,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
448 517 if (usageStatsConsumer != null) {
449 518 usageStatsConsumer.unsubscribe();
450 519 }
  520 + if (firmwareStatesConsumer != null) {
  521 + firmwareStatesConsumer.unsubscribe();
  522 + }
451 523 }
452 524
453 525 }
... ...
... ... @@ -481,6 +481,8 @@ public class DefaultTransportApiService implements TransportApiService {
481 481 builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
482 482 builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits());
483 483 builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits());
  484 + builder.setTitle(firmware.getTitle());
  485 + builder.setVersion(firmware.getVersion());
484 486 builder.setFileName(firmware.getFileName());
485 487 builder.setContentType(firmware.getContentType());
486 488 firmwareCacheWriter.put(firmwareId.toString(), firmware.getData().array());
... ...
... ... @@ -744,6 +744,10 @@ queue:
744 744 sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}"
745 745 sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}"
746 746 security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}"
  747 + consumer-properties-per-topic:
  748 + tb_firmware:
  749 + - key: max.poll.records
  750 + value: 10
747 751 other:
748 752 topic-properties:
749 753 rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
... ... @@ -751,6 +755,7 @@ queue:
751 755 transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
752 756 notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
753 757 js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
  758 + fw-updates: "${TB_QUEUE_KAFKA_FW_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
754 759 consumer-stats:
755 760 enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
756 761 print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
... ... @@ -821,6 +826,10 @@ queue:
821 826 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
822 827 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
823 828 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}"
  829 + firmware:
  830 + topic: "${TB_QUEUE_CORE_FW_TOPIC:tb_firmware}"
  831 + pack-interval-ms: "${TB_QUEUE_CORE_FW_PACK_INTERVAL_MS:60000}"
  832 + pack-size: "${TB_QUEUE_CORE_FW_PACK_SIZE:100}"
824 833 usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
825 834 stats:
826 835 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}"
... ...
... ... @@ -22,7 +22,7 @@ import org.springframework.stereotype.Service;
22 22 import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
23 23
24 24 @Service
25   -@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='caffeine')")
  25 +@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')")
26 26 public class CaffeineFirmwareCacheReader implements FirmwareCacheReader {
27 27
28 28 private final CacheManager cacheManager;
... ...
... ... @@ -22,7 +22,7 @@ import org.springframework.stereotype.Service;
22 22 import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
23 23
24 24 @Service
25   -@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='core') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='caffeine')")
  25 +@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')")
26 26 public class CaffeineFirmwareCacheWriter implements FirmwareCacheWriter {
27 27
28 28 private final CacheManager cacheManager;
... ...
... ... @@ -21,7 +21,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
21 21 import org.springframework.stereotype.Service;
22 22
23 23 @Service
24   -@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='core') && '${cache.type:null}'=='redis'")
  24 +@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && '${cache.type:null}'=='redis'")
25 25 public class RedisFirmwareCacheWriter extends AbstractRedisFirmwareCache implements FirmwareCacheWriter {
26 26
27 27 public RedisFirmwareCacheWriter(RedisConnectionFactory redisConnectionFactory) {
... ...
... ... @@ -61,6 +61,8 @@ public interface DeviceService {
61 61
62 62 PageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
63 63
  64 + PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
  65 +
64 66 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
65 67
66 68 PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink);
... ...
... ... @@ -99,7 +99,7 @@ public class DataConstants {
99 99 public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version";
100 100 public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
101 101 public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
102   - public static final String CURRENT_FIRMWARE_STATE = "cur_fw_state";
  102 + public static final String FIRMWARE_STATE = "fw_state";
103 103
104 104 //attributes
105 105 //telemetry
... ...
... ... @@ -27,14 +27,6 @@ public class Firmware extends FirmwareInfo {
27 27
28 28 private static final long serialVersionUID = 3091601761339422546L;
29 29
30   - private String fileName;
31   -
32   - private String contentType;
33   -
34   - private String checksumAlgorithm;
35   -
36   - private String checksum;
37   -
38 30 private transient ByteBuffer data;
39 31
40 32 public Firmware() {
... ... @@ -47,10 +39,6 @@ public class Firmware extends FirmwareInfo {
47 39
48 40 public Firmware(Firmware firmware) {
49 41 super(firmware);
50   - this.fileName = firmware.getFileName();
51   - this.contentType = firmware.getContentType();
52 42 this.data = firmware.getData();
53   - this.checksumAlgorithm = firmware.getChecksumAlgorithm();
54   - this.checksum = firmware.getChecksum();
55 43 }
56 44 }
... ...
... ... @@ -32,6 +32,12 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
32 32 private String title;
33 33 private String version;
34 34 private boolean hasData;
  35 + private String fileName;
  36 + private String contentType;
  37 + private String checksumAlgorithm;
  38 + private String checksum;
  39 + private Long dataSize;
  40 +
35 41
36 42 public FirmwareInfo() {
37 43 super();
... ... @@ -47,6 +53,11 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
47 53 this.title = firmwareInfo.getTitle();
48 54 this.version = firmwareInfo.getVersion();
49 55 this.hasData = firmwareInfo.isHasData();
  56 + this.fileName = firmwareInfo.getFileName();
  57 + this.contentType = firmwareInfo.getContentType();
  58 + this.checksumAlgorithm = firmwareInfo.getChecksumAlgorithm();
  59 + this.checksum = firmwareInfo.getChecksum();
  60 + this.dataSize = firmwareInfo.getDataSize();
50 61 }
51 62
52 63 @Override
... ...
... ... @@ -69,7 +69,7 @@ public class TbKafkaConsumerStatsService {
69 69 this.adminClient = AdminClient.create(kafkaSettings.toAdminProps());
70 70 this.statsPrintScheduler = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("kafka-consumer-stats"));
71 71
72   - Properties consumerProps = kafkaSettings.toConsumerProps();
  72 + Properties consumerProps = kafkaSettings.toConsumerProps(null);
73 73 consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, "consumer-stats-loader-client");
74 74 consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "consumer-stats-loader-client-group");
75 75 this.consumer = new KafkaConsumer<>(consumerProps);
... ...
... ... @@ -50,7 +50,7 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue
50 50 String clientId, String groupId, String topic,
51 51 TbQueueAdmin admin, TbKafkaConsumerStatsService statsService) {
52 52 super(topic);
53   - Properties props = settings.toConsumerProps();
  53 + Properties props = settings.toConsumerProps(topic);
54 54 props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId);
55 55 if (groupId != null) {
56 56 props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
... ...
... ... @@ -31,7 +31,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
31 31 import org.springframework.boot.context.properties.ConfigurationProperties;
32 32 import org.springframework.stereotype.Component;
33 33
  34 +import java.util.Collections;
34 35 import java.util.List;
  36 +import java.util.Map;
35 37 import java.util.Properties;
36 38
37 39 /**
... ... @@ -95,6 +97,9 @@ public class TbKafkaSettings {
95 97 @Setter
96 98 private List<TbKafkaProperty> other;
97 99
  100 + @Setter
  101 + private Map<String, List<TbKafkaProperty>> consumerPropertiesPerTopic = Collections.emptyMap();
  102 +
98 103 public Properties toAdminProps() {
99 104 Properties props = toProps();
100 105 props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
... ... @@ -103,7 +108,7 @@ public class TbKafkaSettings {
103 108 return props;
104 109 }
105 110
106   - public Properties toConsumerProps() {
  111 + public Properties toConsumerProps(String topic) {
107 112 Properties props = toProps();
108 113 props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
109 114 props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords);
... ... @@ -113,6 +118,10 @@ public class TbKafkaSettings {
113 118
114 119 props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
115 120 props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
  121 +
  122 + consumerPropertiesPerTopic
  123 + .getOrDefault(topic, Collections.emptyList())
  124 + .forEach(kv -> props.put(kv.getKey(), kv.getValue()));
116 125 return props;
117 126 }
118 127
... ...
... ... @@ -19,6 +19,7 @@ import lombok.Getter;
19 19 import org.springframework.beans.factory.annotation.Value;
20 20 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
21 21 import org.springframework.stereotype.Component;
  22 +import org.thingsboard.server.common.data.StringUtils;
22 23
23 24 import javax.annotation.PostConstruct;
24 25 import java.util.HashMap;
... ... @@ -37,6 +38,8 @@ public class TbKafkaTopicConfigs {
37 38 private String notificationsProperties;
38 39 @Value("${queue.kafka.topic-properties.js-executor}")
39 40 private String jsExecutorProperties;
  41 + @Value("${queue.kafka.topic-properties.fw-updates:}")
  42 + private String fwUpdatesProperties;
40 43
41 44 @Getter
42 45 private Map<String, String> coreConfigs;
... ... @@ -48,6 +51,8 @@ public class TbKafkaTopicConfigs {
48 51 private Map<String, String> notificationsConfigs;
49 52 @Getter
50 53 private Map<String, String> jsExecutorConfigs;
  54 + @Getter
  55 + private Map<String, String> fwUpdatesConfigs;
51 56
52 57 @PostConstruct
53 58 private void init() {
... ... @@ -56,15 +61,18 @@ public class TbKafkaTopicConfigs {
56 61 transportApiConfigs = getConfigs(transportApiProperties);
57 62 notificationsConfigs = getConfigs(notificationsProperties);
58 63 jsExecutorConfigs = getConfigs(jsExecutorProperties);
  64 + fwUpdatesConfigs = getConfigs(fwUpdatesProperties);
59 65 }
60 66
61 67 private Map<String, String> getConfigs(String properties) {
62 68 Map<String, String> configs = new HashMap<>();
63   - for (String property : properties.split(";")) {
64   - int delimiterPosition = property.indexOf(":");
65   - String key = property.substring(0, delimiterPosition);
66   - String value = property.substring(delimiterPosition + 1);
67   - configs.put(key, value);
  69 + if (StringUtils.isNotEmpty(properties)) {
  70 + for (String property : properties.split(";")) {
  71 + int delimiterPosition = property.indexOf(":");
  72 + String key = property.substring(0, delimiterPosition);
  73 + String value = property.substring(delimiterPosition + 1);
  74 + configs.put(key, value);
  75 + }
68 76 }
69 77 return configs;
70 78 }
... ...
... ... @@ -186,6 +186,17 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
186 186 msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
187 187 }
188 188
  189 + @Override
  190 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  191 + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getFirmwareTopic(),
  192 + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  193 + }
  194 +
  195 + @Override
  196 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  197 + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getFirmwareTopic());
  198 + }
  199 +
189 200 @PreDestroy
190 201 private void destroy() {
191 202 if (coreAdmin != null) {
... ...
... ... @@ -21,12 +21,13 @@ import org.springframework.context.annotation.Bean;
21 21 import org.springframework.stereotype.Component;
22 22 import org.thingsboard.server.common.msg.queue.ServiceType;
23 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24   -import org.thingsboard.server.gen.transport.TransportProtos;
25 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
26 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
27 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
28 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
29 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
  30 +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
30 31 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
31 32 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
32 33 import org.thingsboard.server.queue.TbQueueAdmin;
... ... @@ -165,14 +166,25 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
165 166 }
166 167
167 168 @Override
168   - public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
  169 + public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
169 170 return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic());
170 171 }
171 172
172 173 @Override
173   - public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() {
  174 + public TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() {
174 175 return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getUsageStatsTopic(),
175   - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  176 + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  177 + }
  178 +
  179 + @Override
  180 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  181 + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getFirmwareTopic(),
  182 + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  183 + }
  184 +
  185 + @Override
  186 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  187 + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getFirmwareTopic());
176 188 }
177 189
178 190 @PreDestroy
... ...
... ... @@ -131,6 +131,16 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE
131 131 }
132 132
133 133 @Override
  134 + public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  135 + return new InMemoryTbQueueConsumer<>(coreSettings.getFirmwareTopic());
  136 + }
  137 +
  138 + @Override
  139 + public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  140 + return new InMemoryTbQueueProducer<>(coreSettings.getFirmwareTopic());
  141 + }
  142 +
  143 + @Override
134 144 public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
135 145 return new InMemoryTbQueueProducer<>(coreSettings.getUsageStatsTopic());
136 146 }
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
... ... @@ -73,6 +74,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi
73 74 private final TbQueueAdmin jsExecutorAdmin;
74 75 private final TbQueueAdmin transportApiAdmin;
75 76 private final TbQueueAdmin notificationAdmin;
  77 + private final TbQueueAdmin fwUpdatesAdmin;
76 78
77 79 public KafkaMonolithQueueFactory(PartitionService partitionService, TbKafkaSettings kafkaSettings,
78 80 TbServiceInfoProvider serviceInfoProvider,
... ... @@ -98,6 +100,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi
98 100 this.jsExecutorAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorConfigs());
99 101 this.transportApiAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiConfigs());
100 102 this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs());
  103 + this.fwUpdatesAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getFwUpdatesConfigs());
101 104 }
102 105
103 106 @Override
... ... @@ -274,6 +277,29 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi
274 277 }
275 278
276 279 @Override
  280 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  281 + TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> consumerBuilder = TbKafkaConsumerTemplate.builder();
  282 + consumerBuilder.settings(kafkaSettings);
  283 + consumerBuilder.topic(coreSettings.getFirmwareTopic());
  284 + consumerBuilder.clientId("monolith-fw-consumer-" + serviceInfoProvider.getServiceId());
  285 + consumerBuilder.groupId("monolith-fw-consumer");
  286 + consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  287 + consumerBuilder.admin(fwUpdatesAdmin);
  288 + consumerBuilder.statsService(consumerStatsService);
  289 + return consumerBuilder.build();
  290 + }
  291 +
  292 + @Override
  293 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  294 + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
  295 + requestBuilder.settings(kafkaSettings);
  296 + requestBuilder.clientId("monolith-fw-producer-" + serviceInfoProvider.getServiceId());
  297 + requestBuilder.defaultTopic(coreSettings.getFirmwareTopic());
  298 + requestBuilder.admin(fwUpdatesAdmin);
  299 + return requestBuilder.build();
  300 + }
  301 +
  302 + @Override
277 303 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
278 304 TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
279 305 requestBuilder.settings(kafkaSettings);
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
... ... @@ -70,6 +71,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory {
70 71 private final TbQueueAdmin jsExecutorAdmin;
71 72 private final TbQueueAdmin transportApiAdmin;
72 73 private final TbQueueAdmin notificationAdmin;
  74 + private final TbQueueAdmin fwUpdatesAdmin;
73 75
74 76 public KafkaTbCoreQueueFactory(PartitionService partitionService, TbKafkaSettings kafkaSettings,
75 77 TbServiceInfoProvider serviceInfoProvider,
... ... @@ -93,6 +95,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory {
93 95 this.jsExecutorAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorConfigs());
94 96 this.transportApiAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiConfigs());
95 97 this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs());
  98 + this.fwUpdatesAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getFwUpdatesConfigs());
96 99 }
97 100
98 101 @Override
... ... @@ -242,6 +245,29 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory {
242 245 }
243 246
244 247 @Override
  248 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  249 + TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> consumerBuilder = TbKafkaConsumerTemplate.builder();
  250 + consumerBuilder.settings(kafkaSettings);
  251 + consumerBuilder.topic(coreSettings.getFirmwareTopic());
  252 + consumerBuilder.clientId("tb-core-fw-consumer-" + serviceInfoProvider.getServiceId());
  253 + consumerBuilder.groupId("tb-core-fw-consumer");
  254 + consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  255 + consumerBuilder.admin(fwUpdatesAdmin);
  256 + consumerBuilder.statsService(consumerStatsService);
  257 + return consumerBuilder.build();
  258 + }
  259 +
  260 + @Override
  261 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  262 + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
  263 + requestBuilder.settings(kafkaSettings);
  264 + requestBuilder.clientId("tb-core-fw-producer-" + serviceInfoProvider.getServiceId());
  265 + requestBuilder.defaultTopic(coreSettings.getFirmwareTopic());
  266 + requestBuilder.admin(fwUpdatesAdmin);
  267 + return requestBuilder.build();
  268 + }
  269 +
  270 + @Override
245 271 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
246 272 TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
247 273 requestBuilder.settings(kafkaSettings);
... ...
... ... @@ -22,6 +22,7 @@ import org.springframework.stereotype.Component;
22 22 import org.thingsboard.server.common.msg.queue.ServiceType;
23 23 import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest;
24 24 import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse;
  25 +import org.thingsboard.server.gen.transport.TransportProtos.*;
25 26 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
26 27 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
... ... @@ -191,6 +192,17 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
191 192 }
192 193
193 194 @Override
  195 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  196 + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic(),
  197 + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  198 + }
  199 +
  200 + @Override
  201 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  202 + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic());
  203 + }
  204 +
  205 + @Override
194 206 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
195 207 return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic());
196 208 }
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
... ... @@ -165,6 +166,17 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory {
165 166 }
166 167
167 168 @Override
  169 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  170 + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic(),
  171 + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  172 + }
  173 +
  174 + @Override
  175 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  176 + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic());
  177 + }
  178 +
  179 + @Override
168 180 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
169 181 return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic());
170 182 }
... ...
... ... @@ -24,6 +24,7 @@ import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest;
24 24 import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse;
25 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
26 26 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  27 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
28 29 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
29 30 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
... ... @@ -189,6 +190,17 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE
189 190 }
190 191
191 192 @Override
  193 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  194 + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic(),
  195 + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  196 + }
  197 +
  198 + @Override
  199 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  200 + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic());
  201 + }
  202 +
  203 + @Override
192 204 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
193 205 return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic());
194 206 }
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
... ... @@ -171,6 +172,17 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory {
171 172 }
172 173
173 174 @Override
  175 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  176 + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic(),
  177 + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  178 + }
  179 +
  180 + @Override
  181 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  182 + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic());
  183 + }
  184 +
  185 + @Override
174 186 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
175 187 return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic());
176 188 }
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
... ... @@ -188,6 +189,17 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul
188 189 }
189 190
190 191 @Override
  192 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  193 + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic(),
  194 + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  195 + }
  196 +
  197 + @Override
  198 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  199 + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic());
  200 + }
  201 +
  202 + @Override
191 203 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
192 204 return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic());
193 205 }
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
... ... @@ -171,6 +172,17 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory {
171 172 }
172 173
173 174 @Override
  175 + public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
  176 + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic(),
  177 + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
  178 + }
  179 +
  180 + @Override
  181 + public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
  182 + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic());
  183 + }
  184 +
  185 + @Override
174 186 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
175 187 return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic());
176 188 }
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.queue.provider;
17 17
18 18 import org.thingsboard.server.gen.js.JsInvokeProtos;
  19 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
19 20 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
20 21 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
21 22 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
... ... @@ -86,6 +87,20 @@ public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory {
86 87 TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer();
87 88
88 89 /**
  90 + * Used to consume messages about firmware update notifications by TB Core Service
  91 + *
  92 + * @return
  93 + */
  94 + TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer();
  95 +
  96 + /**
  97 + * Used to consume messages about firmware update notifications by TB Core Service
  98 + *
  99 + * @return
  100 + */
  101 + TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer();
  102 +
  103 + /**
89 104 * Used to consume high priority messages by TB Core Service
90 105 *
91 106 * @return
... ...
... ... @@ -26,6 +26,9 @@ public class TbQueueCoreSettings {
26 26 @Value("${queue.core.topic}")
27 27 private String topic;
28 28
  29 + @Value("${queue.core.firmware.topic:tb_firmware}")
  30 + private String firmwareTopic;
  31 +
29 32 @Value("${queue.core.usage-stats-topic:tb_usage_stats}")
30 33 private String usageStatsTopic;
31 34
... ...
... ... @@ -363,8 +363,10 @@ message GetFirmwareResponseMsg {
363 363 ResponseStatus responseStatus = 1;
364 364 int64 firmwareIdMSB = 2;
365 365 int64 firmwareIdLSB = 3;
366   - string contentType = 4;
367   - string fileName = 5;
  366 + string title = 4;
  367 + string version = 5;
  368 + string contentType = 6;
  369 + string fileName = 7;
368 370 }
369 371
370 372 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level.
... ... @@ -663,3 +665,13 @@ message ToUsageStatsServiceMsg {
663 665 int64 customerIdMSB = 6;
664 666 int64 customerIdLSB = 7;
665 667 }
  668 +
  669 +message ToFirmwareStateServiceMsg {
  670 + int64 ts = 1;
  671 + int64 tenantIdMSB = 2;
  672 + int64 tenantIdLSB = 3;
  673 + int64 deviceIdMSB = 4;
  674 + int64 deviceIdLSB = 5;
  675 + int64 firmwareIdMSB = 6;
  676 + int64 firmwareIdLSB = 7;
  677 +}
... ...
... ... @@ -207,6 +207,8 @@ public class DeviceApiController {
207 207
208 208 @RequestMapping(value = "/{deviceToken}/firmware", method = RequestMethod.GET)
209 209 public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken,
  210 + @RequestParam(value = "title") String title,
  211 + @RequestParam(value = "version") String version,
210 212 @RequestParam(value = "chunkSize", required = false, defaultValue = "0") int chunkSize,
211 213 @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
212 214 DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
... ... @@ -217,7 +219,7 @@ public class DeviceApiController {
217 219 .setTenantIdLSB(sessionInfo.getTenantIdLSB())
218 220 .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
219 221 .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();
220   - transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, chunkSize, chunk));
  222 + transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, chunkSize, chunk));
221 223 }));
222 224 return responseWriter;
223 225 }
... ... @@ -278,11 +280,15 @@ public class DeviceApiController {
278 280
279 281 private class GetFirmwareCallback implements TransportServiceCallback<TransportProtos.GetFirmwareResponseMsg> {
280 282 private final DeferredResult<ResponseEntity> responseWriter;
  283 + private final String title;
  284 + private final String version;
281 285 private final int chuckSize;
282 286 private final int chuck;
283 287
284   - GetFirmwareCallback(DeferredResult<ResponseEntity> responseWriter, int chuckSize, int chuck) {
  288 + GetFirmwareCallback(DeferredResult<ResponseEntity> responseWriter, String title, String version, int chuckSize, int chuck) {
285 289 this.responseWriter = responseWriter;
  290 + this.title = title;
  291 + this.version = version;
286 292 this.chuckSize = chuckSize;
287 293 this.chuck = chuck;
288 294 }
... ... @@ -291,7 +297,7 @@ public class DeviceApiController {
291 297 public void onSuccess(TransportProtos.GetFirmwareResponseMsg firmwareResponseMsg) {
292 298 if (!TransportProtos.ResponseStatus.SUCCESS.equals(firmwareResponseMsg.getResponseStatus())) {
293 299 responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND));
294   - } else {
  300 + } else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) {
295 301 String firmwareId = new UUID(firmwareResponseMsg.getFirmwareIdMSB(), firmwareResponseMsg.getFirmwareIdLSB()).toString();
296 302 ByteArrayResource resource = new ByteArrayResource(transportContext.getFirmwareCacheReader().get(firmwareId, chuckSize, chuck));
297 303 ResponseEntity<ByteArrayResource> response = ResponseEntity.ok()
... ... @@ -301,6 +307,8 @@ public class DeviceApiController {
301 307 .contentType(parseMediaType(firmwareResponseMsg.getContentType()))
302 308 .body(resource);
303 309 responseWriter.setResult(response);
  310 + } else {
  311 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
304 312 }
305 313 }
306 314
... ...
... ... @@ -81,6 +81,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao {
81 81 */
82 82 PageData<Device> findDevicesByTenantIdAndType(UUID tenantId, String type, PageLink pageLink);
83 83
  84 + PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink);
  85 +
84 86 /**
85 87 * Find device infos by tenantId, type and page link.
86 88 *
... ...
... ... @@ -355,6 +355,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
355 355 }
356 356
357 357 @Override
  358 + public PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) {
  359 + log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
  360 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  361 + validateString(type, "Incorrect type " + type);
  362 + validatePageLink(pageLink);
  363 + return deviceDao.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId.getId(), type, pageLink);
  364 + }
  365 +
  366 + @Override
358 367 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
359 368 log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
360 369 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
... ...
... ... @@ -184,13 +184,7 @@ public class BaseFirmwareService implements FirmwareService {
184 184 protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) {
185 185 FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId());
186 186
187   - if (!firmwareOld.getTitle().equals(firmware.getTitle())) {
188   - throw new DataValidationException("Updating firmware title is prohibited!");
189   - }
190   -
191   - if (!firmwareOld.getVersion().equals(firmware.getVersion())) {
192   - throw new DataValidationException("Updating firmware version is prohibited!");
193   - }
  187 + BaseFirmwareService.validateUpdate(firmware, firmwareOld);
194 188 }
195 189 };
196 190
... ... @@ -261,35 +255,43 @@ public class BaseFirmwareService implements FirmwareService {
261 255 protected void validateUpdate(TenantId tenantId, Firmware firmware) {
262 256 Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId());
263 257
264   - if (!firmwareOld.getTitle().equals(firmware.getTitle())) {
265   - throw new DataValidationException("Updating firmware title is prohibited!");
266   - }
  258 + BaseFirmwareService.validateUpdate(firmware, firmwareOld);
267 259
268   - if (!firmwareOld.getVersion().equals(firmware.getVersion())) {
269   - throw new DataValidationException("Updating firmware version is prohibited!");
  260 + if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) {
  261 + throw new DataValidationException("Updating firmware data is prohibited!");
270 262 }
  263 + }
  264 + };
271 265
272   - if (firmwareOld.getFileName() != null && !firmwareOld.getFileName().equals(firmware.getFileName())) {
273   - throw new DataValidationException("Updating firmware file name is prohibited!");
274   - }
  266 + private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) {
  267 + if (!firmwareOld.getTitle().equals(firmware.getTitle())) {
  268 + throw new DataValidationException("Updating firmware title is prohibited!");
  269 + }
275 270
276   - if (firmwareOld.getContentType() != null && !firmwareOld.getContentType().equals(firmware.getContentType())) {
277   - throw new DataValidationException("Updating firmware content type is prohibited!");
278   - }
  271 + if (!firmwareOld.getVersion().equals(firmware.getVersion())) {
  272 + throw new DataValidationException("Updating firmware version is prohibited!");
  273 + }
279 274
280   - if (firmwareOld.getChecksumAlgorithm() != null && !firmwareOld.getChecksumAlgorithm().equals(firmware.getChecksumAlgorithm())) {
281   - throw new DataValidationException("Updating firmware content type is prohibited!");
282   - }
  275 + if (firmwareOld.getFileName() != null && !firmwareOld.getFileName().equals(firmware.getFileName())) {
  276 + throw new DataValidationException("Updating firmware file name is prohibited!");
  277 + }
283 278
284   - if (firmwareOld.getChecksum() != null && !firmwareOld.getChecksum().equals(firmware.getChecksum())) {
285   - throw new DataValidationException("Updating firmware content type is prohibited!");
286   - }
  279 + if (firmwareOld.getContentType() != null && !firmwareOld.getContentType().equals(firmware.getContentType())) {
  280 + throw new DataValidationException("Updating firmware content type is prohibited!");
  281 + }
287 282
288   - if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) {
289   - throw new DataValidationException("Updating firmware data is prohibited!");
290   - }
  283 + if (firmwareOld.getChecksumAlgorithm() != null && !firmwareOld.getChecksumAlgorithm().equals(firmware.getChecksumAlgorithm())) {
  284 + throw new DataValidationException("Updating firmware content type is prohibited!");
291 285 }
292   - };
  286 +
  287 + if (firmwareOld.getChecksum() != null && !firmwareOld.getChecksum().equals(firmware.getChecksum())) {
  288 + throw new DataValidationException("Updating firmware content type is prohibited!");
  289 + }
  290 +
  291 + if (firmwareOld.getDataSize() != null && !firmwareOld.getDataSize().equals(firmware.getDataSize())) {
  292 + throw new DataValidationException("Updating firmware data size is prohibited!");
  293 + }
  294 + }
293 295
294 296 private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover =
295 297 new PaginatedRemover<>() {
... ...
... ... @@ -483,6 +483,7 @@ public class ModelConstants {
483 483 public static final String FIRMWARE_CHECKSUM_ALGORITHM_COLUMN = "checksum_algorithm";
484 484 public static final String FIRMWARE_CHECKSUM_COLUMN = "checksum";
485 485 public static final String FIRMWARE_DATA_COLUMN = "data";
  486 + public static final String FIRMWARE_DATA_SIZE_COLUMN = "data_size";
486 487 public static final String FIRMWARE_ADDITIONAL_INFO_COLUMN = ADDITIONAL_INFO_PROPERTY;
487 488 public static final String FIRMWARE_HAS_DATA_PROPERTY = "has_data";
488 489
... ...
... ... @@ -38,6 +38,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_
38 38 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN;
39 39 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
40 40 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
  41 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
41 42 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
42 43 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
43 44 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
... ... @@ -76,6 +77,9 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
76 77 @Column(name = FIRMWARE_DATA_COLUMN, columnDefinition = "BINARY")
77 78 private byte[] data;
78 79
  80 + @Column(name = FIRMWARE_DATA_SIZE_COLUMN)
  81 + private Long dataSize;
  82 +
79 83 @Type(type = "json")
80 84 @Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN)
81 85 private JsonNode additionalInfo;
... ... @@ -98,6 +102,7 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
98 102 this.checksumAlgorithm = firmware.getChecksumAlgorithm();
99 103 this.checksum = firmware.getChecksum();
100 104 this.data = firmware.getData().array();
  105 + this.dataSize = firmware.getDataSize();
101 106 this.additionalInfo = firmware.getAdditionalInfo();
102 107 }
103 108
... ... @@ -122,6 +127,7 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
122 127 firmware.setContentType(contentType);
123 128 firmware.setChecksumAlgorithm(checksumAlgorithm);
124 129 firmware.setChecksum(checksum);
  130 + firmware.setDataSize(dataSize);
125 131 if (data != null) {
126 132 firmware.setData(ByteBuffer.wrap(data));
127 133 firmware.setHasData(true);
... ...
... ... @@ -35,6 +35,12 @@ import javax.persistence.Table;
35 35 import javax.persistence.Transient;
36 36 import java.util.UUID;
37 37
  38 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN;
  39 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN;
  40 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
  41 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
  42 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
  43 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
38 44 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY;
39 45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
40 46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
... ... @@ -58,6 +64,21 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
58 64 @Column(name = FIRMWARE_VERSION_COLUMN)
59 65 private String version;
60 66
  67 + @Column(name = FIRMWARE_FILE_NAME_COLUMN)
  68 + private String fileName;
  69 +
  70 + @Column(name = FIRMWARE_CONTENT_TYPE_COLUMN)
  71 + private String contentType;
  72 +
  73 + @Column(name = FIRMWARE_CHECKSUM_ALGORITHM_COLUMN)
  74 + private String checksumAlgorithm;
  75 +
  76 + @Column(name = FIRMWARE_CHECKSUM_COLUMN)
  77 + private String checksum;
  78 +
  79 + @Column(name = FIRMWARE_DATA_SIZE_COLUMN)
  80 + private Long dataSize;
  81 +
61 82 @Type(type = "json")
62 83 @Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN)
63 84 private JsonNode additionalInfo;
... ... @@ -65,7 +86,6 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
65 86 @Column(name = SEARCH_TEXT_PROPERTY)
66 87 private String searchText;
67 88
68   -// @Column(name = FIRMWARE_HAS_DATA_PROPERTY, insertable = false, updatable = false)
69 89 @Transient
70 90 private boolean hasData;
71 91
... ... @@ -79,15 +99,27 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
79 99 this.tenantId = firmware.getTenantId().getId();
80 100 this.title = firmware.getTitle();
81 101 this.version = firmware.getVersion();
  102 + this.fileName = firmware.getFileName();
  103 + this.contentType = firmware.getContentType();
  104 + this.checksumAlgorithm = firmware.getChecksumAlgorithm();
  105 + this.checksum = firmware.getChecksum();
  106 + this.dataSize = firmware.getDataSize();
82 107 this.additionalInfo = firmware.getAdditionalInfo();
83 108 }
84 109
85   - public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version, Object additionalInfo, boolean hasData) {
  110 + public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version,
  111 + String fileName, String contentType, String checksumAlgorithm, String checksum, Long dataSize,
  112 + Object additionalInfo, boolean hasData) {
86 113 this.id = id;
87 114 this.createdTime = createdTime;
88 115 this.tenantId = tenantId;
89 116 this.title = title;
90 117 this.version = version;
  118 + this.fileName = fileName;
  119 + this.contentType = contentType;
  120 + this.checksumAlgorithm = checksumAlgorithm;
  121 + this.checksum = checksum;
  122 + this.dataSize = dataSize;
91 123 this.hasData = hasData;
92 124 this.additionalInfo = JacksonUtil.convertValue(additionalInfo, JsonNode.class);
93 125 }
... ... @@ -109,6 +141,11 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
109 141 firmware.setTenantId(new TenantId(tenantId));
110 142 firmware.setTitle(title);
111 143 firmware.setVersion(version);
  144 + firmware.setFileName(fileName);
  145 + firmware.setContentType(contentType);
  146 + firmware.setChecksumAlgorithm(checksumAlgorithm);
  147 + firmware.setChecksum(checksum);
  148 + firmware.setDataSize(dataSize);
112 149 firmware.setAdditionalInfo(additionalInfo);
113 150 firmware.setHasData(hasData);
114 151 return firmware;
... ...
... ... @@ -94,6 +94,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
94 94 @Param("textSearch") String textSearch,
95 95 Pageable pageable);
96 96
  97 + @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " +
  98 + "AND d.type = :type " +
  99 + "AND d.firmwareId = null " +
  100 + "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
  101 + Page<DeviceEntity> findByTenantIdAndTypeAndFirmwareIdIsNull(@Param("tenantId") UUID tenantId,
  102 + @Param("type") String type,
  103 + @Param("textSearch") String textSearch,
  104 + Pageable pageable);
  105 +
97 106 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
98 107 "FROM DeviceEntity d " +
99 108 "LEFT JOIN CustomerEntity c on c.id = d.customerId " +
... ...
... ... @@ -150,6 +150,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
150 150 }
151 151
152 152 @Override
  153 + public PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink) {
  154 + return DaoUtil.toPageData(
  155 + deviceRepository.findByTenantIdAndTypeAndFirmwareIdIsNull(
  156 + tenantId,
  157 + type,
  158 + Objects.toString(pageLink.getTextSearch(), ""),
  159 + DaoUtil.toPageable(pageLink)));
  160 + }
  161 +
  162 + @Override
153 163 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) {
154 164 return DaoUtil.toPageData(
155 165 deviceRepository.findDeviceInfosByTenantIdAndType(
... ...
... ... @@ -25,14 +25,14 @@ import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity;
25 25 import java.util.UUID;
26 26
27 27 public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> {
28   - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
  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 29 "f.tenantId = :tenantId " +
30 30 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
31 31 Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
32 32 @Param("searchText") String searchText,
33 33 Pageable pageable);
34 34
35   - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
  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 36 "f.tenantId = :tenantId " +
37 37 "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" +
38 38 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
... ... @@ -41,6 +41,6 @@ public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntit
41 41 @Param("searchText") String searchText,
42 42 Pageable pageable);
43 43
44   - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id")
  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")
45 45 FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id);
46 46 }
... ...
... ... @@ -169,6 +169,7 @@ CREATE TABLE IF NOT EXISTS firmware (
169 169 checksum_algorithm varchar(32),
170 170 checksum varchar(1020),
171 171 data binary,
  172 + data_size bigint,
172 173 additional_info varchar,
173 174 search_text varchar(255),
174 175 CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
... ...
... ... @@ -187,6 +187,7 @@ CREATE TABLE IF NOT EXISTS firmware (
187 187 checksum_algorithm varchar(32),
188 188 checksum varchar(1020),
189 189 data bytea,
  190 + data_size bigint,
190 191 additional_info varchar,
191 192 search_text varchar(255),
192 193 CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
... ...
... ... @@ -28,6 +28,7 @@ import java.util.Arrays;
28 28 "org.thingsboard.server.dao.service.attributes.sql.*SqlTest",
29 29 "org.thingsboard.server.dao.service.event.sql.*SqlTest",
30 30 "org.thingsboard.server.dao.service.timeseries.sql.*SqlTest"
  31 +
31 32 })
32 33 public class SqlDaoServiceTestSuite {
33 34
... ...
... ... @@ -122,6 +122,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
122 122
123 123 firmwareService.saveFirmware(firmware);
124 124
  125 + savedFirmwareInfo = firmwareService.findFirmwareInfoById(tenantId, savedFirmwareInfo.getId());
125 126 savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode());
126 127 firmwareService.saveFirmwareInfo(savedFirmwareInfo);
127 128
... ... @@ -421,6 +422,11 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
421 422 firmwareInfo.setTenantId(tenantId);
422 423 firmwareInfo.setTitle(TITLE);
423 424 firmwareInfo.setVersion(VERSION + i);
  425 + firmwareInfo.setFileName(FILE_NAME);
  426 + firmwareInfo.setContentType(CONTENT_TYPE);
  427 + firmwareInfo.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  428 + firmwareInfo.setChecksum(CHECKSUM);
  429 + firmwareInfo.setDataSize((long) DATA.array().length);
424 430 firmwares.add(firmwareService.saveFirmwareInfo(firmwareInfo));
425 431 }
426 432
... ... @@ -451,6 +457,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
451 457 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
452 458 firmware.setChecksum(CHECKSUM);
453 459 firmware.setData(DATA);
  460 + firmware.setDataSize((long) DATA.array().length);
454 461 firmwareService.saveFirmware(firmware);
455 462 f.setHasData(true);
456 463 });
... ...
... ... @@ -26,7 +26,7 @@ import java.util.Arrays;
26 26 @SpringBootConfiguration
27 27 @EnableAsync
28 28 @EnableScheduling
29   -@ComponentScan({"org.thingsboard.server.coap", "org.thingsboard.server.common", "org.thingsboard.server.coapserver", "org.thingsboard.server.transport.coap", "org.thingsboard.server.queue"})
  29 +@ComponentScan({"org.thingsboard.server.coap", "org.thingsboard.server.common", "org.thingsboard.server.coapserver", "org.thingsboard.server.transport.coap", "org.thingsboard.server.queue", "org.thingsboard.server.cache"})
30 30 public class ThingsboardCoapTransportApplication {
31 31
32 32 private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
... ...
... ... @@ -40,6 +40,49 @@ zk:
40 40 # Name of the directory in zookeeper 'filesystem'
41 41 zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}"
42 42
  43 +cache:
  44 + type: "${CACHE_TYPE:redis}"
  45 +
  46 +redis:
  47 + # standalone or cluster
  48 + connection:
  49 + type: "${REDIS_CONNECTION_TYPE:standalone}"
  50 + standalone:
  51 + host: "${REDIS_HOST:localhost}"
  52 + port: "${REDIS_PORT:6379}"
  53 + useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}"
  54 + # this value may be used only if you used not default ClientConfig
  55 + clientName: "${REDIS_CLIENT_NAME:standalone}"
  56 + # this value may be used only if you used not default ClientConfig
  57 + connectTimeout: "${REDIS_CLIENT_CONNECT_TIMEOUT:30000}"
  58 + # this value may be used only if you used not default ClientConfig
  59 + readTimeout: "${REDIS_CLIENT_READ_TIMEOUT:60000}"
  60 + # this value may be used only if you used not default ClientConfig
  61 + usePoolConfig: "${REDIS_CLIENT_USE_POOL_CONFIG:false}"
  62 + cluster:
  63 + # Comma-separated list of "host:port" pairs to bootstrap from.
  64 + nodes: "${REDIS_NODES:}"
  65 + # Maximum number of redirects to follow when executing commands across the cluster.
  66 + max-redirects: "${REDIS_MAX_REDIRECTS:12}"
  67 + useDefaultPoolConfig: "${REDIS_USE_DEFAULT_POOL_CONFIG:true}"
  68 + # db index
  69 + db: "${REDIS_DB:0}"
  70 + # db password
  71 + password: "${REDIS_PASSWORD:}"
  72 + # pool config
  73 + pool_config:
  74 + maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}"
  75 + maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}"
  76 + minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}"
  77 + testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}"
  78 + testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}"
  79 + testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}"
  80 + minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}"
  81 + evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}"
  82 + maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}"
  83 + numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}"
  84 + blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}"
  85 +
43 86 # COAP server parameters
44 87 transport:
45 88 coap:
... ...
... ... @@ -24,7 +24,7 @@ import java.util.Arrays;
24 24
25 25 @SpringBootApplication
26 26 @EnableAsync
27   -@ComponentScan({"org.thingsboard.server.http", "org.thingsboard.server.common", "org.thingsboard.server.transport.http", "org.thingsboard.server.queue"})
  27 +@ComponentScan({"org.thingsboard.server.http", "org.thingsboard.server.common", "org.thingsboard.server.transport.http", "org.thingsboard.server.queue", "org.thingsboard.server.cache"})
28 28 public class ThingsboardHttpTransportApplication {
29 29
30 30 private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
... ...
... ... @@ -35,6 +35,49 @@ zk:
35 35 # Name of the directory in zookeeper 'filesystem'
36 36 zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}"
37 37
  38 +cache:
  39 + type: "${CACHE_TYPE:redis}"
  40 +
  41 +redis:
  42 + # standalone or cluster
  43 + connection:
  44 + type: "${REDIS_CONNECTION_TYPE:standalone}"
  45 + standalone:
  46 + host: "${REDIS_HOST:localhost}"
  47 + port: "${REDIS_PORT:6379}"
  48 + useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}"
  49 + # this value may be used only if you used not default ClientConfig
  50 + clientName: "${REDIS_CLIENT_NAME:standalone}"
  51 + # this value may be used only if you used not default ClientConfig
  52 + connectTimeout: "${REDIS_CLIENT_CONNECT_TIMEOUT:30000}"
  53 + # this value may be used only if you used not default ClientConfig
  54 + readTimeout: "${REDIS_CLIENT_READ_TIMEOUT:60000}"
  55 + # this value may be used only if you used not default ClientConfig
  56 + usePoolConfig: "${REDIS_CLIENT_USE_POOL_CONFIG:false}"
  57 + cluster:
  58 + # Comma-separated list of "host:port" pairs to bootstrap from.
  59 + nodes: "${REDIS_NODES:}"
  60 + # Maximum number of redirects to follow when executing commands across the cluster.
  61 + max-redirects: "${REDIS_MAX_REDIRECTS:12}"
  62 + useDefaultPoolConfig: "${REDIS_USE_DEFAULT_POOL_CONFIG:true}"
  63 + # db index
  64 + db: "${REDIS_DB:0}"
  65 + # db password
  66 + password: "${REDIS_PASSWORD:}"
  67 + # pool config
  68 + pool_config:
  69 + maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}"
  70 + maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}"
  71 + minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}"
  72 + testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}"
  73 + testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}"
  74 + testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}"
  75 + minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}"
  76 + evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}"
  77 + maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}"
  78 + numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}"
  79 + blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}"
  80 +
38 81 # HTTP server parameters
39 82 transport:
40 83 http:
... ...
... ... @@ -41,41 +41,7 @@ zk:
41 41 zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}"
42 42
43 43 cache:
44   - # caffeine or redis
45   - type: "${CACHE_TYPE:caffeine}"
46   -
47   -caffeine:
48   - specs:
49   - relations:
50   - timeToLiveInMinutes: 1440
51   - maxSize: 0
52   - deviceCredentials:
53   - timeToLiveInMinutes: 1440
54   - maxSize: 0
55   - devices:
56   - timeToLiveInMinutes: 1440
57   - maxSize: 0
58   - sessions:
59   - timeToLiveInMinutes: 1440
60   - maxSize: 0
61   - assets:
62   - timeToLiveInMinutes: 1440
63   - maxSize: 0
64   - entityViews:
65   - timeToLiveInMinutes: 1440
66   - maxSize: 0
67   - claimDevices:
68   - timeToLiveInMinutes: 1
69   - maxSize: 0
70   - securitySettings:
71   - timeToLiveInMinutes: 1440
72   - maxSize: 0
73   - tenantProfiles:
74   - timeToLiveInMinutes: 1440
75   - maxSize: 0
76   - deviceProfiles:
77   - timeToLiveInMinutes: 1440
78   - maxSize: 0
  44 + type: "${CACHE_TYPE:redis}"
79 45
80 46 redis:
81 47 # standalone or cluster
... ...
... ... @@ -26,7 +26,7 @@ import java.util.Arrays;
26 26 @SpringBootConfiguration
27 27 @EnableAsync
28 28 @EnableScheduling
29   -@ComponentScan({"org.thingsboard.server.mqtt", "org.thingsboard.server.common", "org.thingsboard.server.transport.mqtt", "org.thingsboard.server.queue"})
  29 +@ComponentScan({"org.thingsboard.server.mqtt", "org.thingsboard.server.common", "org.thingsboard.server.transport.mqtt", "org.thingsboard.server.queue", "org.thingsboard.server.cache"})
30 30 public class ThingsboardMqttTransportApplication {
31 31
32 32 private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
... ...
... ... @@ -40,6 +40,49 @@ zk:
40 40 # Name of the directory in zookeeper 'filesystem'
41 41 zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}"
42 42
  43 +cache:
  44 + type: "${CACHE_TYPE:redis}"
  45 +
  46 +redis:
  47 + # standalone or cluster
  48 + connection:
  49 + type: "${REDIS_CONNECTION_TYPE:standalone}"
  50 + standalone:
  51 + host: "${REDIS_HOST:localhost}"
  52 + port: "${REDIS_PORT:6379}"
  53 + useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}"
  54 + # this value may be used only if you used not default ClientConfig
  55 + clientName: "${REDIS_CLIENT_NAME:standalone}"
  56 + # this value may be used only if you used not default ClientConfig
  57 + connectTimeout: "${REDIS_CLIENT_CONNECT_TIMEOUT:30000}"
  58 + # this value may be used only if you used not default ClientConfig
  59 + readTimeout: "${REDIS_CLIENT_READ_TIMEOUT:60000}"
  60 + # this value may be used only if you used not default ClientConfig
  61 + usePoolConfig: "${REDIS_CLIENT_USE_POOL_CONFIG:false}"
  62 + cluster:
  63 + # Comma-separated list of "host:port" pairs to bootstrap from.
  64 + nodes: "${REDIS_NODES:}"
  65 + # Maximum number of redirects to follow when executing commands across the cluster.
  66 + max-redirects: "${REDIS_MAX_REDIRECTS:12}"
  67 + useDefaultPoolConfig: "${REDIS_USE_DEFAULT_POOL_CONFIG:true}"
  68 + # db index
  69 + db: "${REDIS_DB:0}"
  70 + # db password
  71 + password: "${REDIS_PASSWORD:}"
  72 + # pool config
  73 + pool_config:
  74 + maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}"
  75 + maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}"
  76 + minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}"
  77 + testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}"
  78 + testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}"
  79 + testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}"
  80 + minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}"
  81 + evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}"
  82 + maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}"
  83 + numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}"
  84 + blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}"
  85 +
43 86 # MQTT server parameters
44 87 transport:
45 88 mqtt:
... ...