Commit c350fb7accc7c57f39d37a7683cf4c1c3e170e93

Authored by Andrew Shvayka
Committed by GitHub
2 parents 18f27b18 820fc4a9

Merge pull request #4465 from thingsboard/feature/firmware

Feature/firmware
Showing 93 changed files with 3134 additions and 145 deletions

Too many changes to show.

To preserve performance only 93 of 138 files are displayed.

@@ -31,7 +31,7 @@ CREATE TABLE IF NOT EXISTS edge ( @@ -31,7 +31,7 @@ CREATE TABLE IF NOT EXISTS edge (
31 tenant_id uuid, 31 tenant_id uuid,
32 CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), 32 CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
33 CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key) 33 CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key)
34 -); 34 + );
35 35
36 CREATE TABLE IF NOT EXISTS edge_event ( 36 CREATE TABLE IF NOT EXISTS edge_event (
37 id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY, 37 id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY,
@@ -44,4 +44,57 @@ CREATE TABLE IF NOT EXISTS edge_event ( @@ -44,4 +44,57 @@ CREATE TABLE IF NOT EXISTS edge_event (
44 body varchar(10000000), 44 body varchar(10000000),
45 tenant_id uuid, 45 tenant_id uuid,
46 ts bigint NOT NULL 46 ts bigint NOT NULL
  47 + );
  48 +
  49 +CREATE TABLE IF NOT EXISTS resource (
  50 + id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY,
  51 + created_time bigint NOT NULL,
  52 + tenant_id uuid NOT NULL,
  53 + title varchar(255) NOT NULL,
  54 + resource_type varchar(32) NOT NULL,
  55 + resource_key varchar(255) NOT NULL,
  56 + search_text varchar(255),
  57 + file_name varchar(255) NOT NULL,
  58 + data varchar,
  59 + CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key)
  60 +);
  61 +
  62 +CREATE TABLE IF NOT EXISTS firmware (
  63 + id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
  64 + created_time bigint NOT NULL,
  65 + tenant_id uuid NOT NULL,
  66 + title varchar(255) NOT NULL,
  67 + version varchar(255) NOT NULL,
  68 + file_name varchar(255),
  69 + content_type varchar(255),
  70 + checksum_algorithm varchar(32),
  71 + checksum varchar(1020),
  72 + data bytea,
  73 + data_size bigint,
  74 + additional_info varchar,
  75 + search_text varchar(255),
  76 + CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
47 ); 77 );
  78 +
  79 +ALTER TABLE device_profile
  80 + ADD COLUMN IF NOT EXISTS firmware_id uuid;
  81 +
  82 +ALTER TABLE device
  83 + ADD COLUMN IF NOT EXISTS firmware_id uuid;
  84 +
  85 +DO $$
  86 + BEGIN
  87 + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN
  88 + ALTER TABLE device_profile
  89 + ADD CONSTRAINT fk_firmware_device_profile
  90 + FOREIGN KEY (firmware_id) REFERENCES firmware(id);
  91 + END IF;
  92 +
  93 + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN
  94 + ALTER TABLE device
  95 + ADD CONSTRAINT fk_firmware_device
  96 + FOREIGN KEY (firmware_id) REFERENCES firmware(id);
  97 + END IF;
  98 + END;
  99 +$$;
  100 +
@@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j; @@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j;
24 import org.apache.commons.lang3.StringUtils; 24 import org.apache.commons.lang3.StringUtils;
25 import org.springframework.beans.factory.annotation.Autowired; 25 import org.springframework.beans.factory.annotation.Autowired;
26 import org.springframework.beans.factory.annotation.Value; 26 import org.springframework.beans.factory.annotation.Value;
  27 +import org.springframework.http.MediaType;
27 import org.springframework.security.core.Authentication; 28 import org.springframework.security.core.Authentication;
28 import org.springframework.security.core.context.SecurityContextHolder; 29 import org.springframework.security.core.context.SecurityContextHolder;
29 import org.springframework.web.bind.annotation.ExceptionHandler; 30 import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -38,6 +39,8 @@ import org.thingsboard.server.common.data.EdgeUtils; @@ -38,6 +39,8 @@ import org.thingsboard.server.common.data.EdgeUtils;
38 import org.thingsboard.server.common.data.EntityType; 39 import org.thingsboard.server.common.data.EntityType;
39 import org.thingsboard.server.common.data.EntityView; 40 import org.thingsboard.server.common.data.EntityView;
40 import org.thingsboard.server.common.data.EntityViewInfo; 41 import org.thingsboard.server.common.data.EntityViewInfo;
  42 +import org.thingsboard.server.common.data.Firmware;
  43 +import org.thingsboard.server.common.data.FirmwareInfo;
41 import org.thingsboard.server.common.data.HasName; 44 import org.thingsboard.server.common.data.HasName;
42 import org.thingsboard.server.common.data.HasTenantId; 45 import org.thingsboard.server.common.data.HasTenantId;
43 import org.thingsboard.server.common.data.TbResourceInfo; 46 import org.thingsboard.server.common.data.TbResourceInfo;
@@ -67,6 +70,7 @@ import org.thingsboard.server.common.data.id.EdgeId; @@ -67,6 +70,7 @@ import org.thingsboard.server.common.data.id.EdgeId;
67 import org.thingsboard.server.common.data.id.EntityId; 70 import org.thingsboard.server.common.data.id.EntityId;
68 import org.thingsboard.server.common.data.id.EntityIdFactory; 71 import org.thingsboard.server.common.data.id.EntityIdFactory;
69 import org.thingsboard.server.common.data.id.EntityViewId; 72 import org.thingsboard.server.common.data.id.EntityViewId;
  73 +import org.thingsboard.server.common.data.id.FirmwareId;
70 import org.thingsboard.server.common.data.id.TbResourceId; 74 import org.thingsboard.server.common.data.id.TbResourceId;
71 import org.thingsboard.server.common.data.id.RuleChainId; 75 import org.thingsboard.server.common.data.id.RuleChainId;
72 import org.thingsboard.server.common.data.id.RuleNodeId; 76 import org.thingsboard.server.common.data.id.RuleNodeId;
@@ -106,6 +110,7 @@ import org.thingsboard.server.dao.edge.EdgeService; @@ -106,6 +110,7 @@ import org.thingsboard.server.dao.edge.EdgeService;
106 import org.thingsboard.server.dao.entityview.EntityViewService; 110 import org.thingsboard.server.dao.entityview.EntityViewService;
107 import org.thingsboard.server.dao.exception.DataValidationException; 111 import org.thingsboard.server.dao.exception.DataValidationException;
108 import org.thingsboard.server.dao.exception.IncorrectParameterException; 112 import org.thingsboard.server.dao.exception.IncorrectParameterException;
  113 +import org.thingsboard.server.dao.firmware.FirmwareService;
109 import org.thingsboard.server.dao.model.ModelConstants; 114 import org.thingsboard.server.dao.model.ModelConstants;
110 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; 115 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
111 import org.thingsboard.server.dao.oauth2.OAuth2Service; 116 import org.thingsboard.server.dao.oauth2.OAuth2Service;
@@ -123,6 +128,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; @@ -123,6 +128,7 @@ import org.thingsboard.server.queue.discovery.PartitionService;
123 import org.thingsboard.server.queue.provider.TbQueueProducerProvider; 128 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
124 import org.thingsboard.server.queue.util.TbCoreComponent; 129 import org.thingsboard.server.queue.util.TbCoreComponent;
125 import org.thingsboard.server.service.component.ComponentDiscoveryService; 130 import org.thingsboard.server.service.component.ComponentDiscoveryService;
  131 +import org.thingsboard.server.service.firmware.FirmwareStateService;
126 import org.thingsboard.server.service.edge.EdgeNotificationService; 132 import org.thingsboard.server.service.edge.EdgeNotificationService;
127 import org.thingsboard.server.service.edge.rpc.EdgeGrpcService; 133 import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;
128 import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; 134 import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
@@ -246,6 +252,12 @@ public abstract class BaseController { @@ -246,6 +252,12 @@ public abstract class BaseController {
246 protected TbResourceService resourceService; 252 protected TbResourceService resourceService;
247 253
248 @Autowired 254 @Autowired
  255 + protected FirmwareService firmwareService;
  256 +
  257 + @Autowired
  258 + protected FirmwareStateService firmwareStateService;
  259 +
  260 + @Autowired
249 protected TbQueueProducerProvider producerProvider; 261 protected TbQueueProducerProvider producerProvider;
250 262
251 @Autowired 263 @Autowired
@@ -501,6 +513,9 @@ public abstract class BaseController { @@ -501,6 +513,9 @@ public abstract class BaseController {
501 case TB_RESOURCE: 513 case TB_RESOURCE:
502 checkResourceId(new TbResourceId(entityId.getId()), operation); 514 checkResourceId(new TbResourceId(entityId.getId()), operation);
503 return; 515 return;
  516 + case FIRMWARE:
  517 + checkFirmwareId(new FirmwareId(entityId.getId()), operation);
  518 + return;
504 default: 519 default:
505 throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); 520 throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
506 } 521 }
@@ -756,6 +771,30 @@ public abstract class BaseController { @@ -756,6 +771,30 @@ public abstract class BaseController {
756 } 771 }
757 } 772 }
758 773
  774 + Firmware checkFirmwareId(FirmwareId firmwareId, Operation operation) throws ThingsboardException {
  775 + try {
  776 + validateId(firmwareId, "Incorrect firmwareId " + firmwareId);
  777 + Firmware firmware = firmwareService.findFirmwareById(getCurrentUser().getTenantId(), firmwareId);
  778 + checkNotNull(firmware);
  779 + accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmware);
  780 + return firmware;
  781 + } catch (Exception e) {
  782 + throw handleException(e, false);
  783 + }
  784 + }
  785 +
  786 + FirmwareInfo checkFirmwareInfoId(FirmwareId firmwareId, Operation operation) throws ThingsboardException {
  787 + try {
  788 + validateId(firmwareId, "Incorrect firmwareId " + firmwareId);
  789 + FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(getCurrentUser().getTenantId(), firmwareId);
  790 + checkNotNull(firmwareInfo);
  791 + accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmwareInfo);
  792 + return firmwareInfo;
  793 + } catch (Exception e) {
  794 + throw handleException(e, false);
  795 + }
  796 + }
  797 +
759 @SuppressWarnings("unchecked") 798 @SuppressWarnings("unchecked")
760 protected <I extends EntityId> I emptyId(EntityType entityType) { 799 protected <I extends EntityId> I emptyId(EntityType entityType) {
761 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); 800 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
@@ -1082,4 +1121,11 @@ public abstract class BaseController { @@ -1082,4 +1121,11 @@ public abstract class BaseController {
1082 } 1121 }
1083 } 1122 }
1084 1123
  1124 + protected MediaType parseMediaType(String contentType) {
  1125 + try {
  1126 + return MediaType.parseMediaType(contentType);
  1127 + } catch (Exception e) {
  1128 + return MediaType.APPLICATION_OCTET_STREAM;
  1129 + }
  1130 + }
1085 } 1131 }
@@ -75,6 +75,7 @@ import javax.annotation.Nullable; @@ -75,6 +75,7 @@ import javax.annotation.Nullable;
75 import java.io.IOException; 75 import java.io.IOException;
76 import java.util.ArrayList; 76 import java.util.ArrayList;
77 import java.util.List; 77 import java.util.List;
  78 +import java.util.Objects;
78 import java.util.stream.Collectors; 79 import java.util.stream.Collectors;
79 80
80 import static org.thingsboard.server.controller.EdgeController.EDGE_ID; 81 import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
@@ -124,15 +125,22 @@ public class DeviceController extends BaseController { @@ -124,15 +125,22 @@ public class DeviceController extends BaseController {
124 125
125 checkEntity(device.getId(), device, Resource.DEVICE); 126 checkEntity(device.getId(), device, Resource.DEVICE);
126 127
  128 + boolean created = device.getId() == null;
  129 + Device oldDevice;
  130 + if (!created) {
  131 + oldDevice = deviceService.findDeviceById(getTenantId(), device.getId());
  132 + } else {
  133 + oldDevice = null;
  134 + }
  135 +
127 Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); 136 Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken));
128 137
129 tbClusterService.onDeviceChange(savedDevice, null); 138 tbClusterService.onDeviceChange(savedDevice, null);
130 tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), 139 tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(),
131 savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); 140 savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
132 - tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(),  
133 - device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); 141 + tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
134 142
135 - if (device.getId() != null) { 143 + if (!created) {
136 sendEntityNotificationMsg(savedDevice.getTenantId(), savedDevice.getId(), EdgeEventActionType.UPDATED); 144 sendEntityNotificationMsg(savedDevice.getTenantId(), savedDevice.getId(), EdgeEventActionType.UPDATED);
137 } 145 }
138 146
@@ -145,12 +153,17 @@ public class DeviceController extends BaseController { @@ -145,12 +153,17 @@ public class DeviceController extends BaseController {
145 } else { 153 } else {
146 deviceStateService.onDeviceUpdated(savedDevice); 154 deviceStateService.onDeviceUpdated(savedDevice);
147 } 155 }
  156 +
  157 + firmwareStateService.update(savedDevice, oldDevice);
  158 +
148 return savedDevice; 159 return savedDevice;
149 - } catch (Exception e) { 160 + } catch (
  161 + Exception e) {
150 logEntityAction(emptyId(EntityType.DEVICE), device, 162 logEntityAction(emptyId(EntityType.DEVICE), device,
151 null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); 163 null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
152 throw handleException(e); 164 throw handleException(e);
153 } 165 }
  166 +
154 } 167 }
155 168
156 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 169 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@@ -44,6 +44,7 @@ import org.thingsboard.server.service.security.permission.Operation; @@ -44,6 +44,7 @@ import org.thingsboard.server.service.security.permission.Operation;
44 import org.thingsboard.server.service.security.permission.Resource; 44 import org.thingsboard.server.service.security.permission.Resource;
45 45
46 import java.util.List; 46 import java.util.List;
  47 +import java.util.Objects;
47 import java.util.UUID; 48 import java.util.UUID;
48 49
49 @RestController 50 @RestController
@@ -144,6 +145,15 @@ public class DeviceProfileController extends BaseController { @@ -144,6 +145,15 @@ public class DeviceProfileController extends BaseController {
144 145
145 checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); 146 checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
146 147
  148 + boolean isFirmwareChanged = false;
  149 +
  150 + if (!created) {
  151 + DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId());
  152 + if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
  153 + isFirmwareChanged = true;
  154 + }
  155 + }
  156 +
147 DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); 157 DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
148 158
149 tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); 159 tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
@@ -154,9 +164,11 @@ public class DeviceProfileController extends BaseController { @@ -154,9 +164,11 @@ public class DeviceProfileController extends BaseController {
154 null, 164 null,
155 created ? ActionType.ADDED : ActionType.UPDATED, null); 165 created ? ActionType.ADDED : ActionType.UPDATED, null);
156 166
  167 + if (isFirmwareChanged) {
  168 + firmwareStateService.update(savedDeviceProfile);
  169 + }
157 sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(), 170 sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
158 deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); 171 deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
159 -  
160 return savedDeviceProfile; 172 return savedDeviceProfile;
161 } catch (Exception e) { 173 } catch (Exception e) {
162 logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile, 174 logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile,
  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.controller;
  17 +
  18 +import com.google.common.hash.Hashing;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.apache.commons.lang3.StringUtils;
  21 +import org.springframework.core.io.ByteArrayResource;
  22 +import org.springframework.http.HttpHeaders;
  23 +import org.springframework.http.ResponseEntity;
  24 +import org.springframework.security.access.prepost.PreAuthorize;
  25 +import org.springframework.web.bind.annotation.PathVariable;
  26 +import org.springframework.web.bind.annotation.RequestBody;
  27 +import org.springframework.web.bind.annotation.RequestMapping;
  28 +import org.springframework.web.bind.annotation.RequestMethod;
  29 +import org.springframework.web.bind.annotation.RequestParam;
  30 +import org.springframework.web.bind.annotation.ResponseBody;
  31 +import org.springframework.web.bind.annotation.RestController;
  32 +import org.springframework.web.multipart.MultipartFile;
  33 +import org.thingsboard.server.common.data.Firmware;
  34 +import org.thingsboard.server.common.data.FirmwareInfo;
  35 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  36 +import org.thingsboard.server.common.data.id.FirmwareId;
  37 +import org.thingsboard.server.common.data.page.PageData;
  38 +import org.thingsboard.server.common.data.page.PageLink;
  39 +import org.thingsboard.server.queue.util.TbCoreComponent;
  40 +import org.thingsboard.server.service.security.permission.Operation;
  41 +import org.thingsboard.server.service.security.permission.Resource;
  42 +
  43 +import java.nio.ByteBuffer;
  44 +
  45 +@Slf4j
  46 +@RestController
  47 +@TbCoreComponent
  48 +@RequestMapping("/api")
  49 +public class FirmwareController extends BaseController {
  50 +
  51 + public static final String FIRMWARE_ID = "firmwareId";
  52 +
  53 + @PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')")
  54 + @RequestMapping(value = "/firmware/{firmwareId}/download", method = RequestMethod.GET)
  55 + @ResponseBody
  56 + public ResponseEntity<org.springframework.core.io.Resource> downloadFirmware(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException {
  57 + checkParameter(FIRMWARE_ID, strFirmwareId);
  58 + try {
  59 + FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
  60 + Firmware firmware = checkFirmwareId(firmwareId, Operation.READ);
  61 +
  62 + ByteArrayResource resource = new ByteArrayResource(firmware.getData().array());
  63 + return ResponseEntity.ok()
  64 + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmware.getFileName())
  65 + .header("x-filename", firmware.getFileName())
  66 + .contentLength(resource.contentLength())
  67 + .contentType(parseMediaType(firmware.getContentType()))
  68 + .body(resource);
  69 + } catch (Exception e) {
  70 + throw handleException(e);
  71 + }
  72 + }
  73 +
  74 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  75 + @RequestMapping(value = "/firmware/info/{firmwareId}", method = RequestMethod.GET)
  76 + @ResponseBody
  77 + public FirmwareInfo getFirmwareInfoById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException {
  78 + checkParameter(FIRMWARE_ID, strFirmwareId);
  79 + try {
  80 + FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
  81 + return checkFirmwareInfoId(firmwareId, Operation.READ);
  82 + } catch (Exception e) {
  83 + throw handleException(e);
  84 + }
  85 + }
  86 +
  87 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  88 + @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.GET)
  89 + @ResponseBody
  90 + public Firmware getFirmwareById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException {
  91 + checkParameter(FIRMWARE_ID, strFirmwareId);
  92 + try {
  93 + FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
  94 + return checkFirmwareId(firmwareId, Operation.READ);
  95 + } catch (Exception e) {
  96 + throw handleException(e);
  97 + }
  98 + }
  99 +
  100 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  101 + @RequestMapping(value = "/firmware", method = RequestMethod.POST)
  102 + @ResponseBody
  103 + public FirmwareInfo saveFirmwareInfo(@RequestBody FirmwareInfo firmwareInfo) throws ThingsboardException {
  104 + firmwareInfo.setTenantId(getTenantId());
  105 + checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE);
  106 + try {
  107 + return firmwareService.saveFirmwareInfo(firmwareInfo);
  108 + } catch (Exception e) {
  109 + throw handleException(e);
  110 + }
  111 + }
  112 +
  113 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  114 + @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.POST)
  115 + @ResponseBody
  116 + public Firmware saveFirmwareData(@PathVariable(FIRMWARE_ID) String strFirmwareId,
  117 + @RequestParam(required = false) String checksum,
  118 + @RequestParam(required = false) String checksumAlgorithm,
  119 + @RequestBody MultipartFile file) throws ThingsboardException {
  120 + checkParameter(FIRMWARE_ID, strFirmwareId);
  121 + try {
  122 + FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
  123 + FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.READ);
  124 +
  125 + Firmware firmware = new Firmware(firmwareId);
  126 + firmware.setCreatedTime(info.getCreatedTime());
  127 + firmware.setTenantId(getTenantId());
  128 + firmware.setTitle(info.getTitle());
  129 + firmware.setVersion(info.getVersion());
  130 + firmware.setAdditionalInfo(info.getAdditionalInfo());
  131 +
  132 + byte[] data = file.getBytes();
  133 + if (StringUtils.isEmpty(checksumAlgorithm)) {
  134 + checksumAlgorithm = "sha256";
  135 + checksum = Hashing.sha256().hashBytes(data).toString();
  136 + }
  137 +
  138 + firmware.setChecksumAlgorithm(checksumAlgorithm);
  139 + firmware.setChecksum(checksum);
  140 + firmware.setFileName(file.getOriginalFilename());
  141 + firmware.setContentType(file.getContentType());
  142 + firmware.setData(ByteBuffer.wrap(data));
  143 + firmware.setDataSize((long) data.length);
  144 + return firmwareService.saveFirmware(firmware);
  145 + } catch (Exception e) {
  146 + throw handleException(e);
  147 + }
  148 + }
  149 +
  150 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  151 + @RequestMapping(value = "/firmwares", method = RequestMethod.GET)
  152 + @ResponseBody
  153 + public PageData<FirmwareInfo> getFirmwares(@RequestParam int pageSize,
  154 + @RequestParam int page,
  155 + @RequestParam(required = false) String textSearch,
  156 + @RequestParam(required = false) String sortProperty,
  157 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  158 + try {
  159 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  160 + return checkNotNull(firmwareService.findTenantFirmwaresByTenantId(getTenantId(), pageLink));
  161 + } catch (Exception e) {
  162 + throw handleException(e);
  163 + }
  164 + }
  165 +
  166 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  167 + @RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET)
  168 + @ResponseBody
  169 + public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData,
  170 + @RequestParam int pageSize,
  171 + @RequestParam int page,
  172 + @RequestParam(required = false) String textSearch,
  173 + @RequestParam(required = false) String sortProperty,
  174 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  175 + try {
  176 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  177 + return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndHasData(getTenantId(), hasData, pageLink));
  178 + } catch (Exception e) {
  179 + throw handleException(e);
  180 + }
  181 + }
  182 +
  183 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  184 + @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.DELETE)
  185 + @ResponseBody
  186 + public void deleteResource(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException {
  187 + checkParameter(FIRMWARE_ID, strFirmwareId);
  188 + try {
  189 + FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
  190 + checkFirmwareInfoId(firmwareId, Operation.DELETE);
  191 + firmwareService.deleteFirmware(getTenantId(), firmwareId);
  192 + } catch (Exception e) {
  193 + throw handleException(e);
  194 + }
  195 + }
  196 +
  197 +}
@@ -166,7 +166,7 @@ public class TbResourceController extends BaseController { @@ -166,7 +166,7 @@ public class TbResourceController extends BaseController {
166 @RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.DELETE) 166 @RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.DELETE)
167 @ResponseBody 167 @ResponseBody
168 public void deleteResource(@PathVariable("resourceId") String strResourceId) throws ThingsboardException { 168 public void deleteResource(@PathVariable("resourceId") String strResourceId) throws ThingsboardException {
169 - checkParameter("resourceId", strResourceId); 169 + checkParameter(RESOURCE_ID, strResourceId);
170 try { 170 try {
171 TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); 171 TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
172 TbResource tbResource = checkResourceId(resourceId, Operation.DELETE); 172 TbResource tbResource = checkResourceId(resourceId, Operation.DELETE);
@@ -232,7 +232,6 @@ public class ThingsboardInstallService { @@ -232,7 +232,6 @@ public class ThingsboardInstallService {
232 systemDataLoaderService.createAdminSettings(); 232 systemDataLoaderService.createAdminSettings();
233 systemDataLoaderService.loadSystemWidgets(); 233 systemDataLoaderService.loadSystemWidgets();
234 systemDataLoaderService.createOAuth2Templates(); 234 systemDataLoaderService.createOAuth2Templates();
235 - systemDataLoaderService.loadSystemLwm2mResources();  
236 // systemDataLoaderService.loadSystemPlugins(); 235 // systemDataLoaderService.loadSystemPlugins();
237 // systemDataLoaderService.loadSystemRules(); 236 // systemDataLoaderService.loadSystemRules();
238 237
  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 +import com.google.common.util.concurrent.FutureCallback;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.stereotype.Service;
  21 +import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
  22 +import org.thingsboard.server.common.data.DataConstants;
  23 +import org.thingsboard.server.common.data.Device;
  24 +import org.thingsboard.server.common.data.DeviceProfile;
  25 +import org.thingsboard.server.common.data.Firmware;
  26 +import org.thingsboard.server.common.data.FirmwareInfo;
  27 +import org.thingsboard.server.common.data.id.DeviceId;
  28 +import org.thingsboard.server.common.data.id.FirmwareId;
  29 +import org.thingsboard.server.common.data.id.TenantId;
  30 +import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  31 +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
  32 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  33 +import org.thingsboard.server.common.data.kv.LongDataEntry;
  34 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  35 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  36 +import org.thingsboard.server.common.data.page.PageData;
  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;
  40 +import org.thingsboard.server.dao.device.DeviceProfileService;
  41 +import org.thingsboard.server.dao.device.DeviceService;
  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;
  47 +import org.thingsboard.server.queue.util.TbCoreComponent;
  48 +
  49 +import javax.annotation.Nullable;
  50 +import java.util.ArrayList;
  51 +import java.util.Arrays;
  52 +import java.util.Collections;
  53 +import java.util.List;
  54 +import java.util.UUID;
  55 +import java.util.function.Consumer;
  56 +
  57 +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM;
  58 +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM;
  59 +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE;
  60 +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE;
  61 +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION;
  62 +
  63 +@Slf4j
  64 +@Service
  65 +@TbCoreComponent
  66 +public class DefaultFirmwareStateService implements FirmwareStateService {
  67 +
  68 + private final FirmwareService firmwareService;
  69 + private final DeviceService deviceService;
  70 + private final DeviceProfileService deviceProfileService;
  71 + private final RuleEngineTelemetryService telemetryService;
  72 + private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer;
  73 +
  74 + public DefaultFirmwareStateService(FirmwareService firmwareService,
  75 + DeviceService deviceService,
  76 + DeviceProfileService deviceProfileService,
  77 + RuleEngineTelemetryService telemetryService,
  78 + TbCoreQueueFactory coreQueueFactory) {
  79 + this.firmwareService = firmwareService;
  80 + this.deviceService = deviceService;
  81 + this.deviceProfileService = deviceProfileService;
  82 + this.telemetryService = telemetryService;
  83 + this.fwStateMsgProducer = coreQueueFactory.createToFirmwareStateServiceMsgProducer();
  84 + }
  85 +
  86 + @Override
  87 + public void update(Device device, Device oldDevice) {
  88 + FirmwareId newFirmwareId = device.getFirmwareId();
  89 + if (newFirmwareId == null) {
  90 + DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
  91 + newFirmwareId = newDeviceProfile.getFirmwareId();
  92 + }
  93 + if (oldDevice != null) {
  94 + if (newFirmwareId != null) {
  95 + FirmwareId oldFirmwareId = oldDevice.getFirmwareId();
  96 + if (oldFirmwareId == null) {
  97 + DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId());
  98 + oldFirmwareId = oldDeviceProfile.getFirmwareId();
  99 + }
  100 + if (!newFirmwareId.equals(oldFirmwareId)) {
  101 + // Device was updated and new firmware is different from previous firmware.
  102 + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis());
  103 + }
  104 + } else {
  105 + // Device was updated and new firmware is not set.
  106 + remove(device);
  107 + }
  108 + } else if (newFirmwareId != null) {
  109 + // Device was created and firmware is defined.
  110 + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis());
  111 + }
  112 + }
  113 +
  114 + @Override
  115 + public void update(DeviceProfile deviceProfile) {
  116 + TenantId tenantId = deviceProfile.getTenantId();
  117 +
  118 + Consumer<Device> updateConsumer;
  119 + if (deviceProfile.getFirmwareId() != null) {
  120 + long ts = System.currentTimeMillis();
  121 + updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts);
  122 + } else {
  123 + updateConsumer = this::remove;
  124 + }
  125 +
  126 + PageLink pageLink = new PageLink(100);
  127 + PageData<Device> pageData;
  128 + do {
  129 + pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pageLink);
  130 +
  131 + pageData.getData().forEach(updateConsumer);
  132 +
  133 + if (pageData.hasNext()) {
  134 + pageLink = pageLink.nextPageLink();
  135 + }
  136 + } while (pageData.hasNext());
  137 + }
  138 +
  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);
  186 +
  187 + List<TsKvEntry> telemetry = new ArrayList<>();
  188 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle())));
  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())));
  191 +
  192 + telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
  193 + @Override
  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) {
  215 + log.trace("[{}] Success save telemetry with target firmware for device!", deviceId);
  216 + }
  217 +
  218 + @Override
  219 + public void onFailure(Throwable t) {
  220 + log.error("[{}] Failed to save telemetry with target firmware for device!", deviceId, t);
  221 + }
  222 + });
  223 +
  224 + List<AttributeKvEntry> attributes = new ArrayList<>();
  225 +
  226 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle())));
  227 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion())));
  228 +
  229 + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, firmware.getDataSize())));
  230 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm())));
  231 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum())));
  232 + telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() {
  233 + @Override
  234 + public void onSuccess(@Nullable Void tmp) {
  235 + log.trace("[{}] Success save attributes with target firmware!", deviceId);
  236 + }
  237 +
  238 + @Override
  239 + public void onFailure(Throwable t) {
  240 + log.error("[{}] Failed to save attributes with target firmware!", deviceId, t);
  241 + }
  242 + });
  243 + }
  244 +
  245 + private void remove(Device device) {
  246 + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE,
  247 + Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM),
  248 + new FutureCallback<>() {
  249 + @Override
  250 + public void onSuccess(@Nullable Void tmp) {
  251 + log.trace("[{}] Success remove target firmware attributes!", device.getId());
  252 + }
  253 +
  254 + @Override
  255 + public void onFailure(Throwable t) {
  256 + log.error("[{}] Failed to remove target firmware attributes!", device.getId(), t);
  257 + }
  258 + });
  259 + }
  260 +}
  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 +import org.thingsboard.server.common.data.Device;
  19 +import org.thingsboard.server.common.data.DeviceProfile;
  20 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
  21 +
  22 +public interface FirmwareStateService {
  23 +
  24 + void update(Device device, Device oldDevice);
  25 +
  26 + void update(DeviceProfile deviceProfile);
  27 +
  28 + boolean process(ToFirmwareStateServiceMsg msg);
  29 +
  30 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.firmware;
  17 +
  18 +public enum FirmwareUpdateStatus {
  19 + QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
  20 +}
@@ -444,11 +444,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -444,11 +444,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
444 installScripts.loadSystemWidgets(); 444 installScripts.loadSystemWidgets();
445 } 445 }
446 446
447 - @Override  
448 - public void loadSystemLwm2mResources() throws Exception {  
449 - installScripts.loadSystemLwm2mResources();  
450 - }  
451 -  
452 private User createUser(Authority authority, 447 private User createUser(Authority authority,
453 TenantId tenantId, 448 TenantId tenantId,
454 CustomerId customerId, 449 CustomerId customerId,
@@ -22,8 +22,6 @@ import org.springframework.beans.factory.annotation.Value; @@ -22,8 +22,6 @@ import org.springframework.beans.factory.annotation.Value;
22 import org.springframework.stereotype.Component; 22 import org.springframework.stereotype.Component;
23 import org.springframework.util.StringUtils; 23 import org.springframework.util.StringUtils;
24 import org.thingsboard.server.common.data.Dashboard; 24 import org.thingsboard.server.common.data.Dashboard;
25 -import org.thingsboard.server.common.data.ResourceType;  
26 -import org.thingsboard.server.common.data.TbResource;  
27 import org.thingsboard.server.common.data.id.CustomerId; 25 import org.thingsboard.server.common.data.id.CustomerId;
28 import org.thingsboard.server.common.data.id.EntityId; 26 import org.thingsboard.server.common.data.id.EntityId;
29 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
@@ -33,7 +31,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; @@ -33,7 +31,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
33 import org.thingsboard.server.common.data.widget.WidgetTypeDetails; 31 import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
34 import org.thingsboard.server.common.data.widget.WidgetsBundle; 32 import org.thingsboard.server.common.data.widget.WidgetsBundle;
35 import org.thingsboard.server.dao.dashboard.DashboardService; 33 import org.thingsboard.server.dao.dashboard.DashboardService;
36 -import org.thingsboard.server.dao.exception.DataValidationException;  
37 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; 34 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
38 import org.thingsboard.server.dao.resource.ResourceService; 35 import org.thingsboard.server.dao.resource.ResourceService;
39 import org.thingsboard.server.dao.rule.RuleChainService; 36 import org.thingsboard.server.dao.rule.RuleChainService;
@@ -45,7 +42,6 @@ import java.nio.file.DirectoryStream; @@ -45,7 +42,6 @@ import java.nio.file.DirectoryStream;
45 import java.nio.file.Files; 42 import java.nio.file.Files;
46 import java.nio.file.Path; 43 import java.nio.file.Path;
47 import java.nio.file.Paths; 44 import java.nio.file.Paths;
48 -import java.util.Base64;  
49 import java.util.Optional; 45 import java.util.Optional;
50 46
51 import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper; 47 import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
@@ -210,29 +206,6 @@ public class InstallScripts { @@ -210,29 +206,6 @@ public class InstallScripts {
210 } 206 }
211 } 207 }
212 208
213 - public void loadSystemLwm2mResources() throws Exception {  
214 - Path modelsDir = Paths.get(getDataDir(), MODELS_DIR);  
215 - if (Files.isDirectory(modelsDir)) {  
216 - try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(modelsDir, path -> path.toString().endsWith(XML_EXT))) {  
217 - dirStream.forEach(  
218 - path -> {  
219 - try {  
220 - byte[] fileBytes = Files.readAllBytes(path);  
221 - TbResource resource = new TbResource();  
222 - resource.setFileName(path.getFileName().toString());  
223 - resource.setTenantId(TenantId.SYS_TENANT_ID);  
224 - resource.setResourceType(ResourceType.LWM2M_MODEL);  
225 - resource.setData(Base64.getEncoder().encodeToString(fileBytes));  
226 - resourceService.saveResource(resource);  
227 - } catch (Exception e) {  
228 - throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", path.toString()));  
229 - }  
230 - }  
231 - );  
232 - }  
233 - }  
234 - }  
235 -  
236 public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { 209 public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception {
237 Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR); 210 Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR);
238 try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { 211 try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) {
@@ -450,37 +450,19 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @@ -450,37 +450,19 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
450 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { 450 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
451 log.info("Updating schema ..."); 451 log.info("Updating schema ...");
452 try { 452 try {
453 - conn.createStatement().execute("CREATE TABLE IF NOT EXISTS resource ( " +  
454 - "id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY, " +  
455 - "created_time bigint NOT NULL, " +  
456 - "tenant_id uuid NOT NULL, " +  
457 - "title varchar(255) NOT NULL, " +  
458 - "resource_type varchar(32) NOT NULL, " +  
459 - "resource_key varchar(255) NOT NULL, " +  
460 - "search_text varchar(255), " +  
461 - "file_name varchar(255) NOT NULL, " +  
462 - "data varchar, " +  
463 - "CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key)" +  
464 - ");");  
465 -  
466 - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003000;");  
467 - installScripts.loadSystemLwm2mResources();  
468 -  
469 - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.2.2", SCHEMA_UPDATE_SQL);  
470 - loadSql(schemaUpdateFile, conn);  
471 - try {  
472 - conn.createStatement().execute("ALTER TABLE rule_chain ADD COLUMN type varchar(255) DEFAULT 'CORE'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script  
473 - } catch (Exception ignored) {}  
474 -  
475 - log.info("Load Edge TTL functions ...");  
476 - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.2.2", "schema_update_ttl.sql");  
477 - loadSql(schemaUpdateFile, conn);  
478 - log.info("Edge TTL functions successfully loaded!");  
479 -  
480 - } catch (Exception e) {  
481 - log.error("Failed updating schema!!!", e); 453 + conn.createStatement().execute("ALTER TABLE rule_chain ADD COLUMN type varchar(255) DEFAULT 'CORE'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
  454 + } catch (Exception ignored) {
482 } 455 }
  456 + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.2.2", SCHEMA_UPDATE_SQL);
  457 + loadSql(schemaUpdateFile, conn);
  458 + log.info("Load Edge TTL functions ...");
  459 + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.2.2", "schema_update_ttl.sql");
  460 + loadSql(schemaUpdateFile, conn);
  461 + log.info("Edge TTL functions successfully loaded!");
  462 + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003000;");
483 log.info("Schema updated."); 463 log.info("Schema updated.");
  464 + } catch (Exception e) {
  465 + log.error("Failed updating schema!!!", e);
484 } 466 }
485 break; 467 break;
486 default: 468 default:
@@ -33,6 +33,4 @@ public interface SystemDataLoaderService { @@ -33,6 +33,4 @@ public interface SystemDataLoaderService {
33 33
34 void deleteSystemWidgetBundle(String bundleAlias) throws Exception; 34 void deleteSystemWidgetBundle(String bundleAlias) throws Exception;
35 35
36 - void loadSystemLwm2mResources() throws Exception;  
37 -  
38 } 36 }
@@ -24,6 +24,7 @@ import org.springframework.context.event.EventListener; @@ -24,6 +24,7 @@ import org.springframework.context.event.EventListener;
24 import org.springframework.core.annotation.Order; 24 import org.springframework.core.annotation.Order;
25 import org.springframework.scheduling.annotation.Scheduled; 25 import org.springframework.scheduling.annotation.Scheduled;
26 import org.springframework.stereotype.Service; 26 import org.springframework.stereotype.Service;
  27 +import org.thingsboard.common.util.JacksonUtil;
27 import org.thingsboard.common.util.ThingsBoardThreadFactory; 28 import org.thingsboard.common.util.ThingsBoardThreadFactory;
28 import org.thingsboard.rule.engine.api.RpcError; 29 import org.thingsboard.rule.engine.api.RpcError;
29 import org.thingsboard.server.actors.ActorSystemContext; 30 import org.thingsboard.server.actors.ActorSystemContext;
@@ -35,7 +36,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; @@ -35,7 +36,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
35 import org.thingsboard.server.common.msg.queue.TbCallback; 36 import org.thingsboard.server.common.msg.queue.TbCallback;
36 import org.thingsboard.server.common.stats.StatsFactory; 37 import org.thingsboard.server.common.stats.StatsFactory;
37 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 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 import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto; 40 import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
40 import org.thingsboard.server.gen.transport.TransportProtos.EdgeNotificationMsgProto; 41 import org.thingsboard.server.gen.transport.TransportProtos.EdgeNotificationMsgProto;
41 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; 42 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
@@ -49,6 +50,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseP @@ -49,6 +50,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseP
49 import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; 50 import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto;
50 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 51 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
51 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 52 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  53 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
52 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; 54 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
53 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; 55 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
54 import org.thingsboard.server.queue.TbQueueConsumer; 56 import org.thingsboard.server.queue.TbQueueConsumer;
@@ -58,8 +60,8 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory; @@ -58,8 +60,8 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
58 import org.thingsboard.server.queue.util.TbCoreComponent; 60 import org.thingsboard.server.queue.util.TbCoreComponent;
59 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 61 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
60 import org.thingsboard.server.service.edge.EdgeNotificationService; 62 import org.thingsboard.server.service.edge.EdgeNotificationService;
  63 +import org.thingsboard.server.service.firmware.FirmwareStateService;
61 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 64 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
62 -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;  
63 import org.thingsboard.server.service.queue.processing.AbstractConsumerService; 65 import org.thingsboard.server.service.queue.processing.AbstractConsumerService;
64 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 66 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
65 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; 67 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
@@ -97,6 +99,11 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -97,6 +99,11 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
97 @Value("${queue.core.stats.enabled:false}") 99 @Value("${queue.core.stats.enabled:false}")
98 private boolean statsEnabled; 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 private final TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> mainConsumer; 107 private final TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> mainConsumer;
101 private final DeviceStateService stateService; 108 private final DeviceStateService stateService;
102 private final TbApiUsageStateService statsService; 109 private final TbApiUsageStateService statsService;
@@ -104,11 +111,15 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -104,11 +111,15 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
104 private final SubscriptionManagerService subscriptionManagerService; 111 private final SubscriptionManagerService subscriptionManagerService;
105 private final TbCoreDeviceRpcService tbCoreDeviceRpcService; 112 private final TbCoreDeviceRpcService tbCoreDeviceRpcService;
106 private final EdgeNotificationService edgeNotificationService; 113 private final EdgeNotificationService edgeNotificationService;
  114 + private final FirmwareStateService firmwareStateService;
107 private final TbCoreConsumerStats stats; 115 private final TbCoreConsumerStats stats;
108 protected final TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> usageStatsConsumer; 116 protected final TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> usageStatsConsumer;
  117 + private final TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> firmwareStatesConsumer;
109 118
110 protected volatile ExecutorService usageStatsExecutor; 119 protected volatile ExecutorService usageStatsExecutor;
111 120
  121 + private volatile ExecutorService firmwareStatesExecutor;
  122 +
112 public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory, 123 public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory,
113 ActorSystemContext actorContext, 124 ActorSystemContext actorContext,
114 DeviceStateService stateService, 125 DeviceStateService stateService,
@@ -121,10 +132,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -121,10 +132,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
121 TbApiUsageStateService statsService, 132 TbApiUsageStateService statsService,
122 TbTenantProfileCache tenantProfileCache, 133 TbTenantProfileCache tenantProfileCache,
123 TbApiUsageStateService apiUsageStateService, 134 TbApiUsageStateService apiUsageStateService,
124 - EdgeNotificationService edgeNotificationService) { 135 + EdgeNotificationService edgeNotificationService,
  136 + FirmwareStateService firmwareStateService) {
125 super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer()); 137 super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer());
126 this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer(); 138 this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer();
127 this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer(); 139 this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer();
  140 + this.firmwareStatesConsumer = tbCoreQueueFactory.createToFirmwareStateServiceMsgConsumer();
128 this.stateService = stateService; 141 this.stateService = stateService;
129 this.localSubscriptionService = localSubscriptionService; 142 this.localSubscriptionService = localSubscriptionService;
130 this.subscriptionManagerService = subscriptionManagerService; 143 this.subscriptionManagerService = subscriptionManagerService;
@@ -132,12 +145,14 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -132,12 +145,14 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
132 this.edgeNotificationService = edgeNotificationService; 145 this.edgeNotificationService = edgeNotificationService;
133 this.stats = new TbCoreConsumerStats(statsFactory); 146 this.stats = new TbCoreConsumerStats(statsFactory);
134 this.statsService = statsService; 147 this.statsService = statsService;
  148 + this.firmwareStateService = firmwareStateService;
135 } 149 }
136 150
137 @PostConstruct 151 @PostConstruct
138 public void init() { 152 public void init() {
139 super.init("tb-core-consumer", "tb-core-notifications-consumer"); 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 @PreDestroy 158 @PreDestroy
@@ -146,6 +161,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -146,6 +161,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
146 if (usageStatsExecutor != null) { 161 if (usageStatsExecutor != null) {
147 usageStatsExecutor.shutdownNow(); 162 usageStatsExecutor.shutdownNow();
148 } 163 }
  164 + if (firmwareStatesExecutor != null) {
  165 + firmwareStatesExecutor.shutdownNow();
  166 + }
149 } 167 }
150 168
151 @EventListener(ApplicationReadyEvent.class) 169 @EventListener(ApplicationReadyEvent.class)
@@ -153,6 +171,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -153,6 +171,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
153 public void onApplicationEvent(ApplicationReadyEvent event) { 171 public void onApplicationEvent(ApplicationReadyEvent event) {
154 super.onApplicationEvent(event); 172 super.onApplicationEvent(event);
155 launchUsageStatsConsumer(); 173 launchUsageStatsConsumer();
  174 + launchFirmwareUpdateNotificationConsumer();
156 } 175 }
157 176
158 @Override 177 @Override
@@ -167,6 +186,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -167,6 +186,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
167 .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic())) 186 .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic()))
168 .collect(Collectors.toSet())); 187 .collect(Collectors.toSet()));
169 } 188 }
  189 + this.firmwareStatesConsumer.subscribe();
170 } 190 }
171 191
172 @Override 192 @Override
@@ -336,10 +356,59 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -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 private void handleUsageStats(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) { 404 private void handleUsageStats(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) {
340 statsService.process(msg, callback); 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 private void forwardToCoreRpcService(FromDeviceRPCResponseProto proto, TbCallback callback) { 412 private void forwardToCoreRpcService(FromDeviceRPCResponseProto proto, TbCallback callback) {
344 RpcError error = proto.getError() > 0 ? RpcError.values()[proto.getError()] : null; 413 RpcError error = proto.getError() > 0 ? RpcError.values()[proto.getError()] : null;
345 FromDeviceRpcResponse response = new FromDeviceRpcResponse(new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB()) 414 FromDeviceRpcResponse response = new FromDeviceRpcResponse(new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB())
@@ -448,6 +517,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -448,6 +517,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
448 if (usageStatsConsumer != null) { 517 if (usageStatsConsumer != null) {
449 usageStatsConsumer.unsubscribe(); 518 usageStatsConsumer.unsubscribe();
450 } 519 }
  520 + if (firmwareStatesConsumer != null) {
  521 + firmwareStatesConsumer.unsubscribe();
  522 + }
451 } 523 }
452 524
453 } 525 }
@@ -31,7 +31,7 @@ public interface TbResourceService { @@ -31,7 +31,7 @@ public interface TbResourceService {
31 31
32 TbResource saveResource(TbResource resource) throws ThingsboardException; 32 TbResource saveResource(TbResource resource) throws ThingsboardException;
33 33
34 - TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); 34 + TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceKey);
35 35
36 TbResource findResourceById(TenantId tenantId, TbResourceId resourceId); 36 TbResource findResourceById(TenantId tenantId, TbResourceId resourceId);
37 37
@@ -38,6 +38,7 @@ public enum Resource { @@ -38,6 +38,7 @@ public enum Resource {
38 DEVICE_PROFILE(EntityType.DEVICE_PROFILE), 38 DEVICE_PROFILE(EntityType.DEVICE_PROFILE),
39 API_USAGE_STATE(EntityType.API_USAGE_STATE), 39 API_USAGE_STATE(EntityType.API_USAGE_STATE),
40 TB_RESOURCE(EntityType.TB_RESOURCE), 40 TB_RESOURCE(EntityType.TB_RESOURCE),
  41 + FIRMWARE(EntityType.FIRMWARE),
41 EDGE(EntityType.EDGE); 42 EDGE(EntityType.EDGE);
42 43
43 private final EntityType entityType; 44 private final EntityType entityType;
@@ -42,6 +42,7 @@ public class TenantAdminPermissions extends AbstractPermissions { @@ -42,6 +42,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
42 put(Resource.DEVICE_PROFILE, tenantEntityPermissionChecker); 42 put(Resource.DEVICE_PROFILE, tenantEntityPermissionChecker);
43 put(Resource.API_USAGE_STATE, tenantEntityPermissionChecker); 43 put(Resource.API_USAGE_STATE, tenantEntityPermissionChecker);
44 put(Resource.TB_RESOURCE, tbResourcePermissionChecker); 44 put(Resource.TB_RESOURCE, tbResourcePermissionChecker);
  45 + put(Resource.FIRMWARE, tenantEntityPermissionChecker);
45 put(Resource.EDGE, tenantEntityPermissionChecker); 46 put(Resource.EDGE, tenantEntityPermissionChecker);
46 } 47 }
47 48
@@ -23,16 +23,19 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -23,16 +23,19 @@ import com.google.common.util.concurrent.ListenableFuture;
23 import com.google.common.util.concurrent.MoreExecutors; 23 import com.google.common.util.concurrent.MoreExecutors;
24 import com.google.protobuf.ByteString; 24 import com.google.protobuf.ByteString;
25 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
  26 +import org.springframework.cache.CacheManager;
26 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
27 import org.springframework.util.StringUtils; 28 import org.springframework.util.StringUtils;
28 import org.thingsboard.common.util.JacksonUtil; 29 import org.thingsboard.common.util.JacksonUtil;
  30 +import org.thingsboard.server.cache.firmware.FirmwareCacheWriter;
29 import org.thingsboard.server.common.data.ApiUsageState; 31 import org.thingsboard.server.common.data.ApiUsageState;
30 import org.thingsboard.server.common.data.DataConstants; 32 import org.thingsboard.server.common.data.DataConstants;
31 import org.thingsboard.server.common.data.Device; 33 import org.thingsboard.server.common.data.Device;
32 import org.thingsboard.server.common.data.DeviceProfile; 34 import org.thingsboard.server.common.data.DeviceProfile;
33 import org.thingsboard.server.common.data.EntityType; 35 import org.thingsboard.server.common.data.EntityType;
34 -import org.thingsboard.server.common.data.TbResource; 36 +import org.thingsboard.server.common.data.Firmware;
35 import org.thingsboard.server.common.data.ResourceType; 37 import org.thingsboard.server.common.data.ResourceType;
  38 +import org.thingsboard.server.common.data.TbResource;
36 import org.thingsboard.server.common.data.TenantProfile; 39 import org.thingsboard.server.common.data.TenantProfile;
37 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; 40 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
38 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; 41 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
@@ -40,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileC @@ -40,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileC
40 import org.thingsboard.server.common.data.id.CustomerId; 43 import org.thingsboard.server.common.data.id.CustomerId;
41 import org.thingsboard.server.common.data.id.DeviceId; 44 import org.thingsboard.server.common.data.id.DeviceId;
42 import org.thingsboard.server.common.data.id.DeviceProfileId; 45 import org.thingsboard.server.common.data.id.DeviceProfileId;
  46 +import org.thingsboard.server.common.data.id.FirmwareId;
43 import org.thingsboard.server.common.data.id.TenantId; 47 import org.thingsboard.server.common.data.id.TenantId;
44 import org.thingsboard.server.common.data.relation.EntityRelation; 48 import org.thingsboard.server.common.data.relation.EntityRelation;
45 import org.thingsboard.server.common.data.security.DeviceCredentials; 49 import org.thingsboard.server.common.data.security.DeviceCredentials;
@@ -55,6 +59,7 @@ import org.thingsboard.server.dao.device.DeviceService; @@ -55,6 +59,7 @@ import org.thingsboard.server.dao.device.DeviceService;
55 import org.thingsboard.server.dao.device.provision.ProvisionFailedException; 59 import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
56 import org.thingsboard.server.dao.device.provision.ProvisionRequest; 60 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
57 import org.thingsboard.server.dao.device.provision.ProvisionResponse; 61 import org.thingsboard.server.dao.device.provision.ProvisionResponse;
  62 +import org.thingsboard.server.dao.firmware.FirmwareService;
58 import org.thingsboard.server.dao.relation.RelationService; 63 import org.thingsboard.server.dao.relation.RelationService;
59 import org.thingsboard.server.dao.resource.ResourceService; 64 import org.thingsboard.server.dao.resource.ResourceService;
60 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 65 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
@@ -66,7 +71,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFro @@ -66,7 +71,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFro
66 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; 71 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
67 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg; 72 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg;
68 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; 73 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
69 -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus;  
70 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; 74 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
71 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; 75 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
72 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; 76 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
@@ -79,6 +83,7 @@ import org.thingsboard.server.service.apiusage.TbApiUsageStateService; @@ -79,6 +83,7 @@ import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
79 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 83 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
80 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 84 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
81 import org.thingsboard.server.service.queue.TbClusterService; 85 import org.thingsboard.server.service.queue.TbClusterService;
  86 +import org.thingsboard.server.service.resource.TbResourceService;
82 import org.thingsboard.server.service.state.DeviceStateService; 87 import org.thingsboard.server.service.state.DeviceStateService;
83 88
84 import java.util.Optional; 89 import java.util.Optional;
@@ -109,7 +114,9 @@ public class DefaultTransportApiService implements TransportApiService { @@ -109,7 +114,9 @@ public class DefaultTransportApiService implements TransportApiService {
109 private final TbClusterService tbClusterService; 114 private final TbClusterService tbClusterService;
110 private final DataDecodingEncodingService dataDecodingEncodingService; 115 private final DataDecodingEncodingService dataDecodingEncodingService;
111 private final DeviceProvisionService deviceProvisionService; 116 private final DeviceProvisionService deviceProvisionService;
112 - private final ResourceService resourceService; 117 + private final TbResourceService resourceService;
  118 + private final FirmwareService firmwareService;
  119 + private final FirmwareCacheWriter firmwareCacheWriter;
113 120
114 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); 121 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
115 122
@@ -118,7 +125,7 @@ public class DefaultTransportApiService implements TransportApiService { @@ -118,7 +125,7 @@ public class DefaultTransportApiService implements TransportApiService {
118 RelationService relationService, DeviceCredentialsService deviceCredentialsService, 125 RelationService relationService, DeviceCredentialsService deviceCredentialsService,
119 DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, 126 DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService,
120 TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, 127 TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService,
121 - DeviceProvisionService deviceProvisionService, ResourceService resourceService) { 128 + DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareCacheWriter firmwareCacheWriter) {
122 this.deviceProfileCache = deviceProfileCache; 129 this.deviceProfileCache = deviceProfileCache;
123 this.tenantProfileCache = tenantProfileCache; 130 this.tenantProfileCache = tenantProfileCache;
124 this.apiUsageStateService = apiUsageStateService; 131 this.apiUsageStateService = apiUsageStateService;
@@ -131,6 +138,8 @@ public class DefaultTransportApiService implements TransportApiService { @@ -131,6 +138,8 @@ public class DefaultTransportApiService implements TransportApiService {
131 this.dataDecodingEncodingService = dataDecodingEncodingService; 138 this.dataDecodingEncodingService = dataDecodingEncodingService;
132 this.deviceProvisionService = deviceProvisionService; 139 this.deviceProvisionService = deviceProvisionService;
133 this.resourceService = resourceService; 140 this.resourceService = resourceService;
  141 + this.firmwareService = firmwareService;
  142 + this.firmwareCacheWriter = firmwareCacheWriter;
134 } 143 }
135 144
136 @Override 145 @Override
@@ -167,6 +176,9 @@ public class DefaultTransportApiService implements TransportApiService { @@ -167,6 +176,9 @@ public class DefaultTransportApiService implements TransportApiService {
167 } else if (transportApiRequestMsg.hasResourceRequestMsg()) { 176 } else if (transportApiRequestMsg.hasResourceRequestMsg()) {
168 return Futures.transform(handle(transportApiRequestMsg.getResourceRequestMsg()), 177 return Futures.transform(handle(transportApiRequestMsg.getResourceRequestMsg()),
169 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 178 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  179 + } else if (transportApiRequestMsg.hasFirmwareRequestMsg()) {
  180 + return Futures.transform(handle(transportApiRequestMsg.getFirmwareRequestMsg()),
  181 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
170 } 182 }
171 return Futures.transform(getEmptyTransportApiResponseFuture(), 183 return Futures.transform(getEmptyTransportApiResponseFuture(),
172 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 184 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
@@ -314,14 +326,14 @@ public class DefaultTransportApiService implements TransportApiService { @@ -314,14 +326,14 @@ public class DefaultTransportApiService implements TransportApiService {
314 } catch (ProvisionFailedException e) { 326 } catch (ProvisionFailedException e) {
315 return Futures.immediateFuture(getTransportApiResponseMsg( 327 return Futures.immediateFuture(getTransportApiResponseMsg(
316 new DeviceCredentials(), 328 new DeviceCredentials(),
317 - TransportProtos.ProvisionResponseStatus.valueOf(e.getMessage()))); 329 + TransportProtos.ResponseStatus.valueOf(e.getMessage())));
318 } 330 }
319 - return Futures.transform(provisionResponseFuture, provisionResponse -> getTransportApiResponseMsg(provisionResponse.getDeviceCredentials(), TransportProtos.ProvisionResponseStatus.SUCCESS), 331 + return Futures.transform(provisionResponseFuture, provisionResponse -> getTransportApiResponseMsg(provisionResponse.getDeviceCredentials(), TransportProtos.ResponseStatus.SUCCESS),
320 dbCallbackExecutorService); 332 dbCallbackExecutorService);
321 } 333 }
322 334
323 - private TransportApiResponseMsg getTransportApiResponseMsg(DeviceCredentials deviceCredentials, TransportProtos.ProvisionResponseStatus status) {  
324 - if (!status.equals(ProvisionResponseStatus.SUCCESS)) { 335 + private TransportApiResponseMsg getTransportApiResponseMsg(DeviceCredentials deviceCredentials, TransportProtos.ResponseStatus status) {
  336 + if (!status.equals(TransportProtos.ResponseStatus.SUCCESS)) {
325 return TransportApiResponseMsg.newBuilder().setProvisionDeviceResponseMsg(TransportProtos.ProvisionDeviceResponseMsg.newBuilder().setStatus(status).build()).build(); 337 return TransportApiResponseMsg.newBuilder().setProvisionDeviceResponseMsg(TransportProtos.ProvisionDeviceResponseMsg.newBuilder().setStatus(status).build()).build();
326 } 338 }
327 TransportProtos.ProvisionDeviceResponseMsg.Builder provisionResponse = TransportProtos.ProvisionDeviceResponseMsg.newBuilder() 339 TransportProtos.ProvisionDeviceResponseMsg.Builder provisionResponse = TransportProtos.ProvisionDeviceResponseMsg.newBuilder()
@@ -441,6 +453,48 @@ public class DefaultTransportApiService implements TransportApiService { @@ -441,6 +453,48 @@ public class DefaultTransportApiService implements TransportApiService {
441 } 453 }
442 } 454 }
443 455
  456 + private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) {
  457 + TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
  458 + DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
  459 + Device device = deviceService.findDeviceById(tenantId, deviceId);
  460 +
  461 + if (device == null) {
  462 + return getEmptyTransportApiResponseFuture();
  463 + }
  464 +
  465 + FirmwareId firmwareId = device.getFirmwareId();
  466 +
  467 + if (firmwareId == null) {
  468 + firmwareId = deviceProfileCache.find(device.getDeviceProfileId()).getFirmwareId();
  469 + }
  470 +
  471 + TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder();
  472 +
  473 + if (firmwareId == null) {
  474 + builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND);
  475 + } else {
  476 + Firmware firmware = firmwareService.findFirmwareById(tenantId, firmwareId);
  477 +
  478 + if (firmware == null) {
  479 + builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND);
  480 + } else {
  481 + builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
  482 + builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits());
  483 + builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits());
  484 + builder.setTitle(firmware.getTitle());
  485 + builder.setVersion(firmware.getVersion());
  486 + builder.setFileName(firmware.getFileName());
  487 + builder.setContentType(firmware.getContentType());
  488 + firmwareCacheWriter.put(firmwareId.toString(), firmware.getData().array());
  489 + }
  490 + }
  491 +
  492 + return Futures.immediateFuture(
  493 + TransportApiResponseMsg.newBuilder()
  494 + .setFirmwareResponseMsg(builder.build())
  495 + .build());
  496 + }
  497 +
444 private ListenableFuture<TransportApiResponseMsg> handleRegistration(TransportProtos.LwM2MRegistrationRequestMsg msg) { 498 private ListenableFuture<TransportApiResponseMsg> handleRegistration(TransportProtos.LwM2MRegistrationRequestMsg msg) {
445 TenantId tenantId = new TenantId(UUID.fromString(msg.getTenantId())); 499 TenantId tenantId = new TenantId(UUID.fromString(msg.getTenantId()));
446 String deviceName = msg.getEndpoint(); 500 String deviceName = msg.getEndpoint();
@@ -371,6 +371,9 @@ caffeine: @@ -371,6 +371,9 @@ caffeine:
371 tokensOutdatageTime: 371 tokensOutdatageTime:
372 timeToLiveInMinutes: 20000 372 timeToLiveInMinutes: 20000
373 maxSize: 10000 373 maxSize: 10000
  374 + firmwares:
  375 + timeToLiveInMinutes: 1440
  376 + maxSize: 100
374 edges: 377 edges:
375 timeToLiveInMinutes: 1440 378 timeToLiveInMinutes: 1440
376 maxSize: 0 379 maxSize: 0
@@ -446,6 +449,9 @@ spring.resources.chain: @@ -446,6 +449,9 @@ spring.resources.chain:
446 content: 449 content:
447 enabled: "true" 450 enabled: "true"
448 451
  452 +spring.servlet.multipart.max-file-size: "50MB"
  453 +spring.servlet.multipart.max-request-size: "50MB"
  454 +
449 spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" 455 spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true"
450 spring.jpa.properties.hibernate.order_by.default_null_ordering: "last" 456 spring.jpa.properties.hibernate.order_by.default_null_ordering: "last"
451 457
@@ -738,6 +744,10 @@ queue: @@ -738,6 +744,10 @@ queue:
738 sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}" 744 sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}"
739 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\";}" 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\";}"
740 security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" 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
741 other: 751 other:
742 topic-properties: 752 topic-properties:
743 rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" 753 rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
@@ -745,6 +755,7 @@ queue: @@ -745,6 +755,7 @@ queue:
745 transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" 755 transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
746 notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" 756 notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
747 js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" 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}"
748 consumer-stats: 759 consumer-stats:
749 enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" 760 enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
750 print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" 761 print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
@@ -815,6 +826,10 @@ queue: @@ -815,6 +826,10 @@ queue:
815 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" 826 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
816 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" 827 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
817 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}" 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}"
818 usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" 833 usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
819 stats: 834 stats:
820 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}" 835 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}"
  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.controller;
  17 +
  18 +import com.fasterxml.jackson.core.type.TypeReference;
  19 +import org.junit.After;
  20 +import org.junit.Assert;
  21 +import org.junit.Before;
  22 +import org.junit.Test;
  23 +import org.springframework.mock.web.MockMultipartFile;
  24 +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
  25 +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
  26 +import org.thingsboard.common.util.JacksonUtil;
  27 +import org.thingsboard.server.common.data.Firmware;
  28 +import org.thingsboard.server.common.data.FirmwareInfo;
  29 +import org.thingsboard.server.common.data.Tenant;
  30 +import org.thingsboard.server.common.data.User;
  31 +import org.thingsboard.server.common.data.page.PageData;
  32 +import org.thingsboard.server.common.data.page.PageLink;
  33 +import org.thingsboard.server.common.data.security.Authority;
  34 +
  35 +import java.nio.ByteBuffer;
  36 +import java.util.ArrayList;
  37 +import java.util.Collections;
  38 +import java.util.List;
  39 +
  40 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  41 +
  42 +public abstract class BaseFirmwareControllerTest extends AbstractControllerTest {
  43 +
  44 + private IdComparator<FirmwareInfo> idComparator = new IdComparator<>();
  45 +
  46 + public static final String TITLE = "My firmware";
  47 + private static final String FILE_NAME = "filename.txt";
  48 + private static final String VERSION = "v1.0";
  49 + private static final String CONTENT_TYPE = "text/plain";
  50 + private static final String CHECKSUM_ALGORITHM = "sha256";
  51 + private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a";
  52 + private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1});
  53 +
  54 + private Tenant savedTenant;
  55 + private User tenantAdmin;
  56 +
  57 + @Before
  58 + public void beforeTest() throws Exception {
  59 + loginSysAdmin();
  60 +
  61 + Tenant tenant = new Tenant();
  62 + tenant.setTitle("My tenant");
  63 + savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  64 + Assert.assertNotNull(savedTenant);
  65 +
  66 + tenantAdmin = new User();
  67 + tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
  68 + tenantAdmin.setTenantId(savedTenant.getId());
  69 + tenantAdmin.setEmail("tenant2@thingsboard.org");
  70 + tenantAdmin.setFirstName("Joe");
  71 + tenantAdmin.setLastName("Downs");
  72 +
  73 + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
  74 + }
  75 +
  76 + @After
  77 + public void afterTest() throws Exception {
  78 + loginSysAdmin();
  79 +
  80 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  81 + .andExpect(status().isOk());
  82 + }
  83 +
  84 + @Test
  85 + public void testSaveFirmware() throws Exception {
  86 + FirmwareInfo firmwareInfo = new FirmwareInfo();
  87 + firmwareInfo.setTitle(TITLE);
  88 + firmwareInfo.setVersion(VERSION);
  89 +
  90 + FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
  91 +
  92 + Assert.assertNotNull(savedFirmwareInfo);
  93 + Assert.assertNotNull(savedFirmwareInfo.getId());
  94 + Assert.assertTrue(savedFirmwareInfo.getCreatedTime() > 0);
  95 + Assert.assertEquals(savedTenant.getId(), savedFirmwareInfo.getTenantId());
  96 + Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
  97 + Assert.assertEquals(firmwareInfo.getVersion(), savedFirmwareInfo.getVersion());
  98 +
  99 + savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode());
  100 +
  101 + save(savedFirmwareInfo);
  102 +
  103 + FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class);
  104 + Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
  105 + }
  106 +
  107 + @Test
  108 + public void testSaveFirmwareData() throws Exception {
  109 + FirmwareInfo firmwareInfo = new FirmwareInfo();
  110 + firmwareInfo.setTitle(TITLE);
  111 + firmwareInfo.setVersion(VERSION);
  112 +
  113 + FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
  114 +
  115 + Assert.assertNotNull(savedFirmwareInfo);
  116 + Assert.assertNotNull(savedFirmwareInfo.getId());
  117 + Assert.assertTrue(savedFirmwareInfo.getCreatedTime() > 0);
  118 + Assert.assertEquals(savedTenant.getId(), savedFirmwareInfo.getTenantId());
  119 + Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
  120 + Assert.assertEquals(firmwareInfo.getVersion(), savedFirmwareInfo.getVersion());
  121 +
  122 + savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode());
  123 +
  124 + save(savedFirmwareInfo);
  125 +
  126 + FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class);
  127 + Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
  128 +
  129 + MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
  130 +
  131 + Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
  132 +
  133 + Assert.assertEquals(FILE_NAME, savedFirmware.getFileName());
  134 + Assert.assertEquals(CONTENT_TYPE, savedFirmware.getContentType());
  135 + }
  136 +
  137 + @Test
  138 + public void testUpdateFirmwareFromDifferentTenant() throws Exception {
  139 + FirmwareInfo firmwareInfo = new FirmwareInfo();
  140 + firmwareInfo.setTitle(TITLE);
  141 + firmwareInfo.setVersion(VERSION);
  142 +
  143 + FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
  144 +
  145 + loginDifferentTenant();
  146 + doPost("/api/firmware", savedFirmwareInfo, FirmwareInfo.class, status().isForbidden());
  147 + deleteDifferentTenant();
  148 + }
  149 +
  150 + @Test
  151 + public void testFindFirmwareInfoById() throws Exception {
  152 + FirmwareInfo firmwareInfo = new FirmwareInfo();
  153 + firmwareInfo.setTitle(TITLE);
  154 + firmwareInfo.setVersion(VERSION);
  155 +
  156 + FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
  157 +
  158 + FirmwareInfo foundFirmware = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class);
  159 + Assert.assertNotNull(foundFirmware);
  160 + Assert.assertEquals(savedFirmwareInfo, foundFirmware);
  161 + }
  162 +
  163 + @Test
  164 + public void testFindFirmwareById() throws Exception {
  165 + FirmwareInfo firmwareInfo = new FirmwareInfo();
  166 + firmwareInfo.setTitle(TITLE);
  167 + firmwareInfo.setVersion(VERSION);
  168 +
  169 + FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
  170 +
  171 + MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
  172 +
  173 + Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
  174 +
  175 + Firmware foundFirmware = doGet("/api/firmware/" + savedFirmwareInfo.getId().getId().toString(), Firmware.class);
  176 + Assert.assertNotNull(foundFirmware);
  177 + Assert.assertEquals(savedFirmware, foundFirmware);
  178 + }
  179 +
  180 + @Test
  181 + public void testDeleteFirmware() throws Exception {
  182 + FirmwareInfo firmwareInfo = new FirmwareInfo();
  183 + firmwareInfo.setTitle(TITLE);
  184 + firmwareInfo.setVersion(VERSION);
  185 +
  186 + FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
  187 +
  188 + doDelete("/api/firmware/" + savedFirmwareInfo.getId().getId().toString())
  189 + .andExpect(status().isOk());
  190 +
  191 + doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString())
  192 + .andExpect(status().isNotFound());
  193 + }
  194 +
  195 + @Test
  196 + public void testFindTenantFirmwares() throws Exception {
  197 + List<FirmwareInfo> firmwares = new ArrayList<>();
  198 + for (int i = 0; i < 165; i++) {
  199 + FirmwareInfo firmwareInfo = new FirmwareInfo();
  200 + firmwareInfo.setTitle(TITLE);
  201 + firmwareInfo.setVersion(VERSION + i);
  202 +
  203 + FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
  204 +
  205 + if (i > 100) {
  206 + MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
  207 +
  208 + Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
  209 + firmwares.add(new FirmwareInfo(savedFirmware));
  210 + } else {
  211 + firmwares.add(savedFirmwareInfo);
  212 + }
  213 + }
  214 +
  215 + List<FirmwareInfo> loadedFirmwares = new ArrayList<>();
  216 + PageLink pageLink = new PageLink(24);
  217 + PageData<FirmwareInfo> pageData;
  218 + do {
  219 + pageData = doGetTypedWithPageLink("/api/firmwares?",
  220 + new TypeReference<>() {
  221 + }, pageLink);
  222 + loadedFirmwares.addAll(pageData.getData());
  223 + if (pageData.hasNext()) {
  224 + pageLink = pageLink.nextPageLink();
  225 + }
  226 + } while (pageData.hasNext());
  227 +
  228 + Collections.sort(firmwares, idComparator);
  229 + Collections.sort(loadedFirmwares, idComparator);
  230 +
  231 + Assert.assertEquals(firmwares, loadedFirmwares);
  232 + }
  233 +
  234 + @Test
  235 + public void testFindTenantFirmwaresByHasData() throws Exception {
  236 + List<FirmwareInfo> firmwaresWithData = new ArrayList<>();
  237 + List<FirmwareInfo> firmwaresWithoutData = new ArrayList<>();
  238 +
  239 + for (int i = 0; i < 165; i++) {
  240 + FirmwareInfo firmwareInfo = new FirmwareInfo();
  241 + firmwareInfo.setTitle(TITLE);
  242 + firmwareInfo.setVersion(VERSION + i);
  243 +
  244 + FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
  245 +
  246 + if (i > 100) {
  247 + MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
  248 +
  249 + Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
  250 + firmwaresWithData.add(new FirmwareInfo(savedFirmware));
  251 + } else {
  252 + firmwaresWithoutData.add(savedFirmwareInfo);
  253 + }
  254 + }
  255 +
  256 + List<FirmwareInfo> loadedFirmwaresWithData = new ArrayList<>();
  257 + PageLink pageLink = new PageLink(24);
  258 + PageData<FirmwareInfo> pageData;
  259 + do {
  260 + pageData = doGetTypedWithPageLink("/api/firmwares/true?",
  261 + new TypeReference<>() {
  262 + }, pageLink);
  263 + loadedFirmwaresWithData.addAll(pageData.getData());
  264 + if (pageData.hasNext()) {
  265 + pageLink = pageLink.nextPageLink();
  266 + }
  267 + } while (pageData.hasNext());
  268 +
  269 + List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>();
  270 + pageLink = new PageLink(24);
  271 + do {
  272 + pageData = doGetTypedWithPageLink("/api/firmwares/false?",
  273 + new TypeReference<>() {
  274 + }, pageLink);
  275 + loadedFirmwaresWithoutData.addAll(pageData.getData());
  276 + if (pageData.hasNext()) {
  277 + pageLink = pageLink.nextPageLink();
  278 + }
  279 + } while (pageData.hasNext());
  280 +
  281 + Collections.sort(firmwaresWithData, idComparator);
  282 + Collections.sort(firmwaresWithoutData, idComparator);
  283 + Collections.sort(loadedFirmwaresWithData, idComparator);
  284 + Collections.sort(loadedFirmwaresWithoutData, idComparator);
  285 +
  286 + Assert.assertEquals(firmwaresWithData, loadedFirmwaresWithData);
  287 + Assert.assertEquals(firmwaresWithoutData, loadedFirmwaresWithoutData);
  288 + }
  289 +
  290 +
  291 + private FirmwareInfo save(FirmwareInfo firmwareInfo) throws Exception {
  292 + return doPost("/api/firmware", firmwareInfo, FirmwareInfo.class);
  293 + }
  294 +
  295 + protected Firmware savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
  296 + MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params);
  297 + postRequest.file(content);
  298 + setJwtToken(postRequest);
  299 + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), Firmware.class);
  300 + }
  301 +
  302 +}
  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.controller.sql;
  17 +
  18 +import org.thingsboard.server.controller.BaseFirmwareControllerTest;
  19 +import org.thingsboard.server.dao.service.DaoSqlTest;
  20 +
  21 +@DaoSqlTest
  22 +public class FirmwareControllerSqlTest extends BaseFirmwareControllerTest {
  23 +}
  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.cache.firmware;
  17 +
  18 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  19 +
  20 +import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
  21 +
  22 +public abstract class AbstractRedisFirmwareCache {
  23 +
  24 + protected final RedisConnectionFactory redisConnectionFactory;
  25 +
  26 + protected AbstractRedisFirmwareCache(RedisConnectionFactory redisConnectionFactory) {
  27 + this.redisConnectionFactory = redisConnectionFactory;
  28 + }
  29 +
  30 + protected byte[] toFirmwareCacheKey(String key) {
  31 + return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes();
  32 + }
  33 +}
  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.cache.firmware;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +import org.springframework.cache.CacheManager;
  20 +import org.springframework.stereotype.Service;
  21 +
  22 +import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
  23 +
  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}'=='null')")
  26 +public class CaffeineFirmwareCacheReader implements FirmwareCacheReader {
  27 +
  28 + private final CacheManager cacheManager;
  29 +
  30 + public CaffeineFirmwareCacheReader(CacheManager cacheManager) {
  31 + this.cacheManager = cacheManager;
  32 + }
  33 +
  34 + @Override
  35 + public byte[] get(String key) {
  36 + return get(key, 0, 0);
  37 + }
  38 +
  39 + @Override
  40 + public byte[] get(String key, int chunkSize, int chunk) {
  41 + byte[] data = cacheManager.getCache(FIRMWARE_CACHE).get(key, byte[].class);
  42 +
  43 + if (chunkSize < 1) {
  44 + return data;
  45 + }
  46 +
  47 + if (data != null && data.length > 0) {
  48 + int startIndex = chunkSize * chunk;
  49 +
  50 + int size = Math.min(data.length - startIndex, chunkSize);
  51 +
  52 + if (startIndex < data.length && size > 0) {
  53 + byte[] result = new byte[size];
  54 + System.arraycopy(data, startIndex, result, 0, size);
  55 + return result;
  56 + }
  57 + }
  58 + return new byte[0];
  59 + }
  60 +}
  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.cache.firmware;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +import org.springframework.cache.CacheManager;
  20 +import org.springframework.stereotype.Service;
  21 +
  22 +import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
  23 +
  24 +@Service
  25 +@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')")
  26 +public class CaffeineFirmwareCacheWriter implements FirmwareCacheWriter {
  27 +
  28 + private final CacheManager cacheManager;
  29 +
  30 + public CaffeineFirmwareCacheWriter(CacheManager cacheManager) {
  31 + this.cacheManager = cacheManager;
  32 + }
  33 +
  34 + @Override
  35 + public void put(String key, byte[] value) {
  36 + cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value);
  37 + }
  38 +}
  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.cache.firmware;
  17 +
  18 +public interface FirmwareCacheReader {
  19 + byte[] get(String key);
  20 +
  21 + byte[] get(String key, int chunkSize, int chunk);
  22 +}
  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.cache.firmware;
  17 +
  18 +public interface FirmwareCacheWriter {
  19 + void put(String key, byte[] value);
  20 +}
  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.cache.firmware;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +import org.springframework.data.redis.connection.RedisConnection;
  20 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  21 +import org.springframework.stereotype.Service;
  22 +
  23 +@Service
  24 +@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && '${cache.type:null}'=='redis'")
  25 +public class RedisFirmwareCacheReader extends AbstractRedisFirmwareCache implements FirmwareCacheReader {
  26 +
  27 + public RedisFirmwareCacheReader(RedisConnectionFactory redisConnectionFactory) {
  28 + super(redisConnectionFactory);
  29 + }
  30 +
  31 + @Override
  32 + public byte[] get(String key) {
  33 + return get(key, 0, 0);
  34 + }
  35 +
  36 + @Override
  37 + public byte[] get(String key, int chunkSize, int chunk) {
  38 + try (RedisConnection connection = redisConnectionFactory.getConnection()) {
  39 + if (chunkSize == 0) {
  40 + return connection.get(toFirmwareCacheKey(key));
  41 + }
  42 +
  43 + int startIndex = chunkSize * chunk;
  44 + int endIndex = startIndex + chunkSize - 1;
  45 + return connection.getRange(toFirmwareCacheKey(key), startIndex, endIndex);
  46 + }
  47 + }
  48 +
  49 +}
  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.cache.firmware;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +import org.springframework.data.redis.connection.RedisConnection;
  20 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  21 +import org.springframework.stereotype.Service;
  22 +
  23 +@Service
  24 +@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && '${cache.type:null}'=='redis'")
  25 +public class RedisFirmwareCacheWriter extends AbstractRedisFirmwareCache implements FirmwareCacheWriter {
  26 +
  27 + public RedisFirmwareCacheWriter(RedisConnectionFactory redisConnectionFactory) {
  28 + super(redisConnectionFactory);
  29 + }
  30 +
  31 + @Override
  32 + public void put(String key, byte[] value) {
  33 + try (RedisConnection connection = redisConnectionFactory.getConnection()) {
  34 + connection.set(toFirmwareCacheKey(key), value);
  35 + }
  36 + }
  37 +
  38 +}
@@ -61,6 +61,8 @@ public interface DeviceService { @@ -61,6 +61,8 @@ public interface DeviceService {
61 61
62 PageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); 62 PageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
63 63
  64 + PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
  65 +
64 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); 66 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
65 67
66 PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink); 68 PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink);
  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.dao.firmware;
  17 +
  18 +import org.thingsboard.server.common.data.Firmware;
  19 +import org.thingsboard.server.common.data.FirmwareInfo;
  20 +import org.thingsboard.server.common.data.id.FirmwareId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
  22 +import org.thingsboard.server.common.data.page.PageData;
  23 +import org.thingsboard.server.common.data.page.PageLink;
  24 +
  25 +public interface FirmwareService {
  26 +
  27 + FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo);
  28 +
  29 + Firmware saveFirmware(Firmware firmware);
  30 +
  31 + Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId);
  32 +
  33 + FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId);
  34 +
  35 + PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink);
  36 +
  37 + PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink);
  38 +
  39 + void deleteFirmware(TenantId tenantId, FirmwareId firmwareId);
  40 +
  41 + void deleteFirmwaresByTenantId(TenantId tenantId);
  42 +}
@@ -29,4 +29,5 @@ public class CacheConstants { @@ -29,4 +29,5 @@ public class CacheConstants {
29 public static final String DEVICE_PROFILE_CACHE = "deviceProfiles"; 29 public static final String DEVICE_PROFILE_CACHE = "deviceProfiles";
30 public static final String ATTRIBUTES_CACHE = "attributes"; 30 public static final String ATTRIBUTES_CACHE = "attributes";
31 public static final String TOKEN_OUTDATAGE_TIME_CACHE = "tokensOutdatageTime"; 31 public static final String TOKEN_OUTDATAGE_TIME_CACHE = "tokensOutdatageTime";
  32 + public static final String FIRMWARE_CACHE = "firmwares";
32 } 33 }
@@ -93,6 +93,21 @@ public class DataConstants { @@ -93,6 +93,21 @@ public class DataConstants {
93 public static final String USERNAME = "username"; 93 public static final String USERNAME = "username";
94 public static final String PASSWORD = "password"; 94 public static final String PASSWORD = "password";
95 95
  96 + //firmware
  97 + //telemetry
  98 + public static final String CURRENT_FIRMWARE_TITLE = "cur_fw_title";
  99 + public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version";
  100 + public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
  101 + public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
  102 + public static final String FIRMWARE_STATE = "fw_state";
  103 +
  104 + //attributes
  105 + //telemetry
  106 + public static final String FIRMWARE_TITLE = "fw_title";
  107 + public static final String FIRMWARE_VERSION = "fw_version";
  108 + public static final String FIRMWARE_SIZE = "fw_size";
  109 + public static final String FIRMWARE_CHECKSUM = "fw_checksum";
  110 + public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm";
96 public static final String EDGE_MSG_SOURCE = "edge"; 111 public static final String EDGE_MSG_SOURCE = "edge";
97 public static final String MSG_SOURCE_KEY = "source"; 112 public static final String MSG_SOURCE_KEY = "source";
98 } 113 }
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData;
23 import org.thingsboard.server.common.data.id.CustomerId; 23 import org.thingsboard.server.common.data.id.CustomerId;
24 import org.thingsboard.server.common.data.id.DeviceId; 24 import org.thingsboard.server.common.data.id.DeviceId;
25 import org.thingsboard.server.common.data.id.DeviceProfileId; 25 import org.thingsboard.server.common.data.id.DeviceProfileId;
  26 +import org.thingsboard.server.common.data.id.FirmwareId;
26 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.data.validation.NoXss; 28 import org.thingsboard.server.common.data.validation.NoXss;
28 29
@@ -48,6 +49,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -48,6 +49,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
48 @JsonIgnore 49 @JsonIgnore
49 private byte[] deviceDataBytes; 50 private byte[] deviceDataBytes;
50 51
  52 + private FirmwareId firmwareId;
  53 +
51 public Device() { 54 public Device() {
52 super(); 55 super();
53 } 56 }
@@ -65,6 +68,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -65,6 +68,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
65 this.label = device.getLabel(); 68 this.label = device.getLabel();
66 this.deviceProfileId = device.getDeviceProfileId(); 69 this.deviceProfileId = device.getDeviceProfileId();
67 this.setDeviceData(device.getDeviceData()); 70 this.setDeviceData(device.getDeviceData());
  71 + this.firmwareId = device.getFirmwareId();
68 } 72 }
69 73
70 public Device updateDevice(Device device) { 74 public Device updateDevice(Device device) {
@@ -159,6 +163,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -159,6 +163,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
159 return getName(); 163 return getName();
160 } 164 }
161 165
  166 + public FirmwareId getFirmwareId() {
  167 + return firmwareId;
  168 + }
  169 +
  170 + public void setFirmwareId(FirmwareId firmwareId) {
  171 + this.firmwareId = firmwareId;
  172 + }
  173 +
162 @Override 174 @Override
163 public String toString() { 175 public String toString() {
164 StringBuilder builder = new StringBuilder(); 176 StringBuilder builder = new StringBuilder();
@@ -175,6 +187,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -175,6 +187,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
175 builder.append(", deviceProfileId="); 187 builder.append(", deviceProfileId=");
176 builder.append(deviceProfileId); 188 builder.append(deviceProfileId);
177 builder.append(", deviceData="); 189 builder.append(", deviceData=");
  190 + builder.append(firmwareId);
  191 + builder.append(", firmwareId=");
178 builder.append(deviceData); 192 builder.append(deviceData);
179 builder.append(", additionalInfo="); 193 builder.append(", additionalInfo=");
180 builder.append(getAdditionalInfo()); 194 builder.append(getAdditionalInfo());
@@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode; @@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode;
22 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
23 import org.thingsboard.server.common.data.device.profile.DeviceProfileData; 23 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
24 import org.thingsboard.server.common.data.id.DeviceProfileId; 24 import org.thingsboard.server.common.data.id.DeviceProfileId;
  25 +import org.thingsboard.server.common.data.id.FirmwareId;
25 import org.thingsboard.server.common.data.id.RuleChainId; 26 import org.thingsboard.server.common.data.id.RuleChainId;
26 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.data.validation.NoXss; 28 import org.thingsboard.server.common.data.validation.NoXss;
@@ -56,6 +57,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H @@ -56,6 +57,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
56 @NoXss 57 @NoXss
57 private String provisionDeviceKey; 58 private String provisionDeviceKey;
58 59
  60 + private FirmwareId firmwareId;
  61 +
59 public DeviceProfile() { 62 public DeviceProfile() {
60 super(); 63 super();
61 } 64 }
@@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data;
19 * @author Andrew Shvayka 19 * @author Andrew Shvayka
20 */ 20 */
21 public enum EntityType { 21 public enum EntityType {
22 - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, EDGE; 22 + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, FIRMWARE, EDGE;
23 } 23 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data;
  17 +
  18 +import lombok.Data;
  19 +import lombok.EqualsAndHashCode;
  20 +import org.thingsboard.server.common.data.id.FirmwareId;
  21 +
  22 +import java.nio.ByteBuffer;
  23 +
  24 +@Data
  25 +@EqualsAndHashCode(callSuper = true)
  26 +public class Firmware extends FirmwareInfo {
  27 +
  28 + private static final long serialVersionUID = 3091601761339422546L;
  29 +
  30 + private transient ByteBuffer data;
  31 +
  32 + public Firmware() {
  33 + super();
  34 + }
  35 +
  36 + public Firmware(FirmwareId id) {
  37 + super(id);
  38 + }
  39 +
  40 + public Firmware(Firmware firmware) {
  41 + super(firmware);
  42 + this.data = firmware.getData();
  43 + }
  44 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data;
  17 +
  18 +import lombok.Data;
  19 +import lombok.EqualsAndHashCode;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.thingsboard.server.common.data.id.FirmwareId;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +
  24 +@Slf4j
  25 +@Data
  26 +@EqualsAndHashCode(callSuper = true)
  27 +public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> implements HasTenantId {
  28 +
  29 + private static final long serialVersionUID = 3168391583570815419L;
  30 +
  31 + private TenantId tenantId;
  32 + private String title;
  33 + private String version;
  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 +
  41 +
  42 + public FirmwareInfo() {
  43 + super();
  44 + }
  45 +
  46 + public FirmwareInfo(FirmwareId id) {
  47 + super(id);
  48 + }
  49 +
  50 + public FirmwareInfo(FirmwareInfo firmwareInfo) {
  51 + super(firmwareInfo);
  52 + this.tenantId = firmwareInfo.getTenantId();
  53 + this.title = firmwareInfo.getTitle();
  54 + this.version = firmwareInfo.getVersion();
  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();
  61 + }
  62 +
  63 + @Override
  64 + public String getSearchText() {
  65 + return title;
  66 + }
  67 +}
@@ -27,6 +27,8 @@ import org.thingsboard.server.common.data.validation.NoXss; @@ -27,6 +27,8 @@ import org.thingsboard.server.common.data.validation.NoXss;
27 @EqualsAndHashCode(callSuper = true) 27 @EqualsAndHashCode(callSuper = true)
28 public class TbResourceInfo extends SearchTextBased<TbResourceId> implements HasTenantId { 28 public class TbResourceInfo extends SearchTextBased<TbResourceId> implements HasTenantId {
29 29
  30 + private static final long serialVersionUID = 7282664529021651736L;
  31 +
30 private TenantId tenantId; 32 private TenantId tenantId;
31 @NoXss 33 @NoXss
32 private String title; 34 private String title;
@@ -30,6 +30,9 @@ public class MqttTopics { @@ -30,6 +30,9 @@ public class MqttTopics {
30 private static final String CLAIM = "/claim"; 30 private static final String CLAIM = "/claim";
31 private static final String SUB_TOPIC = "+"; 31 private static final String SUB_TOPIC = "+";
32 private static final String PROVISION = "/provision"; 32 private static final String PROVISION = "/provision";
  33 + private static final String FIRMWARE = "/fw";
  34 + private static final String CHUNK = "/chunk/";
  35 + private static final String ERROR = "/error";
33 36
34 private static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE; 37 private static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE;
35 private static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST; 38 private static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
@@ -69,6 +72,13 @@ public class MqttTopics { @@ -69,6 +72,13 @@ public class MqttTopics {
69 public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_REQUEST; 72 public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_REQUEST;
70 public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_RESPONSE; 73 public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_RESPONSE;
71 74
  75 + // v2 topics
  76 + public static final String BASE_DEVICE_API_TOPIC_V2 = "v2";
  77 +
  78 + public static final String DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/";
  79 + public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + SUB_TOPIC + CHUNK + SUB_TOPIC;
  80 + public static final String DEVICE_FIRMWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + ERROR;
  81 +
72 private MqttTopics() { 82 private MqttTopics() {
73 } 83 }
74 } 84 }
@@ -71,6 +71,8 @@ public class EntityIdFactory { @@ -71,6 +71,8 @@ public class EntityIdFactory {
71 return new ApiUsageStateId(uuid); 71 return new ApiUsageStateId(uuid);
72 case TB_RESOURCE: 72 case TB_RESOURCE:
73 return new TbResourceId(uuid); 73 return new TbResourceId(uuid);
  74 + case FIRMWARE:
  75 + return new FirmwareId(uuid);
74 case EDGE: 76 case EDGE:
75 return new EdgeId(uuid); 77 return new EdgeId(uuid);
76 } 78 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.id;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonIgnore;
  20 +import com.fasterxml.jackson.annotation.JsonProperty;
  21 +import org.thingsboard.server.common.data.EntityType;
  22 +
  23 +import java.util.UUID;
  24 +
  25 +public class FirmwareId extends UUIDBased implements EntityId {
  26 +
  27 + private static final long serialVersionUID = 1L;
  28 +
  29 + @JsonCreator
  30 + public FirmwareId(@JsonProperty("id") UUID id) {
  31 + super(id);
  32 + }
  33 +
  34 + public static FirmwareId fromString(String firmwareId) {
  35 + return new FirmwareId(UUID.fromString(firmwareId));
  36 + }
  37 +
  38 + @JsonIgnore
  39 + @Override
  40 + public EntityType getEntityType() {
  41 + return EntityType.FIRMWARE;
  42 + }
  43 +
  44 +}
@@ -69,7 +69,7 @@ public class TbKafkaConsumerStatsService { @@ -69,7 +69,7 @@ public class TbKafkaConsumerStatsService {
69 this.adminClient = AdminClient.create(kafkaSettings.toAdminProps()); 69 this.adminClient = AdminClient.create(kafkaSettings.toAdminProps());
70 this.statsPrintScheduler = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("kafka-consumer-stats")); 70 this.statsPrintScheduler = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("kafka-consumer-stats"));
71 71
72 - Properties consumerProps = kafkaSettings.toConsumerProps(); 72 + Properties consumerProps = kafkaSettings.toConsumerProps(null);
73 consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, "consumer-stats-loader-client"); 73 consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, "consumer-stats-loader-client");
74 consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "consumer-stats-loader-client-group"); 74 consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "consumer-stats-loader-client-group");
75 this.consumer = new KafkaConsumer<>(consumerProps); 75 this.consumer = new KafkaConsumer<>(consumerProps);
@@ -50,7 +50,7 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue @@ -50,7 +50,7 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue
50 String clientId, String groupId, String topic, 50 String clientId, String groupId, String topic,
51 TbQueueAdmin admin, TbKafkaConsumerStatsService statsService) { 51 TbQueueAdmin admin, TbKafkaConsumerStatsService statsService) {
52 super(topic); 52 super(topic);
53 - Properties props = settings.toConsumerProps(); 53 + Properties props = settings.toConsumerProps(topic);
54 props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId); 54 props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId);
55 if (groupId != null) { 55 if (groupId != null) {
56 props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); 56 props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
@@ -31,7 +31,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -31,7 +31,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
31 import org.springframework.boot.context.properties.ConfigurationProperties; 31 import org.springframework.boot.context.properties.ConfigurationProperties;
32 import org.springframework.stereotype.Component; 32 import org.springframework.stereotype.Component;
33 33
  34 +import java.util.Collections;
34 import java.util.List; 35 import java.util.List;
  36 +import java.util.Map;
35 import java.util.Properties; 37 import java.util.Properties;
36 38
37 /** 39 /**
@@ -95,6 +97,9 @@ public class TbKafkaSettings { @@ -95,6 +97,9 @@ public class TbKafkaSettings {
95 @Setter 97 @Setter
96 private List<TbKafkaProperty> other; 98 private List<TbKafkaProperty> other;
97 99
  100 + @Setter
  101 + private Map<String, List<TbKafkaProperty>> consumerPropertiesPerTopic = Collections.emptyMap();
  102 +
98 public Properties toAdminProps() { 103 public Properties toAdminProps() {
99 Properties props = toProps(); 104 Properties props = toProps();
100 props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, servers); 105 props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
@@ -103,7 +108,7 @@ public class TbKafkaSettings { @@ -103,7 +108,7 @@ public class TbKafkaSettings {
103 return props; 108 return props;
104 } 109 }
105 110
106 - public Properties toConsumerProps() { 111 + public Properties toConsumerProps(String topic) {
107 Properties props = toProps(); 112 Properties props = toProps();
108 props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers); 113 props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
109 props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords); 114 props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords);
@@ -113,6 +118,10 @@ public class TbKafkaSettings { @@ -113,6 +118,10 @@ public class TbKafkaSettings {
113 118
114 props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 119 props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
115 props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class); 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 return props; 125 return props;
117 } 126 }
118 127
@@ -19,6 +19,7 @@ import lombok.Getter; @@ -19,6 +19,7 @@ import lombok.Getter;
19 import org.springframework.beans.factory.annotation.Value; 19 import org.springframework.beans.factory.annotation.Value;
20 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 20 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
21 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
  22 +import org.thingsboard.server.common.data.StringUtils;
22 23
23 import javax.annotation.PostConstruct; 24 import javax.annotation.PostConstruct;
24 import java.util.HashMap; 25 import java.util.HashMap;
@@ -37,6 +38,8 @@ public class TbKafkaTopicConfigs { @@ -37,6 +38,8 @@ public class TbKafkaTopicConfigs {
37 private String notificationsProperties; 38 private String notificationsProperties;
38 @Value("${queue.kafka.topic-properties.js-executor}") 39 @Value("${queue.kafka.topic-properties.js-executor}")
39 private String jsExecutorProperties; 40 private String jsExecutorProperties;
  41 + @Value("${queue.kafka.topic-properties.fw-updates:}")
  42 + private String fwUpdatesProperties;
40 43
41 @Getter 44 @Getter
42 private Map<String, String> coreConfigs; 45 private Map<String, String> coreConfigs;
@@ -48,6 +51,8 @@ public class TbKafkaTopicConfigs { @@ -48,6 +51,8 @@ public class TbKafkaTopicConfigs {
48 private Map<String, String> notificationsConfigs; 51 private Map<String, String> notificationsConfigs;
49 @Getter 52 @Getter
50 private Map<String, String> jsExecutorConfigs; 53 private Map<String, String> jsExecutorConfigs;
  54 + @Getter
  55 + private Map<String, String> fwUpdatesConfigs;
51 56
52 @PostConstruct 57 @PostConstruct
53 private void init() { 58 private void init() {
@@ -56,15 +61,18 @@ public class TbKafkaTopicConfigs { @@ -56,15 +61,18 @@ public class TbKafkaTopicConfigs {
56 transportApiConfigs = getConfigs(transportApiProperties); 61 transportApiConfigs = getConfigs(transportApiProperties);
57 notificationsConfigs = getConfigs(notificationsProperties); 62 notificationsConfigs = getConfigs(notificationsProperties);
58 jsExecutorConfigs = getConfigs(jsExecutorProperties); 63 jsExecutorConfigs = getConfigs(jsExecutorProperties);
  64 + fwUpdatesConfigs = getConfigs(fwUpdatesProperties);
59 } 65 }
60 66
61 private Map<String, String> getConfigs(String properties) { 67 private Map<String, String> getConfigs(String properties) {
62 Map<String, String> configs = new HashMap<>(); 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 return configs; 77 return configs;
70 } 78 }
@@ -186,6 +186,17 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @@ -186,6 +186,17 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
186 msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); 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 @PreDestroy 200 @PreDestroy
190 private void destroy() { 201 private void destroy() {
191 if (coreAdmin != null) { 202 if (coreAdmin != null) {
@@ -21,12 +21,13 @@ import org.springframework.context.annotation.Bean; @@ -21,12 +21,13 @@ import org.springframework.context.annotation.Bean;
21 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
22 import org.thingsboard.server.common.msg.queue.ServiceType; 22 import org.thingsboard.server.common.msg.queue.ServiceType;
23 import org.thingsboard.server.gen.js.JsInvokeProtos; 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 -import org.thingsboard.server.gen.transport.TransportProtos;  
25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
  30 +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
30 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; 31 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
31 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; 32 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
32 import org.thingsboard.server.queue.TbQueueAdmin; 33 import org.thingsboard.server.queue.TbQueueAdmin;
@@ -165,14 +166,25 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { @@ -165,14 +166,25 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
165 } 166 }
166 167
167 @Override 168 @Override
168 - public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 169 + public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
169 return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); 170 return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic());
170 } 171 }
171 172
172 @Override 173 @Override
173 - public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() { 174 + public TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() {
174 return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getUsageStatsTopic(), 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 @PreDestroy 190 @PreDestroy
@@ -131,6 +131,16 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE @@ -131,6 +131,16 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE
131 } 131 }
132 132
133 @Override 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 public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 144 public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
135 return new InMemoryTbQueueProducer<>(coreSettings.getUsageStatsTopic()); 145 return new InMemoryTbQueueProducer<>(coreSettings.getUsageStatsTopic());
136 } 146 }
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 import org.thingsboard.server.gen.js.JsInvokeProtos; 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@@ -73,6 +74,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi @@ -73,6 +74,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi
73 private final TbQueueAdmin jsExecutorAdmin; 74 private final TbQueueAdmin jsExecutorAdmin;
74 private final TbQueueAdmin transportApiAdmin; 75 private final TbQueueAdmin transportApiAdmin;
75 private final TbQueueAdmin notificationAdmin; 76 private final TbQueueAdmin notificationAdmin;
  77 + private final TbQueueAdmin fwUpdatesAdmin;
76 78
77 public KafkaMonolithQueueFactory(PartitionService partitionService, TbKafkaSettings kafkaSettings, 79 public KafkaMonolithQueueFactory(PartitionService partitionService, TbKafkaSettings kafkaSettings,
78 TbServiceInfoProvider serviceInfoProvider, 80 TbServiceInfoProvider serviceInfoProvider,
@@ -98,6 +100,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi @@ -98,6 +100,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi
98 this.jsExecutorAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorConfigs()); 100 this.jsExecutorAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorConfigs());
99 this.transportApiAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiConfigs()); 101 this.transportApiAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiConfigs());
100 this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs()); 102 this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs());
  103 + this.fwUpdatesAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getFwUpdatesConfigs());
101 } 104 }
102 105
103 @Override 106 @Override
@@ -274,6 +277,29 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi @@ -274,6 +277,29 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi
274 } 277 }
275 278
276 @Override 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 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 303 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
278 TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder(); 304 TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
279 requestBuilder.settings(kafkaSettings); 305 requestBuilder.settings(kafkaSettings);
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 import org.thingsboard.server.gen.js.JsInvokeProtos; 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@@ -70,6 +71,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { @@ -70,6 +71,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory {
70 private final TbQueueAdmin jsExecutorAdmin; 71 private final TbQueueAdmin jsExecutorAdmin;
71 private final TbQueueAdmin transportApiAdmin; 72 private final TbQueueAdmin transportApiAdmin;
72 private final TbQueueAdmin notificationAdmin; 73 private final TbQueueAdmin notificationAdmin;
  74 + private final TbQueueAdmin fwUpdatesAdmin;
73 75
74 public KafkaTbCoreQueueFactory(PartitionService partitionService, TbKafkaSettings kafkaSettings, 76 public KafkaTbCoreQueueFactory(PartitionService partitionService, TbKafkaSettings kafkaSettings,
75 TbServiceInfoProvider serviceInfoProvider, 77 TbServiceInfoProvider serviceInfoProvider,
@@ -93,6 +95,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { @@ -93,6 +95,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory {
93 this.jsExecutorAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorConfigs()); 95 this.jsExecutorAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorConfigs());
94 this.transportApiAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiConfigs()); 96 this.transportApiAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiConfigs());
95 this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs()); 97 this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs());
  98 + this.fwUpdatesAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getFwUpdatesConfigs());
96 } 99 }
97 100
98 @Override 101 @Override
@@ -242,6 +245,29 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { @@ -242,6 +245,29 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory {
242 } 245 }
243 246
244 @Override 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 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 271 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
246 TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder(); 272 TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
247 requestBuilder.settings(kafkaSettings); 273 requestBuilder.settings(kafkaSettings);
@@ -22,6 +22,7 @@ import org.springframework.stereotype.Component; @@ -22,6 +22,7 @@ import org.springframework.stereotype.Component;
22 import org.thingsboard.server.common.msg.queue.ServiceType; 22 import org.thingsboard.server.common.msg.queue.ServiceType;
23 import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; 23 import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest;
24 import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; 24 import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse;
  25 +import org.thingsboard.server.gen.transport.TransportProtos.*;
25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 26 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
@@ -191,6 +192,17 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @@ -191,6 +192,17 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
191 } 192 }
192 193
193 @Override 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 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 206 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
195 return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); 207 return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic());
196 } 208 }
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 import org.thingsboard.server.gen.js.JsInvokeProtos; 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@@ -165,6 +166,17 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @@ -165,6 +166,17 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory {
165 } 166 }
166 167
167 @Override 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 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 180 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
169 return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); 181 return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic());
170 } 182 }
@@ -24,6 +24,7 @@ import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; @@ -24,6 +24,7 @@ import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest;
24 import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; 24 import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse;
25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 26 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  27 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 30 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@@ -189,6 +190,17 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE @@ -189,6 +190,17 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE
189 } 190 }
190 191
191 @Override 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 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 204 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
193 return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic()); 205 return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic());
194 } 206 }
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 import org.thingsboard.server.gen.js.JsInvokeProtos; 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@@ -171,6 +172,17 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory { @@ -171,6 +172,17 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory {
171 } 172 }
172 173
173 @Override 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 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 186 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
175 return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic()); 187 return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic());
176 } 188 }
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 import org.thingsboard.server.gen.js.JsInvokeProtos; 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@@ -188,6 +189,17 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul @@ -188,6 +189,17 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul
188 } 189 }
189 190
190 @Override 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 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 203 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
192 return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic()); 204 return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic());
193 } 205 }
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
23 import org.thingsboard.server.gen.js.JsInvokeProtos; 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
  26 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@@ -171,6 +172,17 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory { @@ -171,6 +172,17 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory {
171 } 172 }
172 173
173 @Override 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 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { 186 public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
175 return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic()); 187 return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic());
176 } 188 }
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.queue.provider; 16 package org.thingsboard.server.queue.provider;
17 17
18 import org.thingsboard.server.gen.js.JsInvokeProtos; 18 import org.thingsboard.server.gen.js.JsInvokeProtos;
  19 +import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
19 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 20 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
20 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 21 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
21 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 22 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
@@ -86,6 +87,20 @@ public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory { @@ -86,6 +87,20 @@ public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory {
86 TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer(); 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 * Used to consume high priority messages by TB Core Service 104 * Used to consume high priority messages by TB Core Service
90 * 105 *
91 * @return 106 * @return
@@ -26,6 +26,9 @@ public class TbQueueCoreSettings { @@ -26,6 +26,9 @@ public class TbQueueCoreSettings {
26 @Value("${queue.core.topic}") 26 @Value("${queue.core.topic}")
27 private String topic; 27 private String topic;
28 28
  29 + @Value("${queue.core.firmware.topic:tb_firmware}")
  30 + private String firmwareTopic;
  31 +
29 @Value("${queue.core.usage-stats-topic:tb_usage_stats}") 32 @Value("${queue.core.usage-stats-topic:tb_usage_stats}")
30 private String usageStatsTopic; 33 private String usageStatsTopic;
31 34
@@ -340,17 +340,35 @@ message ProvisionDeviceCredentialsMsg { @@ -340,17 +340,35 @@ message ProvisionDeviceCredentialsMsg {
340 } 340 }
341 341
342 message ProvisionDeviceResponseMsg { 342 message ProvisionDeviceResponseMsg {
343 - ProvisionResponseStatus status = 1; 343 + ResponseStatus status = 1;
344 CredentialsType credentialsType = 2; 344 CredentialsType credentialsType = 2;
345 string credentialsValue = 3; 345 string credentialsValue = 3;
346 } 346 }
347 347
348 -enum ProvisionResponseStatus { 348 +enum ResponseStatus {
349 UNKNOWN = 0; 349 UNKNOWN = 0;
350 SUCCESS = 1; 350 SUCCESS = 1;
351 NOT_FOUND = 2; 351 NOT_FOUND = 2;
352 FAILURE = 3; 352 FAILURE = 3;
353 } 353 }
  354 +
  355 +message GetFirmwareRequestMsg {
  356 + int64 deviceIdMSB = 1;
  357 + int64 deviceIdLSB = 2;
  358 + int64 tenantIdMSB = 3;
  359 + int64 tenantIdLSB = 4;
  360 +}
  361 +
  362 +message GetFirmwareResponseMsg {
  363 + ResponseStatus responseStatus = 1;
  364 + int64 firmwareIdMSB = 2;
  365 + int64 firmwareIdLSB = 3;
  366 + string title = 4;
  367 + string version = 5;
  368 + string contentType = 6;
  369 + string fileName = 7;
  370 +}
  371 +
354 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. 372 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level.
355 message SubscriptionInfoProto { 373 message SubscriptionInfoProto {
356 int64 lastActivityTime = 1; 374 int64 lastActivityTime = 1;
@@ -571,6 +589,7 @@ message TransportApiRequestMsg { @@ -571,6 +589,7 @@ message TransportApiRequestMsg {
571 ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7; 589 ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7;
572 ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; 590 ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8;
573 GetResourceRequestMsg resourceRequestMsg = 9; 591 GetResourceRequestMsg resourceRequestMsg = 9;
  592 + GetFirmwareRequestMsg firmwareRequestMsg = 10;
574 } 593 }
575 594
576 /* Response from ThingsBoard Core Service to Transport Service */ 595 /* Response from ThingsBoard Core Service to Transport Service */
@@ -581,6 +600,7 @@ message TransportApiResponseMsg { @@ -581,6 +600,7 @@ message TransportApiResponseMsg {
581 ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4; 600 ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4;
582 LwM2MResponseMsg lwM2MResponseMsg = 6; 601 LwM2MResponseMsg lwM2MResponseMsg = 6;
583 GetResourceResponseMsg resourceResponseMsg = 7; 602 GetResourceResponseMsg resourceResponseMsg = 7;
  603 + GetFirmwareResponseMsg firmwareResponseMsg = 8;
584 } 604 }
585 605
586 /* Messages that are handled by ThingsBoard Core Service */ 606 /* Messages that are handled by ThingsBoard Core Service */
@@ -645,3 +665,13 @@ message ToUsageStatsServiceMsg { @@ -645,3 +665,13 @@ message ToUsageStatsServiceMsg {
645 int64 customerIdMSB = 6; 665 int64 customerIdMSB = 6;
646 int64 customerIdLSB = 7; 666 int64 customerIdLSB = 7;
647 } 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 +}
@@ -407,7 +407,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -407,7 +407,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
407 @Override 407 @Override
408 public void onSuccess(TransportProtos.ProvisionDeviceResponseMsg msg) { 408 public void onSuccess(TransportProtos.ProvisionDeviceResponseMsg msg) {
409 CoAP.ResponseCode responseCode = CoAP.ResponseCode.CREATED; 409 CoAP.ResponseCode responseCode = CoAP.ResponseCode.CREATED;
410 - if (!msg.getStatus().equals(TransportProtos.ProvisionResponseStatus.SUCCESS)) { 410 + if (!msg.getStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) {
411 responseCode = CoAP.ResponseCode.BAD_REQUEST; 411 responseCode = CoAP.ResponseCode.BAD_REQUEST;
412 } 412 }
413 if (payloadType.equals(TransportPayloadType.JSON)) { 413 if (payloadType.equals(TransportPayloadType.JSON)) {
@@ -20,7 +20,10 @@ import com.google.gson.JsonParser; @@ -20,7 +20,10 @@ import com.google.gson.JsonParser;
20 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
21 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  23 +import org.springframework.core.io.ByteArrayResource;
  24 +import org.springframework.http.HttpHeaders;
23 import org.springframework.http.HttpStatus; 25 import org.springframework.http.HttpStatus;
  26 +import org.springframework.http.MediaType;
24 import org.springframework.http.ResponseEntity; 27 import org.springframework.http.ResponseEntity;
25 import org.springframework.util.StringUtils; 28 import org.springframework.util.StringUtils;
26 import org.springframework.web.bind.annotation.PathVariable; 29 import org.springframework.web.bind.annotation.PathVariable;
@@ -41,7 +44,6 @@ import org.thingsboard.server.common.transport.auth.SessionInfoCreator; @@ -41,7 +44,6 @@ import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
41 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; 44 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
42 import org.thingsboard.server.gen.transport.TransportProtos; 45 import org.thingsboard.server.gen.transport.TransportProtos;
43 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 46 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
44 -import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;  
45 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; 47 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
46 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; 48 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
47 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; 49 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
@@ -53,7 +55,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMs @@ -53,7 +55,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMs
53 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; 55 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
54 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; 56 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
55 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; 57 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
56 -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;  
57 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; 58 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
58 59
59 import javax.servlet.http.HttpServletRequest; 60 import javax.servlet.http.HttpServletRequest;
@@ -204,6 +205,25 @@ public class DeviceApiController { @@ -204,6 +205,25 @@ public class DeviceApiController {
204 return responseWriter; 205 return responseWriter;
205 } 206 }
206 207
  208 + @RequestMapping(value = "/{deviceToken}/firmware", method = RequestMethod.GET)
  209 + public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken,
  210 + @RequestParam(value = "title") String title,
  211 + @RequestParam(value = "version") String version,
  212 + @RequestParam(value = "chunkSize", required = false, defaultValue = "0") int chunkSize,
  213 + @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
  214 + DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
  215 + transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
  216 + new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
  217 + TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
  218 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  219 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  220 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  221 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();
  222 + transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, chunkSize, chunk));
  223 + }));
  224 + return responseWriter;
  225 + }
  226 +
207 @RequestMapping(value = "/provision", method = RequestMethod.POST) 227 @RequestMapping(value = "/provision", method = RequestMethod.POST)
208 public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) { 228 public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
209 DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>(); 229 DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
@@ -258,6 +278,47 @@ public class DeviceApiController { @@ -258,6 +278,47 @@ public class DeviceApiController {
258 } 278 }
259 } 279 }
260 280
  281 + private class GetFirmwareCallback implements TransportServiceCallback<TransportProtos.GetFirmwareResponseMsg> {
  282 + private final DeferredResult<ResponseEntity> responseWriter;
  283 + private final String title;
  284 + private final String version;
  285 + private final int chuckSize;
  286 + private final int chuck;
  287 +
  288 + GetFirmwareCallback(DeferredResult<ResponseEntity> responseWriter, String title, String version, int chuckSize, int chuck) {
  289 + this.responseWriter = responseWriter;
  290 + this.title = title;
  291 + this.version = version;
  292 + this.chuckSize = chuckSize;
  293 + this.chuck = chuck;
  294 + }
  295 +
  296 + @Override
  297 + public void onSuccess(TransportProtos.GetFirmwareResponseMsg firmwareResponseMsg) {
  298 + if (!TransportProtos.ResponseStatus.SUCCESS.equals(firmwareResponseMsg.getResponseStatus())) {
  299 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND));
  300 + } else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) {
  301 + String firmwareId = new UUID(firmwareResponseMsg.getFirmwareIdMSB(), firmwareResponseMsg.getFirmwareIdLSB()).toString();
  302 + ByteArrayResource resource = new ByteArrayResource(transportContext.getFirmwareCacheReader().get(firmwareId, chuckSize, chuck));
  303 + ResponseEntity<ByteArrayResource> response = ResponseEntity.ok()
  304 + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName())
  305 + .header("x-filename", firmwareResponseMsg.getFileName())
  306 + .contentLength(resource.contentLength())
  307 + .contentType(parseMediaType(firmwareResponseMsg.getContentType()))
  308 + .body(resource);
  309 + responseWriter.setResult(response);
  310 + } else {
  311 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
  312 + }
  313 + }
  314 +
  315 + @Override
  316 + public void onError(Throwable e) {
  317 + log.warn("Failed to process request", e);
  318 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
  319 + }
  320 + }
  321 +
261 private static class SessionCloseOnErrorCallback implements TransportServiceCallback<Void> { 322 private static class SessionCloseOnErrorCallback implements TransportServiceCallback<Void> {
262 private final TransportService transportService; 323 private final TransportService transportService;
263 private final SessionInfoProto sessionInfo; 324 private final SessionInfoProto sessionInfo;
@@ -338,4 +399,12 @@ public class DeviceApiController { @@ -338,4 +399,12 @@ public class DeviceApiController {
338 .build(), TransportServiceCallback.EMPTY); 399 .build(), TransportServiceCallback.EMPTY);
339 } 400 }
340 401
  402 + private static MediaType parseMediaType(String contentType) {
  403 + try {
  404 + return MediaType.parseMediaType(contentType);
  405 + } catch (Exception e) {
  406 + return MediaType.APPLICATION_OCTET_STREAM;
  407 + }
  408 + }
  409 +
341 } 410 }
@@ -29,7 +29,6 @@ import redis.clients.jedis.ScanResult; @@ -29,7 +29,6 @@ import redis.clients.jedis.ScanResult;
29 import java.util.Collection; 29 import java.util.Collection;
30 import java.util.LinkedList; 30 import java.util.LinkedList;
31 31
32 -@Service  
33 public class TbLwM2mRedisSecurityStore implements EditableSecurityStore { 32 public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
34 private static final String SEC_EP = "SEC#EP#"; 33 private static final String SEC_EP = "SEC#EP#";
35 34
@@ -40,12 +40,14 @@ import io.netty.util.ReferenceCountUtil; @@ -40,12 +40,14 @@ import io.netty.util.ReferenceCountUtil;
40 import io.netty.util.concurrent.Future; 40 import io.netty.util.concurrent.Future;
41 import io.netty.util.concurrent.GenericFutureListener; 41 import io.netty.util.concurrent.GenericFutureListener;
42 import lombok.extern.slf4j.Slf4j; 42 import lombok.extern.slf4j.Slf4j;
  43 +import org.apache.commons.lang3.StringUtils;
43 import org.thingsboard.server.common.data.DataConstants; 44 import org.thingsboard.server.common.data.DataConstants;
44 import org.thingsboard.server.common.data.Device; 45 import org.thingsboard.server.common.data.Device;
45 import org.thingsboard.server.common.data.DeviceProfile; 46 import org.thingsboard.server.common.data.DeviceProfile;
46 import org.thingsboard.server.common.data.DeviceTransportType; 47 import org.thingsboard.server.common.data.DeviceTransportType;
47 import org.thingsboard.server.common.data.TransportPayloadType; 48 import org.thingsboard.server.common.data.TransportPayloadType;
48 import org.thingsboard.server.common.data.device.profile.MqttTopics; 49 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  50 +import org.thingsboard.server.common.data.id.FirmwareId;
49 import org.thingsboard.server.common.msg.EncryptionUtil; 51 import org.thingsboard.server.common.msg.EncryptionUtil;
50 import org.thingsboard.server.common.msg.tools.TbRateLimitsException; 52 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
51 import org.thingsboard.server.common.transport.SessionMsgListener; 53 import org.thingsboard.server.common.transport.SessionMsgListener;
@@ -69,10 +71,10 @@ import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher; @@ -69,10 +71,10 @@ import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher;
69 import org.thingsboard.server.common.transport.util.SslUtil; 71 import org.thingsboard.server.common.transport.util.SslUtil;
70 72
71 import javax.net.ssl.SSLPeerUnverifiedException; 73 import javax.net.ssl.SSLPeerUnverifiedException;
72 -import java.security.cert.Certificate;  
73 -import java.security.cert.X509Certificate;  
74 import java.io.IOException; 74 import java.io.IOException;
75 import java.net.InetSocketAddress; 75 import java.net.InetSocketAddress;
  76 +import java.security.cert.Certificate;
  77 +import java.security.cert.X509Certificate;
76 import java.util.ArrayList; 78 import java.util.ArrayList;
77 import java.util.List; 79 import java.util.List;
78 import java.util.Optional; 80 import java.util.Optional;
@@ -80,7 +82,10 @@ import java.util.UUID; @@ -80,7 +82,10 @@ import java.util.UUID;
80 import java.util.concurrent.ConcurrentHashMap; 82 import java.util.concurrent.ConcurrentHashMap;
81 import java.util.concurrent.ConcurrentMap; 83 import java.util.concurrent.ConcurrentMap;
82 import java.util.concurrent.TimeUnit; 84 import java.util.concurrent.TimeUnit;
  85 +import java.util.regex.Matcher;
  86 +import java.util.regex.Pattern;
83 87
  88 +import static com.amazonaws.util.StringUtils.UTF8;
84 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; 89 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED;
85 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; 90 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED;
86 import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; 91 import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK;
@@ -99,6 +104,10 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; @@ -99,6 +104,10 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
99 @Slf4j 104 @Slf4j
100 public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener<Future<? super Void>>, SessionMsgListener { 105 public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
101 106
  107 + private static final Pattern FW_PATTERN = Pattern.compile("v2/fw/request/(?<requestId>\\d+)/chunk/(?<chunk>\\d+)");
  108 +
  109 + private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE";
  110 +
102 private static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE; 111 private static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE;
103 112
104 private final UUID sessionId; 113 private final UUID sessionId;
@@ -112,6 +121,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -112,6 +121,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
112 private volatile InetSocketAddress address; 121 private volatile InetSocketAddress address;
113 private volatile GatewaySessionHandler gatewaySessionHandler; 122 private volatile GatewaySessionHandler gatewaySessionHandler;
114 123
  124 + private final ConcurrentHashMap<String, String> fwSessions;
  125 + private final ConcurrentHashMap<String, Integer> fwChunkSizes;
  126 +
115 MqttTransportHandler(MqttTransportContext context, SslHandler sslHandler) { 127 MqttTransportHandler(MqttTransportContext context, SslHandler sslHandler) {
116 this.sessionId = UUID.randomUUID(); 128 this.sessionId = UUID.randomUUID();
117 this.context = context; 129 this.context = context;
@@ -120,6 +132,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -120,6 +132,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
120 this.sslHandler = sslHandler; 132 this.sslHandler = sslHandler;
121 this.mqttQoSMap = new ConcurrentHashMap<>(); 133 this.mqttQoSMap = new ConcurrentHashMap<>();
122 this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap, context); 134 this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap, context);
  135 + this.fwSessions = new ConcurrentHashMap<>();
  136 + this.fwChunkSizes = new ConcurrentHashMap<>();
123 } 137 }
124 138
125 @Override 139 @Override
@@ -280,6 +294,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -280,6 +294,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
280 294
281 private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { 295 private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) {
282 try { 296 try {
  297 + Matcher fwMatcher;
283 MqttTransportAdaptor payloadAdaptor = deviceSessionCtx.getPayloadAdaptor(); 298 MqttTransportAdaptor payloadAdaptor = deviceSessionCtx.getPayloadAdaptor();
284 if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) { 299 if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) {
285 TransportProtos.PostAttributeMsg postAttributeMsg = payloadAdaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg); 300 TransportProtos.PostAttributeMsg postAttributeMsg = payloadAdaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg);
@@ -299,6 +314,38 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -299,6 +314,38 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
299 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { 314 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) {
300 TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); 315 TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg);
301 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); 316 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg));
  317 + } else if ((fwMatcher = FW_PATTERN.matcher(topicName)).find()) {
  318 + String payload = mqttMsg.content().toString(UTF8);
  319 + int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0;
  320 + String requestId = fwMatcher.group("requestId");
  321 + int chunk = Integer.parseInt(fwMatcher.group("chunk"));
  322 +
  323 + if (chunkSize > 0) {
  324 + this.fwChunkSizes.put(requestId, chunkSize);
  325 + } else {
  326 + chunkSize = fwChunkSizes.getOrDefault(requestId, 0);
  327 + }
  328 +
  329 + if (chunkSize > context.getMaxPayloadSize()) {
  330 + sendFirmwareError(ctx, PAYLOAD_TOO_LARGE);
  331 + return;
  332 + }
  333 +
  334 + String firmwareId = fwSessions.get(requestId);
  335 +
  336 + if (firmwareId != null) {
  337 + sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk);
  338 + } else {
  339 + TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo();
  340 + TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
  341 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  342 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  343 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  344 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  345 + .build();
  346 + transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg,
  347 + new FirmwareCallback(ctx, mqttMsg.variableHeader().packetId(), getFirmwareRequestMsg, requestId, chunkSize, chunk));
  348 + }
302 } else { 349 } else {
303 transportService.reportActivity(deviceSessionCtx.getSessionInfo()); 350 transportService.reportActivity(deviceSessionCtx.getSessionInfo());
304 ack(ctx, msgId); 351 ack(ctx, msgId);
@@ -366,6 +413,65 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -366,6 +413,65 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
366 } 413 }
367 } 414 }
368 415
  416 + private class FirmwareCallback implements TransportServiceCallback<TransportProtos.GetFirmwareResponseMsg> {
  417 + private final ChannelHandlerContext ctx;
  418 + private final int msgId;
  419 + private final TransportProtos.GetFirmwareRequestMsg msg;
  420 + private final String requestId;
  421 + private final int chunkSize;
  422 + private final int chunk;
  423 +
  424 + FirmwareCallback(ChannelHandlerContext ctx, int msgId, TransportProtos.GetFirmwareRequestMsg msg, String requestId, int chunkSize, int chunk) {
  425 + this.ctx = ctx;
  426 + this.msgId = msgId;
  427 + this.msg = msg;
  428 + this.requestId = requestId;
  429 + this.chunkSize = chunkSize;
  430 + this.chunk = chunk;
  431 + }
  432 +
  433 + @Override
  434 + public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
  435 + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
  436 + FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB()));
  437 + fwSessions.put(requestId, firmwareId.toString());
  438 + sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk);
  439 + } else {
  440 + sendFirmwareError(ctx, response.getResponseStatus().toString());
  441 + }
  442 + }
  443 +
  444 + @Override
  445 + public void onError(Throwable e) {
  446 + log.trace("[{}] Failed to get firmware: {}", sessionId, msg, e);
  447 + processDisconnect(ctx);
  448 + }
  449 + }
  450 +
  451 + private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk) {
  452 + log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId);
  453 + ack(ctx, msgId);
  454 + try {
  455 + byte[] firmwareChunk = context.getFirmwareCacheReader().get(firmwareId, chunkSize, chunk);
  456 + deviceSessionCtx.getPayloadAdaptor()
  457 + .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk)
  458 + .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
  459 + if (firmwareChunk != null && chunkSize != firmwareChunk.length) {
  460 + scheduler.schedule(() -> processDisconnect(ctx), 60, TimeUnit.SECONDS);
  461 + }
  462 + } catch (Exception e) {
  463 + log.trace("[{}] Failed to send firmware response!", sessionId, e);
  464 + }
  465 + }
  466 +
  467 + private void sendFirmwareError(ChannelHandlerContext ctx, String error) {
  468 + log.warn("[{}] {}", sessionId, error);
  469 + deviceSessionCtx.getChannel().writeAndFlush(deviceSessionCtx
  470 + .getPayloadAdaptor()
  471 + .createMqttPublishMsg(deviceSessionCtx, MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC, error.getBytes()));
  472 + processDisconnect(ctx);
  473 + }
  474 +
369 private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) { 475 private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) {
370 if (!checkConnected(ctx, mqttMsg)) { 476 if (!checkConnected(ctx, mqttMsg)) {
371 return; 477 return;
@@ -396,6 +502,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -396,6 +502,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
396 case MqttTopics.GATEWAY_RPC_TOPIC: 502 case MqttTopics.GATEWAY_RPC_TOPIC:
397 case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC: 503 case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC:
398 case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC: 504 case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC:
  505 + case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC:
  506 + case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC:
399 registerSubQoS(topic, grantedQoSList, reqQoS); 507 registerSubQoS(topic, grantedQoSList, reqQoS);
400 break; 508 break;
401 default: 509 default:
@@ -21,8 +21,6 @@ import com.google.gson.JsonObject; @@ -21,8 +21,6 @@ import com.google.gson.JsonObject;
21 import com.google.gson.JsonParser; 21 import com.google.gson.JsonParser;
22 import com.google.gson.JsonSyntaxException; 22 import com.google.gson.JsonSyntaxException;
23 import io.netty.buffer.ByteBuf; 23 import io.netty.buffer.ByteBuf;
24 -import io.netty.buffer.ByteBufAllocator;  
25 -import io.netty.buffer.UnpooledByteBufAllocator;  
26 import io.netty.handler.codec.mqtt.MqttFixedHeader; 24 import io.netty.handler.codec.mqtt.MqttFixedHeader;
27 import io.netty.handler.codec.mqtt.MqttMessage; 25 import io.netty.handler.codec.mqtt.MqttMessage;
28 import io.netty.handler.codec.mqtt.MqttMessageType; 26 import io.netty.handler.codec.mqtt.MqttMessageType;
@@ -31,10 +29,10 @@ import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; @@ -31,10 +29,10 @@ import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
31 import lombok.extern.slf4j.Slf4j; 29 import lombok.extern.slf4j.Slf4j;
32 import org.springframework.stereotype.Component; 30 import org.springframework.stereotype.Component;
33 import org.springframework.util.StringUtils; 31 import org.springframework.util.StringUtils;
  32 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
34 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 33 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
35 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 34 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
36 import org.thingsboard.server.gen.transport.TransportProtos; 35 import org.thingsboard.server.gen.transport.TransportProtos;
37 -import org.thingsboard.server.common.data.device.profile.MqttTopics;  
38 import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; 36 import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
39 37
40 import java.nio.charset.Charset; 38 import java.nio.charset.Charset;
@@ -55,7 +53,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -55,7 +53,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
55 protected static final Charset UTF8 = StandardCharsets.UTF_8; 53 protected static final Charset UTF8 = StandardCharsets.UTF_8;
56 54
57 private static final Gson GSON = new Gson(); 55 private static final Gson GSON = new Gson();
58 - private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);  
59 56
60 @Override 57 @Override
61 public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { 58 public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
@@ -153,6 +150,11 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -153,6 +150,11 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
153 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, JsonConverter.toJson(provisionResponse))); 150 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, JsonConverter.toJson(provisionResponse)));
154 } 151 }
155 152
  153 + @Override
  154 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) {
  155 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/chunk/" + chunk, firmwareChunk));
  156 + }
  157 +
156 public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { 158 public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
157 String payload = validatePayload(sessionId, payloadData, false); 159 String payload = validatePayload(sessionId, payloadData, false);
158 try { 160 try {
@@ -15,8 +15,14 @@ @@ -15,8 +15,14 @@
15 */ 15 */
16 package org.thingsboard.server.transport.mqtt.adaptors; 16 package org.thingsboard.server.transport.mqtt.adaptors;
17 17
  18 +import io.netty.buffer.ByteBuf;
  19 +import io.netty.buffer.ByteBufAllocator;
  20 +import io.netty.buffer.UnpooledByteBufAllocator;
  21 +import io.netty.handler.codec.mqtt.MqttFixedHeader;
18 import io.netty.handler.codec.mqtt.MqttMessage; 22 import io.netty.handler.codec.mqtt.MqttMessage;
  23 +import io.netty.handler.codec.mqtt.MqttMessageType;
19 import io.netty.handler.codec.mqtt.MqttPublishMessage; 24 import io.netty.handler.codec.mqtt.MqttPublishMessage;
  25 +import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
20 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 26 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
21 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 27 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
22 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; 28 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
@@ -39,6 +45,8 @@ import java.util.Optional; @@ -39,6 +45,8 @@ import java.util.Optional;
39 */ 45 */
40 public interface MqttTransportAdaptor { 46 public interface MqttTransportAdaptor {
41 47
  48 + ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
  49 +
42 PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; 50 PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;
43 51
44 PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; 52 PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;
@@ -69,4 +77,14 @@ public interface MqttTransportAdaptor { @@ -69,4 +77,14 @@ public interface MqttTransportAdaptor {
69 77
70 Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; 78 Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException;
71 79
  80 + Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException;
  81 +
  82 + default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) {
  83 + MqttFixedHeader mqttFixedHeader =
  84 + new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
  85 + MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
  86 + ByteBuf payload = ALLOCATOR.buffer();
  87 + payload.writeBytes(payloadInBytes);
  88 + return new MqttPublishMessage(mqttFixedHeader, header, payload);
  89 + }
72 } 90 }
@@ -22,13 +22,8 @@ import com.google.protobuf.DynamicMessage; @@ -22,13 +22,8 @@ import com.google.protobuf.DynamicMessage;
22 import com.google.protobuf.InvalidProtocolBufferException; 22 import com.google.protobuf.InvalidProtocolBufferException;
23 import com.google.protobuf.util.JsonFormat; 23 import com.google.protobuf.util.JsonFormat;
24 import io.netty.buffer.ByteBuf; 24 import io.netty.buffer.ByteBuf;
25 -import io.netty.buffer.ByteBufAllocator;  
26 -import io.netty.buffer.UnpooledByteBufAllocator;  
27 -import io.netty.handler.codec.mqtt.MqttFixedHeader;  
28 import io.netty.handler.codec.mqtt.MqttMessage; 25 import io.netty.handler.codec.mqtt.MqttMessage;
29 -import io.netty.handler.codec.mqtt.MqttMessageType;  
30 import io.netty.handler.codec.mqtt.MqttPublishMessage; 26 import io.netty.handler.codec.mqtt.MqttPublishMessage;
31 -import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;  
32 import lombok.extern.slf4j.Slf4j; 27 import lombok.extern.slf4j.Slf4j;
33 import org.springframework.stereotype.Component; 28 import org.springframework.stereotype.Component;
34 import org.springframework.util.StringUtils; 29 import org.springframework.util.StringUtils;
@@ -47,8 +42,6 @@ import java.util.Optional; @@ -47,8 +42,6 @@ import java.util.Optional;
47 @Slf4j 42 @Slf4j
48 public class ProtoMqttAdaptor implements MqttTransportAdaptor { 43 public class ProtoMqttAdaptor implements MqttTransportAdaptor {
49 44
50 - private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);  
51 -  
52 @Override 45 @Override
53 public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { 46 public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
54 DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx; 47 DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx;
@@ -149,7 +142,6 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { @@ -149,7 +142,6 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
149 } 142 }
150 } 143 }
151 144
152 -  
153 @Override 145 @Override
154 public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException { 146 public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException {
155 DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx; 147 DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx;
@@ -173,6 +165,11 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { @@ -173,6 +165,11 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
173 } 165 }
174 166
175 @Override 167 @Override
  168 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException {
  169 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/" + chunk, firmwareChunk));
  170 + }
  171 +
  172 + @Override
176 public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { 173 public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
177 if (!StringUtils.isEmpty(responseMsg.getError())) { 174 if (!StringUtils.isEmpty(responseMsg.getError())) {
178 throw new AdaptorException(responseMsg.getError()); 175 throw new AdaptorException(responseMsg.getError());
@@ -210,15 +207,6 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { @@ -210,15 +207,6 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
210 return bytes; 207 return bytes;
211 } 208 }
212 209
213 - private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadBytes) {  
214 - MqttFixedHeader mqttFixedHeader =  
215 - new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);  
216 - MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());  
217 - ByteBuf payload = ALLOCATOR.buffer();  
218 - payload.writeBytes(payloadBytes);  
219 - return new MqttPublishMessage(mqttFixedHeader, header, payload);  
220 - }  
221 -  
222 private int getRequestId(String topicName, String topic) { 210 private int getRequestId(String topicName, String topic) {
223 return Integer.parseInt(topicName.substring(topic.length())); 211 return Integer.parseInt(topicName.substring(topic.length()));
224 } 212 }
@@ -20,10 +20,9 @@ import lombok.Data; @@ -20,10 +20,9 @@ import lombok.Data;
20 import lombok.Getter; 20 import lombok.Getter;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
23 -import org.springframework.stereotype.Service; 23 +import org.thingsboard.server.cache.firmware.FirmwareCacheReader;
24 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 24 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
25 import org.thingsboard.server.queue.scheduler.SchedulerComponent; 25 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
26 -import org.thingsboard.server.queue.util.TbTransportComponent;  
27 26
28 import javax.annotation.PostConstruct; 27 import javax.annotation.PostConstruct;
29 import javax.annotation.PreDestroy; 28 import javax.annotation.PreDestroy;
@@ -51,6 +50,11 @@ public abstract class TransportContext { @@ -51,6 +50,11 @@ public abstract class TransportContext {
51 @Getter 50 @Getter
52 private ExecutorService executor; 51 private ExecutorService executor;
53 52
  53 +
  54 + @Getter
  55 + @Autowired
  56 + private FirmwareCacheReader firmwareCacheReader;
  57 +
54 @PostConstruct 58 @PostConstruct
55 public void init() { 59 public void init() {
56 executor = Executors.newWorkStealingPool(50); 60 executor = Executors.newWorkStealingPool(50);
@@ -24,6 +24,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; @@ -24,6 +24,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
24 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg; 26 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
  27 +import org.thingsboard.server.gen.transport.TransportProtos.GetFirmwareRequestMsg;
  28 +import org.thingsboard.server.gen.transport.TransportProtos.GetFirmwareResponseMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
28 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg; 30 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg;
29 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceResponseMsg; 31 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceResponseMsg;
@@ -98,6 +100,8 @@ public interface TransportService { @@ -98,6 +100,8 @@ public interface TransportService {
98 100
99 void process(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback<Void> callback); 101 void process(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback<Void> callback);
100 102
  103 + void process(SessionInfoProto sessionInfoProto, GetFirmwareRequestMsg msg, TransportServiceCallback<GetFirmwareResponseMsg> callback);
  104 +
101 SessionMetaData registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener); 105 SessionMetaData registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener);
102 106
103 SessionMetaData registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout); 107 SessionMetaData registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout);
@@ -44,7 +44,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; @@ -44,7 +44,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
44 import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; 44 import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
45 import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; 45 import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
46 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; 46 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
47 -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus; 47 +import org.thingsboard.server.gen.transport.TransportProtos.ResponseStatus;
48 import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto; 48 import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto;
49 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; 49 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
50 import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg; 50 import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg;
@@ -427,12 +427,12 @@ public class JsonConverter { @@ -427,12 +427,12 @@ public class JsonConverter {
427 427
428 private static JsonObject toJson(ProvisionDeviceResponseMsg payload, boolean toGateway, int requestId) { 428 private static JsonObject toJson(ProvisionDeviceResponseMsg payload, boolean toGateway, int requestId) {
429 JsonObject result = new JsonObject(); 429 JsonObject result = new JsonObject();
430 - if (payload.getStatus() == TransportProtos.ProvisionResponseStatus.NOT_FOUND) { 430 + if (payload.getStatus() == ResponseStatus.NOT_FOUND) {
431 result.addProperty("errorMsg", "Provision data was not found!"); 431 result.addProperty("errorMsg", "Provision data was not found!");
432 - result.addProperty("status", ProvisionResponseStatus.NOT_FOUND.name());  
433 - } else if (payload.getStatus() == TransportProtos.ProvisionResponseStatus.FAILURE) { 432 + result.addProperty("status", ResponseStatus.NOT_FOUND.name());
  433 + } else if (payload.getStatus() == TransportProtos.ResponseStatus.FAILURE) {
434 result.addProperty("errorMsg", "Failed to provision device!"); 434 result.addProperty("errorMsg", "Failed to provision device!");
435 - result.addProperty("status", ProvisionResponseStatus.FAILURE.name()); 435 + result.addProperty("status", ResponseStatus.FAILURE.name());
436 } else { 436 } else {
437 if (toGateway) { 437 if (toGateway) {
438 result.addProperty("id", requestId); 438 result.addProperty("id", requestId);
@@ -449,7 +449,7 @@ public class JsonConverter { @@ -449,7 +449,7 @@ public class JsonConverter {
449 break; 449 break;
450 } 450 }
451 result.addProperty("credentialsType", payload.getCredentialsType().name()); 451 result.addProperty("credentialsType", payload.getCredentialsType().name());
452 - result.addProperty("status", ProvisionResponseStatus.SUCCESS.name()); 452 + result.addProperty("status", ResponseStatus.SUCCESS.name());
453 } 453 }
454 return result; 454 return result;
455 } 455 }
@@ -380,7 +380,6 @@ public class DefaultTransportService implements TransportService { @@ -380,7 +380,6 @@ public class DefaultTransportService implements TransportService {
380 AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor); 380 AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor);
381 } 381 }
382 382
383 -  
384 @Override 383 @Override
385 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto msg, TransportServiceCallback<Void> callback) { 384 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto msg, TransportServiceCallback<Void> callback) {
386 if (log.isTraceEnabled()) { 385 if (log.isTraceEnabled()) {
@@ -534,6 +533,19 @@ public class DefaultTransportService implements TransportService { @@ -534,6 +533,19 @@ public class DefaultTransportService implements TransportService {
534 } 533 }
535 534
536 @Override 535 @Override
  536 + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.GetFirmwareRequestMsg msg, TransportServiceCallback<TransportProtos.GetFirmwareResponseMsg> callback) {
  537 + if (checkLimits(sessionInfo, msg, callback)) {
  538 + TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg> protoMsg =
  539 + new TbProtoQueueMsg<>(UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder().setFirmwareRequestMsg(msg).build());
  540 +
  541 + AsyncCallbackTemplate.withCallback(transportApiRequestTemplate.send(protoMsg), response -> {
  542 + TransportProtos.GetFirmwareResponseMsg firmwareResponseMsg = response.getValue().getFirmwareResponseMsg();
  543 + callback.onSuccess(firmwareResponseMsg);
  544 + }, callback::onError, transportCallbackExecutor);
  545 + }
  546 + }
  547 +
  548 + @Override
537 public SessionMetaData reportActivity(TransportProtos.SessionInfoProto sessionInfo) { 549 public SessionMetaData reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
538 return reportActivityInternal(sessionInfo); 550 return reportActivityInternal(sessionInfo);
539 } 551 }
@@ -612,11 +624,11 @@ public class DefaultTransportService implements TransportService { @@ -612,11 +624,11 @@ public class DefaultTransportService implements TransportService {
612 sessions.remove(toSessionId(sessionInfo)); 624 sessions.remove(toSessionId(sessionInfo));
613 } 625 }
614 626
615 - private boolean checkLimits(TransportProtos.SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<Void> callback) { 627 + private boolean checkLimits(TransportProtos.SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<?> callback) {
616 return checkLimits(sessionInfo, msg, callback, 0); 628 return checkLimits(sessionInfo, msg, callback, 0);
617 } 629 }
618 630
619 - private boolean checkLimits(TransportProtos.SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<Void> callback, int dataPoints) { 631 + private boolean checkLimits(TransportProtos.SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<?> callback, int dataPoints) {
620 if (log.isTraceEnabled()) { 632 if (log.isTraceEnabled()) {
621 log.trace("[{}] Processing msg: {}", toSessionId(sessionInfo), msg); 633 log.trace("[{}] Processing msg: {}", toSessionId(sessionInfo), msg);
622 } 634 }
@@ -81,6 +81,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao { @@ -81,6 +81,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao {
81 */ 81 */
82 PageData<Device> findDevicesByTenantIdAndType(UUID tenantId, String type, PageLink pageLink); 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 * Find device infos by tenantId, type and page link. 87 * Find device infos by tenantId, type and page link.
86 * 88 *
@@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo;
42 import org.thingsboard.server.common.data.DeviceProfileProvisionType; 42 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
43 import org.thingsboard.server.common.data.DeviceProfileType; 43 import org.thingsboard.server.common.data.DeviceProfileType;
44 import org.thingsboard.server.common.data.DeviceTransportType; 44 import org.thingsboard.server.common.data.DeviceTransportType;
  45 +import org.thingsboard.server.common.data.Firmware;
45 import org.thingsboard.server.common.data.Tenant; 46 import org.thingsboard.server.common.data.Tenant;
46 import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; 47 import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;
47 import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; 48 import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration;
@@ -61,6 +62,7 @@ import org.thingsboard.server.common.data.page.PageData; @@ -61,6 +62,7 @@ import org.thingsboard.server.common.data.page.PageData;
61 import org.thingsboard.server.common.data.page.PageLink; 62 import org.thingsboard.server.common.data.page.PageLink;
62 import org.thingsboard.server.dao.entity.AbstractEntityService; 63 import org.thingsboard.server.dao.entity.AbstractEntityService;
63 import org.thingsboard.server.dao.exception.DataValidationException; 64 import org.thingsboard.server.dao.exception.DataValidationException;
  65 +import org.thingsboard.server.dao.firmware.FirmwareService;
64 import org.thingsboard.server.dao.service.DataValidator; 66 import org.thingsboard.server.dao.service.DataValidator;
65 import org.thingsboard.server.dao.service.PaginatedRemover; 67 import org.thingsboard.server.dao.service.PaginatedRemover;
66 import org.thingsboard.server.dao.service.Validator; 68 import org.thingsboard.server.dao.service.Validator;
@@ -111,6 +113,9 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -111,6 +113,9 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
111 @Autowired 113 @Autowired
112 private CacheManager cacheManager; 114 private CacheManager cacheManager;
113 115
  116 + @Autowired
  117 + private FirmwareService firmwareService;
  118 +
114 private final Lock findOrCreateLock = new ReentrantLock(); 119 private final Lock findOrCreateLock = new ReentrantLock();
115 120
116 @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}") 121 @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}")
@@ -395,6 +400,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -395,6 +400,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
395 } 400 }
396 } 401 }
397 402
  403 + if (deviceProfile.getFirmwareId() != null) {
  404 + Firmware firmware = firmwareService.findFirmwareById(tenantId, deviceProfile.getFirmwareId());
  405 + if (firmware == null) {
  406 + throw new DataValidationException("Can't assign non-existent firmware!");
  407 + }
  408 + if (firmware.getData() == null) {
  409 + throw new DataValidationException("Can't assign firmware with empty data!");
  410 + }
  411 + }
398 } 412 }
399 413
400 @Override 414 @Override
@@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
39 import org.thingsboard.server.common.data.EntitySubtype; 39 import org.thingsboard.server.common.data.EntitySubtype;
40 import org.thingsboard.server.common.data.EntityType; 40 import org.thingsboard.server.common.data.EntityType;
41 import org.thingsboard.server.common.data.EntityView; 41 import org.thingsboard.server.common.data.EntityView;
  42 +import org.thingsboard.server.common.data.Firmware;
42 import org.thingsboard.server.common.data.Tenant; 43 import org.thingsboard.server.common.data.Tenant;
43 import org.thingsboard.server.common.data.asset.Asset; 44 import org.thingsboard.server.common.data.asset.Asset;
44 import org.thingsboard.server.common.data.device.DeviceSearchQuery; 45 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
@@ -72,6 +73,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; @@ -72,6 +73,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus;
72 import org.thingsboard.server.dao.entity.AbstractEntityService; 73 import org.thingsboard.server.dao.entity.AbstractEntityService;
73 import org.thingsboard.server.dao.event.EventService; 74 import org.thingsboard.server.dao.event.EventService;
74 import org.thingsboard.server.dao.exception.DataValidationException; 75 import org.thingsboard.server.dao.exception.DataValidationException;
  76 +import org.thingsboard.server.dao.firmware.FirmwareService;
75 import org.thingsboard.server.dao.service.DataValidator; 77 import org.thingsboard.server.dao.service.DataValidator;
76 import org.thingsboard.server.dao.service.PaginatedRemover; 78 import org.thingsboard.server.dao.service.PaginatedRemover;
77 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 79 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
@@ -131,6 +133,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -131,6 +133,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
131 @Lazy 133 @Lazy
132 private TbTenantProfileCache tenantProfileCache; 134 private TbTenantProfileCache tenantProfileCache;
133 135
  136 + @Autowired
  137 + private FirmwareService firmwareService;
  138 +
134 @Override 139 @Override
135 public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) { 140 public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) {
136 log.trace("Executing findDeviceInfoById [{}]", deviceId); 141 log.trace("Executing findDeviceInfoById [{}]", deviceId);
@@ -350,6 +355,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -350,6 +355,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
350 } 355 }
351 356
352 @Override 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
353 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { 367 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
354 log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); 368 log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
355 validateId(tenantId, INCORRECT_TENANT_ID + tenantId); 369 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
@@ -663,6 +677,16 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -663,6 +677,16 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
663 throw new DataValidationException("Can't assign device to customer from different tenant!"); 677 throw new DataValidationException("Can't assign device to customer from different tenant!");
664 } 678 }
665 } 679 }
  680 +
  681 + if (device.getFirmwareId() != null) {
  682 + Firmware firmware = firmwareService.findFirmwareById(tenantId, device.getFirmwareId());
  683 + if (firmware == null) {
  684 + throw new DataValidationException("Can't assign non-existent firmware!");
  685 + }
  686 + if (firmware.getData() == null) {
  687 + throw new DataValidationException("Can't assign firmware with empty data!");
  688 + }
  689 + }
666 } 690 }
667 }; 691 };
668 692
  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.dao.firmware;
  17 +
  18 +import com.google.common.hash.HashFunction;
  19 +import com.google.common.hash.Hashing;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.apache.commons.lang3.StringUtils;
  22 +import org.hibernate.exception.ConstraintViolationException;
  23 +import org.springframework.cache.Cache;
  24 +import org.springframework.cache.CacheManager;
  25 +import org.springframework.stereotype.Service;
  26 +import org.thingsboard.server.common.data.Firmware;
  27 +import org.thingsboard.server.common.data.FirmwareInfo;
  28 +import org.thingsboard.server.common.data.Tenant;
  29 +import org.thingsboard.server.common.data.id.FirmwareId;
  30 +import org.thingsboard.server.common.data.id.TenantId;
  31 +import org.thingsboard.server.common.data.page.PageData;
  32 +import org.thingsboard.server.common.data.page.PageLink;
  33 +import org.thingsboard.server.dao.exception.DataValidationException;
  34 +import org.thingsboard.server.dao.service.DataValidator;
  35 +import org.thingsboard.server.dao.service.PaginatedRemover;
  36 +import org.thingsboard.server.dao.tenant.TenantDao;
  37 +
  38 +import java.nio.ByteBuffer;
  39 +import java.util.Collections;
  40 +import java.util.Optional;
  41 +
  42 +import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
  43 +import static org.thingsboard.server.dao.service.Validator.validateId;
  44 +import static org.thingsboard.server.dao.service.Validator.validatePageLink;
  45 +
  46 +@Service
  47 +@Slf4j
  48 +public class BaseFirmwareService implements FirmwareService {
  49 + public static final String INCORRECT_FIRMWARE_ID = "Incorrect firmwareId ";
  50 + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
  51 +
  52 + private final TenantDao tenantDao;
  53 + private final FirmwareDao firmwareDao;
  54 + private final FirmwareInfoDao firmwareInfoDao;
  55 + private final CacheManager cacheManager;
  56 +
  57 + public BaseFirmwareService(TenantDao tenantDao, FirmwareDao firmwareDao, FirmwareInfoDao firmwareInfoDao, CacheManager cacheManager) {
  58 + this.tenantDao = tenantDao;
  59 + this.firmwareDao = firmwareDao;
  60 + this.firmwareInfoDao = firmwareInfoDao;
  61 + this.cacheManager = cacheManager;
  62 + }
  63 +
  64 + @Override
  65 + public FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo) {
  66 + log.trace("Executing saveFirmwareInfo [{}]", firmwareInfo);
  67 + firmwareInfoValidator.validate(firmwareInfo, FirmwareInfo::getTenantId);
  68 + try {
  69 + FirmwareId firmwareId = firmwareInfo.getId();
  70 + if (firmwareId != null) {
  71 + cacheManager.getCache(FIRMWARE_CACHE).evict(firmwareId.toString());
  72 + }
  73 + return firmwareInfoDao.save(firmwareInfo.getTenantId(), firmwareInfo);
  74 + } catch (Exception t) {
  75 + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
  76 + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("firmware_tenant_title_version_unq_key")) {
  77 + throw new DataValidationException("Firmware with such title and version already exists!");
  78 + } else {
  79 + throw t;
  80 + }
  81 + }
  82 + }
  83 +
  84 + @Override
  85 + public Firmware saveFirmware(Firmware firmware) {
  86 + log.trace("Executing saveFirmware [{}]", firmware);
  87 + firmwareValidator.validate(firmware, FirmwareInfo::getTenantId);
  88 + try {
  89 + FirmwareId firmwareId = firmware.getId();
  90 + if (firmwareId != null) {
  91 + cacheManager.getCache(FIRMWARE_CACHE).evict(firmwareId.toString());
  92 + }
  93 + return firmwareDao.save(firmware.getTenantId(), firmware);
  94 + } catch (Exception t) {
  95 + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
  96 + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("firmware_tenant_title_version_unq_key")) {
  97 + throw new DataValidationException("Firmware with such title and version already exists!");
  98 + } else {
  99 + throw t;
  100 + }
  101 + }
  102 + }
  103 +
  104 + @Override
  105 + public Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId) {
  106 + log.trace("Executing findFirmwareById [{}]", firmwareId);
  107 + validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId);
  108 + return firmwareDao.findById(tenantId, firmwareId.getId());
  109 + }
  110 +
  111 + @Override
  112 + public FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId) {
  113 + log.trace("Executing findFirmwareInfoById [{}]", firmwareId);
  114 + validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId);
  115 + return firmwareInfoDao.findById(tenantId, firmwareId.getId());
  116 + }
  117 +
  118 + @Override
  119 + public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) {
  120 + log.trace("Executing findTenantFirmwaresByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
  121 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  122 + validatePageLink(pageLink);
  123 + return firmwareInfoDao.findFirmwareInfoByTenantId(tenantId, pageLink);
  124 + }
  125 +
  126 + @Override
  127 + public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) {
  128 + log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink);
  129 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  130 + validatePageLink(pageLink);
  131 + return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink);
  132 + }
  133 +
  134 + @Override
  135 + public void deleteFirmware(TenantId tenantId, FirmwareId firmwareId) {
  136 + log.trace("Executing deleteFirmware [{}]", firmwareId);
  137 + validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId);
  138 + try {
  139 + Cache cache = cacheManager.getCache(FIRMWARE_CACHE);
  140 + cache.evict(Collections.singletonList(firmwareId));
  141 + firmwareDao.removeById(tenantId, firmwareId.getId());
  142 + } catch (Exception t) {
  143 + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
  144 + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device")) {
  145 + throw new DataValidationException("The firmware referenced by the devices cannot be deleted!");
  146 + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) {
  147 + throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!");
  148 + } else {
  149 + throw t;
  150 + }
  151 + }
  152 + }
  153 +
  154 + @Override
  155 + public void deleteFirmwaresByTenantId(TenantId tenantId) {
  156 + log.trace("Executing deleteFirmwaresByTenantId, tenantId [{}]", tenantId);
  157 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  158 + tenantFirmwareRemover.removeEntities(tenantId, tenantId);
  159 + }
  160 +
  161 + private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() {
  162 +
  163 + @Override
  164 + protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmware) {
  165 + if (firmware.getTenantId() == null) {
  166 + throw new DataValidationException("Firmware should be assigned to tenant!");
  167 + } else {
  168 + Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());
  169 + if (tenant == null) {
  170 + throw new DataValidationException("Firmware is referencing to non-existent tenant!");
  171 + }
  172 + }
  173 +
  174 + if (StringUtils.isEmpty(firmware.getTitle())) {
  175 + throw new DataValidationException("Firmware title should be specified!");
  176 + }
  177 +
  178 + if (StringUtils.isEmpty(firmware.getVersion())) {
  179 + throw new DataValidationException("Firmware version should be specified!");
  180 + }
  181 + }
  182 +
  183 + @Override
  184 + protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) {
  185 + FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId());
  186 +
  187 + BaseFirmwareService.validateUpdate(firmware, firmwareOld);
  188 + }
  189 + };
  190 +
  191 + private DataValidator<Firmware> firmwareValidator = new DataValidator<>() {
  192 +
  193 + @Override
  194 + protected void validateDataImpl(TenantId tenantId, Firmware firmware) {
  195 + if (firmware.getTenantId() == null) {
  196 + throw new DataValidationException("Firmware should be assigned to tenant!");
  197 + } else {
  198 + Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());
  199 + if (tenant == null) {
  200 + throw new DataValidationException("Firmware is referencing to non-existent tenant!");
  201 + }
  202 + }
  203 +
  204 + if (StringUtils.isEmpty(firmware.getTitle())) {
  205 + throw new DataValidationException("Firmware title should be specified!");
  206 + }
  207 +
  208 + if (StringUtils.isEmpty(firmware.getVersion())) {
  209 + throw new DataValidationException("Firmware version should be specified!");
  210 + }
  211 +
  212 + if (StringUtils.isEmpty(firmware.getFileName())) {
  213 + throw new DataValidationException("Firmware file name should be specified!");
  214 + }
  215 +
  216 + if (StringUtils.isEmpty(firmware.getContentType())) {
  217 + throw new DataValidationException("Firmware content type should be specified!");
  218 + }
  219 +
  220 + ByteBuffer data = firmware.getData();
  221 + if (data == null || !data.hasArray() || data.array().length == 0) {
  222 + throw new DataValidationException("Firmware data should be specified!");
  223 + }
  224 +
  225 + if (StringUtils.isEmpty(firmware.getChecksumAlgorithm())) {
  226 + throw new DataValidationException("Firmware checksum algorithm should be specified!");
  227 + }
  228 + if (StringUtils.isEmpty(firmware.getChecksum())) {
  229 + throw new DataValidationException("Firmware checksum should be specified!");
  230 + }
  231 +
  232 + HashFunction hashFunction;
  233 + switch (firmware.getChecksumAlgorithm()) {
  234 + case "sha256":
  235 + hashFunction = Hashing.sha256();
  236 + break;
  237 + case "md5":
  238 + hashFunction = Hashing.md5();
  239 + break;
  240 + case "crc32":
  241 + hashFunction = Hashing.crc32();
  242 + break;
  243 + default:
  244 + throw new DataValidationException("Unknown checksum algorithm!");
  245 + }
  246 +
  247 + String currentChecksum = hashFunction.hashBytes(data.array()).toString();
  248 +
  249 + if (!currentChecksum.equals(firmware.getChecksum())) {
  250 + throw new DataValidationException("Wrong firmware file!");
  251 + }
  252 + }
  253 +
  254 + @Override
  255 + protected void validateUpdate(TenantId tenantId, Firmware firmware) {
  256 + Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId());
  257 +
  258 + BaseFirmwareService.validateUpdate(firmware, firmwareOld);
  259 +
  260 + if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) {
  261 + throw new DataValidationException("Updating firmware data is prohibited!");
  262 + }
  263 + }
  264 + };
  265 +
  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 + }
  270 +
  271 + if (!firmwareOld.getVersion().equals(firmware.getVersion())) {
  272 + throw new DataValidationException("Updating firmware version is prohibited!");
  273 + }
  274 +
  275 + if (firmwareOld.getFileName() != null && !firmwareOld.getFileName().equals(firmware.getFileName())) {
  276 + throw new DataValidationException("Updating firmware file name is prohibited!");
  277 + }
  278 +
  279 + if (firmwareOld.getContentType() != null && !firmwareOld.getContentType().equals(firmware.getContentType())) {
  280 + throw new DataValidationException("Updating firmware content type is prohibited!");
  281 + }
  282 +
  283 + if (firmwareOld.getChecksumAlgorithm() != null && !firmwareOld.getChecksumAlgorithm().equals(firmware.getChecksumAlgorithm())) {
  284 + throw new DataValidationException("Updating firmware content type is prohibited!");
  285 + }
  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 + }
  295 +
  296 + private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover =
  297 + new PaginatedRemover<>() {
  298 +
  299 + @Override
  300 + protected PageData<FirmwareInfo> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {
  301 + return firmwareInfoDao.findFirmwareInfoByTenantId(id, pageLink);
  302 + }
  303 +
  304 + @Override
  305 + protected void removeEntity(TenantId tenantId, FirmwareInfo entity) {
  306 + deleteFirmware(tenantId, entity.getId());
  307 + }
  308 + };
  309 +
  310 + protected Optional<ConstraintViolationException> extractConstraintViolationException(Exception t) {
  311 + if (t instanceof ConstraintViolationException) {
  312 + return Optional.of((ConstraintViolationException) t);
  313 + } else if (t.getCause() instanceof ConstraintViolationException) {
  314 + return Optional.of((ConstraintViolationException) (t.getCause()));
  315 + } else {
  316 + return Optional.empty();
  317 + }
  318 + }
  319 +}
  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.dao.firmware;
  17 +
  18 +import org.thingsboard.server.common.data.Firmware;
  19 +import org.thingsboard.server.dao.Dao;
  20 +
  21 +public interface FirmwareDao extends Dao<Firmware> {
  22 +
  23 +}
  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.dao.firmware;
  17 +
  18 +import org.thingsboard.server.common.data.FirmwareInfo;
  19 +import org.thingsboard.server.common.data.id.TenantId;
  20 +import org.thingsboard.server.common.data.page.PageData;
  21 +import org.thingsboard.server.common.data.page.PageLink;
  22 +import org.thingsboard.server.dao.Dao;
  23 +
  24 +import java.util.UUID;
  25 +
  26 +public interface FirmwareInfoDao extends Dao<FirmwareInfo> {
  27 +
  28 + PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink);
  29 +
  30 + PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink);
  31 +
  32 +}
@@ -153,6 +153,7 @@ public class ModelConstants { @@ -153,6 +153,7 @@ public class ModelConstants {
153 public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; 153 public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
154 public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id"; 154 public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id";
155 public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data"; 155 public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data";
  156 + public static final String DEVICE_FIRMWARE_ID_PROPERTY = "firmware_id";
156 157
157 public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; 158 public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
158 public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text"; 159 public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
@@ -176,6 +177,7 @@ public class ModelConstants { @@ -176,6 +177,7 @@ public class ModelConstants {
176 public static final String DEVICE_PROFILE_DEFAULT_RULE_CHAIN_ID_PROPERTY = "default_rule_chain_id"; 177 public static final String DEVICE_PROFILE_DEFAULT_RULE_CHAIN_ID_PROPERTY = "default_rule_chain_id";
177 public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name"; 178 public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name";
178 public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key"; 179 public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key";
  180 + public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id";
179 181
180 /** 182 /**
181 * Cassandra entityView constants. 183 * Cassandra entityView constants.
@@ -470,6 +472,22 @@ public class ModelConstants { @@ -470,6 +472,22 @@ public class ModelConstants {
470 public static final String RESOURCE_DATA_COLUMN = "data"; 472 public static final String RESOURCE_DATA_COLUMN = "data";
471 473
472 /** 474 /**
  475 + * Firmware constants.
  476 + */
  477 + public static final String FIRMWARE_TABLE_NAME = "firmware";
  478 + public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
  479 + public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY;
  480 + public static final String FIRMWARE_VERSION_COLUMN = "version";
  481 + public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name";
  482 + public static final String FIRMWARE_CONTENT_TYPE_COLUMN = "content_type";
  483 + public static final String FIRMWARE_CHECKSUM_ALGORITHM_COLUMN = "checksum_algorithm";
  484 + public static final String FIRMWARE_CHECKSUM_COLUMN = "checksum";
  485 + public static final String FIRMWARE_DATA_COLUMN = "data";
  486 + public static final String FIRMWARE_DATA_SIZE_COLUMN = "data_size";
  487 + public static final String FIRMWARE_ADDITIONAL_INFO_COLUMN = ADDITIONAL_INFO_PROPERTY;
  488 + public static final String FIRMWARE_HAS_DATA_PROPERTY = "has_data";
  489 +
  490 + /**
473 * Edge constants. 491 * Edge constants.
474 */ 492 */
475 public static final String EDGE_COLUMN_FAMILY_NAME = "edge"; 493 public static final String EDGE_COLUMN_FAMILY_NAME = "edge";
@@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData; @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData;
27 import org.thingsboard.server.common.data.id.CustomerId; 27 import org.thingsboard.server.common.data.id.CustomerId;
28 import org.thingsboard.server.common.data.id.DeviceId; 28 import org.thingsboard.server.common.data.id.DeviceId;
29 import org.thingsboard.server.common.data.id.DeviceProfileId; 29 import org.thingsboard.server.common.data.id.DeviceProfileId;
  30 +import org.thingsboard.server.common.data.id.FirmwareId;
30 import org.thingsboard.server.common.data.id.TenantId; 31 import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.dao.model.BaseSqlEntity; 32 import org.thingsboard.server.dao.model.BaseSqlEntity;
32 import org.thingsboard.server.dao.model.ModelConstants; 33 import org.thingsboard.server.dao.model.ModelConstants;
@@ -73,6 +74,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti @@ -73,6 +74,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
73 @Column(name = ModelConstants.DEVICE_DEVICE_PROFILE_ID_PROPERTY, columnDefinition = "uuid") 74 @Column(name = ModelConstants.DEVICE_DEVICE_PROFILE_ID_PROPERTY, columnDefinition = "uuid")
74 private UUID deviceProfileId; 75 private UUID deviceProfileId;
75 76
  77 + @Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid")
  78 + private UUID firmwareId;
  79 +
76 @Type(type = "jsonb") 80 @Type(type = "jsonb")
77 @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb") 81 @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb")
78 private JsonNode deviceData; 82 private JsonNode deviceData;
@@ -95,6 +99,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti @@ -95,6 +99,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
95 if (device.getDeviceProfileId() != null) { 99 if (device.getDeviceProfileId() != null) {
96 this.deviceProfileId = device.getDeviceProfileId().getId(); 100 this.deviceProfileId = device.getDeviceProfileId().getId();
97 } 101 }
  102 + if (device.getFirmwareId() != null) {
  103 + this.firmwareId = device.getFirmwareId().getId();
  104 + }
98 this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class); 105 this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class);
99 this.name = device.getName(); 106 this.name = device.getName();
100 this.type = device.getType(); 107 this.type = device.getType();
@@ -114,6 +121,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti @@ -114,6 +121,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
114 this.label = deviceEntity.getLabel(); 121 this.label = deviceEntity.getLabel();
115 this.searchText = deviceEntity.getSearchText(); 122 this.searchText = deviceEntity.getSearchText();
116 this.additionalInfo = deviceEntity.getAdditionalInfo(); 123 this.additionalInfo = deviceEntity.getAdditionalInfo();
  124 + this.firmwareId = deviceEntity.getFirmwareId();
117 } 125 }
118 126
119 @Override 127 @Override
@@ -138,6 +146,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti @@ -138,6 +146,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
138 if (deviceProfileId != null) { 146 if (deviceProfileId != null) {
139 device.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); 147 device.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
140 } 148 }
  149 + if (firmwareId != null) {
  150 + device.setFirmwareId(new FirmwareId(firmwareId));
  151 + }
141 device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class)); 152 device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class));
142 device.setName(name); 153 device.setName(name);
143 device.setType(type); 154 device.setType(type);
@@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.DeviceProfileProvisionType; @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.DeviceProfileProvisionType;
27 import org.thingsboard.server.common.data.DeviceTransportType; 27 import org.thingsboard.server.common.data.DeviceTransportType;
28 import org.thingsboard.server.common.data.device.profile.DeviceProfileData; 28 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
29 import org.thingsboard.server.common.data.id.DeviceProfileId; 29 import org.thingsboard.server.common.data.id.DeviceProfileId;
  30 +import org.thingsboard.server.common.data.id.FirmwareId;
30 import org.thingsboard.server.common.data.id.RuleChainId; 31 import org.thingsboard.server.common.data.id.RuleChainId;
31 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
32 import org.thingsboard.server.dao.model.BaseSqlEntity; 33 import org.thingsboard.server.dao.model.BaseSqlEntity;
@@ -89,6 +90,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl @@ -89,6 +90,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
89 @Column(name=ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY) 90 @Column(name=ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY)
90 private String provisionDeviceKey; 91 private String provisionDeviceKey;
91 92
  93 + @Column(name=ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY)
  94 + private UUID firmwareId;
  95 +
92 public DeviceProfileEntity() { 96 public DeviceProfileEntity() {
93 super(); 97 super();
94 } 98 }
@@ -113,6 +117,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl @@ -113,6 +117,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
113 } 117 }
114 this.defaultQueueName = deviceProfile.getDefaultQueueName(); 118 this.defaultQueueName = deviceProfile.getDefaultQueueName();
115 this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); 119 this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
  120 + if (deviceProfile.getFirmwareId() != null) {
  121 + this.firmwareId = deviceProfile.getFirmwareId().getId();
  122 + }
116 } 123 }
117 124
118 @Override 125 @Override
@@ -148,6 +155,11 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl @@ -148,6 +155,11 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
148 } 155 }
149 deviceProfile.setDefaultQueueName(defaultQueueName); 156 deviceProfile.setDefaultQueueName(defaultQueueName);
150 deviceProfile.setProvisionDeviceKey(provisionDeviceKey); 157 deviceProfile.setProvisionDeviceKey(provisionDeviceKey);
  158 +
  159 + if (firmwareId != null) {
  160 + deviceProfile.setFirmwareId(new FirmwareId(firmwareId));
  161 + }
  162 +
151 return deviceProfile; 163 return deviceProfile;
152 } 164 }
153 } 165 }
  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.dao.model.sql;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.Data;
  20 +import lombok.EqualsAndHashCode;
  21 +import org.hibernate.annotations.Type;
  22 +import org.hibernate.annotations.TypeDef;
  23 +import org.thingsboard.server.common.data.Firmware;
  24 +import org.thingsboard.server.common.data.id.FirmwareId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.dao.model.BaseSqlEntity;
  27 +import org.thingsboard.server.dao.model.ModelConstants;
  28 +import org.thingsboard.server.dao.model.SearchTextEntity;
  29 +import org.thingsboard.server.dao.util.mapping.JsonStringType;
  30 +
  31 +import javax.persistence.Column;
  32 +import javax.persistence.Entity;
  33 +import javax.persistence.Table;
  34 +import java.nio.ByteBuffer;
  35 +import java.util.UUID;
  36 +
  37 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN;
  38 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN;
  39 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
  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;
  42 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
  43 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
  44 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
  45 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
  46 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
  47 +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
  48 +
  49 +@Data
  50 +@EqualsAndHashCode(callSuper = true)
  51 +@Entity
  52 +@TypeDef(name = "json", typeClass = JsonStringType.class)
  53 +@Table(name = FIRMWARE_TABLE_NAME)
  54 +public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTextEntity<Firmware> {
  55 +
  56 + @Column(name = FIRMWARE_TENANT_ID_COLUMN)
  57 + private UUID tenantId;
  58 +
  59 + @Column(name = FIRMWARE_TITLE_COLUMN)
  60 + private String title;
  61 +
  62 + @Column(name = FIRMWARE_VERSION_COLUMN)
  63 + private String version;
  64 +
  65 + @Column(name = FIRMWARE_FILE_NAME_COLUMN)
  66 + private String fileName;
  67 +
  68 + @Column(name = FIRMWARE_CONTENT_TYPE_COLUMN)
  69 + private String contentType;
  70 +
  71 + @Column(name = FIRMWARE_CHECKSUM_ALGORITHM_COLUMN)
  72 + private String checksumAlgorithm;
  73 +
  74 + @Column(name = FIRMWARE_CHECKSUM_COLUMN)
  75 + private String checksum;
  76 +
  77 + @Column(name = FIRMWARE_DATA_COLUMN, columnDefinition = "BINARY")
  78 + private byte[] data;
  79 +
  80 + @Column(name = FIRMWARE_DATA_SIZE_COLUMN)
  81 + private Long dataSize;
  82 +
  83 + @Type(type = "json")
  84 + @Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN)
  85 + private JsonNode additionalInfo;
  86 +
  87 + @Column(name = SEARCH_TEXT_PROPERTY)
  88 + private String searchText;
  89 +
  90 + public FirmwareEntity() {
  91 + super();
  92 + }
  93 +
  94 + public FirmwareEntity(Firmware firmware) {
  95 + this.createdTime = firmware.getCreatedTime();
  96 + this.setUuid(firmware.getUuidId());
  97 + this.tenantId = firmware.getTenantId().getId();
  98 + this.title = firmware.getTitle();
  99 + this.version = firmware.getVersion();
  100 + this.fileName = firmware.getFileName();
  101 + this.contentType = firmware.getContentType();
  102 + this.checksumAlgorithm = firmware.getChecksumAlgorithm();
  103 + this.checksum = firmware.getChecksum();
  104 + this.data = firmware.getData().array();
  105 + this.dataSize = firmware.getDataSize();
  106 + this.additionalInfo = firmware.getAdditionalInfo();
  107 + }
  108 +
  109 + @Override
  110 + public String getSearchTextSource() {
  111 + return title;
  112 + }
  113 +
  114 + @Override
  115 + public void setSearchText(String searchText) {
  116 + this.searchText = searchText;
  117 + }
  118 +
  119 + @Override
  120 + public Firmware toData() {
  121 + Firmware firmware = new Firmware(new FirmwareId(id));
  122 + firmware.setCreatedTime(createdTime);
  123 + firmware.setTenantId(new TenantId(tenantId));
  124 + firmware.setTitle(title);
  125 + firmware.setVersion(version);
  126 + firmware.setFileName(fileName);
  127 + firmware.setContentType(contentType);
  128 + firmware.setChecksumAlgorithm(checksumAlgorithm);
  129 + firmware.setChecksum(checksum);
  130 + firmware.setDataSize(dataSize);
  131 + if (data != null) {
  132 + firmware.setData(ByteBuffer.wrap(data));
  133 + firmware.setHasData(true);
  134 + }
  135 + firmware.setAdditionalInfo(additionalInfo);
  136 + return firmware;
  137 + }
  138 +}
  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.dao.model.sql;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.Data;
  20 +import lombok.EqualsAndHashCode;
  21 +import org.hibernate.annotations.Type;
  22 +import org.hibernate.annotations.TypeDef;
  23 +import org.thingsboard.common.util.JacksonUtil;
  24 +import org.thingsboard.server.common.data.FirmwareInfo;
  25 +import org.thingsboard.server.common.data.id.FirmwareId;
  26 +import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.dao.model.BaseSqlEntity;
  28 +import org.thingsboard.server.dao.model.ModelConstants;
  29 +import org.thingsboard.server.dao.model.SearchTextEntity;
  30 +import org.thingsboard.server.dao.util.mapping.JsonStringType;
  31 +
  32 +import javax.persistence.Column;
  33 +import javax.persistence.Entity;
  34 +import javax.persistence.Table;
  35 +import javax.persistence.Transient;
  36 +import java.util.UUID;
  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;
  44 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY;
  45 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
  46 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
  47 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
  48 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
  49 +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
  50 +
  51 +@Data
  52 +@EqualsAndHashCode(callSuper = true)
  53 +@Entity
  54 +@TypeDef(name = "json", typeClass = JsonStringType.class)
  55 +@Table(name = FIRMWARE_TABLE_NAME)
  56 +public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements SearchTextEntity<FirmwareInfo> {
  57 +
  58 + @Column(name = FIRMWARE_TENANT_ID_COLUMN)
  59 + private UUID tenantId;
  60 +
  61 + @Column(name = FIRMWARE_TITLE_COLUMN)
  62 + private String title;
  63 +
  64 + @Column(name = FIRMWARE_VERSION_COLUMN)
  65 + private String version;
  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 +
  82 + @Type(type = "json")
  83 + @Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN)
  84 + private JsonNode additionalInfo;
  85 +
  86 + @Column(name = SEARCH_TEXT_PROPERTY)
  87 + private String searchText;
  88 +
  89 + @Transient
  90 + private boolean hasData;
  91 +
  92 + public FirmwareInfoEntity() {
  93 + super();
  94 + }
  95 +
  96 + public FirmwareInfoEntity(FirmwareInfo firmware) {
  97 + this.createdTime = firmware.getCreatedTime();
  98 + this.setUuid(firmware.getUuidId());
  99 + this.tenantId = firmware.getTenantId().getId();
  100 + this.title = firmware.getTitle();
  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();
  107 + this.additionalInfo = firmware.getAdditionalInfo();
  108 + }
  109 +
  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) {
  113 + this.id = id;
  114 + this.createdTime = createdTime;
  115 + this.tenantId = tenantId;
  116 + this.title = title;
  117 + this.version = version;
  118 + this.fileName = fileName;
  119 + this.contentType = contentType;
  120 + this.checksumAlgorithm = checksumAlgorithm;
  121 + this.checksum = checksum;
  122 + this.dataSize = dataSize;
  123 + this.hasData = hasData;
  124 + this.additionalInfo = JacksonUtil.convertValue(additionalInfo, JsonNode.class);
  125 + }
  126 +
  127 + @Override
  128 + public String getSearchTextSource() {
  129 + return title;
  130 + }
  131 +
  132 + @Override
  133 + public void setSearchText(String searchText) {
  134 + this.searchText = searchText;
  135 + }
  136 +
  137 + @Override
  138 + public FirmwareInfo toData() {
  139 + FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id));
  140 + firmware.setCreatedTime(createdTime);
  141 + firmware.setTenantId(new TenantId(tenantId));
  142 + firmware.setTitle(title);
  143 + firmware.setVersion(version);
  144 + firmware.setFileName(fileName);
  145 + firmware.setContentType(contentType);
  146 + firmware.setChecksumAlgorithm(checksumAlgorithm);
  147 + firmware.setChecksum(checksum);
  148 + firmware.setDataSize(dataSize);
  149 + firmware.setAdditionalInfo(additionalInfo);
  150 + firmware.setHasData(hasData);
  151 + return firmware;
  152 + }
  153 +}
@@ -94,6 +94,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit @@ -94,6 +94,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
94 @Param("textSearch") String textSearch, 94 @Param("textSearch") String textSearch,
95 Pageable pageable); 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 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + 106 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
98 "FROM DeviceEntity d " + 107 "FROM DeviceEntity d " +
99 "LEFT JOIN CustomerEntity c on c.id = d.customerId " + 108 "LEFT JOIN CustomerEntity c on c.id = d.customerId " +
@@ -150,6 +150,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> @@ -150,6 +150,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
150 } 150 }
151 151
152 @Override 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 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) { 163 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) {
154 return DaoUtil.toPageData( 164 return DaoUtil.toPageData(
155 deviceRepository.findDeviceInfosByTenantIdAndType( 165 deviceRepository.findDeviceInfosByTenantIdAndType(
  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.dao.sql.firmware;
  17 +
  18 +import org.springframework.data.domain.Page;
  19 +import org.springframework.data.domain.Pageable;
  20 +import org.springframework.data.jpa.repository.Query;
  21 +import org.springframework.data.repository.CrudRepository;
  22 +import org.springframework.data.repository.query.Param;
  23 +import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity;
  24 +
  25 +import java.util.UUID;
  26 +
  27 +public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> {
  28 + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
  29 + "f.tenantId = :tenantId " +
  30 + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
  31 + Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
  32 + @Param("searchText") String searchText,
  33 + Pageable pageable);
  34 +
  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 + "f.tenantId = :tenantId " +
  37 + "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" +
  38 + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
  39 + Page<FirmwareInfoEntity> findAllByTenantIdAndHasData(@Param("tenantId") UUID tenantId,
  40 + @Param("hasData") boolean hasData,
  41 + @Param("searchText") String searchText,
  42 + Pageable pageable);
  43 +
  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 + FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id);
  46 +}
  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.dao.sql.firmware;
  17 +
  18 +import org.springframework.data.repository.CrudRepository;
  19 +import org.thingsboard.server.dao.model.sql.FirmwareEntity;
  20 +
  21 +import java.util.UUID;
  22 +
  23 +public interface FirmwareRepository extends CrudRepository<FirmwareEntity, UUID> {
  24 +}
  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.dao.sql.firmware;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.data.repository.CrudRepository;
  21 +import org.springframework.stereotype.Component;
  22 +import org.thingsboard.server.common.data.Firmware;
  23 +import org.thingsboard.server.dao.firmware.FirmwareDao;
  24 +import org.thingsboard.server.dao.model.sql.FirmwareEntity;
  25 +import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
  26 +
  27 +import java.util.UUID;
  28 +
  29 +@Slf4j
  30 +@Component
  31 +public class JpaFirmwareDao extends JpaAbstractSearchTextDao<FirmwareEntity, Firmware> implements FirmwareDao {
  32 +
  33 + @Autowired
  34 + private FirmwareRepository firmwareRepository;
  35 +
  36 + @Override
  37 + protected Class<FirmwareEntity> getEntityClass() {
  38 + return FirmwareEntity.class;
  39 + }
  40 +
  41 + @Override
  42 + protected CrudRepository<FirmwareEntity, UUID> getCrudRepository() {
  43 + return firmwareRepository;
  44 + }
  45 +
  46 +}
  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.dao.sql.firmware;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.data.repository.CrudRepository;
  21 +import org.springframework.stereotype.Component;
  22 +import org.thingsboard.server.common.data.FirmwareInfo;
  23 +import org.thingsboard.server.common.data.id.TenantId;
  24 +import org.thingsboard.server.common.data.page.PageData;
  25 +import org.thingsboard.server.common.data.page.PageLink;
  26 +import org.thingsboard.server.dao.DaoUtil;
  27 +import org.thingsboard.server.dao.firmware.FirmwareInfoDao;
  28 +import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity;
  29 +import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
  30 +
  31 +import java.util.Objects;
  32 +import java.util.UUID;
  33 +
  34 +@Slf4j
  35 +@Component
  36 +public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao<FirmwareInfoEntity, FirmwareInfo> implements FirmwareInfoDao {
  37 +
  38 + @Autowired
  39 + private FirmwareInfoRepository firmwareInfoRepository;
  40 +
  41 + @Override
  42 + protected Class<FirmwareInfoEntity> getEntityClass() {
  43 + return FirmwareInfoEntity.class;
  44 + }
  45 +
  46 + @Override
  47 + protected CrudRepository<FirmwareInfoEntity, UUID> getCrudRepository() {
  48 + return firmwareInfoRepository;
  49 + }
  50 +
  51 + @Override
  52 + public FirmwareInfo findById(TenantId tenantId, UUID id) {
  53 + return DaoUtil.getData(firmwareInfoRepository.findFirmwareInfoById(id));
  54 + }
  55 +
  56 + @Override
  57 + public FirmwareInfo save(TenantId tenantId, FirmwareInfo firmwareInfo) {
  58 + FirmwareInfo savedFirmware = super.save(tenantId, firmwareInfo);
  59 + if (firmwareInfo.getId() == null) {
  60 + return savedFirmware;
  61 + } else {
  62 + return findById(tenantId, savedFirmware.getId().getId());
  63 + }
  64 + }
  65 +
  66 + @Override
  67 + public PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink) {
  68 + return DaoUtil.toPageData(firmwareInfoRepository
  69 + .findAllByTenantId(
  70 + tenantId.getId(),
  71 + Objects.toString(pageLink.getTextSearch(), ""),
  72 + DaoUtil.toPageable(pageLink)));
  73 + }
  74 +
  75 + @Override
  76 + public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) {
  77 + return DaoUtil.toPageData(firmwareInfoRepository
  78 + .findAllByTenantIdAndHasData(
  79 + tenantId.getId(),
  80 + hasData,
  81 + Objects.toString(pageLink.getTextSearch(), ""),
  82 + DaoUtil.toPageable(pageLink)));
  83 + }
  84 +}
@@ -48,8 +48,6 @@ public interface TbResourceRepository extends CrudRepository<TbResourceEntity, U @@ -48,8 +48,6 @@ public interface TbResourceRepository extends CrudRepository<TbResourceEntity, U
48 @Param("searchText") String search, 48 @Param("searchText") String search,
49 Pageable pageable); 49 Pageable pageable);
50 50
51 - void removeAllByTenantId(UUID tenantId);  
52 -  
53 @Query("SELECT tr FROM TbResourceEntity tr " + 51 @Query("SELECT tr FROM TbResourceEntity tr " +
54 "WHERE tr.resourceType = :resourceType " + 52 "WHERE tr.resourceType = :resourceType " +
55 "AND LOWER(tr.searchText) LIKE LOWER(CONCAT('%', :searchText, '%')) " + 53 "AND LOWER(tr.searchText) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
@@ -158,6 +158,23 @@ CREATE TABLE IF NOT EXISTS rule_node_state ( @@ -158,6 +158,23 @@ CREATE TABLE IF NOT EXISTS rule_node_state (
158 CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE 158 CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE
159 ); 159 );
160 160
  161 +CREATE TABLE IF NOT EXISTS firmware (
  162 + id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
  163 + created_time bigint NOT NULL,
  164 + tenant_id uuid NOT NULL,
  165 + title varchar(255) NOT NULL,
  166 + version varchar(255) NOT NULL,
  167 + file_name varchar(255),
  168 + content_type varchar(255),
  169 + checksum_algorithm varchar(32),
  170 + checksum varchar(1020),
  171 + data binary,
  172 + data_size bigint,
  173 + additional_info varchar,
  174 + search_text varchar(255),
  175 + CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
  176 +);
  177 +
161 CREATE TABLE IF NOT EXISTS device_profile ( 178 CREATE TABLE IF NOT EXISTS device_profile (
162 id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, 179 id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY,
163 created_time bigint NOT NULL, 180 created_time bigint NOT NULL,
@@ -170,12 +187,14 @@ CREATE TABLE IF NOT EXISTS device_profile ( @@ -170,12 +187,14 @@ CREATE TABLE IF NOT EXISTS device_profile (
170 search_text varchar(255), 187 search_text varchar(255),
171 is_default boolean, 188 is_default boolean,
172 tenant_id uuid, 189 tenant_id uuid,
  190 + firmware_id uuid,
173 default_rule_chain_id uuid, 191 default_rule_chain_id uuid,
174 default_queue_name varchar(255), 192 default_queue_name varchar(255),
175 provision_device_key varchar, 193 provision_device_key varchar,
176 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), 194 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
177 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), 195 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
178 - CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) 196 + CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
  197 + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id)
179 ); 198 );
180 199
181 CREATE TABLE IF NOT EXISTS device ( 200 CREATE TABLE IF NOT EXISTS device (
@@ -190,8 +209,10 @@ CREATE TABLE IF NOT EXISTS device ( @@ -190,8 +209,10 @@ CREATE TABLE IF NOT EXISTS device (
190 label varchar(255), 209 label varchar(255),
191 search_text varchar(255), 210 search_text varchar(255),
192 tenant_id uuid, 211 tenant_id uuid,
  212 + firmware_id uuid,
193 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), 213 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
194 - CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) 214 + CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
  215 + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id)
195 ); 216 );
196 217
197 CREATE TABLE IF NOT EXISTS device_credentials ( 218 CREATE TABLE IF NOT EXISTS device_credentials (
@@ -25,7 +25,7 @@ CREATE OR REPLACE PROCEDURE insert_tb_schema_settings() @@ -25,7 +25,7 @@ CREATE OR REPLACE PROCEDURE insert_tb_schema_settings()
25 $$ 25 $$
26 BEGIN 26 BEGIN
27 IF (SELECT COUNT(*) FROM tb_schema_settings) = 0 THEN 27 IF (SELECT COUNT(*) FROM tb_schema_settings) = 0 THEN
28 - INSERT INTO tb_schema_settings (schema_version) VALUES (3002000); 28 + INSERT INTO tb_schema_settings (schema_version) VALUES (3003000);
29 END IF; 29 END IF;
30 END; 30 END;
31 $$; 31 $$;
@@ -176,6 +176,23 @@ CREATE TABLE IF NOT EXISTS rule_node_state ( @@ -176,6 +176,23 @@ CREATE TABLE IF NOT EXISTS rule_node_state (
176 CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE 176 CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE
177 ); 177 );
178 178
  179 +CREATE TABLE IF NOT EXISTS firmware (
  180 + id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
  181 + created_time bigint NOT NULL,
  182 + tenant_id uuid NOT NULL,
  183 + title varchar(255) NOT NULL,
  184 + version varchar(255) NOT NULL,
  185 + file_name varchar(255),
  186 + content_type varchar(255),
  187 + checksum_algorithm varchar(32),
  188 + checksum varchar(1020),
  189 + data bytea,
  190 + data_size bigint,
  191 + additional_info varchar,
  192 + search_text varchar(255),
  193 + CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
  194 +);
  195 +
179 CREATE TABLE IF NOT EXISTS device_profile ( 196 CREATE TABLE IF NOT EXISTS device_profile (
180 id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, 197 id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY,
181 created_time bigint NOT NULL, 198 created_time bigint NOT NULL,
@@ -188,12 +205,14 @@ CREATE TABLE IF NOT EXISTS device_profile ( @@ -188,12 +205,14 @@ CREATE TABLE IF NOT EXISTS device_profile (
188 search_text varchar(255), 205 search_text varchar(255),
189 is_default boolean, 206 is_default boolean,
190 tenant_id uuid, 207 tenant_id uuid,
  208 + firmware_id uuid,
191 default_rule_chain_id uuid, 209 default_rule_chain_id uuid,
192 default_queue_name varchar(255), 210 default_queue_name varchar(255),
193 provision_device_key varchar, 211 provision_device_key varchar,
194 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), 212 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
195 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), 213 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
196 - CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) 214 + CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
  215 + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id)
197 ); 216 );
198 217
199 CREATE TABLE IF NOT EXISTS device ( 218 CREATE TABLE IF NOT EXISTS device (
@@ -208,8 +227,10 @@ CREATE TABLE IF NOT EXISTS device ( @@ -208,8 +227,10 @@ CREATE TABLE IF NOT EXISTS device (
208 label varchar(255), 227 label varchar(255),
209 search_text varchar(255), 228 search_text varchar(255),
210 tenant_id uuid, 229 tenant_id uuid,
  230 + firmware_id uuid,
211 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), 231 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
212 - CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) 232 + CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
  233 + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id)
213 ); 234 );
214 235
215 CREATE TABLE IF NOT EXISTS device_credentials ( 236 CREATE TABLE IF NOT EXISTS device_credentials (
@@ -28,6 +28,7 @@ import java.util.Arrays; @@ -28,6 +28,7 @@ import java.util.Arrays;
28 "org.thingsboard.server.dao.service.attributes.sql.*SqlTest", 28 "org.thingsboard.server.dao.service.attributes.sql.*SqlTest",
29 "org.thingsboard.server.dao.service.event.sql.*SqlTest", 29 "org.thingsboard.server.dao.service.event.sql.*SqlTest",
30 "org.thingsboard.server.dao.service.timeseries.sql.*SqlTest" 30 "org.thingsboard.server.dao.service.timeseries.sql.*SqlTest"
  31 +
31 }) 32 })
32 public class SqlDaoServiceTestSuite { 33 public class SqlDaoServiceTestSuite {
33 34
@@ -54,6 +54,7 @@ import org.thingsboard.server.dao.edge.EdgeService; @@ -54,6 +54,7 @@ import org.thingsboard.server.dao.edge.EdgeService;
54 import org.thingsboard.server.dao.entity.EntityService; 54 import org.thingsboard.server.dao.entity.EntityService;
55 import org.thingsboard.server.dao.entityview.EntityViewService; 55 import org.thingsboard.server.dao.entityview.EntityViewService;
56 import org.thingsboard.server.dao.event.EventService; 56 import org.thingsboard.server.dao.event.EventService;
  57 +import org.thingsboard.server.dao.firmware.FirmwareService;
57 import org.thingsboard.server.dao.relation.RelationService; 58 import org.thingsboard.server.dao.relation.RelationService;
58 import org.thingsboard.server.dao.resource.ResourceService; 59 import org.thingsboard.server.dao.resource.ResourceService;
59 import org.thingsboard.server.dao.rule.RuleChainService; 60 import org.thingsboard.server.dao.rule.RuleChainService;
@@ -157,6 +158,10 @@ public abstract class AbstractServiceTest { @@ -157,6 +158,10 @@ public abstract class AbstractServiceTest {
157 @Autowired 158 @Autowired
158 protected ResourceService resourceService; 159 protected ResourceService resourceService;
159 160
  161 +
  162 + @Autowired
  163 + protected FirmwareService firmwareService;
  164 +
160 public class IdComparator<D extends HasId> implements Comparator<D> { 165 public class IdComparator<D extends HasId> implements Comparator<D> {
161 @Override 166 @Override
162 public int compare(D o1, D o2) { 167 public int compare(D o1, D o2) {
@@ -203,7 +208,7 @@ public abstract class AbstractServiceTest { @@ -203,7 +208,7 @@ public abstract class AbstractServiceTest {
203 208
204 @Bean 209 @Bean
205 public AuditLogLevelFilter auditLogLevelFilter() { 210 public AuditLogLevelFilter auditLogLevelFilter() {
206 - Map<String,String> mask = new HashMap<>(); 211 + Map<String, String> mask = new HashMap<>();
207 for (EntityType entityType : EntityType.values()) { 212 for (EntityType entityType : EntityType.values()) {
208 mask.put(entityType.name().toLowerCase(), AuditLogLevelMask.RW.name()); 213 mask.put(entityType.name().toLowerCase(), AuditLogLevelMask.RW.name());
209 } 214 }