Commit 47994e1a66152fc784a04d4cb58cdba1072073d7

Authored by Viacheslav Klimov
2 parents 4ce81807 1d24a08c

Merge branch 'master' into master-to-snmp

# Conflicts:
#	application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
#	common/queue/src/main/proto/queue.proto
#	common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
#	common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
#	dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
Showing 95 changed files with 2467 additions and 455 deletions

Too many changes to show.

To preserve performance only 95 of 219 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 +
@@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.EntityType; @@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.EntityType;
41 import org.thingsboard.server.common.data.TenantProfile; 41 import org.thingsboard.server.common.data.TenantProfile;
42 import org.thingsboard.server.common.data.alarm.Alarm; 42 import org.thingsboard.server.common.data.alarm.Alarm;
43 import org.thingsboard.server.common.data.asset.Asset; 43 import org.thingsboard.server.common.data.asset.Asset;
  44 +import org.thingsboard.server.common.data.id.CustomerId;
44 import org.thingsboard.server.common.data.id.DeviceId; 45 import org.thingsboard.server.common.data.id.DeviceId;
45 import org.thingsboard.server.common.data.id.EdgeId; 46 import org.thingsboard.server.common.data.id.EdgeId;
46 import org.thingsboard.server.common.data.id.EntityId; 47 import org.thingsboard.server.common.data.id.EntityId;
@@ -269,7 +270,12 @@ class DefaultTbContext implements TbContext { @@ -269,7 +270,12 @@ class DefaultTbContext implements TbContext {
269 270
270 @Override 271 @Override
271 public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { 272 public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
272 - return TbMsg.newMsg(queueName, type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); 273 + return newMsg(queueName, type, originator, null, metaData, data);
  274 + }
  275 +
  276 + @Override
  277 + public TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
  278 + return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
273 } 279 }
274 280
275 @Override 281 @Override
@@ -106,7 +106,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -106,7 +106,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
106 int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter(); 106 int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter();
107 int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage(); 107 int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage();
108 if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) { 108 if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) {
109 - apiUsageClient.report(tenantId, ApiUsageRecordKey.RE_EXEC_COUNT); 109 + apiUsageClient.report(tenantId, tbMsg.getCustomerId(), ApiUsageRecordKey.RE_EXEC_COUNT);
110 if (ruleNode.isDebugMode()) { 110 if (ruleNode.isDebugMode()) {
111 systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self"); 111 systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self");
112 } 112 }
@@ -127,7 +127,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -127,7 +127,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
127 int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter(); 127 int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter();
128 int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage(); 128 int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage();
129 if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) { 129 if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) {
130 - apiUsageClient.report(tenantId, ApiUsageRecordKey.RE_EXEC_COUNT); 130 + apiUsageClient.report(tenantId, tbMsg.getCustomerId(), ApiUsageRecordKey.RE_EXEC_COUNT);
131 if (ruleNode.isDebugMode()) { 131 if (ruleNode.isDebugMode()) {
132 systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType()); 132 systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType());
133 } 133 }
@@ -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);
@@ -919,7 +958,7 @@ public abstract class BaseController { @@ -919,7 +958,7 @@ public abstract class BaseController {
919 entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo)); 958 entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo));
920 } 959 }
921 } 960 }
922 - TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); 961 + TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
923 TenantId tenantId = user.getTenantId(); 962 TenantId tenantId = user.getTenantId();
924 if (tenantId.isNullUid()) { 963 if (tenantId.isNullUid()) {
925 if (entity instanceof HasTenantId) { 964 if (entity instanceof HasTenantId) {
@@ -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 }
@@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.id.EdgeId; @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.id.EdgeId;
38 import org.thingsboard.server.common.data.id.TenantId; 38 import org.thingsboard.server.common.data.id.TenantId;
39 import org.thingsboard.server.common.data.page.PageData; 39 import org.thingsboard.server.common.data.page.PageData;
40 import org.thingsboard.server.common.data.page.PageLink; 40 import org.thingsboard.server.common.data.page.PageLink;
  41 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
41 import org.thingsboard.server.queue.util.TbCoreComponent; 42 import org.thingsboard.server.queue.util.TbCoreComponent;
42 import org.thingsboard.server.service.security.permission.Operation; 43 import org.thingsboard.server.service.security.permission.Operation;
43 import org.thingsboard.server.service.security.permission.Resource; 44 import org.thingsboard.server.service.security.permission.Resource;
@@ -148,6 +149,7 @@ public class CustomerController extends BaseController { @@ -148,6 +149,7 @@ public class CustomerController extends BaseController {
148 ActionType.DELETED, null, strCustomerId); 149 ActionType.DELETED, null, strCustomerId);
149 150
150 sendDeleteNotificationMsg(getTenantId(), customerId, relatedEdgeIds); 151 sendDeleteNotificationMsg(getTenantId(), customerId, relatedEdgeIds);
  152 + tbClusterService.onEntityStateChange(getTenantId(), customerId, ComponentLifecycleEvent.DELETED);
151 } catch (Exception e) { 153 } catch (Exception e) {
152 154
153 logEntityAction(emptyId(EntityType.CUSTOMER), 155 logEntityAction(emptyId(EntityType.CUSTOMER),
@@ -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')")
@@ -645,7 +658,7 @@ public class DeviceController extends BaseController { @@ -645,7 +658,7 @@ public class DeviceController extends BaseController {
645 private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) { 658 private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) {
646 String data = entityToStr(assignedDevice); 659 String data = entityToStr(assignedDevice);
647 if (data != null) { 660 if (data != null) {
648 - TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data); 661 + TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), assignedDevice.getCustomerId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data);
649 tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null); 662 tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null);
650 } 663 }
651 } 664 }
@@ -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 +}
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import org.springframework.beans.factory.annotation.Autowired;
18 import org.springframework.security.access.prepost.PreAuthorize; 19 import org.springframework.security.access.prepost.PreAuthorize;
19 import org.springframework.web.bind.annotation.RequestMapping; 20 import org.springframework.web.bind.annotation.RequestMapping;
20 import org.springframework.web.bind.annotation.RequestMethod; 21 import org.springframework.web.bind.annotation.RequestMethod;
@@ -23,17 +24,23 @@ import org.springframework.web.bind.annotation.ResponseBody; @@ -23,17 +24,23 @@ import org.springframework.web.bind.annotation.ResponseBody;
23 import org.springframework.web.bind.annotation.RestController; 24 import org.springframework.web.bind.annotation.RestController;
24 import org.thingsboard.server.common.data.exception.ThingsboardException; 25 import org.thingsboard.server.common.data.exception.ThingsboardException;
25 import org.thingsboard.server.common.msg.queue.ServiceType; 26 import org.thingsboard.server.common.msg.queue.ServiceType;
  27 +import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
  28 +import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
26 import org.thingsboard.server.queue.util.TbCoreComponent; 29 import org.thingsboard.server.queue.util.TbCoreComponent;
27 30
28 import java.util.Arrays; 31 import java.util.Arrays;
29 import java.util.Collections; 32 import java.util.Collections;
30 import java.util.List; 33 import java.util.List;
  34 +import java.util.stream.Collectors;
31 35
32 @RestController 36 @RestController
33 @TbCoreComponent 37 @TbCoreComponent
34 @RequestMapping("/api") 38 @RequestMapping("/api")
35 public class QueueController extends BaseController { 39 public class QueueController extends BaseController {
36 40
  41 + @Autowired(required = false)
  42 + private TbQueueRuleEngineSettings ruleEngineSettings;
  43 +
37 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 44 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
38 @RequestMapping(value = "/tenant/queues", params = {"serviceType"}, method = RequestMethod.GET) 45 @RequestMapping(value = "/tenant/queues", params = {"serviceType"}, method = RequestMethod.GET)
39 @ResponseBody 46 @ResponseBody
@@ -43,7 +50,12 @@ public class QueueController extends BaseController { @@ -43,7 +50,12 @@ public class QueueController extends BaseController {
43 ServiceType type = ServiceType.valueOf(serviceType); 50 ServiceType type = ServiceType.valueOf(serviceType);
44 switch (type) { 51 switch (type) {
45 case TB_RULE_ENGINE: 52 case TB_RULE_ENGINE:
46 - return Arrays.asList("Main", "HighPriority", "SequentialByOriginator"); 53 + if (ruleEngineSettings == null) {
  54 + return Arrays.asList("Main", "HighPriority", "SequentialByOriginator");
  55 + }
  56 + return ruleEngineSettings.getQueues().stream()
  57 + .map(TbRuleEngineQueueConfiguration::getName)
  58 + .collect(Collectors.toList());
47 default: 59 default:
48 return Collections.emptyList(); 60 return Collections.emptyList();
49 } 61 }
@@ -119,7 +119,7 @@ public class RpcController extends BaseController { @@ -119,7 +119,7 @@ public class RpcController extends BaseController {
119 expTime, 119 expTime,
120 body 120 body
121 ); 121 );
122 - deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse)); 122 + deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser);
123 } 123 }
124 124
125 @Override 125 @Override
@@ -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);
@@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.EntityType; @@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.EntityType;
48 import org.thingsboard.server.common.data.TenantProfile; 48 import org.thingsboard.server.common.data.TenantProfile;
49 import org.thingsboard.server.common.data.audit.ActionType; 49 import org.thingsboard.server.common.data.audit.ActionType;
50 import org.thingsboard.server.common.data.exception.ThingsboardException; 50 import org.thingsboard.server.common.data.exception.ThingsboardException;
  51 +import org.thingsboard.server.common.data.id.CustomerId;
51 import org.thingsboard.server.common.data.id.DeviceId; 52 import org.thingsboard.server.common.data.id.DeviceId;
52 import org.thingsboard.server.common.data.id.EntityId; 53 import org.thingsboard.server.common.data.id.EntityId;
53 import org.thingsboard.server.common.data.id.EntityIdFactory; 54 import org.thingsboard.server.common.data.id.EntityIdFactory;
@@ -449,7 +450,7 @@ public class TelemetryController extends BaseController { @@ -449,7 +450,7 @@ public class TelemetryController extends BaseController {
449 TenantProfile tenantProfile = tenantProfileCache.get(tenantId); 450 TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
450 tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); 451 tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays());
451 } 452 }
452 - tsSubService.saveAndNotify(tenantId, entityId, entries, tenantTtl, new FutureCallback<Void>() { 453 + tsSubService.saveAndNotify(tenantId, user.getCustomerId(), entityId, entries, tenantTtl, new FutureCallback<Void>() {
453 @Override 454 @Override
454 public void onSuccess(@Nullable Void tmp) { 455 public void onSuccess(@Nullable Void tmp) {
455 logTelemetryUpdated(user, entityId, entries, null); 456 logTelemetryUpdated(user, entityId, entries, null);
@@ -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.apiusage;
  17 +
  18 +import lombok.Getter;
  19 +import org.springframework.data.util.Pair;
  20 +import org.thingsboard.server.common.data.ApiFeature;
  21 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
  22 +import org.thingsboard.server.common.data.ApiUsageState;
  23 +import org.thingsboard.server.common.data.ApiUsageStateValue;
  24 +import org.thingsboard.server.common.data.EntityType;
  25 +import org.thingsboard.server.common.data.id.EntityId;
  26 +import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.msg.tools.SchedulerUtils;
  28 +
  29 +import java.util.Arrays;
  30 +import java.util.HashMap;
  31 +import java.util.HashSet;
  32 +import java.util.Map;
  33 +import java.util.Set;
  34 +import java.util.concurrent.ConcurrentHashMap;
  35 +
  36 +public abstract class BaseApiUsageState {
  37 + private final Map<ApiUsageRecordKey, Long> currentCycleValues = new ConcurrentHashMap<>();
  38 + private final Map<ApiUsageRecordKey, Long> currentHourValues = new ConcurrentHashMap<>();
  39 +
  40 + @Getter
  41 + private final ApiUsageState apiUsageState;
  42 + @Getter
  43 + private volatile long currentCycleTs;
  44 + @Getter
  45 + private volatile long nextCycleTs;
  46 + @Getter
  47 + private volatile long currentHourTs;
  48 +
  49 + public BaseApiUsageState(ApiUsageState apiUsageState) {
  50 + this.apiUsageState = apiUsageState;
  51 + this.currentCycleTs = SchedulerUtils.getStartOfCurrentMonth();
  52 + this.nextCycleTs = SchedulerUtils.getStartOfNextMonth();
  53 + this.currentHourTs = SchedulerUtils.getStartOfCurrentHour();
  54 + }
  55 +
  56 + public void put(ApiUsageRecordKey key, Long value) {
  57 + currentCycleValues.put(key, value);
  58 + }
  59 +
  60 + public void putHourly(ApiUsageRecordKey key, Long value) {
  61 + currentHourValues.put(key, value);
  62 + }
  63 +
  64 + public long add(ApiUsageRecordKey key, long value) {
  65 + long result = currentCycleValues.getOrDefault(key, 0L) + value;
  66 + currentCycleValues.put(key, result);
  67 + return result;
  68 + }
  69 +
  70 + public long get(ApiUsageRecordKey key) {
  71 + return currentCycleValues.getOrDefault(key, 0L);
  72 + }
  73 +
  74 + public long addToHourly(ApiUsageRecordKey key, long value) {
  75 + long result = currentHourValues.getOrDefault(key, 0L) + value;
  76 + currentHourValues.put(key, result);
  77 + return result;
  78 + }
  79 +
  80 + public void setHour(long currentHourTs) {
  81 + this.currentHourTs = currentHourTs;
  82 + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
  83 + currentHourValues.put(key, 0L);
  84 + }
  85 + }
  86 +
  87 + public void setCycles(long currentCycleTs, long nextCycleTs) {
  88 + this.currentCycleTs = currentCycleTs;
  89 + this.nextCycleTs = nextCycleTs;
  90 + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
  91 + currentCycleValues.put(key, 0L);
  92 + }
  93 + }
  94 +
  95 + public ApiUsageStateValue getFeatureValue(ApiFeature feature) {
  96 + switch (feature) {
  97 + case TRANSPORT:
  98 + return apiUsageState.getTransportState();
  99 + case RE:
  100 + return apiUsageState.getReExecState();
  101 + case DB:
  102 + return apiUsageState.getDbStorageState();
  103 + case JS:
  104 + return apiUsageState.getJsExecState();
  105 + case EMAIL:
  106 + return apiUsageState.getEmailExecState();
  107 + case SMS:
  108 + return apiUsageState.getSmsExecState();
  109 + default:
  110 + return ApiUsageStateValue.ENABLED;
  111 + }
  112 + }
  113 +
  114 + public boolean setFeatureValue(ApiFeature feature, ApiUsageStateValue value) {
  115 + ApiUsageStateValue currentValue = getFeatureValue(feature);
  116 + switch (feature) {
  117 + case TRANSPORT:
  118 + apiUsageState.setTransportState(value);
  119 + break;
  120 + case RE:
  121 + apiUsageState.setReExecState(value);
  122 + break;
  123 + case DB:
  124 + apiUsageState.setDbStorageState(value);
  125 + break;
  126 + case JS:
  127 + apiUsageState.setJsExecState(value);
  128 + break;
  129 + case EMAIL:
  130 + apiUsageState.setEmailExecState(value);
  131 + break;
  132 + case SMS:
  133 + apiUsageState.setSmsExecState(value);
  134 + break;
  135 + }
  136 + return !currentValue.equals(value);
  137 + }
  138 +
  139 + public abstract EntityType getEntityType();
  140 +
  141 + public TenantId getTenantId() {
  142 + return getApiUsageState().getTenantId();
  143 + }
  144 +
  145 + public EntityId getEntityId() {
  146 + return getApiUsageState().getEntityId();
  147 + }
  148 +}
  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.apiusage;
  17 +
  18 +import org.thingsboard.server.common.data.ApiUsageState;
  19 +import org.thingsboard.server.common.data.EntityType;
  20 +
  21 +public class CustomerApiUsageState extends BaseApiUsageState {
  22 + public CustomerApiUsageState(ApiUsageState apiUsageState) {
  23 + super(apiUsageState);
  24 + }
  25 +
  26 + @Override
  27 + public EntityType getEntityType() {
  28 + return EntityType.CUSTOMER;
  29 + }
  30 +}
@@ -29,10 +29,13 @@ import org.thingsboard.server.common.data.ApiUsageRecordKey; @@ -29,10 +29,13 @@ import org.thingsboard.server.common.data.ApiUsageRecordKey;
29 import org.thingsboard.server.common.data.ApiUsageState; 29 import org.thingsboard.server.common.data.ApiUsageState;
30 import org.thingsboard.server.common.data.ApiUsageStateMailMessage; 30 import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
31 import org.thingsboard.server.common.data.ApiUsageStateValue; 31 import org.thingsboard.server.common.data.ApiUsageStateValue;
  32 +import org.thingsboard.server.common.data.EntityType;
32 import org.thingsboard.server.common.data.Tenant; 33 import org.thingsboard.server.common.data.Tenant;
33 import org.thingsboard.server.common.data.TenantProfile; 34 import org.thingsboard.server.common.data.TenantProfile;
34 import org.thingsboard.server.common.data.exception.ThingsboardException; 35 import org.thingsboard.server.common.data.exception.ThingsboardException;
35 import org.thingsboard.server.common.data.id.ApiUsageStateId; 36 import org.thingsboard.server.common.data.id.ApiUsageStateId;
  37 +import org.thingsboard.server.common.data.id.CustomerId;
  38 +import org.thingsboard.server.common.data.id.EntityId;
36 import org.thingsboard.server.common.data.id.TenantId; 39 import org.thingsboard.server.common.data.id.TenantId;
37 import org.thingsboard.server.common.data.id.TenantProfileId; 40 import org.thingsboard.server.common.data.id.TenantProfileId;
38 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 41 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@@ -45,6 +48,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; @@ -45,6 +48,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
45 import org.thingsboard.server.common.msg.queue.ServiceType; 48 import org.thingsboard.server.common.msg.queue.ServiceType;
46 import org.thingsboard.server.common.msg.queue.TbCallback; 49 import org.thingsboard.server.common.msg.queue.TbCallback;
47 import org.thingsboard.server.common.msg.tools.SchedulerUtils; 50 import org.thingsboard.server.common.msg.tools.SchedulerUtils;
  51 +import org.thingsboard.server.dao.customer.CustomerService;
48 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 52 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
49 import org.thingsboard.server.dao.tenant.TenantService; 53 import org.thingsboard.server.dao.tenant.TenantService;
50 import org.thingsboard.server.dao.timeseries.TimeseriesService; 54 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -63,6 +67,7 @@ import javax.annotation.PostConstruct; @@ -63,6 +67,7 @@ import javax.annotation.PostConstruct;
63 import javax.annotation.PreDestroy; 67 import javax.annotation.PreDestroy;
64 import java.util.ArrayList; 68 import java.util.ArrayList;
65 import java.util.Arrays; 69 import java.util.Arrays;
  70 +import java.util.Collections;
66 import java.util.HashSet; 71 import java.util.HashSet;
67 import java.util.List; 72 import java.util.List;
68 import java.util.Map; 73 import java.util.Map;
@@ -95,6 +100,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -95,6 +100,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
95 private final TbClusterService clusterService; 100 private final TbClusterService clusterService;
96 private final PartitionService partitionService; 101 private final PartitionService partitionService;
97 private final TenantService tenantService; 102 private final TenantService tenantService;
  103 + private final CustomerService customerService;
98 private final TimeseriesService tsService; 104 private final TimeseriesService tsService;
99 private final ApiUsageStateService apiUsageStateService; 105 private final ApiUsageStateService apiUsageStateService;
100 private final SchedulerComponent scheduler; 106 private final SchedulerComponent scheduler;
@@ -105,10 +111,12 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -105,10 +111,12 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
105 @Autowired 111 @Autowired
106 private InternalTelemetryService tsWsService; 112 private InternalTelemetryService tsWsService;
107 113
108 - // Tenants that should be processed on this server  
109 - private final Map<TenantId, TenantApiUsageState> myTenantStates = new ConcurrentHashMap<>();  
110 - // Tenants that should be processed on other servers  
111 - private final Map<TenantId, ApiUsageState> otherTenantStates = new ConcurrentHashMap<>(); 114 + // Entities that should be processed on this server
  115 + private final Map<EntityId, BaseApiUsageState> myUsageStates = new ConcurrentHashMap<>();
  116 + // Entities that should be processed on other servers
  117 + private final Map<EntityId, ApiUsageState> otherUsageStates = new ConcurrentHashMap<>();
  118 +
  119 + private final Set<EntityId> deletedEntities = Collections.newSetFromMap(new ConcurrentHashMap<>());
112 120
113 @Value("${usage.stats.report.enabled:true}") 121 @Value("${usage.stats.report.enabled:true}")
114 private boolean enabled; 122 private boolean enabled;
@@ -123,13 +131,16 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -123,13 +131,16 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
123 public DefaultTbApiUsageStateService(TbClusterService clusterService, 131 public DefaultTbApiUsageStateService(TbClusterService clusterService,
124 PartitionService partitionService, 132 PartitionService partitionService,
125 TenantService tenantService, 133 TenantService tenantService,
  134 + CustomerService customerService,
126 TimeseriesService tsService, 135 TimeseriesService tsService,
127 ApiUsageStateService apiUsageStateService, 136 ApiUsageStateService apiUsageStateService,
128 SchedulerComponent scheduler, 137 SchedulerComponent scheduler,
129 - TbTenantProfileCache tenantProfileCache, MailService mailService) { 138 + TbTenantProfileCache tenantProfileCache,
  139 + MailService mailService) {
130 this.clusterService = clusterService; 140 this.clusterService = clusterService;
131 this.partitionService = partitionService; 141 this.partitionService = partitionService;
132 this.tenantService = tenantService; 142 this.tenantService = tenantService;
  143 + this.customerService = customerService;
133 this.tsService = tsService; 144 this.tsService = tsService;
134 this.apiUsageStateService = apiUsageStateService; 145 this.apiUsageStateService = apiUsageStateService;
135 this.scheduler = scheduler; 146 this.scheduler = scheduler;
@@ -150,74 +161,92 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -150,74 +161,92 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
150 @Override 161 @Override
151 public void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) { 162 public void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) {
152 ToUsageStatsServiceMsg statsMsg = msg.getValue(); 163 ToUsageStatsServiceMsg statsMsg = msg.getValue();
153 - TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB()));  
154 164
155 - if (tenantProfileCache.get(tenantId) == null) {  
156 - return; 165 + TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB()));
  166 + EntityId entityId;
  167 + if (statsMsg.getCustomerIdMSB() != 0 && statsMsg.getCustomerIdLSB() != 0) {
  168 + entityId = new CustomerId(new UUID(statsMsg.getCustomerIdMSB(), statsMsg.getCustomerIdLSB()));
  169 + } else {
  170 + entityId = tenantId;
157 } 171 }
158 172
159 - TenantApiUsageState tenantState; 173 + processEntityUsageStats(tenantId, entityId, statsMsg.getValuesList());
  174 + callback.onSuccess();
  175 + }
  176 +
  177 + private void processEntityUsageStats(TenantId tenantId, EntityId entityId, List<UsageStatsKVProto> values) {
  178 + if (deletedEntities.contains(entityId)) return;
  179 +
  180 + BaseApiUsageState usageState;
160 List<TsKvEntry> updatedEntries; 181 List<TsKvEntry> updatedEntries;
161 Map<ApiFeature, ApiUsageStateValue> result; 182 Map<ApiFeature, ApiUsageStateValue> result;
  183 +
162 updateLock.lock(); 184 updateLock.lock();
163 try { 185 try {
164 - tenantState = getOrFetchState(tenantId);  
165 - long ts = tenantState.getCurrentCycleTs();  
166 - long hourTs = tenantState.getCurrentHourTs(); 186 + usageState = getOrFetchState(tenantId, entityId);
  187 + long ts = usageState.getCurrentCycleTs();
  188 + long hourTs = usageState.getCurrentHourTs();
167 long newHourTs = SchedulerUtils.getStartOfCurrentHour(); 189 long newHourTs = SchedulerUtils.getStartOfCurrentHour();
168 if (newHourTs != hourTs) { 190 if (newHourTs != hourTs) {
169 - tenantState.setHour(newHourTs); 191 + usageState.setHour(newHourTs);
170 } 192 }
171 updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length); 193 updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length);
172 Set<ApiFeature> apiFeatures = new HashSet<>(); 194 Set<ApiFeature> apiFeatures = new HashSet<>();
173 - for (UsageStatsKVProto kvProto : statsMsg.getValuesList()) { 195 + for (UsageStatsKVProto kvProto : values) {
174 ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey()); 196 ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey());
175 - long newValue = tenantState.add(recordKey, kvProto.getValue()); 197 + long newValue = usageState.add(recordKey, kvProto.getValue());
176 updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue))); 198 updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue)));
177 - long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue()); 199 + long newHourlyValue = usageState.addToHourly(recordKey, kvProto.getValue());
178 updatedEntries.add(new BasicTsKvEntry(newHourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue))); 200 updatedEntries.add(new BasicTsKvEntry(newHourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue)));
179 apiFeatures.add(recordKey.getApiFeature()); 201 apiFeatures.add(recordKey.getApiFeature());
180 } 202 }
181 - result = tenantState.checkStateUpdatedDueToThreshold(apiFeatures); 203 + if (usageState.getEntityType() == EntityType.TENANT && !usageState.getEntityId().equals(TenantId.SYS_TENANT_ID)) {
  204 + result = ((TenantApiUsageState) usageState).checkStateUpdatedDueToThreshold(apiFeatures);
  205 + } else {
  206 + result = Collections.emptyMap();
  207 + }
182 } finally { 208 } finally {
183 updateLock.unlock(); 209 updateLock.unlock();
184 } 210 }
185 - tsWsService.saveAndNotifyInternal(tenantId, tenantState.getApiUsageState().getId(), updatedEntries, VOID_CALLBACK); 211 + tsWsService.saveAndNotifyInternal(tenantId, usageState.getApiUsageState().getId(), updatedEntries, VOID_CALLBACK);
186 if (!result.isEmpty()) { 212 if (!result.isEmpty()) {
187 - persistAndNotify(tenantState, result); 213 + persistAndNotify(usageState, result);
188 } 214 }
189 - callback.onSuccess();  
190 } 215 }
191 216
192 @Override 217 @Override
193 protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) { 218 protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
194 if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) { 219 if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) {
195 - myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition());  
196 - otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); 220 + myUsageStates.entrySet().removeIf(entry -> {
  221 + return !partitionService.resolve(ServiceType.TB_CORE, entry.getValue().getTenantId(), entry.getKey()).isMyPartition();
  222 + });
  223 + otherUsageStates.entrySet().removeIf(entry -> {
  224 + return partitionService.resolve(ServiceType.TB_CORE, entry.getValue().getTenantId(), entry.getKey()).isMyPartition();
  225 + });
197 initStatesFromDataBase(); 226 initStatesFromDataBase();
198 } 227 }
199 } 228 }
200 229
201 @Override 230 @Override
202 public ApiUsageState getApiUsageState(TenantId tenantId) { 231 public ApiUsageState getApiUsageState(TenantId tenantId) {
203 - TenantApiUsageState tenantState = myTenantStates.get(tenantId); 232 + TenantApiUsageState tenantState = (TenantApiUsageState) myUsageStates.get(tenantId);
204 if (tenantState != null) { 233 if (tenantState != null) {
205 return tenantState.getApiUsageState(); 234 return tenantState.getApiUsageState();
206 } else { 235 } else {
207 - ApiUsageState state = otherTenantStates.get(tenantId); 236 + ApiUsageState state = otherUsageStates.get(tenantId);
208 if (state != null) { 237 if (state != null) {
209 return state; 238 return state;
210 } else { 239 } else {
211 if (partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) { 240 if (partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) {
212 - return getOrFetchState(tenantId).getApiUsageState(); 241 + return getOrFetchState(tenantId, tenantId).getApiUsageState();
213 } else { 242 } else {
214 updateLock.lock(); 243 updateLock.lock();
215 try { 244 try {
216 - state = otherTenantStates.get(tenantId); 245 + state = otherUsageStates.get(tenantId);
217 if (state == null) { 246 if (state == null) {
218 state = apiUsageStateService.findTenantApiUsageState(tenantId); 247 state = apiUsageStateService.findTenantApiUsageState(tenantId);
219 if (state != null) { 248 if (state != null) {
220 - otherTenantStates.put(tenantId, state); 249 + otherUsageStates.put(tenantId, state);
221 } 250 }
222 } 251 }
223 } finally { 252 } finally {
@@ -231,7 +260,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -231,7 +260,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
231 260
232 @Override 261 @Override
233 public void onApiUsageStateUpdate(TenantId tenantId) { 262 public void onApiUsageStateUpdate(TenantId tenantId) {
234 - otherTenantStates.remove(tenantId); 263 + otherUsageStates.remove(tenantId);
235 } 264 }
236 265
237 @Override 266 @Override
@@ -240,11 +269,14 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -240,11 +269,14 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
240 TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId); 269 TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId);
241 updateLock.lock(); 270 updateLock.lock();
242 try { 271 try {
243 - myTenantStates.values().forEach(state -> {  
244 - if (tenantProfile.getId().equals(state.getTenantProfileId())) {  
245 - updateTenantState(state, tenantProfile);  
246 - }  
247 - }); 272 + myUsageStates.values().stream()
  273 + .filter(state -> state.getEntityType() == EntityType.TENANT)
  274 + .map(state -> (TenantApiUsageState) state)
  275 + .forEach(state -> {
  276 + if (tenantProfile.getId().equals(state.getTenantProfileId())) {
  277 + updateTenantState(state, tenantProfile);
  278 + }
  279 + });
248 } finally { 280 } finally {
249 updateLock.unlock(); 281 updateLock.unlock();
250 } 282 }
@@ -256,7 +288,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -256,7 +288,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
256 TenantProfile tenantProfile = tenantProfileCache.get(tenantId); 288 TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
257 updateLock.lock(); 289 updateLock.lock();
258 try { 290 try {
259 - TenantApiUsageState state = myTenantStates.get(tenantId); 291 + TenantApiUsageState state = (TenantApiUsageState) myUsageStates.get(tenantId);
260 if (state != null && !state.getTenantProfileId().equals(tenantProfile.getId())) { 292 if (state != null && !state.getTenantProfileId().equals(tenantProfile.getId())) {
261 updateTenantState(state, tenantProfile); 293 updateTenantState(state, tenantProfile);
262 } 294 }
@@ -293,29 +325,42 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -293,29 +325,42 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
293 } 325 }
294 } 326 }
295 327
296 - private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, ApiUsageStateValue> result) {  
297 - log.info("[{}] Detected update of the API state: {}", state.getTenantId(), result); 328 + public void onTenantDelete(TenantId tenantId) {
  329 + deletedEntities.add(tenantId);
  330 + myUsageStates.remove(tenantId);
  331 + otherUsageStates.remove(tenantId);
  332 + }
  333 +
  334 + @Override
  335 + public void onCustomerDelete(CustomerId customerId) {
  336 + deletedEntities.add(customerId);
  337 + myUsageStates.remove(customerId);
  338 + }
  339 +
  340 + private void persistAndNotify(BaseApiUsageState state, Map<ApiFeature, ApiUsageStateValue> result) {
  341 + log.info("[{}] Detected update of the API state for {}: {}", state.getEntityId(), state.getEntityType(), result);
298 apiUsageStateService.update(state.getApiUsageState()); 342 apiUsageStateService.update(state.getApiUsageState());
299 clusterService.onApiStateChange(state.getApiUsageState(), null); 343 clusterService.onApiStateChange(state.getApiUsageState(), null);
300 long ts = System.currentTimeMillis(); 344 long ts = System.currentTimeMillis();
301 List<TsKvEntry> stateTelemetry = new ArrayList<>(); 345 List<TsKvEntry> stateTelemetry = new ArrayList<>();
302 - result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))))); 346 + result.forEach((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))));
303 tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); 347 tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK);
304 348
305 - String email = tenantService.findTenantById(state.getTenantId()).getEmail();  
306 -  
307 - if (StringUtils.isNotEmpty(email)) {  
308 - result.forEach((apiFeature, stateValue) -> {  
309 - mailExecutor.submit(() -> {  
310 - try {  
311 - mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, createStateMailMessage(state, apiFeature, stateValue));  
312 - } catch (ThingsboardException e) {  
313 - log.warn("[{}] Can't send update of the API state to tenant with provided email [{}]", state.getTenantId(), email, e);  
314 - } 349 + if (state.getEntityType() == EntityType.TENANT && !state.getEntityId().equals(TenantId.SYS_TENANT_ID)) {
  350 + String email = tenantService.findTenantById(state.getTenantId()).getEmail();
  351 + if (StringUtils.isNotEmpty(email)) {
  352 + result.forEach((apiFeature, stateValue) -> {
  353 + mailExecutor.submit(() -> {
  354 + try {
  355 + mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, createStateMailMessage((TenantApiUsageState) state, apiFeature, stateValue));
  356 + } catch (ThingsboardException e) {
  357 + log.warn("[{}] Can't send update of the API state to tenant with provided email [{}]", state.getTenantId(), email, e);
  358 + }
  359 + });
315 }); 360 });
316 - });  
317 - } else {  
318 - log.warn("[{}] Can't send update of the API state to tenant with empty email!", state.getTenantId()); 361 + } else {
  362 + log.warn("[{}] Can't send update of the API state to tenant with empty email!", state.getTenantId());
  363 + }
319 } 364 }
320 } 365 }
321 366
@@ -350,12 +395,14 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -350,12 +395,14 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
350 updateLock.lock(); 395 updateLock.lock();
351 try { 396 try {
352 long now = System.currentTimeMillis(); 397 long now = System.currentTimeMillis();
353 - myTenantStates.values().forEach(state -> { 398 + myUsageStates.values().forEach(state -> {
354 if ((state.getNextCycleTs() < now) && (now - state.getNextCycleTs() < TimeUnit.HOURS.toMillis(1))) { 399 if ((state.getNextCycleTs() < now) && (now - state.getNextCycleTs() < TimeUnit.HOURS.toMillis(1))) {
355 - TenantId tenantId = state.getTenantId();  
356 state.setCycles(state.getNextCycleTs(), SchedulerUtils.getStartOfNextNextMonth()); 400 state.setCycles(state.getNextCycleTs(), SchedulerUtils.getStartOfNextNextMonth());
357 saveNewCounts(state, Arrays.asList(ApiUsageRecordKey.values())); 401 saveNewCounts(state, Arrays.asList(ApiUsageRecordKey.values()));
358 - updateTenantState(state, tenantProfileCache.get(tenantId)); 402 + if (state.getEntityType() == EntityType.TENANT && !state.getEntityId().equals(TenantId.SYS_TENANT_ID)) {
  403 + TenantId tenantId = state.getTenantId();
  404 + updateTenantState((TenantApiUsageState) state, tenantProfileCache.get(tenantId));
  405 + }
359 } 406 }
360 }); 407 });
361 } finally { 408 } finally {
@@ -363,7 +410,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -363,7 +410,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
363 } 410 }
364 } 411 }
365 412
366 - private void saveNewCounts(TenantApiUsageState state, List<ApiUsageRecordKey> keys) { 413 + private void saveNewCounts(BaseApiUsageState state, List<ApiUsageRecordKey> keys) {
367 List<TsKvEntry> counts = keys.stream() 414 List<TsKvEntry> counts = keys.stream()
368 .map(key -> new BasicTsKvEntry(state.getCurrentCycleTs(), new LongDataEntry(key.getApiCountKey(), 0L))) 415 .map(key -> new BasicTsKvEntry(state.getCurrentCycleTs(), new LongDataEntry(key.getApiCountKey(), 0L)))
369 .collect(Collectors.toList()); 416 .collect(Collectors.toList());
@@ -371,52 +418,66 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -371,52 +418,66 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
371 tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), counts, VOID_CALLBACK); 418 tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), counts, VOID_CALLBACK);
372 } 419 }
373 420
374 - private TenantApiUsageState getOrFetchState(TenantId tenantId) {  
375 - TenantApiUsageState tenantState = myTenantStates.get(tenantId);  
376 - if (tenantState == null) {  
377 - ApiUsageState dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId);  
378 - if (dbStateEntity == null) {  
379 - try {  
380 - dbStateEntity = apiUsageStateService.createDefaultApiUsageState(tenantId);  
381 - } catch (Exception e) {  
382 - dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId);  
383 - }  
384 - }  
385 - TenantProfile tenantProfile = tenantProfileCache.get(tenantId);  
386 - tenantState = new TenantApiUsageState(tenantProfile, dbStateEntity);  
387 - List<ApiUsageRecordKey> newCounts = new ArrayList<>(); 421 + private BaseApiUsageState getOrFetchState(TenantId tenantId, EntityId entityId) {
  422 + if (entityId == null || entityId.isNullUid()) {
  423 + entityId = tenantId;
  424 + }
  425 + BaseApiUsageState state = myUsageStates.get(entityId);
  426 + if (state != null) {
  427 + return state;
  428 + }
  429 +
  430 + ApiUsageState storedState = apiUsageStateService.findApiUsageStateByEntityId(entityId);
  431 + if (storedState == null) {
388 try { 432 try {
389 - List<TsKvEntry> dbValues = tsService.findAllLatest(tenantId, dbStateEntity.getId()).get();  
390 - for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {  
391 - boolean cycleEntryFound = false;  
392 - boolean hourlyEntryFound = false;  
393 - for (TsKvEntry tsKvEntry : dbValues) {  
394 - if (tsKvEntry.getKey().equals(key.getApiCountKey())) {  
395 - cycleEntryFound = true;  
396 -  
397 - boolean oldCount = tsKvEntry.getTs() == tenantState.getCurrentCycleTs();  
398 - tenantState.put(key, oldCount ? tsKvEntry.getLongValue().get() : 0L);  
399 -  
400 - if (!oldCount) {  
401 - newCounts.add(key);  
402 - }  
403 - } else if (tsKvEntry.getKey().equals(key.getApiCountKey() + HOURLY)) {  
404 - hourlyEntryFound = true;  
405 - tenantState.putHourly(key, tsKvEntry.getTs() == tenantState.getCurrentHourTs() ? tsKvEntry.getLongValue().get() : 0L);  
406 - }  
407 - if (cycleEntryFound && hourlyEntryFound) {  
408 - break; 433 + storedState = apiUsageStateService.createDefaultApiUsageState(tenantId, entityId);
  434 + } catch (Exception e) {
  435 + storedState = apiUsageStateService.findApiUsageStateByEntityId(entityId);
  436 + }
  437 + }
  438 + if (entityId.getEntityType() == EntityType.TENANT) {
  439 + if (!entityId.equals(TenantId.SYS_TENANT_ID)) {
  440 + state = new TenantApiUsageState(tenantProfileCache.get((TenantId) entityId), storedState);
  441 + } else {
  442 + state = new TenantApiUsageState(storedState);
  443 + }
  444 + } else {
  445 + state = new CustomerApiUsageState(storedState);
  446 + }
  447 +
  448 + List<ApiUsageRecordKey> newCounts = new ArrayList<>();
  449 + try {
  450 + List<TsKvEntry> dbValues = tsService.findAllLatest(tenantId, storedState.getId()).get();
  451 + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
  452 + boolean cycleEntryFound = false;
  453 + boolean hourlyEntryFound = false;
  454 + for (TsKvEntry tsKvEntry : dbValues) {
  455 + if (tsKvEntry.getKey().equals(key.getApiCountKey())) {
  456 + cycleEntryFound = true;
  457 +
  458 + boolean oldCount = tsKvEntry.getTs() == state.getCurrentCycleTs();
  459 + state.put(key, oldCount ? tsKvEntry.getLongValue().get() : 0L);
  460 +
  461 + if (!oldCount) {
  462 + newCounts.add(key);
409 } 463 }
  464 + } else if (tsKvEntry.getKey().equals(key.getApiCountKey() + HOURLY)) {
  465 + hourlyEntryFound = true;
  466 + state.putHourly(key, tsKvEntry.getTs() == state.getCurrentHourTs() ? tsKvEntry.getLongValue().get() : 0L);
  467 + }
  468 + if (cycleEntryFound && hourlyEntryFound) {
  469 + break;
410 } 470 }
411 } 471 }
412 - log.debug("[{}] Initialized state: {}", tenantId, dbStateEntity);  
413 - myTenantStates.put(tenantId, tenantState);  
414 - saveNewCounts(tenantState, newCounts);  
415 - } catch (InterruptedException | ExecutionException e) {  
416 - log.warn("[{}] Failed to fetch api usage state from db.", tenantId, e);  
417 } 472 }
  473 + log.debug("[{}] Initialized state: {}", entityId, storedState);
  474 + myUsageStates.put(entityId, state);
  475 + saveNewCounts(state, newCounts);
  476 + } catch (InterruptedException | ExecutionException e) {
  477 + log.warn("[{}] Failed to fetch api usage state from db.", tenantId, e);
418 } 478 }
419 - return tenantState; 479 +
  480 + return state;
420 } 481 }
421 482
422 private void initStatesFromDataBase() { 483 private void initStatesFromDataBase() {
@@ -429,11 +490,11 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -429,11 +490,11 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
429 PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, 1024); 490 PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, 1024);
430 List<Future<?>> futures = new ArrayList<>(); 491 List<Future<?>> futures = new ArrayList<>();
431 for (Tenant tenant : tenantIterator) { 492 for (Tenant tenant : tenantIterator) {
432 - if (!myTenantStates.containsKey(tenant.getId()) && partitionService.resolve(ServiceType.TB_CORE, tenant.getId(), tenant.getId()).isMyPartition()) { 493 + if (!myUsageStates.containsKey(tenant.getId()) && partitionService.resolve(ServiceType.TB_CORE, tenant.getId(), tenant.getId()).isMyPartition()) {
433 log.debug("[{}] Initializing tenant state.", tenant.getId()); 494 log.debug("[{}] Initializing tenant state.", tenant.getId());
434 futures.add(tmpInitExecutor.submit(() -> { 495 futures.add(tmpInitExecutor.submit(() -> {
435 try { 496 try {
436 - updateTenantState(getOrFetchState(tenant.getId()), tenantProfileCache.get(tenant.getTenantProfileId())); 497 + updateTenantState((TenantApiUsageState) getOrFetchState(tenant.getId(), tenant.getId()), tenantProfileCache.get(tenant.getTenantProfileId()));
437 log.debug("[{}] Initialized tenant state.", tenant.getId()); 498 log.debug("[{}] Initialized tenant state.", tenant.getId());
438 } catch (Exception e) { 499 } catch (Exception e) {
439 log.warn("[{}] Failed to initialize tenant API state", tenant.getId(), e); 500 log.warn("[{}] Failed to initialize tenant API state", tenant.getId(), e);
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.apiusage; @@ -17,6 +17,7 @@ package org.thingsboard.server.service.apiusage;
17 17
18 import org.springframework.context.ApplicationListener; 18 import org.springframework.context.ApplicationListener;
19 import org.thingsboard.server.common.data.ApiUsageState; 19 import org.thingsboard.server.common.data.ApiUsageState;
  20 +import org.thingsboard.server.common.data.id.CustomerId;
20 import org.thingsboard.server.common.data.id.TenantId; 21 import org.thingsboard.server.common.data.id.TenantId;
21 import org.thingsboard.server.common.data.id.TenantProfileId; 22 import org.thingsboard.server.common.data.id.TenantProfileId;
22 import org.thingsboard.server.common.msg.queue.TbCallback; 23 import org.thingsboard.server.common.msg.queue.TbCallback;
@@ -34,5 +35,9 @@ public interface TbApiUsageStateService extends ApplicationListener<PartitionCha @@ -34,5 +35,9 @@ public interface TbApiUsageStateService extends ApplicationListener<PartitionCha
34 35
35 void onTenantUpdate(TenantId tenantId); 36 void onTenantUpdate(TenantId tenantId);
36 37
  38 + void onTenantDelete(TenantId tenantId);
  39 +
  40 + void onCustomerDelete(CustomerId customerId);
  41 +
37 void onApiUsageStateUpdate(TenantId tenantId); 42 void onApiUsageStateUpdate(TenantId tenantId);
38 } 43 }
@@ -22,85 +22,33 @@ import org.thingsboard.server.common.data.ApiFeature; @@ -22,85 +22,33 @@ import org.thingsboard.server.common.data.ApiFeature;
22 import org.thingsboard.server.common.data.ApiUsageRecordKey; 22 import org.thingsboard.server.common.data.ApiUsageRecordKey;
23 import org.thingsboard.server.common.data.ApiUsageState; 23 import org.thingsboard.server.common.data.ApiUsageState;
24 import org.thingsboard.server.common.data.ApiUsageStateValue; 24 import org.thingsboard.server.common.data.ApiUsageStateValue;
  25 +import org.thingsboard.server.common.data.EntityType;
25 import org.thingsboard.server.common.data.TenantProfile; 26 import org.thingsboard.server.common.data.TenantProfile;
26 -import org.thingsboard.server.common.data.id.TenantId;  
27 import org.thingsboard.server.common.data.id.TenantProfileId; 27 import org.thingsboard.server.common.data.id.TenantProfileId;
28 import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; 28 import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
29 -import org.thingsboard.server.common.msg.tools.SchedulerUtils;  
30 29
31 import java.util.Arrays; 30 import java.util.Arrays;
32 import java.util.HashMap; 31 import java.util.HashMap;
33 import java.util.HashSet; 32 import java.util.HashSet;
34 import java.util.Map; 33 import java.util.Map;
35 import java.util.Set; 34 import java.util.Set;
36 -import java.util.concurrent.ConcurrentHashMap;  
37 -  
38 -public class TenantApiUsageState {  
39 -  
40 - private final Map<ApiUsageRecordKey, Long> currentCycleValues = new ConcurrentHashMap<>();  
41 - private final Map<ApiUsageRecordKey, Long> currentHourValues = new ConcurrentHashMap<>();  
42 35
  36 +public class TenantApiUsageState extends BaseApiUsageState {
43 @Getter 37 @Getter
44 @Setter 38 @Setter
45 private TenantProfileId tenantProfileId; 39 private TenantProfileId tenantProfileId;
46 @Getter 40 @Getter
47 @Setter 41 @Setter
48 private TenantProfileData tenantProfileData; 42 private TenantProfileData tenantProfileData;
49 - @Getter  
50 - private final ApiUsageState apiUsageState;  
51 - @Getter  
52 - private volatile long currentCycleTs;  
53 - @Getter  
54 - private volatile long nextCycleTs;  
55 - @Getter  
56 - private volatile long currentHourTs;  
57 43
58 public TenantApiUsageState(TenantProfile tenantProfile, ApiUsageState apiUsageState) { 44 public TenantApiUsageState(TenantProfile tenantProfile, ApiUsageState apiUsageState) {
  45 + super(apiUsageState);
59 this.tenantProfileId = tenantProfile.getId(); 46 this.tenantProfileId = tenantProfile.getId();
60 this.tenantProfileData = tenantProfile.getProfileData(); 47 this.tenantProfileData = tenantProfile.getProfileData();
61 - this.apiUsageState = apiUsageState;  
62 - this.currentCycleTs = SchedulerUtils.getStartOfCurrentMonth();  
63 - this.nextCycleTs = SchedulerUtils.getStartOfNextMonth();  
64 - this.currentHourTs = SchedulerUtils.getStartOfCurrentHour();  
65 - }  
66 -  
67 - public void put(ApiUsageRecordKey key, Long value) {  
68 - currentCycleValues.put(key, value);  
69 } 48 }
70 49
71 - public void putHourly(ApiUsageRecordKey key, Long value) {  
72 - currentHourValues.put(key, value);  
73 - }  
74 -  
75 - public long add(ApiUsageRecordKey key, long value) {  
76 - long result = currentCycleValues.getOrDefault(key, 0L) + value;  
77 - currentCycleValues.put(key, result);  
78 - return result;  
79 - }  
80 -  
81 - public long get(ApiUsageRecordKey key) {  
82 - return currentCycleValues.getOrDefault(key, 0L);  
83 - }  
84 -  
85 - public long addToHourly(ApiUsageRecordKey key, long value) {  
86 - long result = currentHourValues.getOrDefault(key, 0L) + value;  
87 - currentHourValues.put(key, result);  
88 - return result;  
89 - }  
90 -  
91 - public void setHour(long currentHourTs) {  
92 - this.currentHourTs = currentHourTs;  
93 - for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {  
94 - currentHourValues.put(key, 0L);  
95 - }  
96 - }  
97 -  
98 - public void setCycles(long currentCycleTs, long nextCycleTs) {  
99 - this.currentCycleTs = currentCycleTs;  
100 - this.nextCycleTs = nextCycleTs;  
101 - for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {  
102 - currentCycleValues.put(key, 0L);  
103 - } 50 + public TenantApiUsageState(ApiUsageState apiUsageState) {
  51 + super(apiUsageState);
104 } 52 }
105 53
106 public long getProfileThreshold(ApiUsageRecordKey key) { 54 public long getProfileThreshold(ApiUsageRecordKey key) {
@@ -111,53 +59,25 @@ public class TenantApiUsageState { @@ -111,53 +59,25 @@ public class TenantApiUsageState {
111 return tenantProfileData.getConfiguration().getWarnThreshold(key); 59 return tenantProfileData.getConfiguration().getWarnThreshold(key);
112 } 60 }
113 61
114 - public TenantId getTenantId() {  
115 - return apiUsageState.getTenantId();  
116 - }  
117 -  
118 - public ApiUsageStateValue getFeatureValue(ApiFeature feature) {  
119 - switch (feature) {  
120 - case TRANSPORT:  
121 - return apiUsageState.getTransportState();  
122 - case RE:  
123 - return apiUsageState.getReExecState();  
124 - case DB:  
125 - return apiUsageState.getDbStorageState();  
126 - case JS:  
127 - return apiUsageState.getJsExecState();  
128 - case EMAIL:  
129 - return apiUsageState.getEmailExecState();  
130 - case SMS:  
131 - return apiUsageState.getSmsExecState();  
132 - default:  
133 - return ApiUsageStateValue.ENABLED; 62 + private Pair<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(ApiFeature feature) {
  63 + ApiUsageStateValue featureValue = ApiUsageStateValue.ENABLED;
  64 + for (ApiUsageRecordKey recordKey : ApiUsageRecordKey.getKeys(feature)) {
  65 + long value = get(recordKey);
  66 + long threshold = getProfileThreshold(recordKey);
  67 + long warnThreshold = getProfileWarnThreshold(recordKey);
  68 + ApiUsageStateValue tmpValue;
  69 + if (threshold == 0 || value == 0 || value < warnThreshold) {
  70 + tmpValue = ApiUsageStateValue.ENABLED;
  71 + } else if (value < threshold) {
  72 + tmpValue = ApiUsageStateValue.WARNING;
  73 + } else {
  74 + tmpValue = ApiUsageStateValue.DISABLED;
  75 + }
  76 + featureValue = ApiUsageStateValue.toMoreRestricted(featureValue, tmpValue);
134 } 77 }
  78 + return setFeatureValue(feature, featureValue) ? Pair.of(feature, featureValue) : null;
135 } 79 }
136 80
137 - public boolean setFeatureValue(ApiFeature feature, ApiUsageStateValue value) {  
138 - ApiUsageStateValue currentValue = getFeatureValue(feature);  
139 - switch (feature) {  
140 - case TRANSPORT:  
141 - apiUsageState.setTransportState(value);  
142 - break;  
143 - case RE:  
144 - apiUsageState.setReExecState(value);  
145 - break;  
146 - case DB:  
147 - apiUsageState.setDbStorageState(value);  
148 - break;  
149 - case JS:  
150 - apiUsageState.setJsExecState(value);  
151 - break;  
152 - case EMAIL:  
153 - apiUsageState.setEmailExecState(value);  
154 - break;  
155 - case SMS:  
156 - apiUsageState.setSmsExecState(value);  
157 - break;  
158 - }  
159 - return !currentValue.equals(value);  
160 - }  
161 81
162 public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThresholds() { 82 public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThresholds() {
163 return checkStateUpdatedDueToThreshold(new HashSet<>(Arrays.asList(ApiFeature.values()))); 83 return checkStateUpdatedDueToThreshold(new HashSet<>(Arrays.asList(ApiFeature.values())));
@@ -174,23 +94,9 @@ public class TenantApiUsageState { @@ -174,23 +94,9 @@ public class TenantApiUsageState {
174 return result; 94 return result;
175 } 95 }
176 96
177 - public Pair<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(ApiFeature feature) {  
178 - ApiUsageStateValue featureValue = ApiUsageStateValue.ENABLED;  
179 - for (ApiUsageRecordKey recordKey : ApiUsageRecordKey.getKeys(feature)) {  
180 - long value = get(recordKey);  
181 - long threshold = getProfileThreshold(recordKey);  
182 - long warnThreshold = getProfileWarnThreshold(recordKey);  
183 - ApiUsageStateValue tmpValue;  
184 - if (threshold == 0 || value == 0 || value < warnThreshold) {  
185 - tmpValue = ApiUsageStateValue.ENABLED;  
186 - } else if (value < threshold) {  
187 - tmpValue = ApiUsageStateValue.WARNING;  
188 - } else {  
189 - tmpValue = ApiUsageStateValue.DISABLED;  
190 - }  
191 - featureValue = ApiUsageStateValue.toMoreRestricted(featureValue, tmpValue);  
192 - }  
193 - return setFeatureValue(feature, featureValue) ? Pair.of(feature, featureValue) : null; 97 + @Override
  98 + public EntityType getEntityType() {
  99 + return EntityType.TENANT;
194 } 100 }
195 101
196 } 102 }
@@ -230,7 +230,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { @@ -230,7 +230,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
230 private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) { 230 private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) {
231 try { 231 try {
232 JsonNode entityNode = JacksonUtil.valueToTree(request); 232 JsonNode entityNode = JacksonUtil.valueToTree(request);
233 - TbMsg msg = TbMsg.newMsg(type, device.getId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode)); 233 + TbMsg msg = TbMsg.newMsg(type, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode));
234 sendToRuleEngine(device.getTenantId(), msg, null); 234 sendToRuleEngine(device.getTenantId(), msg, null);
235 } catch (IllegalArgumentException e) { 235 } catch (IllegalArgumentException e) {
236 log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e); 236 log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e);
@@ -240,7 +240,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { @@ -240,7 +240,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
240 private void pushDeviceCreatedEventToRuleEngine(Device device) { 240 private void pushDeviceCreatedEventToRuleEngine(Device device) {
241 try { 241 try {
242 ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); 242 ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device);
243 - TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); 243 + TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
244 sendToRuleEngine(device.getTenantId(), msg, null); 244 sendToRuleEngine(device.getTenantId(), msg, null);
245 } catch (JsonProcessingException | IllegalArgumentException e) { 245 } catch (JsonProcessingException | IllegalArgumentException e) {
246 log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); 246 log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e);
@@ -920,7 +920,7 @@ public final class EdgeGrpcSession implements Closeable { @@ -920,7 +920,7 @@ public final class EdgeGrpcSession implements Closeable {
920 try { 920 try {
921 if (uplinkMsg.getEntityDataCount() > 0) { 921 if (uplinkMsg.getEntityDataCount() > 0) {
922 for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) { 922 for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) {
923 - result.addAll(ctx.getTelemetryProcessor().onTelemetryUpdate(edge.getTenantId(), entityData)); 923 + result.addAll(ctx.getTelemetryProcessor().onTelemetryUpdate(edge.getTenantId(), edge.getCustomerId(), entityData));
924 } 924 }
925 } 925 }
926 if (uplinkMsg.getDeviceUpdateMsgCount() > 0) { 926 if (uplinkMsg.getDeviceUpdateMsgCount() > 0) {
@@ -231,7 +231,7 @@ public class DeviceProcessor extends BaseProcessor { @@ -231,7 +231,7 @@ public class DeviceProcessor extends BaseProcessor {
231 try { 231 try {
232 DeviceId deviceId = device.getId(); 232 DeviceId deviceId = device.getId();
233 ObjectNode entityNode = mapper.valueToTree(device); 233 ObjectNode entityNode = mapper.valueToTree(device);
234 - TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, 234 + TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, device.getCustomerId(),
235 getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, mapper.writeValueAsString(entityNode)); 235 getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, mapper.writeValueAsString(entityNode));
236 tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { 236 tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() {
237 @Override 237 @Override
@@ -70,7 +70,7 @@ public class TelemetryProcessor extends BaseProcessor { @@ -70,7 +70,7 @@ public class TelemetryProcessor extends BaseProcessor {
70 70
71 private final Gson gson = new Gson(); 71 private final Gson gson = new Gson();
72 72
73 - public List<ListenableFuture<Void>> onTelemetryUpdate(TenantId tenantId, EntityDataProto entityData) { 73 + public List<ListenableFuture<Void>> onTelemetryUpdate(TenantId tenantId, CustomerId customerId, EntityDataProto entityData) {
74 log.trace("[{}] onTelemetryUpdate [{}]", tenantId, entityData); 74 log.trace("[{}] onTelemetryUpdate [{}]", tenantId, entityData);
75 List<ListenableFuture<Void>> result = new ArrayList<>(); 75 List<ListenableFuture<Void>> result = new ArrayList<>();
76 EntityId entityId = constructEntityId(entityData); 76 EntityId entityId = constructEntityId(entityData);
@@ -80,14 +80,14 @@ public class TelemetryProcessor extends BaseProcessor { @@ -80,14 +80,14 @@ public class TelemetryProcessor extends BaseProcessor {
80 TbMsgMetaData metaData = new TbMsgMetaData(); 80 TbMsgMetaData metaData = new TbMsgMetaData();
81 metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE); 81 metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE);
82 if (entityData.hasPostAttributesMsg()) { 82 if (entityData.hasPostAttributesMsg()) {
83 - result.add(processPostAttributes(tenantId, entityId, entityData.getPostAttributesMsg(), metaData)); 83 + result.add(processPostAttributes(tenantId, customerId, entityId, entityData.getPostAttributesMsg(), metaData));
84 } 84 }
85 if (entityData.hasAttributesUpdatedMsg()) { 85 if (entityData.hasAttributesUpdatedMsg()) {
86 metaData.putValue("scope", entityData.getPostAttributeScope()); 86 metaData.putValue("scope", entityData.getPostAttributeScope());
87 - result.add(processAttributesUpdate(tenantId, entityId, entityData.getAttributesUpdatedMsg(), metaData)); 87 + result.add(processAttributesUpdate(tenantId, customerId, entityId, entityData.getAttributesUpdatedMsg(), metaData));
88 } 88 }
89 if (entityData.hasPostTelemetryMsg()) { 89 if (entityData.hasPostTelemetryMsg()) {
90 - result.add(processPostTelemetry(tenantId, entityId, entityData.getPostTelemetryMsg(), metaData)); 90 + result.add(processPostTelemetry(tenantId, customerId, entityId, entityData.getPostTelemetryMsg(), metaData));
91 } 91 }
92 } 92 }
93 if (entityData.hasAttributeDeleteMsg()) { 93 if (entityData.hasAttributeDeleteMsg()) {
@@ -148,7 +148,7 @@ public class TelemetryProcessor extends BaseProcessor { @@ -148,7 +148,7 @@ public class TelemetryProcessor extends BaseProcessor {
148 } 148 }
149 } 149 }
150 150
151 - private ListenableFuture<Void> processPostTelemetry(TenantId tenantId, EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) { 151 + private ListenableFuture<Void> processPostTelemetry(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) {
152 SettableFuture<Void> futureToSet = SettableFuture.create(); 152 SettableFuture<Void> futureToSet = SettableFuture.create();
153 for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { 153 for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) {
154 JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); 154 JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
@@ -156,7 +156,7 @@ public class TelemetryProcessor extends BaseProcessor { @@ -156,7 +156,7 @@ public class TelemetryProcessor extends BaseProcessor {
156 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); 156 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
157 String queueName = defaultQueueAndRuleChain.getKey(); 157 String queueName = defaultQueueAndRuleChain.getKey();
158 RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); 158 RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
159 - TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); 159 + TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
160 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { 160 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
161 @Override 161 @Override
162 public void onSuccess(TbQueueMsgMetadata metadata) { 162 public void onSuccess(TbQueueMsgMetadata metadata) {
@@ -173,13 +173,13 @@ public class TelemetryProcessor extends BaseProcessor { @@ -173,13 +173,13 @@ public class TelemetryProcessor extends BaseProcessor {
173 return futureToSet; 173 return futureToSet;
174 } 174 }
175 175
176 - private ListenableFuture<Void> processPostAttributes(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { 176 + private ListenableFuture<Void> processPostAttributes(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
177 SettableFuture<Void> futureToSet = SettableFuture.create(); 177 SettableFuture<Void> futureToSet = SettableFuture.create();
178 JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); 178 JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
179 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); 179 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
180 String queueName = defaultQueueAndRuleChain.getKey(); 180 String queueName = defaultQueueAndRuleChain.getKey();
181 RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); 181 RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
182 - TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); 182 + TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
183 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { 183 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
184 @Override 184 @Override
185 public void onSuccess(TbQueueMsgMetadata metadata) { 185 public void onSuccess(TbQueueMsgMetadata metadata) {
@@ -195,7 +195,7 @@ public class TelemetryProcessor extends BaseProcessor { @@ -195,7 +195,7 @@ public class TelemetryProcessor extends BaseProcessor {
195 return futureToSet; 195 return futureToSet;
196 } 196 }
197 197
198 - private ListenableFuture<Void> processAttributesUpdate(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { 198 + private ListenableFuture<Void> processAttributesUpdate(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
199 SettableFuture<Void> futureToSet = SettableFuture.create(); 199 SettableFuture<Void> futureToSet = SettableFuture.create();
200 JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); 200 JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
201 Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(json); 201 Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(json);
@@ -206,7 +206,7 @@ public class TelemetryProcessor extends BaseProcessor { @@ -206,7 +206,7 @@ public class TelemetryProcessor extends BaseProcessor {
206 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); 206 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
207 String queueName = defaultQueueAndRuleChain.getKey(); 207 String queueName = defaultQueueAndRuleChain.getKey();
208 RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); 208 RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
209 - TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, gson.toJson(json), ruleChainId, null); 209 + TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
210 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { 210 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
211 @Override 211 @Override
212 public void onSuccess(TbQueueMsgMetadata metadata) { 212 public void onSuccess(TbQueueMsgMetadata metadata) {
  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 +}
application/src/main/java/org/thingsboard/server/service/firmware/FirmwareUpdateStatus.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/TypeServer.java
@@ -13,17 +13,8 @@ @@ -13,17 +13,8 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.transport.lwm2m.utils; 16 +package org.thingsboard.server.service.firmware;
17 17
18 -public enum TypeServer {  
19 - BOOTSTRAP(0, "bootstrap"),  
20 - CLIENT(1, "client");  
21 -  
22 - public int code;  
23 - public String type;  
24 -  
25 - TypeServer(int code, String type) {  
26 - this.code = code;  
27 - this.type = type;  
28 - } 18 +public enum FirmwareUpdateStatus {
  19 + QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
29 } 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))) {
@@ -390,7 +390,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @@ -390,7 +390,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
390 pageData = tenantService.findTenants(pageLink); 390 pageData = tenantService.findTenants(pageLink);
391 for (Tenant tenant : pageData.getData()) { 391 for (Tenant tenant : pageData.getData()) {
392 try { 392 try {
393 - apiUsageStateService.createDefaultApiUsageState(tenant.getId()); 393 + apiUsageStateService.createDefaultApiUsageState(tenant.getId(), null);
394 } catch (Exception e) { 394 } catch (Exception e) {
395 } 395 }
396 List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get(); 396 List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get();
@@ -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 }
@@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.ApiUsageStateMailMessage; @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
36 import org.thingsboard.server.common.data.ApiUsageStateValue; 36 import org.thingsboard.server.common.data.ApiUsageStateValue;
37 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 37 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
38 import org.thingsboard.server.common.data.exception.ThingsboardException; 38 import org.thingsboard.server.common.data.exception.ThingsboardException;
  39 +import org.thingsboard.server.common.data.id.CustomerId;
39 import org.thingsboard.server.common.data.id.EntityId; 40 import org.thingsboard.server.common.data.id.EntityId;
40 import org.thingsboard.server.common.data.id.TenantId; 41 import org.thingsboard.server.common.data.id.TenantId;
41 import org.thingsboard.server.dao.exception.IncorrectParameterException; 42 import org.thingsboard.server.dao.exception.IncorrectParameterException;
@@ -233,7 +234,7 @@ public class DefaultMailService implements MailService { @@ -233,7 +234,7 @@ public class DefaultMailService implements MailService {
233 } 234 }
234 235
235 @Override 236 @Override
236 - public void send(TenantId tenantId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException { 237 + public void send(TenantId tenantId, CustomerId customerId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException {
237 if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) { 238 if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) {
238 MimeMessage mailMsg = mailSender.createMimeMessage(); 239 MimeMessage mailMsg = mailSender.createMimeMessage();
239 MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8"); 240 MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8");
@@ -248,7 +249,7 @@ public class DefaultMailService implements MailService { @@ -248,7 +249,7 @@ public class DefaultMailService implements MailService {
248 helper.setSubject(subject); 249 helper.setSubject(subject);
249 helper.setText(body); 250 helper.setText(body);
250 mailSender.send(helper.getMimeMessage()); 251 mailSender.send(helper.getMimeMessage());
251 - apiUsageClient.report(tenantId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1); 252 + apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1);
252 } else { 253 } else {
253 throw new RuntimeException("Email sending is disabled due to API limits!"); 254 throw new RuntimeException("Email sending is disabled due to API limits!");
254 } 255 }
@@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.HasName; @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.HasName;
29 import org.thingsboard.server.common.data.TbResource; 29 import org.thingsboard.server.common.data.TbResource;
30 import org.thingsboard.server.common.data.Tenant; 30 import org.thingsboard.server.common.data.Tenant;
31 import org.thingsboard.server.common.data.TenantProfile; 31 import org.thingsboard.server.common.data.TenantProfile;
  32 +import org.thingsboard.server.common.data.id.CustomerId;
32 import org.thingsboard.server.common.data.id.DeviceId; 33 import org.thingsboard.server.common.data.id.DeviceId;
33 import org.thingsboard.server.common.data.id.DeviceProfileId; 34 import org.thingsboard.server.common.data.id.DeviceProfileId;
34 import org.thingsboard.server.common.data.id.EdgeId; 35 import org.thingsboard.server.common.data.id.EdgeId;
@@ -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 }
@@ -18,14 +18,15 @@ package org.thingsboard.server.service.queue.processing; @@ -18,14 +18,15 @@ package org.thingsboard.server.service.queue.processing;
18 import com.google.protobuf.ByteString; 18 import com.google.protobuf.ByteString;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.springframework.boot.context.event.ApplicationReadyEvent; 20 import org.springframework.boot.context.event.ApplicationReadyEvent;
21 -import org.springframework.context.ApplicationListener;  
22 import org.springframework.context.event.EventListener; 21 import org.springframework.context.event.EventListener;
23 import org.springframework.core.annotation.Order; 22 import org.springframework.core.annotation.Order;
24 import org.thingsboard.common.util.ThingsBoardThreadFactory; 23 import org.thingsboard.common.util.ThingsBoardThreadFactory;
25 import org.thingsboard.server.actors.ActorSystemContext; 24 import org.thingsboard.server.actors.ActorSystemContext;
26 import org.thingsboard.server.common.data.EntityType; 25 import org.thingsboard.server.common.data.EntityType;
  26 +import org.thingsboard.server.common.data.id.CustomerId;
27 import org.thingsboard.server.common.data.id.DeviceId; 27 import org.thingsboard.server.common.data.id.DeviceId;
28 import org.thingsboard.server.common.data.id.DeviceProfileId; 28 import org.thingsboard.server.common.data.id.DeviceProfileId;
  29 +import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.common.data.id.TenantProfileId; 30 import org.thingsboard.server.common.data.id.TenantProfileId;
30 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 31 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
31 import org.thingsboard.server.common.msg.TbActorMsg; 32 import org.thingsboard.server.common.msg.TbActorMsg;
@@ -72,7 +73,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene @@ -72,7 +73,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
72 protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer; 73 protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer;
73 74
74 public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService, 75 public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService,
75 - TbTenantProfileCache tenantProfileCache, TbDeviceProfileCache deviceProfileCache, TbApiUsageStateService apiUsageStateService, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) { 76 + TbTenantProfileCache tenantProfileCache, TbDeviceProfileCache deviceProfileCache,
  77 + TbApiUsageStateService apiUsageStateService, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) {
76 this.actorContext = actorContext; 78 this.actorContext = actorContext;
77 this.encodingService = encodingService; 79 this.encodingService = encodingService;
78 this.tenantProfileCache = tenantProfileCache; 80 this.tenantProfileCache = tenantProfileCache;
@@ -166,6 +168,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene @@ -166,6 +168,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
166 tenantProfileCache.evict(componentLifecycleMsg.getTenantId()); 168 tenantProfileCache.evict(componentLifecycleMsg.getTenantId());
167 if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.UPDATED)) { 169 if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.UPDATED)) {
168 apiUsageStateService.onTenantUpdate(componentLifecycleMsg.getTenantId()); 170 apiUsageStateService.onTenantUpdate(componentLifecycleMsg.getTenantId());
  171 + } else if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.DELETED)) {
  172 + apiUsageStateService.onTenantDelete((TenantId) componentLifecycleMsg.getEntityId());
169 } 173 }
170 } else if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { 174 } else if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
171 deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId())); 175 deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId()));
@@ -173,6 +177,10 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene @@ -173,6 +177,10 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
173 deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceId(componentLifecycleMsg.getEntityId().getId())); 177 deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceId(componentLifecycleMsg.getEntityId().getId()));
174 } else if (EntityType.API_USAGE_STATE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { 178 } else if (EntityType.API_USAGE_STATE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
175 apiUsageStateService.onApiUsageStateUpdate(componentLifecycleMsg.getTenantId()); 179 apiUsageStateService.onApiUsageStateUpdate(componentLifecycleMsg.getTenantId());
  180 + } else if (EntityType.CUSTOMER.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
  181 + if (componentLifecycleMsg.getEvent() == ComponentLifecycleEvent.DELETED) {
  182 + apiUsageStateService.onCustomerDelete((CustomerId) componentLifecycleMsg.getEntityId());
  183 + }
176 } 184 }
177 } 185 }
178 log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg); 186 log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg);
@@ -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
@@ -34,6 +34,7 @@ import org.thingsboard.server.dao.device.DeviceService; @@ -34,6 +34,7 @@ import org.thingsboard.server.dao.device.DeviceService;
34 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 34 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
35 import org.thingsboard.server.queue.util.TbCoreComponent; 35 import org.thingsboard.server.queue.util.TbCoreComponent;
36 import org.thingsboard.server.service.queue.TbClusterService; 36 import org.thingsboard.server.service.queue.TbClusterService;
  37 +import org.thingsboard.server.service.security.model.SecurityUser;
37 38
38 import javax.annotation.PostConstruct; 39 import javax.annotation.PostConstruct;
39 import javax.annotation.PreDestroy; 40 import javax.annotation.PreDestroy;
@@ -95,11 +96,11 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { @@ -95,11 +96,11 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
95 } 96 }
96 97
97 @Override 98 @Override
98 - public void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { 99 + public void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer, SecurityUser currentUser) {
99 log.trace("[{}][{}] Processing REST API call to rule engine [{}]", request.getTenantId(), request.getId(), request.getDeviceId()); 100 log.trace("[{}][{}] Processing REST API call to rule engine [{}]", request.getTenantId(), request.getId(), request.getDeviceId());
100 UUID requestId = request.getId(); 101 UUID requestId = request.getId();
101 localToRuleEngineRpcRequests.put(requestId, responseConsumer); 102 localToRuleEngineRpcRequests.put(requestId, responseConsumer);
102 - sendRpcRequestToRuleEngine(request); 103 + sendRpcRequestToRuleEngine(request, currentUser);
103 scheduleToRuleEngineTimeout(request, requestId); 104 scheduleToRuleEngineTimeout(request, requestId);
104 } 105 }
105 106
@@ -149,7 +150,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { @@ -149,7 +150,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
149 } 150 }
150 } 151 }
151 152
152 - private void sendRpcRequestToRuleEngine(ToDeviceRpcRequest msg) { 153 + private void sendRpcRequestToRuleEngine(ToDeviceRpcRequest msg, SecurityUser currentUser) {
153 ObjectNode entityNode = json.createObjectNode(); 154 ObjectNode entityNode = json.createObjectNode();
154 TbMsgMetaData metaData = new TbMsgMetaData(); 155 TbMsgMetaData metaData = new TbMsgMetaData();
155 metaData.putValue("requestUUID", msg.getId().toString()); 156 metaData.putValue("requestUUID", msg.getId().toString());
@@ -167,7 +168,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { @@ -167,7 +168,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
167 entityNode.put("params", msg.getBody().getParams()); 168 entityNode.put("params", msg.getBody().getParams());
168 169
169 try { 170 try {
170 - TbMsg tbMsg = TbMsg.newMsg(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); 171 + TbMsg tbMsg = TbMsg.newMsg(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), currentUser.getCustomerId(), metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
171 clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null); 172 clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null);
172 } catch (JsonProcessingException e) { 173 } catch (JsonProcessingException e) {
173 throw new RuntimeException(e); 174 throw new RuntimeException(e);
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.service.rpc; 16 package org.thingsboard.server.service.rpc;
17 17
18 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 18 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  19 +import org.thingsboard.server.service.security.model.SecurityUser;
19 20
20 import java.util.function.Consumer; 21 import java.util.function.Consumer;
21 22
@@ -27,11 +28,11 @@ public interface TbCoreDeviceRpcService { @@ -27,11 +28,11 @@ public interface TbCoreDeviceRpcService {
27 /** 28 /**
28 * Handles REST API calls that contain RPC requests to Device and pushes them to Rule Engine. 29 * Handles REST API calls that contain RPC requests to Device and pushes them to Rule Engine.
29 * Schedules the timeout for the RPC call based on the {@link ToDeviceRpcRequest} 30 * Schedules the timeout for the RPC call based on the {@link ToDeviceRpcRequest}
30 - *  
31 - * @param request the RPC request 31 + * @param request the RPC request
32 * @param responseConsumer the consumer of the RPC response 32 * @param responseConsumer the consumer of the RPC response
  33 + * @param currentUser
33 */ 34 */
34 - void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); 35 + void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer, SecurityUser currentUser);
35 36
36 /** 37 /**
37 * Handles the RPC response from the Rule Engine. 38 * Handles the RPC response from the Rule Engine.
@@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture;
20 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
21 import org.thingsboard.common.util.ThingsBoardThreadFactory; 21 import org.thingsboard.common.util.ThingsBoardThreadFactory;
22 import org.thingsboard.server.common.data.ApiUsageRecordKey; 22 import org.thingsboard.server.common.data.ApiUsageRecordKey;
  23 +import org.thingsboard.server.common.data.id.CustomerId;
23 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.queue.usagestats.TbApiUsageClient; 25 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
25 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 26 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
@@ -73,14 +74,14 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { @@ -73,14 +74,14 @@ public abstract class AbstractJsInvokeService implements JsInvokeService {
73 } 74 }
74 75
75 @Override 76 @Override
76 - public ListenableFuture<Object> invokeFunction(TenantId tenantId, UUID scriptId, Object... args) { 77 + public ListenableFuture<Object> invokeFunction(TenantId tenantId, CustomerId customerId, UUID scriptId, Object... args) {
77 if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) { 78 if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) {
78 String functionName = scriptIdToNameMap.get(scriptId); 79 String functionName = scriptIdToNameMap.get(scriptId);
79 if (functionName == null) { 80 if (functionName == null) {
80 return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!")); 81 return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!"));
81 } 82 }
82 if (!isDisabled(scriptId)) { 83 if (!isDisabled(scriptId)) {
83 - apiUsageClient.report(tenantId, ApiUsageRecordKey.JS_EXEC_COUNT, 1); 84 + apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.JS_EXEC_COUNT, 1);
84 return doInvokeFunction(scriptId, functionName, args); 85 return doInvokeFunction(scriptId, functionName, args);
85 } else { 86 } else {
86 return Futures.immediateFailedFuture( 87 return Futures.immediateFailedFuture(
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 package org.thingsboard.server.service.script; 16 package org.thingsboard.server.service.script;
17 17
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 -import org.thingsboard.server.common.data.id.EntityId; 19 +import org.thingsboard.server.common.data.id.CustomerId;
20 import org.thingsboard.server.common.data.id.TenantId; 20 import org.thingsboard.server.common.data.id.TenantId;
21 21
22 import java.util.UUID; 22 import java.util.UUID;
@@ -25,7 +25,7 @@ public interface JsInvokeService { @@ -25,7 +25,7 @@ public interface JsInvokeService {
25 25
26 ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames); 26 ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames);
27 27
28 - ListenableFuture<Object> invokeFunction(TenantId tenantId, UUID scriptId, Object... args); 28 + ListenableFuture<Object> invokeFunction(TenantId tenantId, CustomerId customerId, UUID scriptId, Object... args);
29 29
30 ListenableFuture<Void> release(UUID scriptId); 30 ListenableFuture<Void> release(UUID scriptId);
31 31
@@ -218,7 +218,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S @@ -218,7 +218,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
218 private JsonNode executeScript(TbMsg msg) throws ScriptException { 218 private JsonNode executeScript(TbMsg msg) throws ScriptException {
219 try { 219 try {
220 String[] inArgs = prepareArgs(msg); 220 String[] inArgs = prepareArgs(msg);
221 - String eval = sandboxService.invokeFunction(tenantId, this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString(); 221 + String eval = sandboxService.invokeFunction(tenantId, msg.getCustomerId(), this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString();
222 return mapper.readTree(eval); 222 return mapper.readTree(eval);
223 } catch (ExecutionException e) { 223 } catch (ExecutionException e) {
224 if (e.getCause() instanceof ScriptException) { 224 if (e.getCause() instanceof ScriptException) {
@@ -235,7 +235,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S @@ -235,7 +235,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
235 235
236 private ListenableFuture<JsonNode> executeScriptAsync(TbMsg msg) { 236 private ListenableFuture<JsonNode> executeScriptAsync(TbMsg msg) {
237 String[] inArgs = prepareArgs(msg); 237 String[] inArgs = prepareArgs(msg);
238 - return Futures.transformAsync(sandboxService.invokeFunction(tenantId, this.scriptId, inArgs[0], inArgs[1], inArgs[2]), 238 + return Futures.transformAsync(sandboxService.invokeFunction(tenantId, msg.getCustomerId(), this.scriptId, inArgs[0], inArgs[1], inArgs[2]),
239 o -> { 239 o -> {
240 try { 240 try {
241 return Futures.immediateFuture(mapper.readTree(o.toString())); 241 return Futures.immediateFuture(mapper.readTree(o.toString()));
@@ -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
@@ -22,6 +22,7 @@ import org.springframework.stereotype.Service; @@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
22 import org.thingsboard.rule.engine.api.SmsService; 22 import org.thingsboard.rule.engine.api.SmsService;
23 import org.thingsboard.rule.engine.api.sms.SmsSender; 23 import org.thingsboard.rule.engine.api.sms.SmsSender;
24 import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; 24 import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
  25 +import org.thingsboard.server.common.data.id.CustomerId;
25 import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; 26 import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration;
26 import org.thingsboard.server.common.data.sms.config.TestSmsRequest; 27 import org.thingsboard.server.common.data.sms.config.TestSmsRequest;
27 import org.thingsboard.server.common.data.AdminSettings; 28 import org.thingsboard.server.common.data.AdminSettings;
@@ -94,7 +95,7 @@ public class DefaultSmsService implements SmsService { @@ -94,7 +95,7 @@ public class DefaultSmsService implements SmsService {
94 } 95 }
95 96
96 @Override 97 @Override
97 - public void sendSms(TenantId tenantId, String[] numbersTo, String message) throws ThingsboardException { 98 + public void sendSms(TenantId tenantId, CustomerId customerId, String[] numbersTo, String message) throws ThingsboardException {
98 if (apiUsageStateService.getApiUsageState(tenantId).isSmsSendEnabled()) { 99 if (apiUsageStateService.getApiUsageState(tenantId).isSmsSendEnabled()) {
99 int smsCount = 0; 100 int smsCount = 0;
100 try { 101 try {
@@ -103,7 +104,7 @@ public class DefaultSmsService implements SmsService { @@ -103,7 +104,7 @@ public class DefaultSmsService implements SmsService {
103 } 104 }
104 } finally { 105 } finally {
105 if (smsCount > 0) { 106 if (smsCount > 0) {
106 - apiUsageClient.report(tenantId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount); 107 + apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount);
107 } 108 }
108 } 109 }
109 } else { 110 } else {
@@ -468,6 +468,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit @@ -468,6 +468,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
468 md.putValue("deviceName", device.getName()); 468 md.putValue("deviceName", device.getName());
469 md.putValue("deviceType", device.getType()); 469 md.putValue("deviceType", device.getType());
470 return DeviceStateData.builder() 470 return DeviceStateData.builder()
  471 + .customerId(device.getCustomerId())
471 .tenantId(device.getTenantId()) 472 .tenantId(device.getTenantId())
472 .deviceId(device.getId()) 473 .deviceId(device.getId())
473 .deviceCreationTime(device.getCreatedTime()) 474 .deviceCreationTime(device.getCreatedTime())
@@ -507,7 +508,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit @@ -507,7 +508,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
507 if(!persistToTelemetry){ 508 if(!persistToTelemetry){
508 md.putValue(DataConstants.SCOPE, SERVER_SCOPE); 509 md.putValue(DataConstants.SCOPE, SERVER_SCOPE);
509 } 510 }
510 - TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), md, TbMsgDataType.JSON, data); 511 + TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), stateData.getCustomerId(), md, TbMsgDataType.JSON, data);
511 clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null); 512 clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null);
512 } catch (Exception e) { 513 } catch (Exception e) {
513 log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); 514 log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e);
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.state; @@ -17,6 +17,7 @@ package org.thingsboard.server.service.state;
17 17
18 import lombok.Builder; 18 import lombok.Builder;
19 import lombok.Data; 19 import lombok.Data;
  20 +import org.thingsboard.server.common.data.id.CustomerId;
20 import org.thingsboard.server.common.data.id.DeviceId; 21 import org.thingsboard.server.common.data.id.DeviceId;
21 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
22 import org.thingsboard.server.common.msg.TbMsgMetaData; 23 import org.thingsboard.server.common.msg.TbMsgMetaData;
@@ -29,6 +30,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -29,6 +30,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
29 class DeviceStateData { 30 class DeviceStateData {
30 31
31 private final TenantId tenantId; 32 private final TenantId tenantId;
  33 + private final CustomerId customerId;
32 private final DeviceId deviceId; 34 private final DeviceId deviceId;
33 private final long deviceCreationTime; 35 private final long deviceCreationTime;
34 private TbMsgMetaData metaData; 36 private TbMsgMetaData metaData;
@@ -25,7 +25,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; @@ -25,7 +25,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory;
25 import org.thingsboard.server.common.data.ApiUsageRecordKey; 25 import org.thingsboard.server.common.data.ApiUsageRecordKey;
26 import org.thingsboard.server.common.data.EntityType; 26 import org.thingsboard.server.common.data.EntityType;
27 import org.thingsboard.server.common.data.EntityView; 27 import org.thingsboard.server.common.data.EntityView;
28 -import org.thingsboard.server.common.data.TenantProfile; 28 +import org.thingsboard.server.common.data.id.CustomerId;
29 import org.thingsboard.server.common.data.id.EntityId; 29 import org.thingsboard.server.common.data.id.EntityId;
30 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 31 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@@ -35,13 +35,11 @@ import org.thingsboard.server.common.data.kv.DoubleDataEntry; @@ -35,13 +35,11 @@ import org.thingsboard.server.common.data.kv.DoubleDataEntry;
35 import org.thingsboard.server.common.data.kv.LongDataEntry; 35 import org.thingsboard.server.common.data.kv.LongDataEntry;
36 import org.thingsboard.server.common.data.kv.StringDataEntry; 36 import org.thingsboard.server.common.data.kv.StringDataEntry;
37 import org.thingsboard.server.common.data.kv.TsKvEntry; 37 import org.thingsboard.server.common.data.kv.TsKvEntry;
38 -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;  
39 import org.thingsboard.server.common.msg.queue.ServiceType; 38 import org.thingsboard.server.common.msg.queue.ServiceType;
40 import org.thingsboard.server.common.msg.queue.TbCallback; 39 import org.thingsboard.server.common.msg.queue.TbCallback;
41 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 40 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
42 import org.thingsboard.server.dao.attributes.AttributesService; 41 import org.thingsboard.server.dao.attributes.AttributesService;
43 import org.thingsboard.server.dao.entityview.EntityViewService; 42 import org.thingsboard.server.dao.entityview.EntityViewService;
44 -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;  
45 import org.thingsboard.server.dao.timeseries.TimeseriesService; 43 import org.thingsboard.server.dao.timeseries.TimeseriesService;
46 import org.thingsboard.server.gen.transport.TransportProtos; 44 import org.thingsboard.server.gen.transport.TransportProtos;
47 import org.thingsboard.server.queue.discovery.PartitionService; 45 import org.thingsboard.server.queue.discovery.PartitionService;
@@ -115,11 +113,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer @@ -115,11 +113,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
115 113
116 @Override 114 @Override
117 public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) { 115 public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) {
118 - saveAndNotify(tenantId, entityId, ts, 0L, callback); 116 + saveAndNotify(tenantId, null, entityId, ts, 0L, callback);
119 } 117 }
120 118
121 @Override 119 @Override
122 - public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) { 120 + public void saveAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) {
123 checkInternalEntity(entityId); 121 checkInternalEntity(entityId);
124 boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; 122 boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null;
125 if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { 123 if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) {
@@ -127,7 +125,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer @@ -127,7 +125,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
127 @Override 125 @Override
128 public void onSuccess(Integer result) { 126 public void onSuccess(Integer result) {
129 if (!sysTenant && result != null && result > 0) { 127 if (!sysTenant && result != null && result > 0) {
130 - apiUsageClient.report(tenantId, ApiUsageRecordKey.STORAGE_DP_COUNT, result); 128 + apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.STORAGE_DP_COUNT, result);
131 } 129 }
132 callback.onSuccess(null); 130 callback.onSuccess(null);
133 } 131 }
@@ -23,17 +23,20 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -23,17 +23,20 @@ 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.DeviceTransportType; 35 import org.thingsboard.server.common.data.DeviceTransportType;
34 import org.thingsboard.server.common.data.EntityType; 36 import org.thingsboard.server.common.data.EntityType;
35 -import org.thingsboard.server.common.data.TbResource; 37 +import org.thingsboard.server.common.data.Firmware;
36 import org.thingsboard.server.common.data.ResourceType; 38 import org.thingsboard.server.common.data.ResourceType;
  39 +import org.thingsboard.server.common.data.TbResource;
37 import org.thingsboard.server.common.data.TenantProfile; 40 import org.thingsboard.server.common.data.TenantProfile;
38 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; 41 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
39 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; 42 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
@@ -41,6 +44,7 @@ import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileC @@ -41,6 +44,7 @@ import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileC
41 import org.thingsboard.server.common.data.id.CustomerId; 44 import org.thingsboard.server.common.data.id.CustomerId;
42 import org.thingsboard.server.common.data.id.DeviceId; 45 import org.thingsboard.server.common.data.id.DeviceId;
43 import org.thingsboard.server.common.data.id.DeviceProfileId; 46 import org.thingsboard.server.common.data.id.DeviceProfileId;
  47 +import org.thingsboard.server.common.data.id.FirmwareId;
44 import org.thingsboard.server.common.data.id.TenantId; 48 import org.thingsboard.server.common.data.id.TenantId;
45 import org.thingsboard.server.common.data.relation.EntityRelation; 49 import org.thingsboard.server.common.data.relation.EntityRelation;
46 import org.thingsboard.server.common.data.security.DeviceCredentials; 50 import org.thingsboard.server.common.data.security.DeviceCredentials;
@@ -56,6 +60,7 @@ import org.thingsboard.server.dao.device.DeviceService; @@ -56,6 +60,7 @@ import org.thingsboard.server.dao.device.DeviceService;
56 import org.thingsboard.server.dao.device.provision.ProvisionFailedException; 60 import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
57 import org.thingsboard.server.dao.device.provision.ProvisionRequest; 61 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
58 import org.thingsboard.server.dao.device.provision.ProvisionResponse; 62 import org.thingsboard.server.dao.device.provision.ProvisionResponse;
  63 +import org.thingsboard.server.dao.firmware.FirmwareService;
59 import org.thingsboard.server.dao.relation.RelationService; 64 import org.thingsboard.server.dao.relation.RelationService;
60 import org.thingsboard.server.dao.resource.ResourceService; 65 import org.thingsboard.server.dao.resource.ResourceService;
61 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 66 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
@@ -71,7 +76,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesReques @@ -71,7 +76,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesReques
71 import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesResponseMsg; 76 import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesResponseMsg;
72 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg; 77 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg;
73 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; 78 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
74 -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus;  
75 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; 79 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
76 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; 80 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
77 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; 81 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
@@ -84,9 +88,9 @@ import org.thingsboard.server.service.apiusage.TbApiUsageStateService; @@ -84,9 +88,9 @@ import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
84 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 88 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
85 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 89 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
86 import org.thingsboard.server.service.queue.TbClusterService; 90 import org.thingsboard.server.service.queue.TbClusterService;
  91 +import org.thingsboard.server.service.resource.TbResourceService;
87 import org.thingsboard.server.service.state.DeviceStateService; 92 import org.thingsboard.server.service.state.DeviceStateService;
88 93
89 -import java.util.Collections;  
90 import java.util.List; 94 import java.util.List;
91 import java.util.Optional; 95 import java.util.Optional;
92 import java.util.UUID; 96 import java.util.UUID;
@@ -117,7 +121,9 @@ public class DefaultTransportApiService implements TransportApiService { @@ -117,7 +121,9 @@ public class DefaultTransportApiService implements TransportApiService {
117 private final TbClusterService tbClusterService; 121 private final TbClusterService tbClusterService;
118 private final DataDecodingEncodingService dataDecodingEncodingService; 122 private final DataDecodingEncodingService dataDecodingEncodingService;
119 private final DeviceProvisionService deviceProvisionService; 123 private final DeviceProvisionService deviceProvisionService;
120 - private final ResourceService resourceService; 124 + private final TbResourceService resourceService;
  125 + private final FirmwareService firmwareService;
  126 + private final FirmwareCacheWriter firmwareCacheWriter;
121 127
122 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); 128 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
123 129
@@ -126,7 +132,7 @@ public class DefaultTransportApiService implements TransportApiService { @@ -126,7 +132,7 @@ public class DefaultTransportApiService implements TransportApiService {
126 RelationService relationService, DeviceCredentialsService deviceCredentialsService, 132 RelationService relationService, DeviceCredentialsService deviceCredentialsService,
127 DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, 133 DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService,
128 TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, 134 TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService,
129 - DeviceProvisionService deviceProvisionService, ResourceService resourceService) { 135 + DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareCacheWriter firmwareCacheWriter) {
130 this.deviceProfileCache = deviceProfileCache; 136 this.deviceProfileCache = deviceProfileCache;
131 this.tenantProfileCache = tenantProfileCache; 137 this.tenantProfileCache = tenantProfileCache;
132 this.apiUsageStateService = apiUsageStateService; 138 this.apiUsageStateService = apiUsageStateService;
@@ -139,6 +145,8 @@ public class DefaultTransportApiService implements TransportApiService { @@ -139,6 +145,8 @@ public class DefaultTransportApiService implements TransportApiService {
139 this.dataDecodingEncodingService = dataDecodingEncodingService; 145 this.dataDecodingEncodingService = dataDecodingEncodingService;
140 this.deviceProvisionService = deviceProvisionService; 146 this.deviceProvisionService = deviceProvisionService;
141 this.resourceService = resourceService; 147 this.resourceService = resourceService;
  148 + this.firmwareService = firmwareService;
  149 + this.firmwareCacheWriter = firmwareCacheWriter;
142 } 150 }
143 151
144 @Override 152 @Override
@@ -174,6 +182,8 @@ public class DefaultTransportApiService implements TransportApiService { @@ -174,6 +182,8 @@ public class DefaultTransportApiService implements TransportApiService {
174 result = handle(transportApiRequestMsg.getDeviceRequestMsg()); 182 result = handle(transportApiRequestMsg.getDeviceRequestMsg());
175 } else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) { 183 } else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) {
176 result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg()); 184 result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg());
  185 + } else if (transportApiRequestMsg.hasFirmwareRequestMsg()) {
  186 + result = handle(transportApiRequestMsg.getFirmwareRequestMsg());
177 } 187 }
178 188
179 return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), 189 return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture),
@@ -282,7 +292,7 @@ public class DefaultTransportApiService implements TransportApiService { @@ -282,7 +292,7 @@ public class DefaultTransportApiService implements TransportApiService {
282 292
283 DeviceId deviceId = device.getId(); 293 DeviceId deviceId = device.getId();
284 ObjectNode entityNode = mapper.valueToTree(device); 294 ObjectNode entityNode = mapper.valueToTree(device);
285 - TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, mapper.writeValueAsString(entityNode)); 295 + TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, customerId, metaData, TbMsgDataType.JSON, mapper.writeValueAsString(entityNode));
286 tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null); 296 tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null);
287 } 297 }
288 GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() 298 GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder()
@@ -323,14 +333,14 @@ public class DefaultTransportApiService implements TransportApiService { @@ -323,14 +333,14 @@ public class DefaultTransportApiService implements TransportApiService {
323 } catch (ProvisionFailedException e) { 333 } catch (ProvisionFailedException e) {
324 return Futures.immediateFuture(getTransportApiResponseMsg( 334 return Futures.immediateFuture(getTransportApiResponseMsg(
325 new DeviceCredentials(), 335 new DeviceCredentials(),
326 - TransportProtos.ProvisionResponseStatus.valueOf(e.getMessage()))); 336 + TransportProtos.ResponseStatus.valueOf(e.getMessage())));
327 } 337 }
328 - return Futures.transform(provisionResponseFuture, provisionResponse -> getTransportApiResponseMsg(provisionResponse.getDeviceCredentials(), TransportProtos.ProvisionResponseStatus.SUCCESS), 338 + return Futures.transform(provisionResponseFuture, provisionResponse -> getTransportApiResponseMsg(provisionResponse.getDeviceCredentials(), TransportProtos.ResponseStatus.SUCCESS),
329 dbCallbackExecutorService); 339 dbCallbackExecutorService);
330 } 340 }
331 341
332 - private TransportApiResponseMsg getTransportApiResponseMsg(DeviceCredentials deviceCredentials, TransportProtos.ProvisionResponseStatus status) {  
333 - if (!status.equals(ProvisionResponseStatus.SUCCESS)) { 342 + private TransportApiResponseMsg getTransportApiResponseMsg(DeviceCredentials deviceCredentials, TransportProtos.ResponseStatus status) {
  343 + if (!status.equals(TransportProtos.ResponseStatus.SUCCESS)) {
334 return TransportApiResponseMsg.newBuilder().setProvisionDeviceResponseMsg(TransportProtos.ProvisionDeviceResponseMsg.newBuilder().setStatus(status).build()).build(); 344 return TransportApiResponseMsg.newBuilder().setProvisionDeviceResponseMsg(TransportProtos.ProvisionDeviceResponseMsg.newBuilder().setStatus(status).build()).build();
335 } 345 }
336 TransportProtos.ProvisionDeviceResponseMsg.Builder provisionResponse = TransportProtos.ProvisionDeviceResponseMsg.newBuilder() 346 TransportProtos.ProvisionDeviceResponseMsg.Builder provisionResponse = TransportProtos.ProvisionDeviceResponseMsg.newBuilder()
@@ -468,6 +478,8 @@ public class DefaultTransportApiService implements TransportApiService { @@ -468,6 +478,8 @@ public class DefaultTransportApiService implements TransportApiService {
468 return DeviceInfoProto.newBuilder() 478 return DeviceInfoProto.newBuilder()
469 .setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits()) 479 .setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits())
470 .setTenantIdLSB(device.getTenantId().getId().getLeastSignificantBits()) 480 .setTenantIdLSB(device.getTenantId().getId().getLeastSignificantBits())
  481 + .setCustomerIdMSB(Optional.ofNullable(device.getCustomerId()).map(customerId -> customerId.getId().getMostSignificantBits()).orElse(0L))
  482 + .setCustomerIdLSB(Optional.ofNullable(device.getCustomerId()).map(customerId -> customerId.getId().getLeastSignificantBits()).orElse(0L))
471 .setDeviceIdMSB(device.getId().getId().getMostSignificantBits()) 483 .setDeviceIdMSB(device.getId().getId().getMostSignificantBits())
472 .setDeviceIdLSB(device.getId().getId().getLeastSignificantBits()) 484 .setDeviceIdLSB(device.getId().getId().getLeastSignificantBits())
473 .setDeviceName(device.getName()) 485 .setDeviceName(device.getName())
@@ -495,6 +507,48 @@ public class DefaultTransportApiService implements TransportApiService { @@ -495,6 +507,48 @@ public class DefaultTransportApiService implements TransportApiService {
495 } 507 }
496 } 508 }
497 509
  510 + private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) {
  511 + TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
  512 + DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
  513 + Device device = deviceService.findDeviceById(tenantId, deviceId);
  514 +
  515 + if (device == null) {
  516 + return getEmptyTransportApiResponseFuture();
  517 + }
  518 +
  519 + FirmwareId firmwareId = device.getFirmwareId();
  520 +
  521 + if (firmwareId == null) {
  522 + firmwareId = deviceProfileCache.find(device.getDeviceProfileId()).getFirmwareId();
  523 + }
  524 +
  525 + TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder();
  526 +
  527 + if (firmwareId == null) {
  528 + builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND);
  529 + } else {
  530 + Firmware firmware = firmwareService.findFirmwareById(tenantId, firmwareId);
  531 +
  532 + if (firmware == null) {
  533 + builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND);
  534 + } else {
  535 + builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
  536 + builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits());
  537 + builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits());
  538 + builder.setTitle(firmware.getTitle());
  539 + builder.setVersion(firmware.getVersion());
  540 + builder.setFileName(firmware.getFileName());
  541 + builder.setContentType(firmware.getContentType());
  542 + firmwareCacheWriter.put(firmwareId.toString(), firmware.getData().array());
  543 + }
  544 + }
  545 +
  546 + return Futures.immediateFuture(
  547 + TransportApiResponseMsg.newBuilder()
  548 + .setFirmwareResponseMsg(builder.build())
  549 + .build());
  550 + }
  551 +
498 private ListenableFuture<TransportApiResponseMsg> handleRegistration(TransportProtos.LwM2MRegistrationRequestMsg msg) { 552 private ListenableFuture<TransportApiResponseMsg> handleRegistration(TransportProtos.LwM2MRegistrationRequestMsg msg) {
499 TenantId tenantId = new TenantId(UUID.fromString(msg.getTenantId())); 553 TenantId tenantId = new TenantId(UUID.fromString(msg.getTenantId()));
500 String deviceName = msg.getEndpoint(); 554 String deviceName = msg.getEndpoint();
@@ -124,6 +124,7 @@ usage: @@ -124,6 +124,7 @@ usage:
124 stats: 124 stats:
125 report: 125 report:
126 enabled: "${USAGE_STATS_REPORT_ENABLED:true}" 126 enabled: "${USAGE_STATS_REPORT_ENABLED:true}"
  127 + enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}"
127 interval: "${USAGE_STATS_REPORT_INTERVAL:10}" 128 interval: "${USAGE_STATS_REPORT_INTERVAL:10}"
128 check: 129 check:
129 cycle: "${USAGE_STATS_CHECK_CYCLE:60000}" 130 cycle: "${USAGE_STATS_CHECK_CYCLE:60000}"
@@ -370,6 +371,9 @@ caffeine: @@ -370,6 +371,9 @@ caffeine:
370 tokensOutdatageTime: 371 tokensOutdatageTime:
371 timeToLiveInMinutes: 20000 372 timeToLiveInMinutes: 20000
372 maxSize: 10000 373 maxSize: 10000
  374 + firmwares:
  375 + timeToLiveInMinutes: 1440
  376 + maxSize: 100
373 edges: 377 edges:
374 timeToLiveInMinutes: 1440 378 timeToLiveInMinutes: 1440
375 maxSize: 0 379 maxSize: 0
@@ -445,6 +449,9 @@ spring.resources.chain: @@ -445,6 +449,9 @@ spring.resources.chain:
445 content: 449 content:
446 enabled: "true" 450 enabled: "true"
447 451
  452 +spring.servlet.multipart.max-file-size: "50MB"
  453 +spring.servlet.multipart.max-request-size: "50MB"
  454 +
448 spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" 455 spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true"
449 spring.jpa.properties.hibernate.order_by.default_null_ordering: "last" 456 spring.jpa.properties.hibernate.order_by.default_null_ordering: "last"
450 457
@@ -730,6 +737,7 @@ queue: @@ -730,6 +737,7 @@ queue:
730 bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" 737 bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
731 acks: "${TB_KAFKA_ACKS:all}" 738 acks: "${TB_KAFKA_ACKS:all}"
732 retries: "${TB_KAFKA_RETRIES:1}" 739 retries: "${TB_KAFKA_RETRIES:1}"
  740 + compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip
733 batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" 741 batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
734 linger.ms: "${TB_KAFKA_LINGER_MS:1}" 742 linger.ms: "${TB_KAFKA_LINGER_MS:1}"
735 buffer.memory: "${TB_BUFFER_MEMORY:33554432}" 743 buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
@@ -744,6 +752,10 @@ queue: @@ -744,6 +752,10 @@ queue:
744 sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}" 752 sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}"
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\";}" 753 sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}"
746 security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" 754 security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}"
  755 + consumer-properties-per-topic:
  756 + tb_firmware:
  757 + - key: max.poll.records
  758 + value: 10
747 other: 759 other:
748 topic-properties: 760 topic-properties:
749 rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" 761 rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
@@ -751,6 +763,7 @@ queue: @@ -751,6 +763,7 @@ queue:
751 transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" 763 transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
752 notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" 764 notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
753 js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" 765 js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
  766 + fw-updates: "${TB_QUEUE_KAFKA_FW_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
754 consumer-stats: 767 consumer-stats:
755 enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" 768 enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
756 print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" 769 print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
@@ -821,6 +834,10 @@ queue: @@ -821,6 +834,10 @@ queue:
821 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" 834 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
822 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" 835 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
823 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}" 836 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}"
  837 + firmware:
  838 + topic: "${TB_QUEUE_CORE_FW_TOPIC:tb_firmware}"
  839 + pack-interval-ms: "${TB_QUEUE_CORE_FW_PACK_INTERVAL_MS:60000}"
  840 + pack-size: "${TB_QUEUE_CORE_FW_PACK_SIZE:100}"
824 usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" 841 usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
825 stats: 842 stats:
826 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}" 843 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 +}
@@ -21,12 +21,12 @@ import org.checkerframework.checker.nullness.qual.Nullable; @@ -21,12 +21,12 @@ import org.checkerframework.checker.nullness.qual.Nullable;
21 import org.junit.After; 21 import org.junit.After;
22 import org.junit.Assert; 22 import org.junit.Assert;
23 import org.junit.Before; 23 import org.junit.Before;
24 -import org.junit.Ignore;  
25 import org.junit.Test; 24 import org.junit.Test;
26 import org.springframework.beans.factory.annotation.Autowired; 25 import org.springframework.beans.factory.annotation.Autowired;
27 import org.thingsboard.server.common.data.Device; 26 import org.thingsboard.server.common.data.Device;
28 import org.thingsboard.server.common.data.Tenant; 27 import org.thingsboard.server.common.data.Tenant;
29 import org.thingsboard.server.common.data.User; 28 import org.thingsboard.server.common.data.User;
  29 +import org.thingsboard.server.common.data.id.CustomerId;
30 import org.thingsboard.server.common.data.kv.Aggregation; 30 import org.thingsboard.server.common.data.kv.Aggregation;
31 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 31 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
32 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; 32 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
@@ -750,7 +750,7 @@ public class BaseWebsocketApiTest extends AbstractWebsocketTest { @@ -750,7 +750,7 @@ public class BaseWebsocketApiTest extends AbstractWebsocketTest {
750 750
751 private void sendTelemetry(Device device, List<TsKvEntry> tsData) throws InterruptedException { 751 private void sendTelemetry(Device device, List<TsKvEntry> tsData) throws InterruptedException {
752 CountDownLatch latch = new CountDownLatch(1); 752 CountDownLatch latch = new CountDownLatch(1);
753 - tsService.saveAndNotify(device.getTenantId(), device.getId(), tsData, 0, new FutureCallback<Void>() { 753 + tsService.saveAndNotify(device.getTenantId(), null, device.getId(), tsData, 0, new FutureCallback<Void>() {
754 @Override 754 @Override
755 public void onSuccess(@Nullable Void result) { 755 public void onSuccess(@Nullable Void result) {
756 latch.countDown(); 756 latch.countDown();
  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 +}
common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheWriter.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultsResourceValue.java
@@ -13,20 +13,8 @@ @@ -13,20 +13,8 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.transport.lwm2m.server.client; 16 +package org.thingsboard.server.cache.firmware;
17 17
18 -import lombok.Data;  
19 -import org.thingsboard.server.common.data.kv.DataType;  
20 -  
21 -@Data  
22 -public class ResultsResourceValue {  
23 - DataType dataType;  
24 - Object value;  
25 - String resourceName;  
26 -  
27 - public ResultsResourceValue (DataType dataType, Object value, String resourceName) {  
28 - this.dataType = dataType;  
29 - this.value = value;  
30 - this.resourceName = resourceName;  
31 - } 18 +public interface FirmwareCacheWriter {
  19 + void put(String key, byte[] value);
32 } 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 +}
@@ -63,6 +63,8 @@ public interface DeviceService { @@ -63,6 +63,8 @@ public interface DeviceService {
63 63
64 PageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); 64 PageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
65 65
  66 + PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
  67 +
66 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); 68 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
67 69
68 PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink); 70 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 +}
@@ -17,17 +17,22 @@ package org.thingsboard.server.dao.usagerecord; @@ -17,17 +17,22 @@ package org.thingsboard.server.dao.usagerecord;
17 17
18 import org.thingsboard.server.common.data.ApiUsageState; 18 import org.thingsboard.server.common.data.ApiUsageState;
19 import org.thingsboard.server.common.data.id.ApiUsageStateId; 19 import org.thingsboard.server.common.data.id.ApiUsageStateId;
  20 +import org.thingsboard.server.common.data.id.EntityId;
20 import org.thingsboard.server.common.data.id.TenantId; 21 import org.thingsboard.server.common.data.id.TenantId;
21 22
22 public interface ApiUsageStateService { 23 public interface ApiUsageStateService {
23 24
24 - ApiUsageState createDefaultApiUsageState(TenantId id); 25 + ApiUsageState createDefaultApiUsageState(TenantId id, EntityId entityId);
25 26
26 ApiUsageState update(ApiUsageState apiUsageState); 27 ApiUsageState update(ApiUsageState apiUsageState);
27 28
28 ApiUsageState findTenantApiUsageState(TenantId tenantId); 29 ApiUsageState findTenantApiUsageState(TenantId tenantId);
29 30
  31 + ApiUsageState findApiUsageStateByEntityId(EntityId entityId);
  32 +
30 void deleteApiUsageStateByTenantId(TenantId tenantId); 33 void deleteApiUsageStateByTenantId(TenantId tenantId);
31 34
  35 + void deleteApiUsageStateByEntityId(EntityId entityId);
  36 +
32 ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id); 37 ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id);
33 } 38 }
@@ -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 +}
@@ -16,10 +16,13 @@ @@ -16,10 +16,13 @@
16 package org.thingsboard.server.common.data.query; 16 package org.thingsboard.server.common.data.query;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.common.data.id.EntityId; 19 +import org.thingsboard.server.common.data.id.CustomerId;
20 20
21 @Data 21 @Data
22 public class ApiUsageStateFilter implements EntityFilter { 22 public class ApiUsageStateFilter implements EntityFilter {
  23 +
  24 + private CustomerId customerId;
  25 +
23 @Override 26 @Override
24 public EntityFilterType getType() { 27 public EntityFilterType getType() {
25 return EntityFilterType.API_USAGE_STATE; 28 return EntityFilterType.API_USAGE_STATE;
@@ -19,10 +19,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore; @@ -19,10 +19,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.google.protobuf.ByteString; 19 import com.google.protobuf.ByteString;
20 import com.google.protobuf.InvalidProtocolBufferException; 20 import com.google.protobuf.InvalidProtocolBufferException;
21 import lombok.AccessLevel; 21 import lombok.AccessLevel;
22 -import lombok.Builder;  
23 import lombok.Data; 22 import lombok.Data;
24 import lombok.Getter; 23 import lombok.Getter;
25 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
  25 +import org.thingsboard.server.common.data.EntityType;
  26 +import org.thingsboard.server.common.data.id.CustomerId;
26 import org.thingsboard.server.common.data.id.EntityId; 27 import org.thingsboard.server.common.data.id.EntityId;
27 import org.thingsboard.server.common.data.id.EntityIdFactory; 28 import org.thingsboard.server.common.data.id.EntityIdFactory;
28 import org.thingsboard.server.common.data.id.RuleChainId; 29 import org.thingsboard.server.common.data.id.RuleChainId;
@@ -47,6 +48,7 @@ public final class TbMsg implements Serializable { @@ -47,6 +48,7 @@ public final class TbMsg implements Serializable {
47 private final long ts; 48 private final long ts;
48 private final String type; 49 private final String type;
49 private final EntityId originator; 50 private final EntityId originator;
  51 + private final CustomerId customerId;
50 private final TbMsgMetaData metaData; 52 private final TbMsgMetaData metaData;
51 private final TbMsgDataType dataType; 53 private final TbMsgDataType dataType;
52 private final String data; 54 private final String data;
@@ -55,6 +57,7 @@ public final class TbMsg implements Serializable { @@ -55,6 +57,7 @@ public final class TbMsg implements Serializable {
55 @Getter(value = AccessLevel.NONE) 57 @Getter(value = AccessLevel.NONE)
56 private final AtomicInteger ruleNodeExecCounter; 58 private final AtomicInteger ruleNodeExecCounter;
57 59
  60 +
58 public int getAndIncrementRuleNodeCounter() { 61 public int getAndIncrementRuleNodeCounter() {
59 return ruleNodeExecCounter.getAndIncrement(); 62 return ruleNodeExecCounter.getAndIncrement();
60 } 63 }
@@ -64,60 +67,81 @@ public final class TbMsg implements Serializable { @@ -64,60 +67,81 @@ public final class TbMsg implements Serializable {
64 transient private final TbMsgCallback callback; 67 transient private final TbMsgCallback callback;
65 68
66 public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { 69 public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
67 - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, 70 + return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId);
  71 + }
  72 +
  73 + public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
  74 + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId,
68 metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY); 75 metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY);
69 } 76 }
70 77
71 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { 78 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
72 - return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY); 79 + return newMsg(type, originator, null, metaData, data);
  80 + }
  81 +
  82 + public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
  83 + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY);
73 } 84 }
74 85
75 // REALLY NEW MSG 86 // REALLY NEW MSG
76 87
77 public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { 88 public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
78 - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY); 89 + return newMsg(queueName, type, originator, null, metaData, data);
  90 + }
  91 +
  92 + public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
  93 + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY);
  94 + }
  95 +
  96 + public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data) {
  97 + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, metaData.copy(), dataType, data, null, null, 0, TbMsgCallback.EMPTY);
79 } 98 }
80 99
81 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { 100 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) {
82 - return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, 0, TbMsgCallback.EMPTY); 101 + return newMsg(type, originator, null, metaData, dataType, data);
83 } 102 }
84 103
85 // For Tests only 104 // For Tests only
86 105
87 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { 106 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
88 - return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY); 107 + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY);
89 } 108 }
90 109
91 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { 110 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) {
92 - return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, callback); 111 + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, callback);
93 } 112 }
94 113
95 public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { 114 public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
96 - return new TbMsg(tbMsg.getQueueName(), tbMsg.getId(), tbMsg.getTs(), type, originator, metaData.copy(), tbMsg.getDataType(),  
97 - data, tbMsg.getRuleChainId(), tbMsg.getRuleNodeId(), tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); 115 + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType,
  116 + data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ruleNodeExecCounter.get(), tbMsg.callback);
  117 + }
  118 +
  119 + public static TbMsg transformMsg(TbMsg tbMsg, CustomerId customerId) {
  120 + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType,
  121 + tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback());
98 } 122 }
99 123
100 public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId) { 124 public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId) {
101 - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.metaData, tbMsg.dataType, 125 + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
102 tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); 126 tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback());
103 } 127 }
104 128
105 public static TbMsg transformMsg(TbMsg tbMsg, String queueName) { 129 public static TbMsg transformMsg(TbMsg tbMsg, String queueName) {
106 - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.metaData, tbMsg.dataType, 130 + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
107 tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); 131 tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback());
108 } 132 }
109 133
110 public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) { 134 public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) {
111 - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.metaData, tbMsg.dataType, 135 + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
112 tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); 136 tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback());
113 } 137 }
114 138
115 public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { 139 public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
116 - return new TbMsg(tbMsg.getQueueName(), UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), 140 + return new TbMsg(tbMsg.getQueueName(), UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(),
117 tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.ruleNodeExecCounter.get(), TbMsgCallback.EMPTY); 141 tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.ruleNodeExecCounter.get(), TbMsgCallback.EMPTY);
118 } 142 }
119 143
120 - private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, 144 + private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data,
121 RuleChainId ruleChainId, RuleNodeId ruleNodeId, int ruleNodeExecCounter, TbMsgCallback callback) { 145 RuleChainId ruleChainId, RuleNodeId ruleNodeId, int ruleNodeExecCounter, TbMsgCallback callback) {
122 this.id = id; 146 this.id = id;
123 this.queueName = queueName != null ? queueName : ServiceQueue.MAIN; 147 this.queueName = queueName != null ? queueName : ServiceQueue.MAIN;
@@ -128,6 +152,15 @@ public final class TbMsg implements Serializable { @@ -128,6 +152,15 @@ public final class TbMsg implements Serializable {
128 } 152 }
129 this.type = type; 153 this.type = type;
130 this.originator = originator; 154 this.originator = originator;
  155 + if (customerId == null || customerId.isNullUid()) {
  156 + if (originator != null && originator.getEntityType() == EntityType.CUSTOMER) {
  157 + this.customerId = (CustomerId) originator;
  158 + } else {
  159 + this.customerId = null;
  160 + }
  161 + } else {
  162 + this.customerId = customerId;
  163 + }
131 this.metaData = metaData; 164 this.metaData = metaData;
132 this.dataType = dataType; 165 this.dataType = dataType;
133 this.data = data; 166 this.data = data;
@@ -154,6 +187,11 @@ public final class TbMsg implements Serializable { @@ -154,6 +187,11 @@ public final class TbMsg implements Serializable {
154 builder.setEntityIdMSB(msg.getOriginator().getId().getMostSignificantBits()); 187 builder.setEntityIdMSB(msg.getOriginator().getId().getMostSignificantBits());
155 builder.setEntityIdLSB(msg.getOriginator().getId().getLeastSignificantBits()); 188 builder.setEntityIdLSB(msg.getOriginator().getId().getLeastSignificantBits());
156 189
  190 + if (msg.getCustomerId() != null) {
  191 + builder.setCustomerIdMSB(msg.getCustomerId().getId().getMostSignificantBits());
  192 + builder.setCustomerIdLSB(msg.getCustomerId().getId().getLeastSignificantBits());
  193 + }
  194 +
157 if (msg.getRuleChainId() != null) { 195 if (msg.getRuleChainId() != null) {
158 builder.setRuleChainIdMSB(msg.getRuleChainId().getId().getMostSignificantBits()); 196 builder.setRuleChainIdMSB(msg.getRuleChainId().getId().getMostSignificantBits());
159 builder.setRuleChainIdLSB(msg.getRuleChainId().getId().getLeastSignificantBits()); 197 builder.setRuleChainIdLSB(msg.getRuleChainId().getId().getLeastSignificantBits());
@@ -179,16 +217,21 @@ public final class TbMsg implements Serializable { @@ -179,16 +217,21 @@ public final class TbMsg implements Serializable {
179 MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data); 217 MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data);
180 TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); 218 TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap());
181 EntityId entityId = EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB())); 219 EntityId entityId = EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB()));
  220 + CustomerId customerId = null;
182 RuleChainId ruleChainId = null; 221 RuleChainId ruleChainId = null;
183 RuleNodeId ruleNodeId = null; 222 RuleNodeId ruleNodeId = null;
  223 + if (proto.getCustomerIdMSB() != 0L && proto.getCustomerIdLSB() != 0L) {
  224 + customerId = new CustomerId(new UUID(proto.getCustomerIdMSB(), proto.getCustomerIdLSB()));
  225 + }
184 if (proto.getRuleChainIdMSB() != 0L && proto.getRuleChainIdLSB() != 0L) { 226 if (proto.getRuleChainIdMSB() != 0L && proto.getRuleChainIdLSB() != 0L) {
185 ruleChainId = new RuleChainId(new UUID(proto.getRuleChainIdMSB(), proto.getRuleChainIdLSB())); 227 ruleChainId = new RuleChainId(new UUID(proto.getRuleChainIdMSB(), proto.getRuleChainIdLSB()));
186 } 228 }
187 if (proto.getRuleNodeIdMSB() != 0L && proto.getRuleNodeIdLSB() != 0L) { 229 if (proto.getRuleNodeIdMSB() != 0L && proto.getRuleNodeIdLSB() != 0L) {
188 ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB())); 230 ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB()));
189 } 231 }
  232 +
190 TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; 233 TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
191 - return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, proto.getRuleNodeExecCounter(), callback); 234 + return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, customerId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, proto.getRuleNodeExecCounter(), callback);
192 } catch (InvalidProtocolBufferException e) { 235 } catch (InvalidProtocolBufferException e) {
193 throw new IllegalStateException("Could not parse protobuf for TbMsg", e); 236 throw new IllegalStateException("Could not parse protobuf for TbMsg", e);
194 } 237 }
@@ -199,11 +242,11 @@ public final class TbMsg implements Serializable { @@ -199,11 +242,11 @@ public final class TbMsg implements Serializable {
199 } 242 }
200 243
201 public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) { 244 public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) {
202 - return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, this.ruleNodeExecCounter.get(), callback); 245 + return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId, this.metaData, this.dataType, this.data, ruleChainId, null, this.ruleNodeExecCounter.get(), callback);
203 } 246 }
204 247
205 public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) { 248 public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) {
206 - return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ruleNodeExecCounter.get(), callback); 249 + return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ruleNodeExecCounter.get(), callback);
207 } 250 }
208 251
209 public TbMsgCallback getCallback() { 252 public TbMsgCallback getCallback() {
@@ -46,4 +46,7 @@ message TbMsgProto { @@ -46,4 +46,7 @@ message TbMsgProto {
46 46
47 int64 ts = 15; 47 int64 ts = 15;
48 int32 ruleNodeExecCounter = 16; 48 int32 ruleNodeExecCounter = 16;
  49 +
  50 + int64 customerIdMSB = 17;
  51 + int64 customerIdLSB = 18;
49 } 52 }
@@ -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 /**
@@ -52,6 +54,9 @@ public class TbKafkaSettings { @@ -52,6 +54,9 @@ public class TbKafkaSettings {
52 @Value("${queue.kafka.retries}") 54 @Value("${queue.kafka.retries}")
53 private int retries; 55 private int retries;
54 56
  57 + @Value("${queue.kafka.compression.type:none}")
  58 + private String compressionType;
  59 +
55 @Value("${queue.kafka.batch.size}") 60 @Value("${queue.kafka.batch.size}")
56 private int batchSize; 61 private int batchSize;
57 62
@@ -95,6 +100,9 @@ public class TbKafkaSettings { @@ -95,6 +100,9 @@ public class TbKafkaSettings {
95 @Setter 100 @Setter
96 private List<TbKafkaProperty> other; 101 private List<TbKafkaProperty> other;
97 102
  103 + @Setter
  104 + private Map<String, List<TbKafkaProperty>> consumerPropertiesPerTopic = Collections.emptyMap();
  105 +
98 public Properties toAdminProps() { 106 public Properties toAdminProps() {
99 Properties props = toProps(); 107 Properties props = toProps();
100 props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, servers); 108 props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
@@ -103,7 +111,7 @@ public class TbKafkaSettings { @@ -103,7 +111,7 @@ public class TbKafkaSettings {
103 return props; 111 return props;
104 } 112 }
105 113
106 - public Properties toConsumerProps() { 114 + public Properties toConsumerProps(String topic) {
107 Properties props = toProps(); 115 Properties props = toProps();
108 props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers); 116 props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
109 props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords); 117 props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords);
@@ -113,6 +121,10 @@ public class TbKafkaSettings { @@ -113,6 +121,10 @@ public class TbKafkaSettings {
113 121
114 props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 122 props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
115 props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class); 123 props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
  124 +
  125 + consumerPropertiesPerTopic
  126 + .getOrDefault(topic, Collections.emptyList())
  127 + .forEach(kv -> props.put(kv.getKey(), kv.getValue()));
116 return props; 128 return props;
117 } 129 }
118 130
@@ -126,6 +138,7 @@ public class TbKafkaSettings { @@ -126,6 +138,7 @@ public class TbKafkaSettings {
126 props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory); 138 props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory);
127 props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 139 props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
128 props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class); 140 props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
  141 + props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, compressionType);
129 return props; 142 return props;
130 } 143 }
131 144
@@ -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
@@ -15,10 +15,14 @@ @@ -15,10 +15,14 @@
15 */ 15 */
16 package org.thingsboard.server.queue.usagestats; 16 package org.thingsboard.server.queue.usagestats;
17 17
  18 +import lombok.Data;
18 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.beans.factory.annotation.Value; 20 import org.springframework.beans.factory.annotation.Value;
20 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
21 import org.thingsboard.server.common.data.ApiUsageRecordKey; 22 import org.thingsboard.server.common.data.ApiUsageRecordKey;
  23 +import org.thingsboard.server.common.data.EntityType;
  24 +import org.thingsboard.server.common.data.id.CustomerId;
  25 +import org.thingsboard.server.common.data.id.EntityId;
22 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
23 import org.thingsboard.server.common.msg.queue.ServiceType; 27 import org.thingsboard.server.common.msg.queue.ServiceType;
24 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 28 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
@@ -31,6 +35,8 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider; @@ -31,6 +35,8 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
31 import org.thingsboard.server.queue.scheduler.SchedulerComponent; 35 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
32 36
33 import javax.annotation.PostConstruct; 37 import javax.annotation.PostConstruct;
  38 +import java.util.EnumMap;
  39 +import java.util.Optional;
34 import java.util.Random; 40 import java.util.Random;
35 import java.util.UUID; 41 import java.util.UUID;
36 import java.util.concurrent.ConcurrentHashMap; 42 import java.util.concurrent.ConcurrentHashMap;
@@ -44,11 +50,13 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient { @@ -44,11 +50,13 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient {
44 50
45 @Value("${usage.stats.report.enabled:true}") 51 @Value("${usage.stats.report.enabled:true}")
46 private boolean enabled; 52 private boolean enabled;
  53 + @Value("${usage.stats.report.enabled_per_customer:false}")
  54 + private boolean enabledPerCustomer;
47 @Value("${usage.stats.report.interval:10}") 55 @Value("${usage.stats.report.interval:10}")
48 private int interval; 56 private int interval;
49 57
50 - @SuppressWarnings("unchecked")  
51 - private final ConcurrentMap<TenantId, AtomicLong>[] values = new ConcurrentMap[ApiUsageRecordKey.values().length]; 58 + private final EnumMap<ApiUsageRecordKey, ConcurrentMap<OwnerId, AtomicLong>> stats = new EnumMap<>(ApiUsageRecordKey.class);
  59 +
52 private final PartitionService partitionService; 60 private final PartitionService partitionService;
53 private final SchedulerComponent scheduler; 61 private final SchedulerComponent scheduler;
54 private final TbQueueProducerProvider producerProvider; 62 private final TbQueueProducerProvider producerProvider;
@@ -65,55 +73,93 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient { @@ -65,55 +73,93 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient {
65 if (enabled) { 73 if (enabled) {
66 msgProducer = this.producerProvider.getTbUsageStatsMsgProducer(); 74 msgProducer = this.producerProvider.getTbUsageStatsMsgProducer();
67 for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { 75 for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
68 - values[key.ordinal()] = new ConcurrentHashMap<>(); 76 + stats.put(key, new ConcurrentHashMap<>());
69 } 77 }
70 - scheduler.scheduleWithFixedDelay(this::reportStats, new Random().nextInt(interval), interval, TimeUnit.SECONDS); 78 + scheduler.scheduleWithFixedDelay(() -> {
  79 + try {
  80 + reportStats();
  81 + } catch (Exception e) {
  82 + log.warn("Failed to report statistics: ", e);
  83 + }
  84 + }, new Random().nextInt(interval), interval, TimeUnit.SECONDS);
71 } 85 }
72 } 86 }
73 87
74 private void reportStats() { 88 private void reportStats() {
75 - try {  
76 - ConcurrentMap<TenantId, ToUsageStatsServiceMsg.Builder> report = new ConcurrentHashMap<>(); 89 + ConcurrentMap<OwnerId, ToUsageStatsServiceMsg.Builder> report = new ConcurrentHashMap<>();
77 90
78 - for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {  
79 - values[key.ordinal()].forEach(((tenantId, atomicLong) -> {  
80 - long value = atomicLong.getAndSet(0);  
81 - if (value > 0) {  
82 - ToUsageStatsServiceMsg.Builder msgBuilder = report.computeIfAbsent(tenantId, id -> {  
83 - ToUsageStatsServiceMsg.Builder msg = ToUsageStatsServiceMsg.newBuilder();  
84 - msg.setTenantIdMSB(tenantId.getId().getMostSignificantBits());  
85 - msg.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());  
86 - return msg;  
87 - });  
88 - msgBuilder.addValues(UsageStatsKVProto.newBuilder().setKey(key.name()).setValue(value).build()); 91 + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
  92 + ConcurrentMap<OwnerId, AtomicLong> statsForKey = stats.get(key);
  93 + statsForKey.forEach((ownerId, statsValue) -> {
  94 + long value = statsValue.get();
  95 + if (value == 0) return;
  96 +
  97 + ToUsageStatsServiceMsg.Builder statsMsgBuilder = report.computeIfAbsent(ownerId, id -> {
  98 + ToUsageStatsServiceMsg.Builder newStatsMsgBuilder = ToUsageStatsServiceMsg.newBuilder();
  99 +
  100 + TenantId tenantId = ownerId.getTenantId();
  101 + newStatsMsgBuilder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
  102 + newStatsMsgBuilder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
  103 +
  104 + EntityId entityId = ownerId.getEntityId();
  105 + if (entityId != null && entityId.getEntityType() == EntityType.CUSTOMER) {
  106 + newStatsMsgBuilder.setCustomerIdMSB(entityId.getId().getMostSignificantBits());
  107 + newStatsMsgBuilder.setCustomerIdLSB(entityId.getId().getLeastSignificantBits());
89 } 108 }
90 - }));  
91 - }  
92 109
93 - report.forEach(((tenantId, builder) -> {  
94 - //TODO: figure out how to minimize messages into the queue. Maybe group by 100s of messages?  
95 - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).newByTopic(msgProducer.getDefaultTopic());  
96 - msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), builder.build()), null);  
97 - }));  
98 - if (!report.isEmpty()) {  
99 - log.info("Report statistics for: {} tenants", report.size());  
100 - }  
101 - } catch (Exception e) {  
102 - log.warn("Failed to report statistics: ", e); 110 + return newStatsMsgBuilder;
  111 + });
  112 +
  113 + statsMsgBuilder.addValues(UsageStatsKVProto.newBuilder().setKey(key.name()).setValue(value).build());
  114 + });
  115 + statsForKey.clear();
  116 + }
  117 +
  118 + report.forEach(((ownerId, statsMsg) -> {
  119 + //TODO: figure out how to minimize messages into the queue. Maybe group by 100s of messages?
  120 +
  121 + TenantId tenantId = ownerId.getTenantId();
  122 + EntityId entityId = Optional.ofNullable(ownerId.getEntityId()).orElse(tenantId);
  123 + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId).newByTopic(msgProducer.getDefaultTopic());
  124 + msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), statsMsg.build()), null);
  125 + }));
  126 +
  127 + if (!report.isEmpty()) {
  128 + log.info("Reporting API usage statistics for {} tenants and customers", report.size());
103 } 129 }
104 } 130 }
105 131
106 @Override 132 @Override
107 - public void report(TenantId tenantId, ApiUsageRecordKey key, long value) { 133 + public void report(TenantId tenantId, CustomerId customerId, ApiUsageRecordKey key, long value) {
108 if (enabled) { 134 if (enabled) {
109 - ConcurrentMap<TenantId, AtomicLong> map = values[key.ordinal()];  
110 - AtomicLong atomicValue = map.computeIfAbsent(tenantId, id -> new AtomicLong());  
111 - atomicValue.addAndGet(value); 135 + ConcurrentMap<OwnerId, AtomicLong> statsForKey = stats.get(key);
  136 +
  137 + statsForKey.computeIfAbsent(new OwnerId(tenantId), id -> new AtomicLong()).addAndGet(value);
  138 + statsForKey.computeIfAbsent(new OwnerId(TenantId.SYS_TENANT_ID), id -> new AtomicLong()).addAndGet(value);
  139 +
  140 + if (enabledPerCustomer && customerId != null && !customerId.isNullUid()) {
  141 + statsForKey.computeIfAbsent(new OwnerId(tenantId, customerId), id -> new AtomicLong()).addAndGet(value);
  142 + }
112 } 143 }
113 } 144 }
114 145
115 @Override 146 @Override
116 - public void report(TenantId tenantId, ApiUsageRecordKey key) {  
117 - report(tenantId, key, 1L); 147 + public void report(TenantId tenantId, CustomerId customerId, ApiUsageRecordKey key) {
  148 + report(tenantId, customerId, key, 1);
  149 + }
  150 +
  151 + @Data
  152 + private static class OwnerId {
  153 + private TenantId tenantId;
  154 + private EntityId entityId;
  155 +
  156 + public OwnerId(TenantId tenantId) {
  157 + this.tenantId = tenantId;
  158 + }
  159 +
  160 + public OwnerId(TenantId tenantId, EntityId entityId) {
  161 + this.tenantId = tenantId;
  162 + this.entityId = entityId;
  163 + }
118 } 164 }
119 } 165 }
@@ -16,12 +16,13 @@ @@ -16,12 +16,13 @@
16 package org.thingsboard.server.queue.usagestats; 16 package org.thingsboard.server.queue.usagestats;
17 17
18 import org.thingsboard.server.common.data.ApiUsageRecordKey; 18 import org.thingsboard.server.common.data.ApiUsageRecordKey;
  19 +import org.thingsboard.server.common.data.id.CustomerId;
19 import org.thingsboard.server.common.data.id.TenantId; 20 import org.thingsboard.server.common.data.id.TenantId;
20 21
21 public interface TbApiUsageClient { 22 public interface TbApiUsageClient {
22 23
23 - void report(TenantId tenantId, ApiUsageRecordKey key, long value); 24 + void report(TenantId tenantId, CustomerId customerId, ApiUsageRecordKey key, long value);
24 25
25 - void report(TenantId tenantId, ApiUsageRecordKey key); 26 + void report(TenantId tenantId, CustomerId customerId, ApiUsageRecordKey key);
26 27
27 } 28 }
@@ -54,6 +54,8 @@ message SessionInfoProto { @@ -54,6 +54,8 @@ message SessionInfoProto {
54 int64 gwSessionIdLSB = 11; 54 int64 gwSessionIdLSB = 11;
55 int64 deviceProfileIdMSB = 12; 55 int64 deviceProfileIdMSB = 12;
56 int64 deviceProfileIdLSB = 13; 56 int64 deviceProfileIdLSB = 13;
  57 + int64 customerIdMSB = 14;
  58 + int64 customerIdLSB = 15;
57 } 59 }
58 60
59 enum SessionEvent { 61 enum SessionEvent {
@@ -111,6 +113,8 @@ message DeviceInfoProto { @@ -111,6 +113,8 @@ message DeviceInfoProto {
111 string additionalInfo = 7; 113 string additionalInfo = 7;
112 int64 deviceProfileIdMSB = 8; 114 int64 deviceProfileIdMSB = 8;
113 int64 deviceProfileIdLSB = 9; 115 int64 deviceProfileIdLSB = 9;
  116 + int64 customerIdMSB = 10;
  117 + int64 customerIdLSB = 11;
114 } 118 }
115 119
116 /** 120 /**
@@ -365,17 +369,35 @@ message ProvisionDeviceCredentialsMsg { @@ -365,17 +369,35 @@ message ProvisionDeviceCredentialsMsg {
365 } 369 }
366 370
367 message ProvisionDeviceResponseMsg { 371 message ProvisionDeviceResponseMsg {
368 - ProvisionResponseStatus status = 1; 372 + ResponseStatus status = 1;
369 CredentialsType credentialsType = 2; 373 CredentialsType credentialsType = 2;
370 string credentialsValue = 3; 374 string credentialsValue = 3;
371 } 375 }
372 376
373 -enum ProvisionResponseStatus { 377 +enum ResponseStatus {
374 UNKNOWN = 0; 378 UNKNOWN = 0;
375 SUCCESS = 1; 379 SUCCESS = 1;
376 NOT_FOUND = 2; 380 NOT_FOUND = 2;
377 FAILURE = 3; 381 FAILURE = 3;
378 } 382 }
  383 +
  384 +message GetFirmwareRequestMsg {
  385 + int64 deviceIdMSB = 1;
  386 + int64 deviceIdLSB = 2;
  387 + int64 tenantIdMSB = 3;
  388 + int64 tenantIdLSB = 4;
  389 +}
  390 +
  391 +message GetFirmwareResponseMsg {
  392 + ResponseStatus responseStatus = 1;
  393 + int64 firmwareIdMSB = 2;
  394 + int64 firmwareIdLSB = 3;
  395 + string title = 4;
  396 + string version = 5;
  397 + string contentType = 6;
  398 + string fileName = 7;
  399 +}
  400 +
379 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. 401 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level.
380 message SubscriptionInfoProto { 402 message SubscriptionInfoProto {
381 int64 lastActivityTime = 1; 403 int64 lastActivityTime = 1;
@@ -596,9 +618,10 @@ message TransportApiRequestMsg { @@ -596,9 +618,10 @@ message TransportApiRequestMsg {
596 ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7; 618 ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7;
597 ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; 619 ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8;
598 GetResourceRequestMsg resourceRequestMsg = 9; 620 GetResourceRequestMsg resourceRequestMsg = 9;
599 - GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 10;  
600 - GetDeviceRequestMsg deviceRequestMsg = 11;  
601 - GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 12; 621 + GetFirmwareRequestMsg firmwareRequestMsg = 10;
  622 + GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11;
  623 + GetDeviceRequestMsg deviceRequestMsg = 12;
  624 + GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13;
602 } 625 }
603 626
604 /* Response from ThingsBoard Core Service to Transport Service */ 627 /* Response from ThingsBoard Core Service to Transport Service */
@@ -610,8 +633,9 @@ message TransportApiResponseMsg { @@ -610,8 +633,9 @@ message TransportApiResponseMsg {
610 GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5; 633 GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5;
611 LwM2MResponseMsg lwM2MResponseMsg = 6; 634 LwM2MResponseMsg lwM2MResponseMsg = 6;
612 GetResourceResponseMsg resourceResponseMsg = 7; 635 GetResourceResponseMsg resourceResponseMsg = 7;
613 - GetDeviceResponseMsg deviceResponseMsg = 8;  
614 - GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 9; 636 + GetFirmwareResponseMsg firmwareResponseMsg = 8;
  637 + GetDeviceResponseMsg deviceResponseMsg = 9;
  638 + GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10;
615 } 639 }
616 640
617 /* Messages that are handled by ThingsBoard Core Service */ 641 /* Messages that are handled by ThingsBoard Core Service */
@@ -673,4 +697,16 @@ message ToUsageStatsServiceMsg { @@ -673,4 +697,16 @@ message ToUsageStatsServiceMsg {
673 int64 entityIdMSB = 3; 697 int64 entityIdMSB = 3;
674 int64 entityIdLSB = 4; 698 int64 entityIdLSB = 4;
675 repeated UsageStatsKVProto values = 5; 699 repeated UsageStatsKVProto values = 5;
  700 + int64 customerIdMSB = 6;
  701 + int64 customerIdLSB = 7;
  702 +}
  703 +
  704 +message ToFirmwareStateServiceMsg {
  705 + int64 ts = 1;
  706 + int64 tenantIdMSB = 2;
  707 + int64 tenantIdLSB = 3;
  708 + int64 deviceIdMSB = 4;
  709 + int64 deviceIdLSB = 5;
  710 + int64 firmwareIdMSB = 6;
  711 + int64 firmwareIdLSB = 7;
676 } 712 }
@@ -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)) {