Browse Source

Merge pull request #1236 from thingsboard/feature/tenant_level_rate_limits

tenant level rate limits
pull/1240/head
Igor Kulikov 8 years ago
committed by GitHub
parent
commit
8cc1e9f72b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  2. 10
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  3. 4
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
  4. 2
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  5. 5
      application/src/main/java/org/thingsboard/server/controller/AdminController.java
  6. 8
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  7. 10
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  8. 25
      application/src/main/java/org/thingsboard/server/controller/AuthController.java
  9. 29
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  10. 2
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  11. 18
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  12. 14
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  13. 24
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  14. 12
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  15. 12
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  16. 67
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  17. 16
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  18. 2
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  19. 6
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  20. 9
      application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
  21. 4
      application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java
  22. 14
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  23. 5
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  24. 4
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  25. 30
      application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
  26. 9
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java
  27. 7
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java
  28. 10
      application/src/main/java/org/thingsboard/server/service/security/device/DefaultDeviceAuthService.java
  29. 3
      application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java
  30. 10
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  31. 39
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  32. 56
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
  33. 1
      application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
  34. 2
      application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionState.java
  35. 7
      application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java
  36. 11
      application/src/main/proto/cluster.proto
  37. 3
      application/src/main/resources/thingsboard.yml
  38. 8
      application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java
  39. 2
      application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java
  40. 3
      common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java
  41. 4
      common/message/pom.xml
  42. 8
      common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java
  43. 4
      common/transport/transport-api/pom.xml
  44. 2
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java
  45. 13
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java
  46. 11
      dao/src/main/java/org/thingsboard/server/dao/Dao.java
  47. 6
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  48. 12
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  49. 92
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  50. 16
      dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java
  51. 3
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
  52. 12
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
  53. 88
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  54. 23
      dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java
  55. 11
      dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java
  56. 11
      dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java
  57. 21
      dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java
  58. 25
      dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java
  59. 6
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java
  60. 25
      dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java
  61. 6
      dao/src/main/java/org/thingsboard/server/dao/cache/PreviousDeviceCredentialsIdKeyGenerator.java
  62. 34
      dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java
  63. 37
      dao/src/main/java/org/thingsboard/server/dao/component/CassandraBaseComponentDescriptorDao.java
  64. 15
      dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorDao.java
  65. 15
      dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorService.java
  66. 8
      dao/src/main/java/org/thingsboard/server/dao/customer/CassandraCustomerDao.java
  67. 3
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java
  68. 6
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
  69. 42
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  70. 7
      dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java
  71. 3
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java
  72. 18
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
  73. 78
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  74. 9
      dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceCredentialsDao.java
  75. 23
      dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
  76. 8
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDao.java
  77. 9
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java
  78. 46
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
  79. 3
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  80. 12
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  81. 93
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  82. 5
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  83. 24
      dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java
  84. 5
      dao/src/main/java/org/thingsboard/server/dao/entity/EntityService.java
  85. 21
      dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
  86. 3
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
  87. 13
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
  88. 85
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
  89. 10
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  90. 22
      dao/src/main/java/org/thingsboard/server/dao/event/CassandraBaseEventDao.java
  91. 3
      dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java
  92. 25
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java
  93. 54
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractModelDao.java
  94. 15
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractSearchTextDao.java
  95. 21
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractSearchTimeDao.java
  96. 15
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java
  97. 2
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraStatementTask.java
  98. 77
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
  99. 132
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
  100. 31
      dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java

6
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java

@ -123,7 +123,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
} }
private void initAttributes() { private void initAttributes() {
Device device = systemContext.getDeviceService().findDeviceById(deviceId); Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId);
this.deviceName = device.getName(); this.deviceName = device.getName();
this.deviceType = device.getType(); this.deviceType = device.getType();
this.defaultMetaData = new TbMsgMetaData(); this.defaultMetaData = new TbMsgMetaData();
@ -290,9 +290,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
private ListenableFuture<List<AttributeKvEntry>> getAttributeKvEntries(DeviceId deviceId, String scope, Optional<Set<String>> names) { private ListenableFuture<List<AttributeKvEntry>> getAttributeKvEntries(DeviceId deviceId, String scope, Optional<Set<String>> names) {
if (names.isPresent()) { if (names.isPresent()) {
if (!names.get().isEmpty()) { if (!names.get().isEmpty()) {
return systemContext.getAttributesService().find(deviceId, scope, names.get()); return systemContext.getAttributesService().find(tenantId, deviceId, scope, names.get());
} else { } else {
return systemContext.getAttributesService().findAll(deviceId, scope); return systemContext.getAttributesService().findAll(tenantId, deviceId, scope);
} }
} else { } else {
return Futures.immediateFuture(Collections.emptyList()); return Futures.immediateFuture(Collections.emptyList());

10
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java

@ -90,9 +90,9 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
@Override @Override
public void start(ActorContext context) { public void start(ActorContext context) {
if (!started) { if (!started) {
RuleChain ruleChain = service.findRuleChainById(entityId); RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
ruleChainName = ruleChain.getName(); ruleChainName = ruleChain.getName();
List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId); List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
// Creating and starting the actors; // Creating and starting the actors;
for (RuleNode ruleNode : ruleNodeList) { for (RuleNode ruleNode : ruleNodeList) {
@ -109,9 +109,9 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
@Override @Override
public void onUpdate(ActorContext context) { public void onUpdate(ActorContext context) {
RuleChain ruleChain = service.findRuleChainById(entityId); RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
ruleChainName = ruleChain.getName(); ruleChainName = ruleChain.getName();
List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId); List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
for (RuleNode ruleNode : ruleNodeList) { for (RuleNode ruleNode : ruleNodeList) {
RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); RuleNodeCtx existing = nodeActors.get(ruleNode.getId());
@ -164,7 +164,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
nodeRoutes.clear(); nodeRoutes.clear();
// Populating the routes map; // Populating the routes map;
for (RuleNode ruleNode : ruleNodeList) { for (RuleNode ruleNode : ruleNodeList) {
List<EntityRelation> relations = service.getRuleNodeRelations(ruleNode.getId()); List<EntityRelation> relations = service.getRuleNodeRelations(TenantId.SYS_TENANT_ID, ruleNode.getId());
log.trace("[{}][{}][{}] Processing rule node relations [{}]", tenantId, entityId, ruleNode.getId(), relations.size()); log.trace("[{}][{}][{}] Processing rule node relations [{}]", tenantId, entityId, ruleNode.getId(), relations.size());
if (relations.size() == 0) { if (relations.size() == 0) {
nodeRoutes.put(ruleNode.getId(), Collections.emptyList()); nodeRoutes.put(ruleNode.getId(), Collections.emptyList());

4
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java

@ -49,7 +49,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
this.parent = parent; this.parent = parent;
this.self = self; this.self = self;
this.service = systemContext.getRuleChainService(); this.service = systemContext.getRuleChainService();
this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(entityId); this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
this.defaultCtx = new DefaultTbContext(systemContext, new RuleNodeCtx(tenantId, parent, self, ruleNode)); this.defaultCtx = new DefaultTbContext(systemContext, new RuleNodeCtx(tenantId, parent, self, ruleNode));
} }
@ -61,7 +61,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
@Override @Override
public void onUpdate(ActorContext context) throws Exception { public void onUpdate(ActorContext context) throws Exception {
RuleNode newRuleNode = systemContext.getRuleChainService().findRuleNodeById(entityId); RuleNode newRuleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
boolean restartRequired = !(ruleNode.getType().equals(newRuleNode.getType()) boolean restartRequired = !(ruleNode.getType().equals(newRuleNode.getType())
&& ruleNode.getConfiguration().equals(newRuleNode.getConfiguration())); && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration()));
this.ruleNode = newRuleNode; this.ruleNode = newRuleNode;

2
application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java

@ -143,7 +143,7 @@ public class TenantActor extends RuleChainManagerActor {
if (target != null) { if (target != null) {
if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
RuleChain ruleChain = systemContext.getRuleChainService(). RuleChain ruleChain = systemContext.getRuleChainService().
findRuleChainById(new RuleChainId(msg.getEntityId().getId())); findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId()));
ruleChainManager.visit(ruleChain, target); ruleChainManager.visit(ruleChain, target);
} }
target.tell(msg, ActorRef.noSender()); target.tell(msg, ActorRef.noSender());

5
application/src/main/java/org/thingsboard/server/controller/AdminController.java

@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.service.update.UpdateService; import org.thingsboard.server.service.update.UpdateService;
import org.thingsboard.server.service.update.model.UpdateMessage; import org.thingsboard.server.service.update.model.UpdateMessage;
@ -48,7 +49,7 @@ public class AdminController extends BaseController {
@ResponseBody @ResponseBody
public AdminSettings getAdminSettings(@PathVariable("key") String key) throws ThingsboardException { public AdminSettings getAdminSettings(@PathVariable("key") String key) throws ThingsboardException {
try { try {
return checkNotNull(adminSettingsService.findAdminSettingsByKey(key)); return checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -59,7 +60,7 @@ public class AdminController extends BaseController {
@ResponseBody @ResponseBody
public AdminSettings saveAdminSettings(@RequestBody AdminSettings adminSettings) throws ThingsboardException { public AdminSettings saveAdminSettings(@RequestBody AdminSettings adminSettings) throws ThingsboardException {
try { try {
adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(adminSettings)); adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings));
if (adminSettings.getKey().equals("mail")) { if (adminSettings.getKey().equals("mail")) {
mailService.updateMailConfiguration(); mailService.updateMailConfiguration();
} }

8
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -100,7 +100,7 @@ public class AlarmController extends BaseController {
try { try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId); Alarm alarm = checkAlarmId(alarmId);
alarmService.ackAlarm(alarmId, System.currentTimeMillis()).get(); alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, System.currentTimeMillis()).get();
logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -115,7 +115,7 @@ public class AlarmController extends BaseController {
try { try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId); Alarm alarm = checkAlarmId(alarmId);
alarmService.clearAlarm(alarmId, null, System.currentTimeMillis()).get(); alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, System.currentTimeMillis()).get();
logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -149,7 +149,7 @@ public class AlarmController extends BaseController {
checkEntityId(entityId); checkEntityId(entityId);
try { try {
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -175,7 +175,7 @@ public class AlarmController extends BaseController {
} }
checkEntityId(entityId); checkEntityId(entityId);
try { try {
return alarmService.findHighestAlarmSeverity(entityId, alarmSearchStatus, alarmStatus); return alarmService.findHighestAlarmSeverity(getCurrentUser().getTenantId(), entityId, alarmSearchStatus, alarmStatus);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

10
application/src/main/java/org/thingsboard/server/controller/AssetController.java

@ -104,7 +104,7 @@ public class AssetController extends BaseController {
try { try {
AssetId assetId = new AssetId(toUUID(strAssetId)); AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId); Asset asset = checkAssetId(assetId);
assetService.deleteAsset(assetId); assetService.deleteAsset(getTenantId(), assetId);
logEntityAction(assetId, asset, logEntityAction(assetId, asset,
asset.getCustomerId(), asset.getCustomerId(),
@ -133,7 +133,7 @@ public class AssetController extends BaseController {
AssetId assetId = new AssetId(toUUID(strAssetId)); AssetId assetId = new AssetId(toUUID(strAssetId));
checkAssetId(assetId); checkAssetId(assetId);
Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(assetId, customerId)); Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(getTenantId(), assetId, customerId));
logEntityAction(assetId, savedAsset, logEntityAction(assetId, savedAsset,
savedAsset.getCustomerId(), savedAsset.getCustomerId(),
@ -164,7 +164,7 @@ public class AssetController extends BaseController {
Customer customer = checkCustomerId(asset.getCustomerId()); Customer customer = checkCustomerId(asset.getCustomerId());
Asset savedAsset = checkNotNull(assetService.unassignAssetFromCustomer(assetId)); Asset savedAsset = checkNotNull(assetService.unassignAssetFromCustomer(getTenantId(), assetId));
logEntityAction(assetId, asset, logEntityAction(assetId, asset,
asset.getCustomerId(), asset.getCustomerId(),
@ -190,7 +190,7 @@ public class AssetController extends BaseController {
AssetId assetId = new AssetId(toUUID(strAssetId)); AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId); Asset asset = checkAssetId(assetId);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(asset.getTenantId()); Customer publicCustomer = customerService.findOrCreatePublicCustomer(asset.getTenantId());
Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(assetId, publicCustomer.getId())); Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(getTenantId(), assetId, publicCustomer.getId()));
logEntityAction(assetId, savedAsset, logEntityAction(assetId, savedAsset,
savedAsset.getCustomerId(), savedAsset.getCustomerId(),
@ -303,7 +303,7 @@ public class AssetController extends BaseController {
checkNotNull(query.getAssetTypes()); checkNotNull(query.getAssetTypes());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId());
try { try {
List<Asset> assets = checkNotNull(assetService.findAssetsByQuery(query).get()); List<Asset> assets = checkNotNull(assetService.findAssetsByQuery(getTenantId(), query).get());
assets = assets.stream().filter(asset -> { assets = assets.stream().filter(asset -> {
try { try {
checkAsset(asset); checkAsset(asset);

25
application/src/main/java/org/thingsboard/server/controller/AuthController.java

@ -36,6 +36,7 @@ import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
@ -52,8 +53,6 @@ import java.net.URISyntaxException;
@Slf4j @Slf4j
public class AuthController extends BaseController { public class AuthController extends BaseController {
@Autowired @Autowired
private BCryptPasswordEncoder passwordEncoder; private BCryptPasswordEncoder passwordEncoder;
@ -71,7 +70,7 @@ public class AuthController extends BaseController {
public @ResponseBody User getUser() throws ThingsboardException { public @ResponseBody User getUser() throws ThingsboardException {
try { try {
SecurityUser securityUser = getCurrentUser(); SecurityUser securityUser = getCurrentUser();
return userService.findUserById(securityUser.getId()); return userService.findUserById(securityUser.getTenantId(), securityUser.getId());
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -86,12 +85,12 @@ public class AuthController extends BaseController {
String currentPassword = changePasswordRequest.get("currentPassword").asText(); String currentPassword = changePasswordRequest.get("currentPassword").asText();
String newPassword = changePasswordRequest.get("newPassword").asText(); String newPassword = changePasswordRequest.get("newPassword").asText();
SecurityUser securityUser = getCurrentUser(); SecurityUser securityUser = getCurrentUser();
UserCredentials userCredentials = userService.findUserCredentialsByUserId(securityUser.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, securityUser.getId());
if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) { if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) {
throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }
userCredentials.setPassword(passwordEncoder.encode(newPassword)); userCredentials.setPassword(passwordEncoder.encode(newPassword));
userService.saveUserCredentials(userCredentials); userService.saveUserCredentials(securityUser.getTenantId(), userCredentials);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -102,7 +101,7 @@ public class AuthController extends BaseController {
@RequestParam(value = "activateToken") String activateToken) { @RequestParam(value = "activateToken") String activateToken) {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
HttpStatus responseStatus; HttpStatus responseStatus;
UserCredentials userCredentials = userService.findUserCredentialsByActivateToken(activateToken); UserCredentials userCredentials = userService.findUserCredentialsByActivateToken(TenantId.SYS_TENANT_ID, activateToken);
if (userCredentials != null) { if (userCredentials != null) {
String createURI = "/login/createPassword"; String createURI = "/login/createPassword";
try { try {
@ -126,7 +125,7 @@ public class AuthController extends BaseController {
HttpServletRequest request) throws ThingsboardException { HttpServletRequest request) throws ThingsboardException {
try { try {
String email = resetPasswordByEmailRequest.get("email").asText(); String email = resetPasswordByEmailRequest.get("email").asText();
UserCredentials userCredentials = userService.requestPasswordReset(email); UserCredentials userCredentials = userService.requestPasswordReset(TenantId.SYS_TENANT_ID, email);
String baseUrl = constructBaseUrl(request); String baseUrl = constructBaseUrl(request);
String resetUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl, String resetUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl,
userCredentials.getResetToken()); userCredentials.getResetToken());
@ -143,7 +142,7 @@ public class AuthController extends BaseController {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
HttpStatus responseStatus; HttpStatus responseStatus;
String resetURI = "/login/resetPassword"; String resetURI = "/login/resetPassword";
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(resetToken); UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken);
if (userCredentials != null) { if (userCredentials != null) {
try { try {
URI location = new URI(resetURI + "?resetToken=" + resetToken); URI location = new URI(resetURI + "?resetToken=" + resetToken);
@ -169,8 +168,8 @@ public class AuthController extends BaseController {
String activateToken = activateRequest.get("activateToken").asText(); String activateToken = activateRequest.get("activateToken").asText();
String password = activateRequest.get("password").asText(); String password = activateRequest.get("password").asText();
String encodedPassword = passwordEncoder.encode(password); String encodedPassword = passwordEncoder.encode(password);
UserCredentials credentials = userService.activateUserCredentials(activateToken, encodedPassword); UserCredentials credentials = userService.activateUserCredentials(TenantId.SYS_TENANT_ID, activateToken, encodedPassword);
User user = userService.findUserById(credentials.getUserId()); User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId());
UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal);
String baseUrl = constructBaseUrl(request); String baseUrl = constructBaseUrl(request);
@ -205,13 +204,13 @@ public class AuthController extends BaseController {
try { try {
String resetToken = resetPasswordRequest.get("resetToken").asText(); String resetToken = resetPasswordRequest.get("resetToken").asText();
String password = resetPasswordRequest.get("password").asText(); String password = resetPasswordRequest.get("password").asText();
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(resetToken); UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken);
if (userCredentials != null) { if (userCredentials != null) {
String encodedPassword = passwordEncoder.encode(password); String encodedPassword = passwordEncoder.encode(password);
userCredentials.setPassword(encodedPassword); userCredentials.setPassword(encodedPassword);
userCredentials.setResetToken(null); userCredentials.setResetToken(null);
userCredentials = userService.saveUserCredentials(userCredentials); userCredentials = userService.saveUserCredentials(TenantId.SYS_TENANT_ID, userCredentials);
User user = userService.findUserById(userCredentials.getUserId()); User user = userService.findUserById(TenantId.SYS_TENANT_ID, userCredentials.getUserId());
UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), principal); SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), principal);
String baseUrl = constructBaseUrl(request); String baseUrl = constructBaseUrl(request);

29
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -275,7 +275,7 @@ public abstract class BaseController {
ThingsboardErrorCode.PERMISSION_DENIED); ThingsboardErrorCode.PERMISSION_DENIED);
} }
if (customerId != null && !customerId.isNullUid()) { if (customerId != null && !customerId.isNullUid()) {
Customer customer = customerService.findCustomerById(customerId); Customer customer = customerService.findCustomerById(authUser.getTenantId(), customerId);
checkCustomer(customer); checkCustomer(customer);
return customer; return customer;
} else { } else {
@ -294,7 +294,7 @@ public abstract class BaseController {
User checkUserId(UserId userId) throws ThingsboardException { User checkUserId(UserId userId) throws ThingsboardException {
try { try {
validateId(userId, "Incorrect userId " + userId); validateId(userId, "Incorrect userId " + userId);
User user = userService.findUserById(userId); User user = userService.findUserById(getCurrentUser().getTenantId(), userId);
checkUser(user); checkUser(user);
return user; return user;
} catch (Exception e) { } catch (Exception e) {
@ -314,9 +314,10 @@ public abstract class BaseController {
try { try {
checkNotNull(entityId); checkNotNull(entityId);
validateId(entityId.getId(), "Incorrect entityId " + entityId); validateId(entityId.getId(), "Incorrect entityId " + entityId);
SecurityUser authUser = getCurrentUser();
switch (entityId.getEntityType()) { switch (entityId.getEntityType()) {
case DEVICE: case DEVICE:
checkDevice(deviceService.findDeviceById(new DeviceId(entityId.getId()))); checkDevice(deviceService.findDeviceById(authUser.getTenantId(), new DeviceId(entityId.getId())));
return; return;
case CUSTOMER: case CUSTOMER:
checkCustomerId(new CustomerId(entityId.getId())); checkCustomerId(new CustomerId(entityId.getId()));
@ -328,7 +329,7 @@ public abstract class BaseController {
checkRuleChain(new RuleChainId(entityId.getId())); checkRuleChain(new RuleChainId(entityId.getId()));
return; return;
case ASSET: case ASSET:
checkAsset(assetService.findAssetById(new AssetId(entityId.getId()))); checkAsset(assetService.findAssetById(authUser.getTenantId(), new AssetId(entityId.getId())));
return; return;
case DASHBOARD: case DASHBOARD:
checkDashboardId(new DashboardId(entityId.getId())); checkDashboardId(new DashboardId(entityId.getId()));
@ -350,7 +351,7 @@ public abstract class BaseController {
Device checkDeviceId(DeviceId deviceId) throws ThingsboardException { Device checkDeviceId(DeviceId deviceId) throws ThingsboardException {
try { try {
validateId(deviceId, "Incorrect deviceId " + deviceId); validateId(deviceId, "Incorrect deviceId " + deviceId);
Device device = deviceService.findDeviceById(deviceId); Device device = deviceService.findDeviceById(getCurrentUser().getTenantId(), deviceId);
checkDevice(device); checkDevice(device);
return device; return device;
} catch (Exception e) { } catch (Exception e) {
@ -367,7 +368,7 @@ public abstract class BaseController {
protected EntityView checkEntityViewId(EntityViewId entityViewId) throws ThingsboardException { protected EntityView checkEntityViewId(EntityViewId entityViewId) throws ThingsboardException {
try { try {
validateId(entityViewId, "Incorrect entityViewId " + entityViewId); validateId(entityViewId, "Incorrect entityViewId " + entityViewId);
EntityView entityView = entityViewService.findEntityViewById(entityViewId); EntityView entityView = entityViewService.findEntityViewById(getCurrentUser().getTenantId(), entityViewId);
checkEntityView(entityView); checkEntityView(entityView);
return entityView; return entityView;
} catch (Exception e) { } catch (Exception e) {
@ -384,7 +385,7 @@ public abstract class BaseController {
Asset checkAssetId(AssetId assetId) throws ThingsboardException { Asset checkAssetId(AssetId assetId) throws ThingsboardException {
try { try {
validateId(assetId, "Incorrect assetId " + assetId); validateId(assetId, "Incorrect assetId " + assetId);
Asset asset = assetService.findAssetById(assetId); Asset asset = assetService.findAssetById(getCurrentUser().getTenantId(), assetId);
checkAsset(asset); checkAsset(asset);
return asset; return asset;
} catch (Exception e) { } catch (Exception e) {
@ -401,7 +402,7 @@ public abstract class BaseController {
Alarm checkAlarmId(AlarmId alarmId) throws ThingsboardException { Alarm checkAlarmId(AlarmId alarmId) throws ThingsboardException {
try { try {
validateId(alarmId, "Incorrect alarmId " + alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId);
Alarm alarm = alarmService.findAlarmByIdAsync(alarmId).get(); Alarm alarm = alarmService.findAlarmByIdAsync(getCurrentUser().getTenantId(), alarmId).get();
checkAlarm(alarm); checkAlarm(alarm);
return alarm; return alarm;
} catch (Exception e) { } catch (Exception e) {
@ -412,7 +413,7 @@ public abstract class BaseController {
AlarmInfo checkAlarmInfoId(AlarmId alarmId) throws ThingsboardException { AlarmInfo checkAlarmInfoId(AlarmId alarmId) throws ThingsboardException {
try { try {
validateId(alarmId, "Incorrect alarmId " + alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId);
AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(alarmId).get(); AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(getCurrentUser().getTenantId(), alarmId).get();
checkAlarm(alarmInfo); checkAlarm(alarmInfo);
return alarmInfo; return alarmInfo;
} catch (Exception e) { } catch (Exception e) {
@ -428,7 +429,7 @@ public abstract class BaseController {
WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException { WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException {
try { try {
validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId);
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(widgetsBundleId); WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(getCurrentUser().getTenantId(), widgetsBundleId);
checkWidgetsBundle(widgetsBundle, modify); checkWidgetsBundle(widgetsBundle, modify);
return widgetsBundle; return widgetsBundle;
} catch (Exception e) { } catch (Exception e) {
@ -449,7 +450,7 @@ public abstract class BaseController {
WidgetType checkWidgetTypeId(WidgetTypeId widgetTypeId, boolean modify) throws ThingsboardException { WidgetType checkWidgetTypeId(WidgetTypeId widgetTypeId, boolean modify) throws ThingsboardException {
try { try {
validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId);
WidgetType widgetType = widgetTypeService.findWidgetTypeById(widgetTypeId); WidgetType widgetType = widgetTypeService.findWidgetTypeById(getCurrentUser().getTenantId(), widgetTypeId);
checkWidgetType(widgetType, modify); checkWidgetType(widgetType, modify);
return widgetType; return widgetType;
} catch (Exception e) { } catch (Exception e) {
@ -470,7 +471,7 @@ public abstract class BaseController {
Dashboard checkDashboardId(DashboardId dashboardId) throws ThingsboardException { Dashboard checkDashboardId(DashboardId dashboardId) throws ThingsboardException {
try { try {
validateId(dashboardId, "Incorrect dashboardId " + dashboardId); validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
Dashboard dashboard = dashboardService.findDashboardById(dashboardId); Dashboard dashboard = dashboardService.findDashboardById(getCurrentUser().getTenantId(), dashboardId);
checkDashboard(dashboard); checkDashboard(dashboard);
return dashboard; return dashboard;
} catch (Exception e) { } catch (Exception e) {
@ -481,7 +482,7 @@ public abstract class BaseController {
DashboardInfo checkDashboardInfoId(DashboardId dashboardId) throws ThingsboardException { DashboardInfo checkDashboardInfoId(DashboardId dashboardId) throws ThingsboardException {
try { try {
validateId(dashboardId, "Incorrect dashboardId " + dashboardId); validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId); DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(getCurrentUser().getTenantId(), dashboardId);
checkDashboard(dashboardInfo); checkDashboard(dashboardInfo);
return dashboardInfo; return dashboardInfo;
} catch (Exception e) { } catch (Exception e) {
@ -530,7 +531,7 @@ public abstract class BaseController {
protected RuleChain checkRuleChain(RuleChainId ruleChainId) throws ThingsboardException { protected RuleChain checkRuleChain(RuleChainId ruleChainId) throws ThingsboardException {
checkNotNull(ruleChainId); checkNotNull(ruleChainId);
return checkRuleChain(ruleChainService.findRuleChainById(ruleChainId)); return checkRuleChain(ruleChainService.findRuleChainById(getCurrentUser().getTenantId(), ruleChainId));
} }
protected RuleChain checkRuleChain(RuleChain ruleChain) throws ThingsboardException { protected RuleChain checkRuleChain(RuleChain ruleChain) throws ThingsboardException {

2
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -119,7 +119,7 @@ public class CustomerController extends BaseController {
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId); Customer customer = checkCustomerId(customerId);
customerService.deleteCustomer(customerId); customerService.deleteCustomer(getTenantId(), customerId);
logEntityAction(customerId, customer, logEntityAction(customerId, customer,
customer.getId(), customer.getId(),

18
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -124,7 +124,7 @@ public class DashboardController extends BaseController {
try { try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId);
dashboardService.deleteDashboard(dashboardId); dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId);
logEntityAction(dashboardId, dashboard, logEntityAction(dashboardId, dashboard,
null, null,
@ -155,7 +155,7 @@ public class DashboardController extends BaseController {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId); checkDashboardId(dashboardId);
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
logEntityAction(dashboardId, savedDashboard, logEntityAction(dashboardId, savedDashboard,
customerId, customerId,
@ -186,7 +186,7 @@ public class DashboardController extends BaseController {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId);
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
logEntityAction(dashboardId, dashboard, logEntityAction(dashboardId, dashboard,
customerId, customerId,
@ -242,7 +242,7 @@ public class DashboardController extends BaseController {
} else { } else {
Dashboard savedDashboard = null; Dashboard savedDashboard = null;
for (CustomerId customerId : addedCustomerIds) { for (CustomerId customerId : addedCustomerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
logEntityAction(dashboardId, savedDashboard, logEntityAction(dashboardId, savedDashboard,
customerId, customerId,
@ -250,7 +250,7 @@ public class DashboardController extends BaseController {
} }
for (CustomerId customerId : removedCustomerIds) { for (CustomerId customerId : removedCustomerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
logEntityAction(dashboardId, dashboard, logEntityAction(dashboardId, dashboard,
customerId, customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
@ -293,7 +293,7 @@ public class DashboardController extends BaseController {
} else { } else {
Dashboard savedDashboard = null; Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) { for (CustomerId customerId : customerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
logEntityAction(dashboardId, savedDashboard, logEntityAction(dashboardId, savedDashboard,
customerId, customerId,
@ -337,7 +337,7 @@ public class DashboardController extends BaseController {
Dashboard savedDashboard = null; Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) { for (CustomerId customerId : customerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
logEntityAction(dashboardId, dashboard, logEntityAction(dashboardId, dashboard,
customerId, customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
@ -364,7 +364,7 @@ public class DashboardController extends BaseController {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId()); Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, publicCustomer.getId())); Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId()));
logEntityAction(dashboardId, savedDashboard, logEntityAction(dashboardId, savedDashboard,
publicCustomer.getId(), publicCustomer.getId(),
@ -391,7 +391,7 @@ public class DashboardController extends BaseController {
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId()); Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, publicCustomer.getId())); Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId()));
logEntityAction(dashboardId, dashboard, logEntityAction(dashboardId, dashboard,
publicCustomer.getId(), publicCustomer.getId(),

14
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -117,7 +117,7 @@ public class DeviceController extends BaseController {
try { try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId); Device device = checkDeviceId(deviceId);
deviceService.deleteDevice(deviceId); deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId);
logEntityAction(deviceId, device, logEntityAction(deviceId, device,
device.getCustomerId(), device.getCustomerId(),
@ -147,7 +147,7 @@ public class DeviceController extends BaseController {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
checkDeviceId(deviceId); checkDeviceId(deviceId);
Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(deviceId, customerId)); Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(getCurrentUser().getTenantId(), deviceId, customerId));
logEntityAction(deviceId, savedDevice, logEntityAction(deviceId, savedDevice,
savedDevice.getCustomerId(), savedDevice.getCustomerId(),
@ -175,7 +175,7 @@ public class DeviceController extends BaseController {
} }
Customer customer = checkCustomerId(device.getCustomerId()); Customer customer = checkCustomerId(device.getCustomerId());
Device savedDevice = checkNotNull(deviceService.unassignDeviceFromCustomer(deviceId)); Device savedDevice = checkNotNull(deviceService.unassignDeviceFromCustomer(getCurrentUser().getTenantId(), deviceId));
logEntityAction(deviceId, device, logEntityAction(deviceId, device,
device.getCustomerId(), device.getCustomerId(),
@ -199,7 +199,7 @@ public class DeviceController extends BaseController {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId); Device device = checkDeviceId(deviceId);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(device.getTenantId()); Customer publicCustomer = customerService.findOrCreatePublicCustomer(device.getTenantId());
Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(deviceId, publicCustomer.getId())); Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(getCurrentUser().getTenantId(), deviceId, publicCustomer.getId()));
logEntityAction(deviceId, savedDevice, logEntityAction(deviceId, savedDevice,
savedDevice.getCustomerId(), savedDevice.getCustomerId(),
@ -222,7 +222,7 @@ public class DeviceController extends BaseController {
try { try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId); Device device = checkDeviceId(deviceId);
DeviceCredentials deviceCredentials = checkNotNull(deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId)); DeviceCredentials deviceCredentials = checkNotNull(deviceCredentialsService.findDeviceCredentialsByDeviceId(getCurrentUser().getTenantId(), deviceId));
logEntityAction(deviceId, device, logEntityAction(deviceId, device,
device.getCustomerId(), device.getCustomerId(),
ActionType.CREDENTIALS_READ, null, strDeviceId); ActionType.CREDENTIALS_READ, null, strDeviceId);
@ -242,7 +242,7 @@ public class DeviceController extends BaseController {
checkNotNull(deviceCredentials); checkNotNull(deviceCredentials);
try { try {
Device device = checkDeviceId(deviceCredentials.getDeviceId()); Device device = checkDeviceId(deviceCredentials.getDeviceId());
DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(deviceCredentials)); DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
actorService.onCredentialsUpdate(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId()); actorService.onCredentialsUpdate(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId());
logEntityAction(device.getId(), device, logEntityAction(device.getId(), device,
device.getCustomerId(), device.getCustomerId(),
@ -352,7 +352,7 @@ public class DeviceController extends BaseController {
checkNotNull(query.getDeviceTypes()); checkNotNull(query.getDeviceTypes());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId());
try { try {
List<Device> devices = checkNotNull(deviceService.findDevicesByQuery(query).get()); List<Device> devices = checkNotNull(deviceService.findDevicesByQuery(getCurrentUser().getTenantId(), query).get());
devices = devices.stream().filter(device -> { devices = devices.stream().filter(device -> {
try { try {
checkDevice(device); checkDevice(device);

24
application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java

@ -60,7 +60,7 @@ public class EntityRelationController extends BaseController {
if (relation.getTypeGroup() == null) { if (relation.getTypeGroup() == null) {
relation.setTypeGroup(RelationTypeGroup.COMMON); relation.setTypeGroup(RelationTypeGroup.COMMON);
} }
relationService.saveRelation(relation); relationService.saveRelation(getTenantId(), relation);
logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
ActionType.RELATION_ADD_OR_UPDATE, null, relation); ActionType.RELATION_ADD_OR_UPDATE, null, relation);
logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(), logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
@ -94,7 +94,7 @@ public class EntityRelationController extends BaseController {
RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
EntityRelation relation = new EntityRelation(fromId, toId, strRelationType, relationTypeGroup); EntityRelation relation = new EntityRelation(fromId, toId, strRelationType, relationTypeGroup);
try { try {
Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup); Boolean found = relationService.deleteRelation(getTenantId(), fromId, toId, strRelationType, relationTypeGroup);
if (!found) { if (!found) {
throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
} }
@ -121,7 +121,7 @@ public class EntityRelationController extends BaseController {
EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId); EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId);
checkEntityId(entityId); checkEntityId(entityId);
try { try {
relationService.deleteEntityRelations(entityId); relationService.deleteEntityRelations(getTenantId(), entityId);
logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, null); logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, null);
} catch (Exception e) { } catch (Exception e) {
logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, e); logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, e);
@ -148,7 +148,7 @@ public class EntityRelationController extends BaseController {
checkEntityId(fromId); checkEntityId(fromId);
checkEntityId(toId); checkEntityId(toId);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
return checkNotNull(relationService.getRelation(fromId, toId, strRelationType, typeGroup)); return checkNotNull(relationService.getRelation(getTenantId(), fromId, toId, strRelationType, typeGroup));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -166,7 +166,7 @@ public class EntityRelationController extends BaseController {
checkEntityId(entityId); checkEntityId(entityId);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findByFrom(entityId, typeGroup)); return checkNotNull(relationService.findByFrom(getTenantId(), entityId, typeGroup));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -184,7 +184,7 @@ public class EntityRelationController extends BaseController {
checkEntityId(entityId); checkEntityId(entityId);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findInfoByFrom(entityId, typeGroup).get()); return checkNotNull(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get());
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -204,7 +204,7 @@ public class EntityRelationController extends BaseController {
checkEntityId(entityId); checkEntityId(entityId);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findByFromAndType(entityId, strRelationType, typeGroup)); return checkNotNull(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -222,7 +222,7 @@ public class EntityRelationController extends BaseController {
checkEntityId(entityId); checkEntityId(entityId);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findByTo(entityId, typeGroup)); return checkNotNull(relationService.findByTo(getTenantId(), entityId, typeGroup));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -240,7 +240,7 @@ public class EntityRelationController extends BaseController {
checkEntityId(entityId); checkEntityId(entityId);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findInfoByTo(entityId, typeGroup).get()); return checkNotNull(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get());
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -260,7 +260,7 @@ public class EntityRelationController extends BaseController {
checkEntityId(entityId); checkEntityId(entityId);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findByToAndType(entityId, strRelationType, typeGroup)); return checkNotNull(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -275,7 +275,7 @@ public class EntityRelationController extends BaseController {
checkNotNull(query.getFilters()); checkNotNull(query.getFilters());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId());
try { try {
return checkNotNull(relationService.findByQuery(query).get()); return checkNotNull(relationService.findByQuery(getTenantId(), query).get());
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -290,7 +290,7 @@ public class EntityRelationController extends BaseController {
checkNotNull(query.getFilters()); checkNotNull(query.getFilters());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId());
try { try {
return checkNotNull(relationService.findInfoByQuery(query).get()); return checkNotNull(relationService.findInfoByQuery(getTenantId(), query).get());
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

12
application/src/main/java/org/thingsboard/server/controller/EntityViewController.java

@ -114,7 +114,7 @@ public class EntityViewController extends BaseController {
private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys, SecurityUser user) throws ThingsboardException { private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys, SecurityUser user) throws ThingsboardException {
EntityViewId entityId = entityView.getId(); EntityViewId entityId = entityView.getId();
if (keys != null && !keys.isEmpty()) { if (keys != null && !keys.isEmpty()) {
ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys); ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(getTenantId(), entityView.getEntityId(), scope, keys);
return Futures.transform(getAttrFuture, attributeKvEntries -> { return Futures.transform(getAttrFuture, attributeKvEntries -> {
List<AttributeKvEntry> attributes; List<AttributeKvEntry> attributes;
if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) {
@ -129,7 +129,7 @@ public class EntityViewController extends BaseController {
(startTime == 0 && endTime > lastUpdateTs) (startTime == 0 && endTime > lastUpdateTs)
? true : startTime < lastUpdateTs && endTime > lastUpdateTs; ? true : startTime < lastUpdateTs && endTime > lastUpdateTs;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
tsSubService.saveAndNotify(entityId, scope, attributes, new FutureCallback<Void>() { tsSubService.saveAndNotify(entityView.getTenantId(), entityId, scope, attributes, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
try { try {
@ -169,7 +169,7 @@ public class EntityViewController extends BaseController {
try { try {
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
EntityView entityView = checkEntityViewId(entityViewId); EntityView entityView = checkEntityViewId(entityViewId);
entityViewService.deleteEntityView(entityViewId); entityViewService.deleteEntityView(getTenantId(), entityViewId);
logEntityAction(entityViewId, entityView, entityView.getCustomerId(), logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
ActionType.DELETED, null, strEntityViewId); ActionType.DELETED, null, strEntityViewId);
} catch (Exception e) { } catch (Exception e) {
@ -208,7 +208,7 @@ public class EntityViewController extends BaseController {
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
checkEntityViewId(entityViewId); checkEntityViewId(entityViewId);
EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(entityViewId, customerId)); EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(getTenantId(), entityViewId, customerId));
logEntityAction(entityViewId, savedEntityView, logEntityAction(entityViewId, savedEntityView,
savedEntityView.getCustomerId(), savedEntityView.getCustomerId(),
ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName()); ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName());
@ -233,7 +233,7 @@ public class EntityViewController extends BaseController {
throw new IncorrectParameterException("Entity View isn't assigned to any customer!"); throw new IncorrectParameterException("Entity View isn't assigned to any customer!");
} }
Customer customer = checkCustomerId(entityView.getCustomerId()); Customer customer = checkCustomerId(entityView.getCustomerId());
EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(entityViewId)); EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(getTenantId(), entityViewId));
logEntityAction(entityViewId, entityView, logEntityAction(entityViewId, entityView,
entityView.getCustomerId(), entityView.getCustomerId(),
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName()); ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName());
@ -305,7 +305,7 @@ public class EntityViewController extends BaseController {
checkNotNull(query.getEntityViewTypes()); checkNotNull(query.getEntityViewTypes());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId());
try { try {
List<EntityView> entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(query).get()); List<EntityView> entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(getTenantId(), query).get());
entityViews = entityViews.stream().filter(entityView -> { entityViews = entityViews.stream().filter(entityView -> {
try { try {
checkEntityView(entityView); checkEntityView(entityView);

12
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -94,7 +94,7 @@ public class RuleChainController extends BaseController {
try { try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
checkRuleChain(ruleChainId); checkRuleChain(ruleChainId);
return ruleChainService.loadRuleChainMetaData(ruleChainId); return ruleChainService.loadRuleChainMetaData(getTenantId(), ruleChainId);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -137,9 +137,9 @@ public class RuleChainController extends BaseController {
RuleChain ruleChain = checkRuleChain(ruleChainId); RuleChain ruleChain = checkRuleChain(ruleChainId);
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId); RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId);
if (ruleChainService.setRootRuleChain(ruleChainId)) { if (ruleChainService.setRootRuleChain(getTenantId(), ruleChainId)) {
previousRootRuleChain = ruleChainService.findRuleChainById(previousRootRuleChain.getId()); previousRootRuleChain = ruleChainService.findRuleChainById(getTenantId(), previousRootRuleChain.getId());
actorService.onEntityStateChange(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(), actorService.onEntityStateChange(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
ComponentLifecycleEvent.UPDATED); ComponentLifecycleEvent.UPDATED);
@ -147,7 +147,7 @@ public class RuleChainController extends BaseController {
logEntityAction(previousRootRuleChain.getId(), previousRootRuleChain, logEntityAction(previousRootRuleChain.getId(), previousRootRuleChain,
null, ActionType.UPDATED, null); null, ActionType.UPDATED, null);
ruleChain = ruleChainService.findRuleChainById(ruleChainId); ruleChain = ruleChainService.findRuleChainById(getTenantId(), ruleChainId);
actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(),
ComponentLifecycleEvent.UPDATED); ComponentLifecycleEvent.UPDATED);
@ -172,7 +172,7 @@ public class RuleChainController extends BaseController {
public RuleChainMetaData saveRuleChainMetaData(@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException { public RuleChainMetaData saveRuleChainMetaData(@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException {
try { try {
RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId()); RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId());
RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(ruleChainMetaData)); RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(getTenantId(), ruleChainMetaData));
actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
@ -216,7 +216,7 @@ public class RuleChainController extends BaseController {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId); RuleChain ruleChain = checkRuleChain(ruleChainId);
ruleChainService.deleteRuleChainById(ruleChainId); ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId);
actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);

67
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.AttributeKey; import org.thingsboard.server.common.data.kv.AttributeKey;
@ -127,7 +128,7 @@ public class TelemetryController extends BaseController {
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr
, @PathVariable("scope") String scope) throws ThingsboardException { , @PathVariable("scope") String scope) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr,
(result, entityId) -> getAttributeKeysCallback(result, entityId, scope)); (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope));
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -138,7 +139,7 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr,
(result, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr));
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -150,7 +151,7 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr,
(result, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -159,9 +160,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getTimeseriesKeys( public DeferredResult<ResponseEntity> getTimeseriesKeys(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr,
(result, entityId) -> { (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result)));
Futures.addCallback(tsService.findAllLatest(entityId), getTsKeysToResponseCallback(result));
});
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -173,7 +172,7 @@ public class TelemetryController extends BaseController {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr,
(result, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr)); (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr));
} }
@ -190,13 +189,13 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "agg", defaultValue = "NONE") String aggStr @RequestParam(name = "agg", defaultValue = "NONE") String aggStr
) throws ThingsboardException { ) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr,
(result, entityId) -> { (result, tenantId, entityId) -> {
// If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr);
List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg)) List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg))
.collect(Collectors.toList()); .collect(Collectors.toList());
Futures.addCallback(tsService.findAll(entityId, queries), getTsKvListCallback(result)); Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result));
}); });
} }
@ -206,7 +205,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope, public DeferredResult<ResponseEntity> saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope,
@RequestBody JsonNode request) throws ThingsboardException { @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return saveAttributes(entityId, scope, request); return saveAttributes(getTenantId(), entityId, scope, request);
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -216,7 +215,7 @@ public class TelemetryController extends BaseController {
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestBody JsonNode request) throws ThingsboardException { @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(entityId, scope, request); return saveAttributes(getTenantId(), entityId, scope, request);
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -226,7 +225,7 @@ public class TelemetryController extends BaseController {
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestBody JsonNode request) throws ThingsboardException { @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(entityId, scope, request); return saveAttributes(getTenantId(), entityId, scope, request);
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -236,7 +235,7 @@ public class TelemetryController extends BaseController {
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestBody String requestBody) throws ThingsboardException { @RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveTelemetry(entityId, requestBody, 0L); return saveTelemetry(getTenantId(), entityId, requestBody, 0L);
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -246,7 +245,7 @@ public class TelemetryController extends BaseController {
@PathVariable("scope") String scope, @PathVariable("ttl") Long ttl, @PathVariable("scope") String scope, @PathVariable("ttl") Long ttl,
@RequestBody String requestBody) throws ThingsboardException { @RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveTelemetry(entityId, requestBody, ttl); return saveTelemetry(getTenantId(), entityId, requestBody, ttl);
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -280,13 +279,13 @@ public class TelemetryController extends BaseController {
deleteToTs = endTs; deleteToTs = endTs;
} }
return accessValidator.validateEntityAndCallback(user, entityIdStr, (result, entityId) -> { return accessValidator.validateEntityAndCallback(user, entityIdStr, (result, tenantId, entityId) -> {
List<DeleteTsKvQuery> deleteTsKvQueries = new ArrayList<>(); List<DeleteTsKvQuery> deleteTsKvQueries = new ArrayList<>();
for (String key : keys) { for (String key : keys) {
deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted)); deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted));
} }
ListenableFuture<List<Void>> future = tsService.remove(entityId, deleteTsKvQueries); ListenableFuture<List<Void>> future = tsService.remove(user.getTenantId(), entityId, deleteTsKvQueries);
Futures.addCallback(future, new FutureCallback<List<Void>>() { Futures.addCallback(future, new FutureCallback<List<Void>>() {
@Override @Override
public void onSuccess(@Nullable List<Void> tmp) { public void onSuccess(@Nullable List<Void> tmp) {
@ -333,8 +332,8 @@ public class TelemetryController extends BaseController {
if (DataConstants.SERVER_SCOPE.equals(scope) || if (DataConstants.SERVER_SCOPE.equals(scope) ||
DataConstants.SHARED_SCOPE.equals(scope) || DataConstants.SHARED_SCOPE.equals(scope) ||
DataConstants.CLIENT_SCOPE.equals(scope)) { DataConstants.CLIENT_SCOPE.equals(scope)) {
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdStr, (result, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdStr, (result, tenantId, entityId) -> {
ListenableFuture<List<Void>> future = attributesService.removeAll(entityId, scope, keys); ListenableFuture<List<Void>> future = attributesService.removeAll(user.getTenantId(), entityId, scope, keys);
Futures.addCallback(future, new FutureCallback<List<Void>>() { Futures.addCallback(future, new FutureCallback<List<Void>>() {
@Override @Override
public void onSuccess(@Nullable List<Void> tmp) { public void onSuccess(@Nullable List<Void> tmp) {
@ -362,7 +361,7 @@ public class TelemetryController extends BaseController {
} }
} }
private DeferredResult<ResponseEntity> saveAttributes(EntityId entityIdSrc, String scope, JsonNode json) throws ThingsboardException { private DeferredResult<ResponseEntity> saveAttributes(TenantId srcTenantId, EntityId entityIdSrc, String scope, JsonNode json) throws ThingsboardException {
if (!DataConstants.SERVER_SCOPE.equals(scope) && !DataConstants.SHARED_SCOPE.equals(scope)) { if (!DataConstants.SERVER_SCOPE.equals(scope) && !DataConstants.SHARED_SCOPE.equals(scope)) {
return getImmediateDeferredResult("Invalid scope: " + scope, HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("Invalid scope: " + scope, HttpStatus.BAD_REQUEST);
} }
@ -372,8 +371,8 @@ public class TelemetryController extends BaseController {
return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST);
} }
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdSrc, (result, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdSrc, (result, tenantId, entityId) -> {
tsSubService.saveAndNotify(entityId, scope, attributes, new FutureCallback<Void>() { tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
logAttributesUpdated(user, entityId, scope, attributes, null); logAttributesUpdated(user, entityId, scope, attributes, null);
@ -398,7 +397,7 @@ public class TelemetryController extends BaseController {
} }
} }
private DeferredResult<ResponseEntity> saveTelemetry(EntityId entityIdSrc, String requestBody, long ttl) throws ThingsboardException { private DeferredResult<ResponseEntity> saveTelemetry(TenantId curTenantId, EntityId entityIdSrc, String requestBody, long ttl) throws ThingsboardException {
Map<Long, List<KvEntry>> telemetryRequest; Map<Long, List<KvEntry>> telemetryRequest;
JsonElement telemetryJson; JsonElement telemetryJson;
try { try {
@ -421,8 +420,8 @@ public class TelemetryController extends BaseController {
return getImmediateDeferredResult("No timeseries data found in request body!", HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("No timeseries data found in request body!", HttpStatus.BAD_REQUEST);
} }
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdSrc, (result, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdSrc, (result, tenantId, entityId) -> {
tsSubService.saveAndNotify(entityId, entries, ttl, new FutureCallback<Void>() { tsSubService.saveAndNotify(tenantId, entityId, entries, ttl, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
result.setResult(new ResponseEntity(HttpStatus.OK)); result.setResult(new ResponseEntity(HttpStatus.OK));
@ -439,9 +438,9 @@ public class TelemetryController extends BaseController {
private void getLatestTimeseriesValuesCallback(@Nullable DeferredResult<ResponseEntity> result, SecurityUser user, EntityId entityId, String keys) { private void getLatestTimeseriesValuesCallback(@Nullable DeferredResult<ResponseEntity> result, SecurityUser user, EntityId entityId, String keys) {
ListenableFuture<List<TsKvEntry>> future; ListenableFuture<List<TsKvEntry>> future;
if (StringUtils.isEmpty(keys)) { if (StringUtils.isEmpty(keys)) {
future = tsService.findAllLatest(entityId); future = tsService.findAllLatest(user.getTenantId(), entityId);
} else { } else {
future = tsService.findLatest(entityId, toKeysList(keys)); future = tsService.findLatest(user.getTenantId(), entityId, toKeysList(keys));
} }
Futures.addCallback(future, getTsKvListCallback(result)); Futures.addCallback(future, getTsKvListCallback(result));
} }
@ -451,17 +450,17 @@ public class TelemetryController extends BaseController {
FutureCallback<List<AttributeKvEntry>> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keyList); FutureCallback<List<AttributeKvEntry>> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keyList);
if (!StringUtils.isEmpty(scope)) { if (!StringUtils.isEmpty(scope)) {
if (keyList != null && !keyList.isEmpty()) { if (keyList != null && !keyList.isEmpty()) {
Futures.addCallback(attributesService.find(entityId, scope, keyList), callback); Futures.addCallback(attributesService.find(user.getTenantId(), entityId, scope, keyList), callback);
} else { } else {
Futures.addCallback(attributesService.findAll(entityId, scope), callback); Futures.addCallback(attributesService.findAll(user.getTenantId(), entityId, scope), callback);
} }
} else { } else {
List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
for (String tmpScope : DataConstants.allScopes()) { for (String tmpScope : DataConstants.allScopes()) {
if (keyList != null && !keyList.isEmpty()) { if (keyList != null && !keyList.isEmpty()) {
futures.add(attributesService.find(entityId, tmpScope, keyList)); futures.add(attributesService.find(user.getTenantId(), entityId, tmpScope, keyList));
} else { } else {
futures.add(attributesService.findAll(entityId, tmpScope)); futures.add(attributesService.findAll(user.getTenantId(), entityId, tmpScope));
} }
} }
@ -471,14 +470,14 @@ public class TelemetryController extends BaseController {
} }
} }
private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, EntityId entityId, String scope) { private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, TenantId tenantId, EntityId entityId, String scope) {
Futures.addCallback(attributesService.findAll(entityId, scope), getAttributeKeysToResponseCallback(result)); Futures.addCallback(attributesService.findAll(tenantId, entityId, scope), getAttributeKeysToResponseCallback(result));
} }
private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, EntityId entityId) { private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, TenantId tenantId, EntityId entityId) {
List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
for (String scope : DataConstants.allScopes()) { for (String scope : DataConstants.allScopes()) {
futures.add(attributesService.findAll(entityId, scope)); futures.add(attributesService.findAll(tenantId, entityId, scope));
} }
ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures); ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures);

16
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -107,14 +107,14 @@ public class UserController extends BaseController {
try { try {
UserId userId = new UserId(toUUID(strUserId)); UserId userId = new UserId(toUUID(strUserId));
SecurityUser authUser = getCurrentUser(); SecurityUser authUser = getCurrentUser();
User user = userService.findUserById(userId); User user = userService.findUserById(authUser.getTenantId(), userId);
if (!userTokenAccessEnabled || (authUser.getAuthority() == Authority.SYS_ADMIN && user.getAuthority() != Authority.TENANT_ADMIN) if (!userTokenAccessEnabled || (authUser.getAuthority() == Authority.SYS_ADMIN && user.getAuthority() != Authority.TENANT_ADMIN)
|| (authUser.getAuthority() == Authority.TENANT_ADMIN && !authUser.getTenantId().equals(user.getTenantId()))) { || (authUser.getAuthority() == Authority.TENANT_ADMIN && !authUser.getTenantId().equals(user.getTenantId()))) {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED); ThingsboardErrorCode.PERMISSION_DENIED);
} }
UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
UserCredentials credentials = userService.findUserCredentialsByUserId(userId); UserCredentials credentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), userId);
SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal);
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
@ -146,7 +146,7 @@ public class UserController extends BaseController {
} }
User savedUser = checkNotNull(userService.saveUser(user)); User savedUser = checkNotNull(userService.saveUser(user));
if (sendEmail) { if (sendEmail) {
UserCredentials userCredentials = userService.findUserCredentialsByUserId(savedUser.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), savedUser.getId());
String baseUrl = constructBaseUrl(request); String baseUrl = constructBaseUrl(request);
String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
userCredentials.getActivateToken()); userCredentials.getActivateToken());
@ -154,7 +154,7 @@ public class UserController extends BaseController {
try { try {
mailService.sendActivationEmail(activateUrl, email); mailService.sendActivationEmail(activateUrl, email);
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
userService.deleteUser(savedUser.getId()); userService.deleteUser(authUser.getTenantId(), savedUser.getId());
throw e; throw e;
} }
} }
@ -180,8 +180,8 @@ public class UserController extends BaseController {
@RequestParam(value = "email") String email, @RequestParam(value = "email") String email,
HttpServletRequest request) throws ThingsboardException { HttpServletRequest request) throws ThingsboardException {
try { try {
User user = checkNotNull(userService.findUserByEmail(email)); User user = checkNotNull(userService.findUserByEmail(getCurrentUser().getTenantId(), email));
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId());
if (!userCredentials.isEnabled()) { if (!userCredentials.isEnabled()) {
String baseUrl = constructBaseUrl(request); String baseUrl = constructBaseUrl(request);
String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
@ -210,7 +210,7 @@ public class UserController extends BaseController {
ThingsboardErrorCode.PERMISSION_DENIED); ThingsboardErrorCode.PERMISSION_DENIED);
} }
User user = checkUserId(userId); User user = checkUserId(userId);
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId());
if (!userCredentials.isEnabled()) { if (!userCredentials.isEnabled()) {
String baseUrl = constructBaseUrl(request); String baseUrl = constructBaseUrl(request);
String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
@ -232,7 +232,7 @@ public class UserController extends BaseController {
try { try {
UserId userId = new UserId(toUUID(strUserId)); UserId userId = new UserId(toUUID(strUserId));
User user = checkUserId(userId); User user = checkUserId(userId);
userService.deleteUser(userId); userService.deleteUser(getCurrentUser().getTenantId(), userId);
logEntityAction(userId, user, logEntityAction(userId, user,
user.getCustomerId(), user.getCustomerId(),

2
application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java

@ -75,7 +75,7 @@ public class WidgetTypeController extends BaseController {
try { try {
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
checkWidgetTypeId(widgetTypeId, true); checkWidgetTypeId(widgetTypeId, true);
widgetTypeService.deleteWidgetType(widgetTypeId); widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

6
application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java

@ -77,7 +77,7 @@ public class WidgetsBundleController extends BaseController {
try { try {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
checkWidgetsBundleId(widgetsBundleId, true); checkWidgetsBundleId(widgetsBundleId, true);
widgetsBundleService.deleteWidgetsBundle(widgetsBundleId); widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -94,7 +94,7 @@ public class WidgetsBundleController extends BaseController {
try { try {
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(pageLink)); return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), pageLink));
} else { } else {
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink)); return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink));
@ -110,7 +110,7 @@ public class WidgetsBundleController extends BaseController {
public List<WidgetsBundle> getWidgetsBundles() throws ThingsboardException { public List<WidgetsBundle> getWidgetsBundles() throws ThingsboardException {
try { try {
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
return checkNotNull(widgetsBundleService.findSystemWidgetsBundles()); return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId()));
} else { } else {
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId)); return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId));

9
application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java

@ -30,6 +30,7 @@ import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.rule.engine.api.NodeDefinition; import org.thingsboard.rule.engine.api.NodeDefinition;
import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.TbRelationTypes;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.dao.component.ComponentDescriptorService; import org.thingsboard.server.dao.component.ComponentDescriptorService;
@ -159,18 +160,18 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e); log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
ComponentDescriptor persistedComponent = componentDescriptorService.findByClazz(clazzName); ComponentDescriptor persistedComponent = componentDescriptorService.findByClazz(TenantId.SYS_TENANT_ID, clazzName);
if (persistedComponent == null) { if (persistedComponent == null) {
log.info("Persisting new component: {}", scannedComponent); log.info("Persisting new component: {}", scannedComponent);
scannedComponent = componentDescriptorService.saveComponent(scannedComponent); scannedComponent = componentDescriptorService.saveComponent(TenantId.SYS_TENANT_ID, scannedComponent);
} else if (scannedComponent.equals(persistedComponent)) { } else if (scannedComponent.equals(persistedComponent)) {
log.info("Component is already persisted: {}", persistedComponent); log.info("Component is already persisted: {}", persistedComponent);
scannedComponent = persistedComponent; scannedComponent = persistedComponent;
} else { } else {
log.info("Component {} will be updated to {}", persistedComponent, scannedComponent); log.info("Component {} will be updated to {}", persistedComponent, scannedComponent);
componentDescriptorService.deleteByClazz(persistedComponent.getClazz()); componentDescriptorService.deleteByClazz(TenantId.SYS_TENANT_ID, persistedComponent.getClazz());
scannedComponent.setId(persistedComponent.getId()); scannedComponent.setId(persistedComponent.getId());
scannedComponent = componentDescriptorService.saveComponent(scannedComponent); scannedComponent = componentDescriptorService.saveComponent(TenantId.SYS_TENANT_ID, scannedComponent);
} }
return scannedComponent; return scannedComponent;
} }

4
application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java

@ -25,6 +25,8 @@ import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.dashboard.DashboardService;
import java.io.IOException; import java.io.IOException;
@ -96,7 +98,7 @@ public class DatabaseHelper {
} }
} }
for (CustomerId customerId : customerIds) { for (CustomerId customerId : customerIds) {
dashboardService.assignDashboardToCustomer(dashboardId, customerId); dashboardService.assignDashboardToCustomer(new TenantId(EntityId.NULL_UUID), dashboardId, customerId);
} }
}); });
} }

14
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -96,7 +96,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
ObjectNode node = objectMapper.createObjectNode(); ObjectNode node = objectMapper.createObjectNode();
node.put("baseUrl", "http://localhost:8080"); node.put("baseUrl", "http://localhost:8080");
generalSettings.setJsonValue(node); generalSettings.setJsonValue(node);
adminSettingsService.saveAdminSettings(generalSettings); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings);
AdminSettings mailSettings = new AdminSettings(); AdminSettings mailSettings = new AdminSettings();
mailSettings.setKey("mail"); mailSettings.setKey("mail");
@ -110,7 +110,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
node.put("username", ""); node.put("username", "");
node.put("password", ""); //NOSONAR, key used to identify password field (not password value itself) node.put("password", ""); //NOSONAR, key used to identify password field (not password value itself)
mailSettings.setJsonValue(node); mailSettings.setJsonValue(node);
adminSettingsService.saveAdminSettings(mailSettings); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
} }
@Override @Override
@ -158,7 +158,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
public void deleteSystemWidgetBundle(String bundleAlias) throws Exception { public void deleteSystemWidgetBundle(String bundleAlias) throws Exception {
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(new TenantId(ModelConstants.NULL_UUID), bundleAlias); WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(new TenantId(ModelConstants.NULL_UUID), bundleAlias);
if (widgetsBundle != null) { if (widgetsBundle != null) {
widgetsBundleService.deleteWidgetsBundle(widgetsBundle.getId()); widgetsBundleService.deleteWidgetsBundle(TenantId.SYS_TENANT_ID, widgetsBundle.getId());
} }
} }
@ -178,11 +178,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
user.setTenantId(tenantId); user.setTenantId(tenantId);
user.setCustomerId(customerId); user.setCustomerId(customerId);
user = userService.saveUser(user); user = userService.saveUser(user);
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, user.getId());
userCredentials.setPassword(passwordEncoder.encode(password)); userCredentials.setPassword(passwordEncoder.encode(password));
userCredentials.setEnabled(true); userCredentials.setEnabled(true);
userCredentials.setActivateToken(null); userCredentials.setActivateToken(null);
userService.saveUserCredentials(userCredentials); userService.saveUserCredentials(TenantId.SYS_TENANT_ID, userCredentials);
return user; return user;
} }
@ -203,9 +203,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
device.setAdditionalInfo(additionalInfo); device.setAdditionalInfo(additionalInfo);
} }
device = deviceService.saveDevice(device); device = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId()); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, device.getId());
deviceCredentials.setCredentialsId(accessToken); deviceCredentials.setCredentialsId(accessToken);
deviceCredentialsService.updateDeviceCredentials(deviceCredentials); deviceCredentialsService.updateDeviceCredentials(TenantId.SYS_TENANT_ID, deviceCredentials);
return device; return device;
} }

5
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -23,6 +23,7 @@ import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
@ -116,7 +117,7 @@ public class InstallScripts {
ruleChain = ruleChainService.saveRuleChain(ruleChain); ruleChain = ruleChainService.saveRuleChain(ruleChain);
ruleChainMetaData.setRuleChainId(ruleChain.getId()); ruleChainMetaData.setRuleChainId(ruleChain.getId());
ruleChainService.saveRuleChainMetaData(ruleChainMetaData); ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData);
} catch (Exception e) { } catch (Exception e) {
log.error("Unable to load rule chain from json: [{}]", path.toString()); log.error("Unable to load rule chain from json: [{}]", path.toString());
throw new RuntimeException("Unable to load rule chain from json", e); throw new RuntimeException("Unable to load rule chain from json", e);
@ -169,7 +170,7 @@ public class InstallScripts {
dashboard.setTenantId(tenantId); dashboard.setTenantId(tenantId);
Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); Dashboard savedDashboard = dashboardService.saveDashboard(dashboard);
if (customerId != null && !customerId.isNullUid()) { if (customerId != null && !customerId.isNullUid()) {
dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId); dashboardService.assignDashboardToCustomer(new TenantId(EntityId.NULL_UUID), savedDashboard.getId(), customerId);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Unable to load dashboard from json: [{}]", path.toString()); log.error("Unable to load dashboard from json: [{}]", path.toString());

4
application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java

@ -31,6 +31,8 @@ import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.AdminSettingsService;
@ -70,7 +72,7 @@ public class DefaultMailService implements MailService {
@Override @Override
public void updateMailConfiguration() { public void updateMailConfiguration() {
AdminSettings settings = adminSettingsService.findAdminSettingsByKey("mail"); AdminSettings settings = adminSettingsService.findAdminSettingsByKey(new TenantId(EntityId.NULL_UUID), "mail");
if (settings != null) { if (settings != null) {
JsonNode jsonConfig = settings.getJsonValue(); JsonNode jsonConfig = settings.getJsonValue();
mailSender = createMailSender(jsonConfig); mailSender = createMailSender(jsonConfig);

30
application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java

@ -110,24 +110,24 @@ public class AccessValidator {
} }
public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, String entityType, String entityIdStr, public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, String entityType, String entityIdStr,
BiConsumer<DeferredResult<ResponseEntity>, EntityId> onSuccess) throws ThingsboardException { ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess) throws ThingsboardException {
return validateEntityAndCallback(currentUser, entityType, entityIdStr, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR)); return validateEntityAndCallback(currentUser, entityType, entityIdStr, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
} }
public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, String entityType, String entityIdStr, public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, String entityType, String entityIdStr,
BiConsumer<DeferredResult<ResponseEntity>, EntityId> onSuccess, ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess,
BiConsumer<DeferredResult<ResponseEntity>, Throwable> onFailure) throws ThingsboardException { BiConsumer<DeferredResult<ResponseEntity>, Throwable> onFailure) throws ThingsboardException {
return validateEntityAndCallback(currentUser, EntityIdFactory.getByTypeAndId(entityType, entityIdStr), return validateEntityAndCallback(currentUser, EntityIdFactory.getByTypeAndId(entityType, entityIdStr),
onSuccess, onFailure); onSuccess, onFailure);
} }
public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, EntityId entityId, public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, EntityId entityId,
BiConsumer<DeferredResult<ResponseEntity>, EntityId> onSuccess) throws ThingsboardException { ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess) throws ThingsboardException {
return validateEntityAndCallback(currentUser, entityId, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR)); return validateEntityAndCallback(currentUser, entityId, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
} }
public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, EntityId entityId, public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, EntityId entityId,
BiConsumer<DeferredResult<ResponseEntity>, EntityId> onSuccess, ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess,
BiConsumer<DeferredResult<ResponseEntity>, Throwable> onFailure) throws ThingsboardException { BiConsumer<DeferredResult<ResponseEntity>, Throwable> onFailure) throws ThingsboardException {
final DeferredResult<ResponseEntity> response = new DeferredResult<>(); final DeferredResult<ResponseEntity> response = new DeferredResult<>();
@ -136,7 +136,7 @@ public class AccessValidator {
new FutureCallback<DeferredResult<ResponseEntity>>() { new FutureCallback<DeferredResult<ResponseEntity>>() {
@Override @Override
public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
onSuccess.accept(response, entityId); onSuccess.accept(response, currentUser.getTenantId(), entityId);
} }
@Override @Override
@ -178,7 +178,7 @@ public class AccessValidator {
if (currentUser.isSystemAdmin()) { if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
ListenableFuture<Device> deviceFuture = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); ListenableFuture<Device> deviceFuture = deviceService.findDeviceByIdAsync(currentUser.getTenantId(), new DeviceId(entityId.getId()));
Futures.addCallback(deviceFuture, getCallback(callback, device -> { Futures.addCallback(deviceFuture, getCallback(callback, device -> {
if (device == null) { if (device == null) {
return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND); return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND);
@ -199,7 +199,7 @@ public class AccessValidator {
if (currentUser.isSystemAdmin()) { if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
ListenableFuture<Asset> assetFuture = assetService.findAssetByIdAsync(new AssetId(entityId.getId())); ListenableFuture<Asset> assetFuture = assetService.findAssetByIdAsync(currentUser.getTenantId(), new AssetId(entityId.getId()));
Futures.addCallback(assetFuture, getCallback(callback, asset -> { Futures.addCallback(assetFuture, getCallback(callback, asset -> {
if (asset == null) { if (asset == null) {
return ValidationResult.entityNotFound("Asset with requested id wasn't found!"); return ValidationResult.entityNotFound("Asset with requested id wasn't found!");
@ -220,7 +220,7 @@ public class AccessValidator {
if (currentUser.isCustomerUser()) { if (currentUser.isCustomerUser()) {
callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
ListenableFuture<RuleChain> ruleChainFuture = ruleChainService.findRuleChainByIdAsync(new RuleChainId(entityId.getId())); ListenableFuture<RuleChain> ruleChainFuture = ruleChainService.findRuleChainByIdAsync(currentUser.getTenantId(), new RuleChainId(entityId.getId()));
Futures.addCallback(ruleChainFuture, getCallback(callback, ruleChain -> { Futures.addCallback(ruleChainFuture, getCallback(callback, ruleChain -> {
if (ruleChain == null) { if (ruleChain == null) {
return ValidationResult.entityNotFound("Rule chain with requested id wasn't found!"); return ValidationResult.entityNotFound("Rule chain with requested id wasn't found!");
@ -241,7 +241,7 @@ public class AccessValidator {
if (currentUser.isCustomerUser()) { if (currentUser.isCustomerUser()) {
callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
ListenableFuture<RuleNode> ruleNodeFuture = ruleChainService.findRuleNodeByIdAsync(new RuleNodeId(entityId.getId())); ListenableFuture<RuleNode> ruleNodeFuture = ruleChainService.findRuleNodeByIdAsync(currentUser.getTenantId(), new RuleNodeId(entityId.getId()));
Futures.addCallback(ruleNodeFuture, getCallback(callback, ruleNodeTmp -> { Futures.addCallback(ruleNodeFuture, getCallback(callback, ruleNodeTmp -> {
RuleNode ruleNode = ruleNodeTmp; RuleNode ruleNode = ruleNodeTmp;
if (ruleNode == null) { if (ruleNode == null) {
@ -250,7 +250,7 @@ public class AccessValidator {
return ValidationResult.entityNotFound("Rule chain with requested node id wasn't found!"); return ValidationResult.entityNotFound("Rule chain with requested node id wasn't found!");
} else { } else {
//TODO: make async //TODO: make async
RuleChain ruleChain = ruleChainService.findRuleChainById(ruleNode.getRuleChainId()); RuleChain ruleChain = ruleChainService.findRuleChainById(currentUser.getTenantId(), ruleNode.getRuleChainId());
if (currentUser.isTenantAdmin() && !ruleChain.getTenantId().equals(currentUser.getTenantId())) { if (currentUser.isTenantAdmin() && !ruleChain.getTenantId().equals(currentUser.getTenantId())) {
return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!"); return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!");
} else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { } else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) {
@ -267,7 +267,7 @@ public class AccessValidator {
if (currentUser.isSystemAdmin()) { if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
ListenableFuture<Customer> customerFuture = customerService.findCustomerByIdAsync(new CustomerId(entityId.getId())); ListenableFuture<Customer> customerFuture = customerService.findCustomerByIdAsync(currentUser.getTenantId(), new CustomerId(entityId.getId()));
Futures.addCallback(customerFuture, getCallback(callback, customer -> { Futures.addCallback(customerFuture, getCallback(callback, customer -> {
if (customer == null) { if (customer == null) {
return ValidationResult.entityNotFound("Customer with requested id wasn't found!"); return ValidationResult.entityNotFound("Customer with requested id wasn't found!");
@ -290,7 +290,7 @@ public class AccessValidator {
} else if (currentUser.isSystemAdmin()) { } else if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.ok(null)); callback.onSuccess(ValidationResult.ok(null));
} else { } else {
ListenableFuture<Tenant> tenantFuture = tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); ListenableFuture<Tenant> tenantFuture = tenantService.findTenantByIdAsync(currentUser.getTenantId(), new TenantId(entityId.getId()));
Futures.addCallback(tenantFuture, getCallback(callback, tenant -> { Futures.addCallback(tenantFuture, getCallback(callback, tenant -> {
if (tenant == null) { if (tenant == null) {
return ValidationResult.entityNotFound("Tenant with requested id wasn't found!"); return ValidationResult.entityNotFound("Tenant with requested id wasn't found!");
@ -307,7 +307,7 @@ public class AccessValidator {
if (currentUser.isSystemAdmin()) { if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
ListenableFuture<EntityView> entityViewFuture = entityViewService.findEntityViewByIdAsync(new EntityViewId(entityId.getId())); ListenableFuture<EntityView> entityViewFuture = entityViewService.findEntityViewByIdAsync(currentUser.getTenantId(), new EntityViewId(entityId.getId()));
Futures.addCallback(entityViewFuture, getCallback(callback, entityView -> { Futures.addCallback(entityViewFuture, getCallback(callback, entityView -> {
if (entityView == null) { if (entityView == null) {
return ValidationResult.entityNotFound(ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND); return ValidationResult.entityNotFound(ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND);
@ -349,4 +349,8 @@ public class AccessValidator {
} }
response.setResult(responseEntity); response.setResult(responseEntity);
} }
public interface ThreeConsumer<A, B, C> {
void accept(A a, B b, C c);
}
} }

9
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java

@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
@ -72,12 +73,13 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide
} }
private SecurityUser authenticateByUserId(UserId userId) { private SecurityUser authenticateByUserId(UserId userId) {
User user = userService.findUserById(userId); TenantId systemId = new TenantId(EntityId.NULL_UUID);
User user = userService.findUserById(systemId, userId);
if (user == null) { if (user == null) {
throw new UsernameNotFoundException("User not found by refresh token"); throw new UsernameNotFoundException("User not found by refresh token");
} }
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(systemId, user.getId());
if (userCredentials == null) { if (userCredentials == null) {
throw new UsernameNotFoundException("User credentials not found"); throw new UsernameNotFoundException("User credentials not found");
} }
@ -96,13 +98,14 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide
} }
private SecurityUser authenticateByPublicId(String publicId) { private SecurityUser authenticateByPublicId(String publicId) {
TenantId systemId = new TenantId(EntityId.NULL_UUID);
CustomerId customerId; CustomerId customerId;
try { try {
customerId = new CustomerId(UUID.fromString(publicId)); customerId = new CustomerId(UUID.fromString(publicId));
} catch (Exception e) { } catch (Exception e) {
throw new BadCredentialsException("Refresh token is not valid"); throw new BadCredentialsException("Refresh token is not valid");
} }
Customer publicCustomer = customerService.findCustomerById(customerId); Customer publicCustomer = customerService.findCustomerById(systemId, customerId);
if (publicCustomer == null) { if (publicCustomer == null) {
throw new UsernameNotFoundException("Public entity not found by refresh token"); throw new UsernameNotFoundException("Public entity not found by refresh token");
} }

7
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java

@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
@ -76,12 +77,12 @@ public class RestAuthenticationProvider implements AuthenticationProvider {
} }
private Authentication authenticateByUsernameAndPassword(UserPrincipal userPrincipal, String username, String password) { private Authentication authenticateByUsernameAndPassword(UserPrincipal userPrincipal, String username, String password) {
User user = userService.findUserByEmail(username); User user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, username);
if (user == null) { if (user == null) {
throw new UsernameNotFoundException("User not found: " + username); throw new UsernameNotFoundException("User not found: " + username);
} }
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, user.getId());
if (userCredentials == null) { if (userCredentials == null) {
throw new UsernameNotFoundException("User credentials not found"); throw new UsernameNotFoundException("User credentials not found");
} }
@ -108,7 +109,7 @@ public class RestAuthenticationProvider implements AuthenticationProvider {
} catch (Exception e) { } catch (Exception e) {
throw new BadCredentialsException("Authentication Failed. Public Id is not valid."); throw new BadCredentialsException("Authentication Failed. Public Id is not valid.");
} }
Customer publicCustomer = customerService.findCustomerById(customerId); Customer publicCustomer = customerService.findCustomerById(TenantId.SYS_TENANT_ID, customerId);
if (publicCustomer == null) { if (publicCustomer == null) {
throw new UsernameNotFoundException("Public entity not found: " + publicId); throw new UsernameNotFoundException("Public entity not found: " + publicId);
} }

10
application/src/main/java/org/thingsboard/server/service/security/device/DefaultDeviceAuthService.java

@ -18,8 +18,6 @@ package org.thingsboard.server.service.security.device;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; import org.thingsboard.server.common.data.security.DeviceCredentialsFilter;
import org.thingsboard.server.common.transport.auth.DeviceAuthResult; import org.thingsboard.server.common.transport.auth.DeviceAuthResult;
@ -27,8 +25,6 @@ import org.thingsboard.server.common.transport.auth.DeviceAuthService;
import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.DeviceService;
import java.util.Optional;
@Service @Service
@Slf4j @Slf4j
public class DefaultDeviceAuthService implements DeviceAuthService { public class DefaultDeviceAuthService implements DeviceAuthService {
@ -65,8 +61,4 @@ public class DefaultDeviceAuthService implements DeviceAuthService {
} }
} }
@Override }
public Optional<Device> findDeviceById(DeviceId deviceId) {
return Optional.ofNullable(deviceService.findDeviceById(deviceId));
}
}

3
application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java

@ -21,6 +21,7 @@ import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -69,7 +70,7 @@ public class JwtTokenFactory {
UserPrincipal principal = securityUser.getUserPrincipal(); UserPrincipal principal = securityUser.getUserPrincipal();
String subject = principal.getValue(); String subject = principal.getValue();
Claims claims = Jwts.claims().setSubject(subject); Claims claims = Jwts.claims().setSubject(subject);
claims.put(SCOPES, securityUser.getAuthorities().stream().map(s -> s.getAuthority()).collect(Collectors.toList())); claims.put(SCOPES, securityUser.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
claims.put(USER_ID, securityUser.getId().getId().toString()); claims.put(USER_ID, securityUser.getId().getId().toString());
claims.put(FIRST_NAME, securityUser.getFirstName()); claims.put(FIRST_NAME, securityUser.getFirstName());
claims.put(LAST_NAME, securityUser.getLastName()); claims.put(LAST_NAME, securityUser.getLastName());

10
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -207,7 +207,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
if (proto.getDeleted()) { if (proto.getDeleted()) {
queueExecutor.submit(() -> onDeviceDeleted(tenantId, deviceId)); queueExecutor.submit(() -> onDeviceDeleted(tenantId, deviceId));
} else { } else {
Device device = deviceService.findDeviceById(deviceId); Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, deviceId);
if (device != null) { if (device != null) {
if (proto.getAdded()) { if (proto.getAdded()) {
onDeviceAdded(device); onDeviceAdded(device);
@ -320,7 +320,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
DeviceStateData deviceStateData = deviceStates.get(deviceId); DeviceStateData deviceStateData = deviceStates.get(deviceId);
if (deviceStateData == null) { if (deviceStateData == null) {
if (!routingService.resolveById(deviceId).isPresent()) { if (!routingService.resolveById(deviceId).isPresent()) {
Device device = deviceService.findDeviceById(deviceId); Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, deviceId);
if (device != null) { if (device != null) {
try { try {
deviceStateData = fetchDeviceState(device).get(); deviceStateData = fetchDeviceState(device).get();
@ -414,7 +414,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
} }
private ListenableFuture<DeviceStateData> fetchDeviceState(Device device) { private ListenableFuture<DeviceStateData> fetchDeviceState(Device device) {
ListenableFuture<List<AttributeKvEntry>> attributes = attributesService.find(device.getId(), DataConstants.SERVER_SCOPE, PERSISTENT_ATTRIBUTES); ListenableFuture<List<AttributeKvEntry>> attributes = attributesService.find(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.SERVER_SCOPE, PERSISTENT_ATTRIBUTES);
return Futures.transform(attributes, new Function<List<AttributeKvEntry>, DeviceStateData>() { return Futures.transform(attributes, new Function<List<AttributeKvEntry>, DeviceStateData>() {
@Nullable @Nullable
@Override @Override
@ -465,11 +465,11 @@ public class DefaultDeviceStateService implements DeviceStateService {
} }
private void saveAttribute(DeviceId deviceId, String key, long value) { private void saveAttribute(DeviceId deviceId, String key, long value) {
tsSubService.saveAttrAndNotify(deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value)); tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value));
} }
private void saveAttribute(DeviceId deviceId, String key, boolean value) { private void saveAttribute(DeviceId deviceId, String key, boolean value) {
tsSubService.saveAttrAndNotify(deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value)); tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value));
} }
private class AttributeSaveCallback implements FutureCallback<Void> { private class AttributeSaveCallback implements FutureCallback<Void> {

39
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java

@ -63,6 +63,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -131,7 +132,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
long startTime = 0L; long startTime = 0L;
long endTime = 0L; long endTime = 0L;
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW) && TelemetryFeature.TIMESERIES.equals(sub.getType())) { if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW) && TelemetryFeature.TIMESERIES.equals(sub.getType())) {
EntityView entityView = entityViewService.findEntityViewById(new EntityViewId(entityId.getId())); EntityView entityView = entityViewService.findEntityViewById(TenantId.SYS_TENANT_ID, new EntityViewId(entityId.getId()));
entityId = entityView.getEntityId(); entityId = entityView.getEntityId();
startTime = entityView.getStartTimeMs(); startTime = entityView.getStartTimeMs();
endTime = entityView.getEndTimeMs(); endTime = entityView.getEndTimeMs();
@ -160,7 +161,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
.stream().filter(entry -> entityView.getKeys().getTimeseries().contains(entry.getKey())) .stream().filter(entry -> entityView.getKeys().getTimeseries().contains(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
} }
return new SubscriptionState(sub.getWsSessionId(), sub.getSubscriptionId(), entityId, sub.getType(), false, keyStates, sub.getScope()); return new SubscriptionState(sub.getWsSessionId(), sub.getSubscriptionId(), sub.getTenantId(), entityId, sub.getType(), false, keyStates, sub.getScope());
} }
@Override @Override
@ -185,45 +186,45 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
} }
@Override @Override
public void saveAndNotify(EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) { public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) {
saveAndNotify(entityId, ts, 0L, callback); saveAndNotify(tenantId, entityId, ts, 0L, callback);
} }
@Override @Override
public void saveAndNotify(EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) { public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) {
ListenableFuture<List<Void>> saveFuture = tsService.save(entityId, ts, ttl); ListenableFuture<List<Void>> saveFuture = tsService.save(tenantId, entityId, ts, ttl);
addMainCallback(saveFuture, callback); addMainCallback(saveFuture, callback);
addWsCallback(saveFuture, success -> onTimeseriesUpdate(entityId, ts)); addWsCallback(saveFuture, success -> onTimeseriesUpdate(entityId, ts));
} }
@Override @Override
public void saveAndNotify(EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback) { public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback) {
ListenableFuture<List<Void>> saveFuture = attrService.save(entityId, scope, attributes); ListenableFuture<List<Void>> saveFuture = attrService.save(tenantId, entityId, scope, attributes);
addMainCallback(saveFuture, callback); addMainCallback(saveFuture, callback);
addWsCallback(saveFuture, success -> onAttributesUpdate(entityId, scope, attributes)); addWsCallback(saveFuture, success -> onAttributesUpdate(entityId, scope, attributes));
} }
@Override @Override
public void saveAttrAndNotify(EntityId entityId, String scope, String key, long value, FutureCallback<Void> callback) { public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, long value, FutureCallback<Void> callback) {
saveAndNotify(entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(key, value) saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(key, value)
, System.currentTimeMillis())), callback); , System.currentTimeMillis())), callback);
} }
@Override @Override
public void saveAttrAndNotify(EntityId entityId, String scope, String key, String value, FutureCallback<Void> callback) { public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, String value, FutureCallback<Void> callback) {
saveAndNotify(entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(key, value) saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(key, value)
, System.currentTimeMillis())), callback); , System.currentTimeMillis())), callback);
} }
@Override @Override
public void saveAttrAndNotify(EntityId entityId, String scope, String key, double value, FutureCallback<Void> callback) { public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, double value, FutureCallback<Void> callback) {
saveAndNotify(entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new DoubleDataEntry(key, value) saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new DoubleDataEntry(key, value)
, System.currentTimeMillis())), callback); , System.currentTimeMillis())), callback);
} }
@Override @Override
public void saveAttrAndNotify(EntityId entityId, String scope, String key, boolean value, FutureCallback<Void> callback) { public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, boolean value, FutureCallback<Void> callback) {
saveAndNotify(entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value) saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value)
, System.currentTimeMillis())), callback); , System.currentTimeMillis())), callback);
} }
@ -246,6 +247,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
Collectors.toMap(ClusterAPIProtos.SubscriptionKetStateProto::getKey, ClusterAPIProtos.SubscriptionKetStateProto::getTs)); Collectors.toMap(ClusterAPIProtos.SubscriptionKetStateProto::getKey, ClusterAPIProtos.SubscriptionKetStateProto::getTs));
Subscription subscription = new Subscription( Subscription subscription = new Subscription(
new SubscriptionState(proto.getSessionId(), proto.getSubscriptionId(), new SubscriptionState(proto.getSessionId(), proto.getSubscriptionId(),
new TenantId(UUID.fromString(proto.getTenantId())),
EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()), EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()),
TelemetryFeature.valueOf(proto.getType()), proto.getAllKeys(), statesMap, proto.getScope()), TelemetryFeature.valueOf(proto.getType()), proto.getAllKeys(), statesMap, proto.getScope()),
false, new ServerAddress(serverAddress.getHost(), serverAddress.getPort(), serverAddress.getServerType())); false, new ServerAddress(serverAddress.getHost(), serverAddress.getPort(), serverAddress.getServerType()));
@ -372,7 +374,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
registerSubscription(sessionId, entityId, subscription); registerSubscription(sessionId, entityId, subscription);
if (subscription.getType() == TelemetryFeature.ATTRIBUTES) { if (subscription.getType() == TelemetryFeature.ATTRIBUTES) {
final Map<String, Long> keyStates = subscription.getKeyStates(); final Map<String, Long> keyStates = subscription.getKeyStates();
DonAsynchron.withCallback(attrService.find(entityId, DataConstants.CLIENT_SCOPE, keyStates.keySet()), values -> { DonAsynchron.withCallback(attrService.find(subscription.getSub().getTenantId(), entityId, DataConstants.CLIENT_SCOPE, keyStates.keySet()), values -> {
List<TsKvEntry> missedUpdates = new ArrayList<>(); List<TsKvEntry> missedUpdates = new ArrayList<>();
values.forEach(latestEntry -> { values.forEach(latestEntry -> {
if (latestEntry.getLastUpdateTs() > keyStates.get(latestEntry.getKey())) { if (latestEntry.getLastUpdateTs() > keyStates.get(latestEntry.getKey())) {
@ -395,7 +397,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
} }
}); });
if (!queries.isEmpty()) { if (!queries.isEmpty()) {
DonAsynchron.withCallback(tsService.findAll(entityId, queries), DonAsynchron.withCallback(tsService.findAll(subscription.getSub().getTenantId(), entityId, queries),
missedUpdates -> { missedUpdates -> {
if (missedUpdates != null && !missedUpdates.isEmpty()) { if (missedUpdates != null && !missedUpdates.isEmpty()) {
tellRemoteSubUpdate(address, sessionId, new SubscriptionUpdate(subscription.getSubscriptionId(), missedUpdates)); tellRemoteSubUpdate(address, sessionId, new SubscriptionUpdate(subscription.getSubscriptionId(), missedUpdates));
@ -606,6 +608,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
ClusterAPIProtos.SubscriptionProto.Builder builder = ClusterAPIProtos.SubscriptionProto.newBuilder(); ClusterAPIProtos.SubscriptionProto.Builder builder = ClusterAPIProtos.SubscriptionProto.newBuilder();
builder.setSessionId(sessionId); builder.setSessionId(sessionId);
builder.setSubscriptionId(sub.getSubscriptionId()); builder.setSubscriptionId(sub.getSubscriptionId());
builder.setTenantId(sub.getSub().getTenantId().getId().toString());
builder.setEntityType(sub.getEntityId().getEntityType().name()); builder.setEntityType(sub.getEntityId().getEntityType().name());
builder.setEntityId(sub.getEntityId().getId().toString()); builder.setEntityId(sub.getEntityId().getId().toString());
builder.setType(sub.getType().name()); builder.setType(sub.getType().name());

56
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* * <p>
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* * <p>
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* * <p>
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -28,6 +28,7 @@ import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
@ -36,6 +37,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.util.TenantRateLimitException;
import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.AccessValidator;
import org.thingsboard.server.service.security.ValidationCallback; import org.thingsboard.server.service.security.ValidationCallback;
import org.thingsboard.server.service.security.ValidationResult; import org.thingsboard.server.service.security.ValidationResult;
@ -206,7 +208,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
keys.forEach(key -> subState.put(key, 0L)); keys.forEach(key -> subState.put(key, 0L));
attributesData.forEach(v -> subState.put(v.getKey(), v.getTs())); attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, TelemetryFeature.ATTRIBUTES, false, subState, cmd.getScope()); SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), sessionRef.getSecurityCtx().getTenantId(), entityId, TelemetryFeature.ATTRIBUTES, false, subState, cmd.getScope());
subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub);
} }
@ -226,9 +228,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}; };
if (StringUtils.isEmpty(cmd.getScope())) { if (StringUtils.isEmpty(cmd.getScope())) {
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, keys, callback)); accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, keys, callback));
} else { } else {
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, cmd.getScope(), keys, callback)); accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, cmd.getScope(), keys, callback));
} }
} }
@ -279,7 +281,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
}; };
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, accessValidator.validate(sessionRef.getSecurityCtx(), entityId,
on(r -> Futures.addCallback(tsService.findAll(entityId, queries), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure));
} }
private void handleWsAttributesSubscription(TelemetryWebSocketSessionRef sessionRef, private void handleWsAttributesSubscription(TelemetryWebSocketSessionRef sessionRef,
@ -293,7 +295,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
Map<String, Long> subState = new HashMap<>(attributesData.size()); Map<String, Long> subState = new HashMap<>(attributesData.size());
attributesData.forEach(v -> subState.put(v.getKey(), v.getTs())); attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, TelemetryFeature.ATTRIBUTES, true, subState, cmd.getScope()); SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), sessionRef.getSecurityCtx().getTenantId(), entityId, TelemetryFeature.ATTRIBUTES, true, subState, cmd.getScope());
subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub);
} }
@ -308,9 +310,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
if (StringUtils.isEmpty(cmd.getScope())) { if (StringUtils.isEmpty(cmd.getScope())) {
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, callback)); accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, callback));
} else { } else {
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, cmd.getScope(), callback)); accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, cmd.getScope(), callback));
} }
} }
@ -347,14 +349,14 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys);
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, accessValidator.validate(sessionRef.getSecurityCtx(), entityId,
on(r -> Futures.addCallback(tsService.findAll(entityId, queries), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure));
} else { } else {
List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet()));
startTs = System.currentTimeMillis(); startTs = System.currentTimeMillis();
log.debug("[{}] fetching latest timeseries data for keys: ({}) for device : {}", sessionId, cmd.getKeys(), entityId); log.debug("[{}] fetching latest timeseries data for keys: ({}) for device : {}", sessionId, cmd.getKeys(), entityId);
final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys);
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, accessValidator.validate(sessionRef.getSecurityCtx(), entityId,
on(r -> Futures.addCallback(tsService.findLatest(entityId, keys), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findLatest(sessionRef.getSecurityCtx().getTenantId(), entityId, keys), callback, executor), callback::onFailure));
} }
} }
@ -366,7 +368,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data)); sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data));
Map<String, Long> subState = new HashMap<>(data.size()); Map<String, Long> subState = new HashMap<>(data.size());
data.forEach(v -> subState.put(v.getKey(), v.getTs())); data.forEach(v -> subState.put(v.getKey(), v.getTs()));
SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, TelemetryFeature.TIMESERIES, true, subState, cmd.getScope()); SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), sessionRef.getSecurityCtx().getTenantId(), entityId, TelemetryFeature.TIMESERIES, true, subState, cmd.getScope());
subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub);
} }
@ -384,7 +386,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
} }
}; };
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, accessValidator.validate(sessionRef.getSecurityCtx(), entityId,
on(r -> Futures.addCallback(tsService.findAllLatest(entityId), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findAllLatest(sessionRef.getSecurityCtx().getTenantId(), entityId), callback, executor), callback::onFailure));
} }
private FutureCallback<List<TsKvEntry>> getSubscriptionCallback(final TelemetryWebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List<String> keys) { private FutureCallback<List<TsKvEntry>> getSubscriptionCallback(final TelemetryWebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List<String> keys) {
@ -396,13 +398,17 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
Map<String, Long> subState = new HashMap<>(keys.size()); Map<String, Long> subState = new HashMap<>(keys.size());
keys.forEach(key -> subState.put(key, startTs)); keys.forEach(key -> subState.put(key, startTs));
data.forEach(v -> subState.put(v.getKey(), v.getTs())); data.forEach(v -> subState.put(v.getKey(), v.getTs()));
SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, TelemetryFeature.TIMESERIES, false, subState, cmd.getScope()); SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), sessionRef.getSecurityCtx().getTenantId(), entityId, TelemetryFeature.TIMESERIES, false, subState, cmd.getScope());
subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub);
} }
@Override @Override
public void onFailure(Throwable e) { public void onFailure(Throwable e) {
log.error(FAILED_TO_FETCH_DATA, e); if (e instanceof TenantRateLimitException || e.getCause() instanceof TenantRateLimitException) {
log.trace("[{}] Tenant rate limit detected for subscription: [{}]:{}", sessionRef.getSecurityCtx().getTenantId(), entityId, cmd);
} else {
log.info(FAILED_TO_FETCH_DATA, e);
}
SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR,
FAILED_TO_FETCH_DATA); FAILED_TO_FETCH_DATA);
sendWsMsg(sessionRef, update); sendWsMsg(sessionRef, update);
@ -472,13 +478,13 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}, executor); }, executor);
} }
private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final EntityId entityId, final List<String> keys, final FutureCallback<List<AttributeKvEntry>> callback) { private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final TenantId tenantId, final EntityId entityId, final List<String> keys, final FutureCallback<List<AttributeKvEntry>> callback) {
return new FutureCallback<ValidationResult>() { return new FutureCallback<ValidationResult>() {
@Override @Override
public void onSuccess(@Nullable ValidationResult result) { public void onSuccess(@Nullable ValidationResult result) {
List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
for (String scope : DataConstants.allScopes()) { for (String scope : DataConstants.allScopes()) {
futures.add(attributesService.find(entityId, scope, keys)); futures.add(attributesService.find(tenantId, entityId, scope, keys));
} }
ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures); ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures);
@ -492,11 +498,11 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}; };
} }
private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final EntityId entityId, final String scope, final List<String> keys, final FutureCallback<List<AttributeKvEntry>> callback) { private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final TenantId tenantId, final EntityId entityId, final String scope, final List<String> keys, final FutureCallback<List<AttributeKvEntry>> callback) {
return new FutureCallback<ValidationResult>() { return new FutureCallback<ValidationResult>() {
@Override @Override
public void onSuccess(@Nullable ValidationResult result) { public void onSuccess(@Nullable ValidationResult result) {
Futures.addCallback(attributesService.find(entityId, scope, keys), callback); Futures.addCallback(attributesService.find(tenantId, entityId, scope, keys), callback);
} }
@Override @Override
@ -506,13 +512,13 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}; };
} }
private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final EntityId entityId, final FutureCallback<List<AttributeKvEntry>> callback) { private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final TenantId tenantId, final EntityId entityId, final FutureCallback<List<AttributeKvEntry>> callback) {
return new FutureCallback<ValidationResult>() { return new FutureCallback<ValidationResult>() {
@Override @Override
public void onSuccess(@Nullable ValidationResult result) { public void onSuccess(@Nullable ValidationResult result) {
List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
for (String scope : DataConstants.allScopes()) { for (String scope : DataConstants.allScopes()) {
futures.add(attributesService.findAll(entityId, scope)); futures.add(attributesService.findAll(tenantId, entityId, scope));
} }
ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures); ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures);
@ -526,11 +532,11 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}; };
} }
private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final EntityId entityId, final String scope, final FutureCallback<List<AttributeKvEntry>> callback) { private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final TenantId tenantId, final EntityId entityId, final String scope, final FutureCallback<List<AttributeKvEntry>> callback) {
return new FutureCallback<ValidationResult>() { return new FutureCallback<ValidationResult>() {
@Override @Override
public void onSuccess(@Nullable ValidationResult result) { public void onSuccess(@Nullable ValidationResult result) {
Futures.addCallback(attributesService.findAll(entityId, scope), callback); Futures.addCallback(attributesService.findAll(tenantId, entityId, scope), callback);
} }
@Override @Override

1
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.service.telemetry;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.service.telemetry.sub.SubscriptionState; import org.thingsboard.server.service.telemetry.sub.SubscriptionState;

2
application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionState.java

@ -18,6 +18,7 @@ package org.thingsboard.server.service.telemetry.sub;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.telemetry.TelemetryFeature; import org.thingsboard.server.service.telemetry.TelemetryFeature;
import java.util.Map; import java.util.Map;
@ -30,6 +31,7 @@ public class SubscriptionState {
@Getter private final String wsSessionId; @Getter private final String wsSessionId;
@Getter private final int subscriptionId; @Getter private final int subscriptionId;
@Getter private final TenantId tenantId;
@Getter private final EntityId entityId; @Getter private final EntityId entityId;
@Getter private final TelemetryFeature type; @Getter private final TelemetryFeature type;
@Getter private final boolean allKeys; @Getter private final boolean allKeys;

7
application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java

@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.DeviceCredentialsType;
@ -107,7 +108,7 @@ public class LocalTransportApiService implements TransportApiService {
private ListenableFuture<TransportApiResponseMsg> handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) { private ListenableFuture<TransportApiResponseMsg> handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) {
DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB()));
ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(gatewayId); ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId);
return Futures.transform(gatewayFuture, gateway -> { return Futures.transform(gatewayFuture, gateway -> {
deviceCreationLock.lock(); deviceCreationLock.lock();
try { try {
@ -119,7 +120,7 @@ public class LocalTransportApiService implements TransportApiService {
device.setType(requestMsg.getDeviceType()); device.setType(requestMsg.getDeviceType());
device.setCustomerId(gateway.getCustomerId()); device.setCustomerId(gateway.getCustomerId());
device = deviceService.saveDevice(device); device = deviceService.saveDevice(device);
relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created")); relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
deviceStateService.onDeviceAdded(device); deviceStateService.onDeviceAdded(device);
} }
return TransportApiResponseMsg.newBuilder() return TransportApiResponseMsg.newBuilder()
@ -135,7 +136,7 @@ public class LocalTransportApiService implements TransportApiService {
private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId) { private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId) {
return Futures.transform(deviceService.findDeviceByIdAsync(deviceId), device -> { return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> {
if (device == null) { if (device == null) {
log.trace("[{}] Failed to lookup device by id", deviceId); log.trace("[{}] Failed to lookup device by id", deviceId);
return getEmptyTransportApiResponse(); return getEmptyTransportApiResponse();

11
application/src/main/proto/cluster.proto

@ -67,11 +67,12 @@ message SubscriptionProto {
string sessionId = 1; string sessionId = 1;
int32 subscriptionId = 2; int32 subscriptionId = 2;
string entityType = 3; string entityType = 3;
string entityId = 4; string tenantId = 4;
string type = 5; string entityId = 5;
bool allKeys = 6; string type = 6;
repeated SubscriptionKetStateProto keyStates = 7; bool allKeys = 7;
string scope = 8; repeated SubscriptionKetStateProto keyStates = 8;
string scope = 9;
} }
message SubscriptionUpdateProto { message SubscriptionUpdateProto {

3
application/src/main/resources/thingsboard.yml

@ -143,6 +143,9 @@ cassandra:
concurrent_limit: "${CASSANDRA_QUERY_CONCURRENT_LIMIT:1000}" concurrent_limit: "${CASSANDRA_QUERY_CONCURRENT_LIMIT:1000}"
permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}" permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}"
rate_limit_print_interval_ms: "${CASSANDRA_QUERY_RATE_LIMIT_PRINT_MS:10000}" rate_limit_print_interval_ms: "${CASSANDRA_QUERY_RATE_LIMIT_PRINT_MS:10000}"
tenant_rate_limits:
enabled: "${CASSANDRA_QUERY_TENANT_RATE_LIMITS_ENABLED:false}"
configuration: "${CASSANDRA_QUERY_TENANT_RATE_LIMITS_VALUE:1000:1,30000:60}"
# SQL configuration parameters # SQL configuration parameters
sql: sql:

8
application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java

@ -142,9 +142,9 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
device.setType("default"); device.setType("default");
device = doPost("/api/device", device, Device.class); device = doPost("/api/device", device, Device.class);
attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis()))); Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis())));
attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis()))); Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis())));
@ -257,9 +257,9 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
device.setType("default"); device.setType("default");
device = doPost("/api/device", device, Device.class); device = doPost("/api/device", device, Device.class);
attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis()))); Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis())));
attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis()))); Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis())));

2
application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java

@ -131,7 +131,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
device.setType("default"); device.setType("default");
device = doPost("/api/device", device, Device.class); device = doPost("/api/device", device, Device.class);
attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis()))); Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis())));
Thread.sleep(1000); Thread.sleep(1000);

3
common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java

@ -24,6 +24,9 @@ import org.thingsboard.server.common.data.EntityType;
public final class TenantId extends UUIDBased implements EntityId { public final class TenantId extends UUIDBased implements EntityId {
@JsonIgnore
public static final TenantId SYS_TENANT_ID = new TenantId(EntityId.NULL_UUID);
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonCreator @JsonCreator

4
common/message/pom.xml

@ -60,6 +60,10 @@
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>

8
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TbTransportRateLimits.java → common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.transport.service; package org.thingsboard.server.common.msg.tools;
import io.github.bucket4j.Bandwidth; import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket4j; import io.github.bucket4j.Bucket4j;
@ -25,10 +25,10 @@ import java.time.Duration;
/** /**
* Created by ashvayka on 22.10.18. * Created by ashvayka on 22.10.18.
*/ */
class TbTransportRateLimits { public class TbRateLimits {
private final LocalBucket bucket; private final LocalBucket bucket;
public TbTransportRateLimits(String limitsConfiguration) { public TbRateLimits(String limitsConfiguration) {
LocalBucketBuilder builder = Bucket4j.builder(); LocalBucketBuilder builder = Bucket4j.builder();
boolean initialized = false; boolean initialized = false;
for (String limitSrc : limitsConfiguration.split(",")) { for (String limitSrc : limitsConfiguration.split(",")) {
@ -46,7 +46,7 @@ class TbTransportRateLimits {
} }
boolean tryConsume() { public boolean tryConsume() {
return bucket.tryConsume(1); return bucket.tryConsume(1);
} }

4
common/transport/transport-api/pom.xml

@ -99,10 +99,6 @@
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

2
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java

@ -25,6 +25,4 @@ public interface DeviceAuthService {
DeviceAuthResult process(DeviceCredentialsFilter credentials); DeviceAuthResult process(DeviceCredentialsFilter credentials);
Optional<Device> findDeviceById(DeviceId deviceId);
} }

13
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java

@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.TransportServiceCallback;
@ -58,8 +59,8 @@ public abstract class AbstractTransportService implements TransportService {
private ConcurrentMap<UUID, SessionMetaData> sessions = new ConcurrentHashMap<>(); private ConcurrentMap<UUID, SessionMetaData> sessions = new ConcurrentHashMap<>();
//TODO: Implement cleanup of this maps. //TODO: Implement cleanup of this maps.
private ConcurrentMap<TenantId, TbTransportRateLimits> perTenantLimits = new ConcurrentHashMap<>(); private ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
private ConcurrentMap<DeviceId, TbTransportRateLimits> perDeviceLimits = new ConcurrentHashMap<>(); private ConcurrentMap<DeviceId, TbRateLimits> perDeviceLimits = new ConcurrentHashMap<>();
@Override @Override
public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) { public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) {
@ -204,7 +205,7 @@ public abstract class AbstractTransportService implements TransportService {
return true; return true;
} }
TenantId tenantId = new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); TenantId tenantId = new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB()));
TbTransportRateLimits rateLimits = perTenantLimits.computeIfAbsent(tenantId, id -> new TbTransportRateLimits(perTenantLimitsConf)); TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(tenantId, id -> new TbRateLimits(perTenantLimitsConf));
if (!rateLimits.tryConsume()) { if (!rateLimits.tryConsume()) {
if (callback != null) { if (callback != null) {
callback.onError(new TbRateLimitsException(EntityType.TENANT)); callback.onError(new TbRateLimitsException(EntityType.TENANT));
@ -215,7 +216,7 @@ public abstract class AbstractTransportService implements TransportService {
return false; return false;
} }
DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
rateLimits = perDeviceLimits.computeIfAbsent(deviceId, id -> new TbTransportRateLimits(perDevicesLimitsConf)); rateLimits = perDeviceLimits.computeIfAbsent(deviceId, id -> new TbRateLimits(perDevicesLimitsConf));
if (!rateLimits.tryConsume()) { if (!rateLimits.tryConsume()) {
if (callback != null) { if (callback != null) {
callback.onError(new TbRateLimitsException(EntityType.DEVICE)); callback.onError(new TbRateLimitsException(EntityType.DEVICE));
@ -271,8 +272,8 @@ public abstract class AbstractTransportService implements TransportService {
public void init() { public void init() {
if (rateLimitEnabled) { if (rateLimitEnabled) {
//Just checking the configuration parameters //Just checking the configuration parameters
new TbTransportRateLimits(perTenantLimitsConf); new TbRateLimits(perTenantLimitsConf);
new TbTransportRateLimits(perDevicesLimitsConf); new TbRateLimits(perDevicesLimitsConf);
} }
this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(); this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor();
this.transportCallbackExecutor = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); this.transportCallbackExecutor = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());

11
dao/src/main/java/org/thingsboard/server/dao/Dao.java

@ -16,20 +16,21 @@
package org.thingsboard.server.dao; package org.thingsboard.server.dao;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public interface Dao<T> { public interface Dao<T> {
List<T> find(); List<T> find(TenantId tenantId);
T findById(UUID id); T findById(TenantId tenantId, UUID id);
ListenableFuture<T> findByIdAsync(UUID id); ListenableFuture<T> findByIdAsync(TenantId tenantId, UUID id);
T save(T t); T save(TenantId tenantId, T t);
boolean removeById(UUID id); boolean removeById(TenantId tenantId, UUID id);
} }

6
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java

@ -33,9 +33,9 @@ public interface AlarmDao extends Dao<Alarm> {
ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
ListenableFuture<Alarm> findAlarmByIdAsync(UUID key); ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key);
Alarm save(Alarm alarm); Alarm save(TenantId tenantId, Alarm alarm);
ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query); ListenableFuture<List<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query);
} }

12
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -35,17 +35,17 @@ public interface AlarmService {
Alarm createOrUpdateAlarm(Alarm alarm); Alarm createOrUpdateAlarm(Alarm alarm);
ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTs); ListenableFuture<Boolean> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs);
ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, JsonNode details, long ackTs); ListenableFuture<Boolean> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long ackTs);
ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId); ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId);
ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId); ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId);
ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query); ListenableFuture<TimePageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query);
AlarmSeverity findHighestAlarmSeverity(EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
AlarmStatus alarmStatus); AlarmStatus alarmStatus);
ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);

92
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -91,7 +91,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
@Override @Override
public Alarm createOrUpdateAlarm(Alarm alarm) { public Alarm createOrUpdateAlarm(Alarm alarm) {
alarmDataValidator.validate(alarm); alarmDataValidator.validate(alarm, Alarm::getTenantId);
try { try {
if (alarm.getStartTs() == 0L) { if (alarm.getStartTs() == 0L) {
alarm.setStartTs(System.currentTimeMillis()); alarm.setStartTs(System.currentTimeMillis());
@ -120,7 +120,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
private Alarm createAlarm(Alarm alarm) throws InterruptedException, ExecutionException { private Alarm createAlarm(Alarm alarm) throws InterruptedException, ExecutionException {
log.debug("New Alarm : {}", alarm); log.debug("New Alarm : {}", alarm);
Alarm saved = alarmDao.save(alarm); Alarm saved = alarmDao.save(alarm.getTenantId(), alarm);
createAlarmRelations(saved); createAlarmRelations(saved);
return saved; return saved;
} }
@ -129,17 +129,17 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm.isPropagate()) { if (alarm.isPropagate()) {
EntityRelationsQuery query = new EntityRelationsQuery(); EntityRelationsQuery query = new EntityRelationsQuery();
query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); List<EntityId> parentEntities = relationService.findByQuery(alarm.getTenantId(), query).get().stream().map(EntityRelation::getFrom).collect(Collectors.toList());
for (EntityId parentId : parentEntities) { for (EntityId parentId : parentEntities) {
createAlarmRelation(parentId, alarm.getId(), alarm.getStatus(), true); createAlarmRelation(alarm.getTenantId(), parentId, alarm.getId(), alarm.getStatus(), true);
} }
} }
createAlarmRelation(alarm.getOriginator(), alarm.getId(), alarm.getStatus(), true); createAlarmRelation(alarm.getTenantId(), alarm.getOriginator(), alarm.getId(), alarm.getStatus(), true);
} }
private ListenableFuture<Alarm> updateAlarm(Alarm update) { private ListenableFuture<Alarm> updateAlarm(Alarm update) {
alarmDataValidator.validate(update); alarmDataValidator.validate(update, Alarm::getTenantId);
return getAndUpdate(update.getId(), new Function<Alarm, Alarm>() { return getAndUpdate(update.getTenantId(), update.getId(), new Function<Alarm, Alarm>() {
@Nullable @Nullable
@Override @Override
public Alarm apply(@Nullable Alarm alarm) { public Alarm apply(@Nullable Alarm alarm) {
@ -157,7 +157,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
AlarmStatus newStatus = newAlarm.getStatus(); AlarmStatus newStatus = newAlarm.getStatus();
boolean oldPropagate = oldAlarm.isPropagate(); boolean oldPropagate = oldAlarm.isPropagate();
boolean newPropagate = newAlarm.isPropagate(); boolean newPropagate = newAlarm.isPropagate();
Alarm result = alarmDao.save(merge(oldAlarm, newAlarm)); Alarm result = alarmDao.save(newAlarm.getTenantId(), merge(oldAlarm, newAlarm));
if (!oldPropagate && newPropagate) { if (!oldPropagate && newPropagate) {
try { try {
createAlarmRelations(result); createAlarmRelations(result);
@ -172,8 +172,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
@Override @Override
public ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTime) { public ListenableFuture<Boolean> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTime) {
return getAndUpdate(alarmId, new Function<Alarm, Boolean>() { return getAndUpdate(tenantId, alarmId, new Function<Alarm, Boolean>() {
@Nullable @Nullable
@Override @Override
public Boolean apply(@Nullable Alarm alarm) { public Boolean apply(@Nullable Alarm alarm) {
@ -184,7 +184,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK; AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK;
alarm.setStatus(newStatus); alarm.setStatus(newStatus);
alarm.setAckTs(ackTime); alarm.setAckTs(ackTime);
alarmDao.save(alarm); alarmDao.save(alarm.getTenantId(), alarm);
updateRelations(alarm, oldStatus, newStatus); updateRelations(alarm, oldStatus, newStatus);
return true; return true;
} }
@ -193,8 +193,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
@Override @Override
public ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, JsonNode details, long clearTime) { public ListenableFuture<Boolean> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTime) {
return getAndUpdate(alarmId, new Function<Alarm, Boolean>() { return getAndUpdate(tenantId, alarmId, new Function<Alarm, Boolean>() {
@Nullable @Nullable
@Override @Override
public Boolean apply(@Nullable Alarm alarm) { public Boolean apply(@Nullable Alarm alarm) {
@ -208,7 +208,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (details != null) { if (details != null) {
alarm.setDetails(details); alarm.setDetails(details);
} }
alarmDao.save(alarm); alarmDao.save(alarm.getTenantId(), alarm);
updateRelations(alarm, oldStatus, newStatus); updateRelations(alarm, oldStatus, newStatus);
return true; return true;
} }
@ -217,21 +217,21 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
@Override @Override
public ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId) { public ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId) {
log.trace("Executing findAlarmById [{}]", alarmId); log.trace("Executing findAlarmById [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId);
return alarmDao.findAlarmByIdAsync(alarmId.getId()); return alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId());
} }
@Override @Override
public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) { public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId) {
log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId); log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId);
return Futures.transformAsync(alarmDao.findAlarmByIdAsync(alarmId.getId()), return Futures.transformAsync(alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()),
a -> { a -> {
AlarmInfo alarmInfo = new AlarmInfo(a); AlarmInfo alarmInfo = new AlarmInfo(a);
return Futures.transform( return Futures.transform(
entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), originatorName -> { entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {
alarmInfo.setOriginatorName(originatorName); alarmInfo.setOriginatorName(originatorName);
return alarmInfo; return alarmInfo;
} }
@ -240,14 +240,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
@Override @Override
public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) { public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) {
ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query); ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(tenantId, query);
if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
alarms = Futures.transformAsync(alarms, input -> { alarms = Futures.transformAsync(alarms, input -> {
List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size()); List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
for (AlarmInfo alarmInfo : input) { for (AlarmInfo alarmInfo : input) {
alarmFutures.add(Futures.transform( alarmFutures.add(Futures.transform(
entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), originatorName -> { entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {
if (originatorName == null) { if (originatorName == null) {
originatorName = "Deleted"; originatorName = "Deleted";
} }
@ -269,7 +269,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
@Override @Override
public AlarmSeverity findHighestAlarmSeverity(EntityId entityId, AlarmSearchStatus alarmSearchStatus, public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
AlarmStatus alarmStatus) { AlarmStatus alarmStatus) {
TimePageLink nextPageLink = new TimePageLink(100); TimePageLink nextPageLink = new TimePageLink(100);
boolean hasNext = true; boolean hasNext = true;
@ -279,7 +279,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
query = new AlarmQuery(entityId, nextPageLink, alarmSearchStatus, alarmStatus, false); query = new AlarmQuery(entityId, nextPageLink, alarmSearchStatus, alarmStatus, false);
List<AlarmInfo> alarms; List<AlarmInfo> alarms;
try { try {
alarms = alarmDao.findAlarms(query).get(); alarms = alarmDao.findAlarms(tenantId, query).get();
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
log.warn("Failed to find highest alarm severity. EntityId: [{}], AlarmSearchStatus: [{}], AlarmStatus: [{}]", log.warn("Failed to find highest alarm severity. EntityId: [{}], AlarmSearchStatus: [{}], AlarmStatus: [{}]",
entityId, alarmSearchStatus, alarmStatus); entityId, alarmSearchStatus, alarmStatus);
@ -312,14 +312,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
} }
private void deleteRelation(EntityRelation alarmRelation) throws ExecutionException, InterruptedException { private void deleteRelation(TenantId tenantId, EntityRelation alarmRelation) throws ExecutionException, InterruptedException {
log.debug("Deleting Alarm relation: {}", alarmRelation); log.debug("Deleting Alarm relation: {}", alarmRelation);
relationService.deleteRelationAsync(alarmRelation).get(); relationService.deleteRelationAsync(tenantId, alarmRelation).get();
} }
private void createRelation(EntityRelation alarmRelation) throws ExecutionException, InterruptedException { private void createRelation(TenantId tenantId, EntityRelation alarmRelation) throws ExecutionException, InterruptedException {
log.debug("Creating Alarm relation: {}", alarmRelation); log.debug("Creating Alarm relation: {}", alarmRelation);
relationService.saveRelationAsync(alarmRelation).get(); relationService.saveRelationAsync(tenantId, alarmRelation).get();
} }
private Alarm merge(Alarm existing, Alarm alarm) { private Alarm merge(Alarm existing, Alarm alarm) {
@ -344,10 +344,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
private void updateRelations(Alarm alarm, AlarmStatus oldStatus, AlarmStatus newStatus) { private void updateRelations(Alarm alarm, AlarmStatus oldStatus, AlarmStatus newStatus) {
try { try {
List<EntityRelation> relations = relationService.findByToAsync(alarm.getId(), RelationTypeGroup.ALARM).get(); List<EntityRelation> relations = relationService.findByToAsync(alarm.getTenantId(), alarm.getId(), RelationTypeGroup.ALARM).get();
Set<EntityId> parents = relations.stream().map(EntityRelation::getFrom).collect(Collectors.toSet()); Set<EntityId> parents = relations.stream().map(EntityRelation::getFrom).collect(Collectors.toSet());
for (EntityId parentId : parents) { for (EntityId parentId : parents) {
updateAlarmRelation(parentId, alarm.getId(), oldStatus, newStatus); updateAlarmRelation(alarm.getTenantId(), parentId, alarm.getId(), oldStatus, newStatus);
} }
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus); log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus);
@ -355,39 +355,39 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
} }
private void createAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) { private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) {
try { try {
if (createAnyRelation) { if (createAnyRelation) {
createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM)); createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM));
} }
createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM)); createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM)); createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM)); createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create relation. Status: [{}]", alarmId, status); log.warn("[{}] Failed to create relation. Status: [{}]", alarmId, status);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
private void deleteAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status) { private void deleteAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status) {
try { try {
deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM)); deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM)); deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM)); deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete relation. Status: [{}]", alarmId, status); log.warn("[{}] Failed to delete relation. Status: [{}]", alarmId, status);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
private void updateAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) { private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) {
deleteAlarmRelation(entityId, alarmId, oldStatus); deleteAlarmRelation(tenantId, entityId, alarmId, oldStatus);
createAlarmRelation(entityId, alarmId, newStatus, false); createAlarmRelation(tenantId, entityId, alarmId, newStatus, false);
} }
private <T> ListenableFuture<T> getAndUpdate(AlarmId alarmId, Function<Alarm, T> function) { private <T> ListenableFuture<T> getAndUpdate(TenantId tenantId, AlarmId alarmId, Function<Alarm, T> function) {
validateId(alarmId, "Alarm id should be specified!"); validateId(alarmId, "Alarm id should be specified!");
ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId()); ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId());
return Futures.transform(entity, function, readResultsProcessingExecutor); return Futures.transform(entity, function, readResultsProcessingExecutor);
} }
@ -395,7 +395,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
new DataValidator<Alarm>() { new DataValidator<Alarm>() {
@Override @Override
protected void validateDataImpl(Alarm alarm) { protected void validateDataImpl(TenantId tenantId, Alarm alarm) {
if (StringUtils.isEmpty(alarm.getType())) { if (StringUtils.isEmpty(alarm.getType())) {
throw new DataValidationException("Alarm type should be specified!"); throw new DataValidationException("Alarm type should be specified!");
} }
@ -411,7 +411,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm.getTenantId() == null) { if (alarm.getTenantId() == null) {
throw new DataValidationException("Alarm should be assigned to tenant!"); throw new DataValidationException("Alarm should be assigned to tenant!");
} else { } else {
Tenant tenant = tenantDao.findById(alarm.getTenantId().getId()); Tenant tenant = tenantDao.findById(alarm.getTenantId(), alarm.getTenantId().getId());
if (tenant == null) { if (tenant == null) {
throw new DataValidationException("Alarm is referencing to non-existent tenant!"); throw new DataValidationException("Alarm is referencing to non-existent tenant!");
} }

16
dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java

@ -73,9 +73,9 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
} }
@Override @Override
public Alarm save(Alarm alarm) { public Alarm save(TenantId tenantId, Alarm alarm) {
log.debug("Save asset [{}] ", alarm); log.debug("Save asset [{}] ", alarm);
return super.save(alarm); return super.save(tenantId, alarm);
} }
@Override @Override
@ -88,11 +88,11 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
query.and(eq(ALARM_TYPE_PROPERTY, type)); query.and(eq(ALARM_TYPE_PROPERTY, type));
query.limit(1); query.limit(1);
query.orderBy(QueryBuilder.asc(ModelConstants.ALARM_TYPE_PROPERTY), QueryBuilder.desc(ModelConstants.ID_PROPERTY)); query.orderBy(QueryBuilder.asc(ModelConstants.ALARM_TYPE_PROPERTY), QueryBuilder.desc(ModelConstants.ID_PROPERTY));
return findOneByStatementAsync(query); return findOneByStatementAsync(tenantId, query);
} }
@Override @Override
public ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query) { public ListenableFuture<List<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) {
log.trace("Try to find alarms by entity [{}], searchStatus [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getSearchStatus(), query.getStatus(), query.getPageLink()); log.trace("Try to find alarms by entity [{}], searchStatus [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getSearchStatus(), query.getStatus(), query.getPageLink());
EntityId affectedEntity = query.getAffectedEntityId(); EntityId affectedEntity = query.getAffectedEntityId();
String searchStatusName; String searchStatusName;
@ -104,12 +104,12 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
searchStatusName = query.getStatus().name(); searchStatusName = query.getStatus().name();
} }
String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName; String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName;
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(tenantId, affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink());
return Futures.transformAsync(relations, input -> { return Futures.transformAsync(relations, input -> {
List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size()); List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) { for (EntityRelation relation : input) {
alarmFutures.add(Futures.transform( alarmFutures.add(Futures.transform(
findAlarmByIdAsync(relation.getTo().getId()), findAlarmByIdAsync(tenantId, relation.getTo().getId()),
AlarmInfo::new)); AlarmInfo::new));
} }
return Futures.successfulAsList(alarmFutures); return Futures.successfulAsList(alarmFutures);
@ -117,11 +117,11 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
} }
@Override @Override
public ListenableFuture<Alarm> findAlarmByIdAsync(UUID key) { public ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key) {
log.debug("Get alarm by id {}", key); log.debug("Get alarm by id {}", key);
Select.Where query = select().from(ALARM_BY_ID_VIEW_NAME).where(eq(ModelConstants.ID_PROPERTY, key)); Select.Where query = select().from(ALARM_BY_ID_VIEW_NAME).where(eq(ModelConstants.ID_PROPERTY, key));
query.limit(1); query.limit(1);
log.trace("Execute query {}", query); log.trace("Execute query {}", query);
return findOneByStatementAsync(query); return findOneByStatementAsync(tenantId, query);
} }
} }

3
dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.asset;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
@ -37,7 +38,7 @@ public interface AssetDao extends Dao<Asset> {
* @param asset the asset object * @param asset the asset object
* @return saved asset object * @return saved asset object
*/ */
Asset save(Asset asset); Asset save(TenantId tenantId, Asset asset);
/** /**
* Find assets by tenantId and page link. * Find assets by tenantId and page link.

12
dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java

@ -30,19 +30,19 @@ import java.util.Optional;
public interface AssetService { public interface AssetService {
Asset findAssetById(AssetId assetId); Asset findAssetById(TenantId tenantId, AssetId assetId);
ListenableFuture<Asset> findAssetByIdAsync(AssetId assetId); ListenableFuture<Asset> findAssetByIdAsync(TenantId tenantId, AssetId assetId);
Asset findAssetByTenantIdAndName(TenantId tenantId, String name); Asset findAssetByTenantIdAndName(TenantId tenantId, String name);
Asset saveAsset(Asset asset); Asset saveAsset(Asset asset);
Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId); Asset assignAssetToCustomer(TenantId tenantId, AssetId assetId, CustomerId customerId);
Asset unassignAssetFromCustomer(AssetId assetId); Asset unassignAssetFromCustomer(TenantId tenantId, AssetId assetId);
void deleteAsset(AssetId assetId); void deleteAsset(TenantId tenantId, AssetId assetId);
TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink); TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink);
@ -60,7 +60,7 @@ public interface AssetService {
void unassignCustomerAssets(TenantId tenantId, CustomerId customerId); void unassignCustomerAssets(TenantId tenantId, CustomerId customerId);
ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query); ListenableFuture<List<Asset>> findAssetsByQuery(TenantId tenantId, AssetSearchQuery query);
ListenableFuture<List<EntitySubtype>> findAssetTypesByTenantId(TenantId tenantId); ListenableFuture<List<EntitySubtype>> findAssetTypesByTenantId(TenantId tenantId);
} }

88
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -86,17 +86,17 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
private CacheManager cacheManager; private CacheManager cacheManager;
@Override @Override
public Asset findAssetById(AssetId assetId) { public Asset findAssetById(TenantId tenantId, AssetId assetId) {
log.trace("Executing findAssetById [{}]", assetId); log.trace("Executing findAssetById [{}]", assetId);
validateId(assetId, INCORRECT_ASSET_ID + assetId); validateId(assetId, INCORRECT_ASSET_ID + assetId);
return assetDao.findById(assetId.getId()); return assetDao.findById(tenantId, assetId.getId());
} }
@Override @Override
public ListenableFuture<Asset> findAssetByIdAsync(AssetId assetId) { public ListenableFuture<Asset> findAssetByIdAsync(TenantId tenantId, AssetId assetId) {
log.trace("Executing findAssetById [{}]", assetId); log.trace("Executing findAssetById [{}]", assetId);
validateId(assetId, INCORRECT_ASSET_ID + assetId); validateId(assetId, INCORRECT_ASSET_ID + assetId);
return assetDao.findByIdAsync(assetId.getId()); return assetDao.findByIdAsync(tenantId, assetId.getId());
} }
@Cacheable(cacheNames = ASSET_CACHE, key = "{#tenantId, #name}") @Cacheable(cacheNames = ASSET_CACHE, key = "{#tenantId, #name}")
@ -112,31 +112,31 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
@Override @Override
public Asset saveAsset(Asset asset) { public Asset saveAsset(Asset asset) {
log.trace("Executing saveAsset [{}]", asset); log.trace("Executing saveAsset [{}]", asset);
assetValidator.validate(asset); assetValidator.validate(asset, Asset::getTenantId);
return assetDao.save(asset); return assetDao.save(asset.getTenantId(), asset);
} }
@Override @Override
public Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId) { public Asset assignAssetToCustomer(TenantId tenantId, AssetId assetId, CustomerId customerId) {
Asset asset = findAssetById(assetId); Asset asset = findAssetById(tenantId, assetId);
asset.setCustomerId(customerId); asset.setCustomerId(customerId);
return saveAsset(asset); return saveAsset(asset);
} }
@Override @Override
public Asset unassignAssetFromCustomer(AssetId assetId) { public Asset unassignAssetFromCustomer(TenantId tenantId, AssetId assetId) {
Asset asset = findAssetById(assetId); Asset asset = findAssetById(tenantId, assetId);
asset.setCustomerId(null); asset.setCustomerId(null);
return saveAsset(asset); return saveAsset(asset);
} }
@Override @Override
public void deleteAsset(AssetId assetId) { public void deleteAsset(TenantId tenantId, AssetId assetId) {
log.trace("Executing deleteAsset [{}]", assetId); log.trace("Executing deleteAsset [{}]", assetId);
validateId(assetId, INCORRECT_ASSET_ID + assetId); validateId(assetId, INCORRECT_ASSET_ID + assetId);
deleteEntityRelations(assetId); deleteEntityRelations(tenantId, assetId);
Asset asset = assetDao.findById(assetId.getId()); Asset asset = assetDao.findById(tenantId, assetId.getId());
try { try {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get(); List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get();
if (entityViews != null && !entityViews.isEmpty()) { if (entityViews != null && !entityViews.isEmpty()) {
@ -153,7 +153,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
Cache cache = cacheManager.getCache(ASSET_CACHE); Cache cache = cacheManager.getCache(ASSET_CACHE);
cache.evict(list); cache.evict(list);
assetDao.removeById(assetId.getId()); assetDao.removeById(tenantId, assetId.getId());
} }
@Override @Override
@ -187,7 +187,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
public void deleteAssetsByTenantId(TenantId tenantId) { public void deleteAssetsByTenantId(TenantId tenantId) {
log.trace("Executing deleteAssetsByTenantId, tenantId [{}]", tenantId); log.trace("Executing deleteAssetsByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantAssetsRemover.removeEntities(tenantId); tenantAssetsRemover.removeEntities(tenantId, tenantId);
} }
@Override @Override
@ -225,24 +225,24 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
log.trace("Executing unassignCustomerAssets, tenantId [{}], customerId [{}]", tenantId, customerId); log.trace("Executing unassignCustomerAssets, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
new CustomerAssetsUnassigner(tenantId).removeEntities(customerId); customerAssetsUnasigner.removeEntities(tenantId, customerId);
} }
@Override @Override
public ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query) { public ListenableFuture<List<Asset>> findAssetsByQuery(TenantId tenantId, AssetSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery()); ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery());
ListenableFuture<List<Asset>> assets = Futures.transformAsync(relations, r -> { ListenableFuture<List<Asset>> assets = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection(); EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<Asset>> futures = new ArrayList<>(); List<ListenableFuture<Asset>> futures = new ArrayList<>();
for (EntityRelation relation : r) { for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom(); EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
if (entityId.getEntityType() == EntityType.ASSET) { if (entityId.getEntityType() == EntityType.ASSET) {
futures.add(findAssetByIdAsync(new AssetId(entityId.getId()))); futures.add(findAssetByIdAsync(tenantId, new AssetId(entityId.getId())));
} }
} }
return Futures.successfulAsList(futures); return Futures.successfulAsList(futures);
}); });
assets = Futures.transform(assets, (Function<List<Asset>, List<Asset>>)assetList -> assets = Futures.transform(assets, assetList ->
assetList == null ? Collections.emptyList() : assetList.stream().filter(asset -> query.getAssetTypes().contains(asset.getType())).collect(Collectors.toList()) assetList == null ? Collections.emptyList() : assetList.stream().filter(asset -> query.getAssetTypes().contains(asset.getType())).collect(Collectors.toList())
); );
return assets; return assets;
@ -254,7 +254,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
ListenableFuture<List<EntitySubtype>> tenantAssetTypes = assetDao.findTenantAssetTypesAsync(tenantId.getId()); ListenableFuture<List<EntitySubtype>> tenantAssetTypes = assetDao.findTenantAssetTypesAsync(tenantId.getId());
return Futures.transform(tenantAssetTypes, return Futures.transform(tenantAssetTypes,
(Function<List<EntitySubtype>, List<EntitySubtype>>) assetTypes -> { assetTypes -> {
assetTypes.sort(Comparator.comparing(EntitySubtype::getType)); assetTypes.sort(Comparator.comparing(EntitySubtype::getType));
return assetTypes; return assetTypes;
}); });
@ -264,7 +264,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
new DataValidator<Asset>() { new DataValidator<Asset>() {
@Override @Override
protected void validateCreate(Asset asset) { protected void validateCreate(TenantId tenantId, Asset asset) {
assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent( assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
d -> { d -> {
throw new DataValidationException("Asset with such name already exists!"); throw new DataValidationException("Asset with such name already exists!");
@ -273,7 +273,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
} }
@Override @Override
protected void validateUpdate(Asset asset) { protected void validateUpdate(TenantId tenantId, Asset asset) {
assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent( assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
d -> { d -> {
if (!d.getId().equals(asset.getId())) { if (!d.getId().equals(asset.getId())) {
@ -284,7 +284,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
} }
@Override @Override
protected void validateDataImpl(Asset asset) { protected void validateDataImpl(TenantId tenantId, Asset asset) {
if (StringUtils.isEmpty(asset.getType())) { if (StringUtils.isEmpty(asset.getType())) {
throw new DataValidationException("Asset type should be specified!"); throw new DataValidationException("Asset type should be specified!");
} }
@ -294,7 +294,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
if (asset.getTenantId() == null) { if (asset.getTenantId() == null) {
throw new DataValidationException("Asset should be assigned to tenant!"); throw new DataValidationException("Asset should be assigned to tenant!");
} else { } else {
Tenant tenant = tenantDao.findById(asset.getTenantId().getId()); Tenant tenant = tenantDao.findById(tenantId, asset.getTenantId().getId());
if (tenant == null) { if (tenant == null) {
throw new DataValidationException("Asset is referencing to non-existent tenant!"); throw new DataValidationException("Asset is referencing to non-existent tenant!");
} }
@ -302,7 +302,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
if (asset.getCustomerId() == null) { if (asset.getCustomerId() == null) {
asset.setCustomerId(new CustomerId(NULL_UUID)); asset.setCustomerId(new CustomerId(NULL_UUID));
} else if (!asset.getCustomerId().getId().equals(NULL_UUID)) { } else if (!asset.getCustomerId().getId().equals(NULL_UUID)) {
Customer customer = customerDao.findById(asset.getCustomerId().getId()); Customer customer = customerDao.findById(tenantId, asset.getCustomerId().getId());
if (customer == null) { if (customer == null) {
throw new DataValidationException("Can't assign asset to non-existent customer!"); throw new DataValidationException("Can't assign asset to non-existent customer!");
} }
@ -314,35 +314,29 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
}; };
private PaginatedRemover<TenantId, Asset> tenantAssetsRemover = private PaginatedRemover<TenantId, Asset> tenantAssetsRemover =
new PaginatedRemover<TenantId, Asset>() { new PaginatedRemover<TenantId, Asset>() {
@Override @Override
protected List<Asset> findEntities(TenantId id, TextPageLink pageLink) { protected List<Asset> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
return assetDao.findAssetsByTenantId(id.getId(), pageLink); return assetDao.findAssetsByTenantId(id.getId(), pageLink);
} }
@Override
protected void removeEntity(Asset entity) {
deleteAsset(new AssetId(entity.getId().getId()));
}
};
class CustomerAssetsUnassigner extends PaginatedRemover<CustomerId, Asset> { @Override
protected void removeEntity(TenantId tenantId, Asset entity) {
deleteAsset(tenantId, new AssetId(entity.getId().getId()));
}
};
private TenantId tenantId; private PaginatedRemover<CustomerId, Asset> customerAssetsUnasigner = new PaginatedRemover<CustomerId, Asset>() {
CustomerAssetsUnassigner(TenantId tenantId) {
this.tenantId = tenantId;
}
@Override @Override
protected List<Asset> findEntities(CustomerId id, TextPageLink pageLink) { protected List<Asset> findEntities(TenantId tenantId, CustomerId id, TextPageLink pageLink) {
return assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink); return assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
} }
@Override @Override
protected void removeEntity(Asset entity) { protected void removeEntity(TenantId tenantId, Asset entity) {
unassignAssetFromCustomer(new AssetId(entity.getId().getId())); unassignAssetFromCustomer(tenantId, new AssetId(entity.getId().getId()));
} }
} };
} }

23
dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java

@ -28,6 +28,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.EntitySubtypeEntity; import org.thingsboard.server.dao.model.EntitySubtypeEntity;
@ -77,19 +78,19 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
} }
@Override @Override
public Asset save(Asset domain) { public Asset save(TenantId tenantId, Asset domain) {
Asset savedAsset = super.save(domain); Asset savedAsset = super.save(tenantId, domain);
EntitySubtype entitySubtype = new EntitySubtype(savedAsset.getTenantId(), EntityType.ASSET, savedAsset.getType()); EntitySubtype entitySubtype = new EntitySubtype(savedAsset.getTenantId(), EntityType.ASSET, savedAsset.getType());
EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype); EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype);
Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity); Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity);
executeWrite(saveStatement); executeWrite(tenantId, saveStatement);
return savedAsset; return savedAsset;
} }
@Override @Override
public List<Asset> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink) { public List<Asset> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find assets by tenantId [{}] and pageLink [{}]", tenantId, pageLink); log.debug("Try to find assets by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<AssetEntity> assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Collections.singletonList(eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink); Collections.singletonList(eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found assets [{}] by tenantId [{}] and pageLink [{}]", assetEntities, tenantId, pageLink); log.trace("Found assets [{}] by tenantId [{}] and pageLink [{}]", assetEntities, tenantId, pageLink);
@ -99,7 +100,7 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
@Override @Override
public List<Asset> findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) { public List<Asset> findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
log.debug("Try to find assets by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink); log.debug("Try to find assets by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<AssetEntity> assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ASSET_TYPE_PROPERTY, type), Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink); eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found assets [{}] by tenantId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, type, pageLink); log.trace("Found assets [{}] by tenantId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, type, pageLink);
@ -112,13 +113,13 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
Select.Where query = select.where(); Select.Where query = select.where();
query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId)); query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
query.and(in(ID_PROPERTY, assetIds)); query.and(in(ID_PROPERTY, assetIds));
return findListByStatementAsync(query); return findListByStatementAsync(new TenantId(tenantId), query);
} }
@Override @Override
public List<Asset> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { public List<Asset> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
log.debug("Try to find assets by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); log.debug("Try to find assets by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<AssetEntity> assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId), Arrays.asList(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
eq(ASSET_TENANT_ID_PROPERTY, tenantId)), eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
pageLink); pageLink);
@ -130,7 +131,7 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
@Override @Override
public List<Asset> findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { public List<Asset> findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) {
log.debug("Try to find assets by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink); log.debug("Try to find assets by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<AssetEntity> assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ASSET_TYPE_PROPERTY, type), Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
eq(ASSET_CUSTOMER_ID_PROPERTY, customerId), eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
eq(ASSET_TENANT_ID_PROPERTY, tenantId)), eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
@ -148,7 +149,7 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId)); query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
query.and(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId)); query.and(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId));
query.and(in(ID_PROPERTY, assetIds)); query.and(in(ID_PROPERTY, assetIds));
return findListByStatementAsync(query); return findListByStatementAsync(new TenantId(tenantId), query);
} }
@Override @Override
@ -157,7 +158,7 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
Select.Where query = select.where(); Select.Where query = select.where();
query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId)); query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
query.and(eq(ASSET_NAME_PROPERTY, assetName)); query.and(eq(ASSET_NAME_PROPERTY, assetName));
AssetEntity assetEntity = (AssetEntity) findOneByStatement(query); AssetEntity assetEntity = (AssetEntity) findOneByStatement(new TenantId(tenantId), query);
return Optional.ofNullable(DaoUtil.getData(assetEntity)); return Optional.ofNullable(DaoUtil.getData(assetEntity));
} }
@ -168,7 +169,7 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId)); query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId));
query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.ASSET)); query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.ASSET));
query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(query); ResultSetFuture resultSetFuture = executeAsyncRead(new TenantId(tenantId), query);
return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() { return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() {
@Nullable @Nullable
@Override @Override

11
dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.attributes;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import java.util.Collection; import java.util.Collection;
@ -28,13 +29,13 @@ import java.util.Optional;
*/ */
public interface AttributesDao { public interface AttributesDao {
ListenableFuture<Optional<AttributeKvEntry>> find(EntityId entityId, String attributeType, String attributeKey); ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String attributeType, String attributeKey);
ListenableFuture<List<AttributeKvEntry>> find(EntityId entityId, String attributeType, Collection<String> attributeKey); ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String attributeType, Collection<String> attributeKey);
ListenableFuture<List<AttributeKvEntry>> findAll(EntityId entityId, String attributeType); ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String attributeType);
ListenableFuture<Void> save(EntityId entityId, String attributeType, AttributeKvEntry attribute); ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute);
ListenableFuture<List<Void>> removeAll(EntityId entityId, String attributeType, List<String> keys); ListenableFuture<List<Void>> removeAll(TenantId tenantId, EntityId entityId, String attributeType, List<String> keys);
} }

11
dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.attributes;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import java.util.Collection; import java.util.Collection;
@ -28,13 +29,13 @@ import java.util.Optional;
*/ */
public interface AttributesService { public interface AttributesService {
ListenableFuture<Optional<AttributeKvEntry>> find(EntityId entityId, String scope, String attributeKey); ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey);
ListenableFuture<List<AttributeKvEntry>> find(EntityId entityId, String scope, Collection<String> attributeKeys); ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, Collection<String> attributeKeys);
ListenableFuture<List<AttributeKvEntry>> findAll(EntityId entityId, String scope); ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String scope);
ListenableFuture<List<Void>> save(EntityId entityId, String scope, List<AttributeKvEntry> attributes); ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes);
ListenableFuture<List<Void>> removeAll(EntityId entityId, String scope, List<String> attributeKeys); ListenableFuture<List<Void>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> attributeKeys);
} }

21
dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java

@ -21,6 +21,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.service.Validator;
@ -39,40 +40,40 @@ public class BaseAttributesService implements AttributesService {
private AttributesDao attributesDao; private AttributesDao attributesDao;
@Override @Override
public ListenableFuture<Optional<AttributeKvEntry>> find(EntityId entityId, String scope, String attributeKey) { public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey) {
validate(entityId, scope); validate(entityId, scope);
Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey);
return attributesDao.find(entityId, scope, attributeKey); return attributesDao.find(tenantId, entityId, scope, attributeKey);
} }
@Override @Override
public ListenableFuture<List<AttributeKvEntry>> find(EntityId entityId, String scope, Collection<String> attributeKeys) { public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, Collection<String> attributeKeys) {
validate(entityId, scope); validate(entityId, scope);
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey));
return attributesDao.find(entityId, scope, attributeKeys); return attributesDao.find(tenantId, entityId, scope, attributeKeys);
} }
@Override @Override
public ListenableFuture<List<AttributeKvEntry>> findAll(EntityId entityId, String scope) { public ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String scope) {
validate(entityId, scope); validate(entityId, scope);
return attributesDao.findAll(entityId, scope); return attributesDao.findAll(tenantId, entityId, scope);
} }
@Override @Override
public ListenableFuture<List<Void>> save(EntityId entityId, String scope, List<AttributeKvEntry> attributes) { public ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) {
validate(entityId, scope); validate(entityId, scope);
attributes.forEach(attribute -> validate(attribute)); attributes.forEach(attribute -> validate(attribute));
List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(attributes.size()); List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(attributes.size());
for (AttributeKvEntry attribute : attributes) { for (AttributeKvEntry attribute : attributes) {
futures.add(attributesDao.save(entityId, scope, attribute)); futures.add(attributesDao.save(tenantId, entityId, scope, attribute));
} }
return Futures.allAsList(futures); return Futures.allAsList(futures);
} }
@Override @Override
public ListenableFuture<List<Void>> removeAll(EntityId entityId, String scope, List<String> keys) { public ListenableFuture<List<Void>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> keys) {
validate(entityId, scope); validate(entityId, scope);
return attributesDao.removeAll(entityId, scope, keys); return attributesDao.removeAll(tenantId, entityId, scope, keys);
} }
private static void validate(EntityId id, String scope) { private static void validate(EntityId id, String scope) {

25
dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java

@ -28,6 +28,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
@ -73,22 +74,22 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem
} }
@Override @Override
public ListenableFuture<Optional<AttributeKvEntry>> find(EntityId entityId, String attributeType, String attributeKey) { public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String attributeType, String attributeKey) {
Select.Where select = select().from(ATTRIBUTES_KV_CF) Select.Where select = select().from(ATTRIBUTES_KV_CF)
.where(eq(ENTITY_TYPE_COLUMN, entityId.getEntityType())) .where(eq(ENTITY_TYPE_COLUMN, entityId.getEntityType()))
.and(eq(ENTITY_ID_COLUMN, entityId.getId())) .and(eq(ENTITY_ID_COLUMN, entityId.getId()))
.and(eq(ATTRIBUTE_TYPE_COLUMN, attributeType)) .and(eq(ATTRIBUTE_TYPE_COLUMN, attributeType))
.and(eq(ATTRIBUTE_KEY_COLUMN, attributeKey)); .and(eq(ATTRIBUTE_KEY_COLUMN, attributeKey));
log.trace("Generated query [{}] for entityId {} and key {}", select, entityId, attributeKey); log.trace("Generated query [{}] for entityId {} and key {}", select, entityId, attributeKey);
return Futures.transform(executeAsyncRead(select), (Function<? super ResultSet, ? extends Optional<AttributeKvEntry>>) input -> return Futures.transform(executeAsyncRead(tenantId, select), (Function<? super ResultSet, ? extends Optional<AttributeKvEntry>>) input ->
Optional.ofNullable(convertResultToAttributesKvEntry(attributeKey, input.one())) Optional.ofNullable(convertResultToAttributesKvEntry(attributeKey, input.one()))
, readResultsProcessingExecutor); , readResultsProcessingExecutor);
} }
@Override @Override
public ListenableFuture<List<AttributeKvEntry>> find(EntityId entityId, String attributeType, Collection<String> attributeKeys) { public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String attributeType, Collection<String> attributeKeys) {
List<ListenableFuture<Optional<AttributeKvEntry>>> entries = new ArrayList<>(); List<ListenableFuture<Optional<AttributeKvEntry>>> entries = new ArrayList<>();
attributeKeys.forEach(attributeKey -> entries.add(find(entityId, attributeType, attributeKey))); attributeKeys.forEach(attributeKey -> entries.add(find(tenantId, entityId, attributeType, attributeKey)));
return Futures.transform(Futures.allAsList(entries), (Function<List<Optional<AttributeKvEntry>>, ? extends List<AttributeKvEntry>>) input -> { return Futures.transform(Futures.allAsList(entries), (Function<List<Optional<AttributeKvEntry>>, ? extends List<AttributeKvEntry>>) input -> {
List<AttributeKvEntry> result = new ArrayList<>(); List<AttributeKvEntry> result = new ArrayList<>();
input.stream().filter(opt -> opt.isPresent()).forEach(opt -> result.add(opt.get())); input.stream().filter(opt -> opt.isPresent()).forEach(opt -> result.add(opt.get()));
@ -98,19 +99,19 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem
@Override @Override
public ListenableFuture<List<AttributeKvEntry>> findAll(EntityId entityId, String attributeType) { public ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String attributeType) {
Select.Where select = select().from(ATTRIBUTES_KV_CF) Select.Where select = select().from(ATTRIBUTES_KV_CF)
.where(eq(ENTITY_TYPE_COLUMN, entityId.getEntityType())) .where(eq(ENTITY_TYPE_COLUMN, entityId.getEntityType()))
.and(eq(ENTITY_ID_COLUMN, entityId.getId())) .and(eq(ENTITY_ID_COLUMN, entityId.getId()))
.and(eq(ATTRIBUTE_TYPE_COLUMN, attributeType)); .and(eq(ATTRIBUTE_TYPE_COLUMN, attributeType));
log.trace("Generated query [{}] for entityId {} and attributeType {}", select, entityId, attributeType); log.trace("Generated query [{}] for entityId {} and attributeType {}", select, entityId, attributeType);
return Futures.transform(executeAsyncRead(select), (Function<? super ResultSet, ? extends List<AttributeKvEntry>>) input -> return Futures.transform(executeAsyncRead(tenantId, select), (Function<? super ResultSet, ? extends List<AttributeKvEntry>>) input ->
convertResultToAttributesKvEntryList(input) convertResultToAttributesKvEntryList(input)
, readResultsProcessingExecutor); , readResultsProcessingExecutor);
} }
@Override @Override
public ListenableFuture<Void> save(EntityId entityId, String attributeType, AttributeKvEntry attribute) { public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute) {
BoundStatement stmt = getSaveStmt().bind(); BoundStatement stmt = getSaveStmt().bind();
stmt.setString(0, entityId.getEntityType().name()); stmt.setString(0, entityId.getEntityType().name());
stmt.setUUID(1, entityId.getId()); stmt.setUUID(1, entityId.getId());
@ -137,26 +138,26 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem
stmt.setToNull(8); stmt.setToNull(8);
} }
log.trace("Generated save stmt [{}] for entityId {} and attributeType {} and attribute", stmt, entityId, attributeType, attribute); log.trace("Generated save stmt [{}] for entityId {} and attributeType {} and attribute", stmt, entityId, attributeType, attribute);
return getFuture(executeAsyncWrite(stmt), rs -> null); return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
} }
@Override @Override
public ListenableFuture<List<Void>> removeAll(EntityId entityId, String attributeType, List<String> keys) { public ListenableFuture<List<Void>> removeAll(TenantId tenantId, EntityId entityId, String attributeType, List<String> keys) {
List<ListenableFuture<Void>> futures = keys List<ListenableFuture<Void>> futures = keys
.stream() .stream()
.map(key -> delete(entityId, attributeType, key)) .map(key -> delete(tenantId, entityId, attributeType, key))
.collect(Collectors.toList()); .collect(Collectors.toList());
return Futures.allAsList(futures); return Futures.allAsList(futures);
} }
private ListenableFuture<Void> delete(EntityId entityId, String attributeType, String key) { private ListenableFuture<Void> delete(TenantId tenantId, EntityId entityId, String attributeType, String key) {
Statement delete = QueryBuilder.delete().all().from(ModelConstants.ATTRIBUTES_KV_CF) Statement delete = QueryBuilder.delete().all().from(ModelConstants.ATTRIBUTES_KV_CF)
.where(eq(ENTITY_TYPE_COLUMN, entityId.getEntityType())) .where(eq(ENTITY_TYPE_COLUMN, entityId.getEntityType()))
.and(eq(ENTITY_ID_COLUMN, entityId.getId())) .and(eq(ENTITY_ID_COLUMN, entityId.getId()))
.and(eq(ATTRIBUTE_TYPE_COLUMN, attributeType)) .and(eq(ATTRIBUTE_TYPE_COLUMN, attributeType))
.and(eq(ATTRIBUTE_KEY_COLUMN, key)); .and(eq(ATTRIBUTE_KEY_COLUMN, key));
log.debug("Remove request: {}", delete.toString()); log.debug("Remove request: {}", delete.toString());
return getFuture(executeAsyncWrite(delete), rs -> null); return getFuture(executeAsyncWrite(tenantId, delete), rs -> null);
} }
private PreparedStatement getSaveStmt() { private PreparedStatement getSaveStmt() {

6
dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java

@ -128,7 +128,7 @@ public class AuditLogServiceImpl implements AuditLogService {
entityName = entity.getName(); entityName = entity.getName();
} else { } else {
try { try {
entityName = entityService.fetchEntityNameAsync(entityId).get(); entityName = entityService.fetchEntityNameAsync(tenantId, entityId).get();
} catch (Exception ex) {} } catch (Exception ex) {}
} }
if (e != null) { if (e != null) {
@ -315,7 +315,7 @@ public class AuditLogServiceImpl implements AuditLogService {
AuditLog auditLogEntry = createAuditLogEntry(tenantId, entityId, entityName, customerId, userId, userName, AuditLog auditLogEntry = createAuditLogEntry(tenantId, entityId, entityName, customerId, userId, userName,
actionType, actionData, actionStatus, actionFailureDetails); actionType, actionData, actionStatus, actionFailureDetails);
log.trace("Executing logAction [{}]", auditLogEntry); log.trace("Executing logAction [{}]", auditLogEntry);
auditLogValidator.validate(auditLogEntry); auditLogValidator.validate(auditLogEntry, AuditLog::getTenantId);
List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(INSERTS_PER_ENTRY); List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(INSERTS_PER_ENTRY);
futures.add(auditLogDao.savePartitionsByTenantId(auditLogEntry)); futures.add(auditLogDao.savePartitionsByTenantId(auditLogEntry));
futures.add(auditLogDao.saveByTenantId(auditLogEntry)); futures.add(auditLogDao.saveByTenantId(auditLogEntry));
@ -331,7 +331,7 @@ public class AuditLogServiceImpl implements AuditLogService {
private DataValidator<AuditLog> auditLogValidator = private DataValidator<AuditLog> auditLogValidator =
new DataValidator<AuditLog>() { new DataValidator<AuditLog>() {
@Override @Override
protected void validateDataImpl(AuditLog auditLog) { protected void validateDataImpl(TenantId tenantId, AuditLog auditLog) {
if (auditLog.getEntityId() == null) { if (auditLog.getEntityId() == null) {
throw new DataValidationException("Entity Id should be specified!"); throw new DataValidationException("Entity Id should be specified!");
} }

25
dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java

@ -32,6 +32,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.audit.AuditLog;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
@ -142,7 +143,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
long partition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli()); long partition = toPartitionTs(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
BoundStatement stmt = getSaveByTenantStmt().bind(); BoundStatement stmt = getSaveByTenantStmt().bind();
stmt = setSaveStmtVariables(stmt, auditLog, partition); stmt = setSaveStmtVariables(stmt, auditLog, partition);
return getFuture(executeAsyncWrite(stmt), rs -> null); return getFuture(executeAsyncWrite(auditLog.getTenantId(), stmt), rs -> null);
} }
@Override @Override
@ -151,7 +152,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
BoundStatement stmt = getSaveByTenantIdAndEntityIdStmt().bind(); BoundStatement stmt = getSaveByTenantIdAndEntityIdStmt().bind();
stmt = setSaveStmtVariables(stmt, auditLog, -1); stmt = setSaveStmtVariables(stmt, auditLog, -1);
return getFuture(executeAsyncWrite(stmt), rs -> null); return getFuture(executeAsyncWrite(auditLog.getTenantId(), stmt), rs -> null);
} }
@Override @Override
@ -160,7 +161,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
BoundStatement stmt = getSaveByTenantIdAndCustomerIdStmt().bind(); BoundStatement stmt = getSaveByTenantIdAndCustomerIdStmt().bind();
stmt = setSaveStmtVariables(stmt, auditLog, -1); stmt = setSaveStmtVariables(stmt, auditLog, -1);
return getFuture(executeAsyncWrite(stmt), rs -> null); return getFuture(executeAsyncWrite(auditLog.getTenantId(), stmt), rs -> null);
} }
@Override @Override
@ -169,11 +170,11 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
BoundStatement stmt = getSaveByTenantIdAndUserIdStmt().bind(); BoundStatement stmt = getSaveByTenantIdAndUserIdStmt().bind();
stmt = setSaveStmtVariables(stmt, auditLog, -1); stmt = setSaveStmtVariables(stmt, auditLog, -1);
return getFuture(executeAsyncWrite(stmt), rs -> null); return getFuture(executeAsyncWrite(auditLog.getTenantId(), stmt), rs -> null);
} }
private BoundStatement setSaveStmtVariables(BoundStatement stmt, AuditLog auditLog, long partition) { private BoundStatement setSaveStmtVariables(BoundStatement stmt, AuditLog auditLog, long partition) {
stmt.setUUID(0, auditLog.getId().getId()) stmt.setUUID(0, auditLog.getId().getId())
.setUUID(1, auditLog.getTenantId().getId()) .setUUID(1, auditLog.getTenantId().getId())
.setUUID(2, auditLog.getCustomerId().getId()) .setUUID(2, auditLog.getCustomerId().getId())
.setUUID(3, auditLog.getEntityId().getId()) .setUUID(3, auditLog.getEntityId().getId())
@ -200,7 +201,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
BoundStatement stmt = getPartitionInsertStmt().bind(); BoundStatement stmt = getPartitionInsertStmt().bind();
stmt = stmt.setUUID(0, auditLog.getTenantId().getId()) stmt = stmt.setUUID(0, auditLog.getTenantId().getId())
.setLong(1, partition); .setLong(1, partition);
return getFuture(executeAsyncWrite(stmt), rs -> null); return getFuture(executeAsyncWrite(auditLog.getTenantId(), stmt), rs -> null);
} }
private PreparedStatement getSaveByTenantStmt() { private PreparedStatement getSaveByTenantStmt() {
@ -249,7 +250,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
columnsList.add(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY); columnsList.add(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY);
} }
StringJoiner values = new StringJoiner(","); StringJoiner values = new StringJoiner(",");
for (int i=0;i<columnsList.size();i++) { for (int i = 0; i < columnsList.size(); i++) {
values.add("?"); values.add("?");
} }
String statementString = INSERT_INTO + cfName + " (" + String.join(",", columnsList) + ") VALUES (" + values.toString() + ")"; String statementString = INSERT_INTO + cfName + " (" + String.join(",", columnsList) + ") VALUES (" + values.toString() + ")";
@ -274,7 +275,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
@Override @Override
public List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink) { public List<AuditLog> findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, TimePageLink pageLink) {
log.trace("Try to find audit logs by tenant [{}], entity [{}] and pageLink [{}]", tenantId, entityId, pageLink); log.trace("Try to find audit logs by tenant [{}], entity [{}] and pageLink [{}]", tenantId, entityId, pageLink);
List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_ENTITY_ID_CF, List<AuditLogEntity> entities = findPageWithTimeSearch(new TenantId(tenantId), AUDIT_LOG_BY_ENTITY_ID_CF,
Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId), Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId),
eq(ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY, entityId.getEntityType()), eq(ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY, entityId.getEntityType()),
eq(ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY, entityId.getId())), eq(ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY, entityId.getId())),
@ -286,7 +287,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
@Override @Override
public List<AuditLog> findAuditLogsByTenantIdAndCustomerId(UUID tenantId, CustomerId customerId, TimePageLink pageLink) { public List<AuditLog> findAuditLogsByTenantIdAndCustomerId(UUID tenantId, CustomerId customerId, TimePageLink pageLink) {
log.trace("Try to find audit logs by tenant [{}], customer [{}] and pageLink [{}]", tenantId, customerId, pageLink); log.trace("Try to find audit logs by tenant [{}], customer [{}] and pageLink [{}]", tenantId, customerId, pageLink);
List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_CUSTOMER_ID_CF, List<AuditLogEntity> entities = findPageWithTimeSearch(new TenantId(tenantId), AUDIT_LOG_BY_CUSTOMER_ID_CF,
Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId), Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId),
eq(ModelConstants.AUDIT_LOG_CUSTOMER_ID_PROPERTY, customerId.getId())), eq(ModelConstants.AUDIT_LOG_CUSTOMER_ID_PROPERTY, customerId.getId())),
pageLink); pageLink);
@ -297,7 +298,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
@Override @Override
public List<AuditLog> findAuditLogsByTenantIdAndUserId(UUID tenantId, UserId userId, TimePageLink pageLink) { public List<AuditLog> findAuditLogsByTenantIdAndUserId(UUID tenantId, UserId userId, TimePageLink pageLink) {
log.trace("Try to find audit logs by tenant [{}], user [{}] and pageLink [{}]", tenantId, userId, pageLink); log.trace("Try to find audit logs by tenant [{}], user [{}] and pageLink [{}]", tenantId, userId, pageLink);
List<AuditLogEntity> entities = findPageWithTimeSearch(AUDIT_LOG_BY_USER_ID_CF, List<AuditLogEntity> entities = findPageWithTimeSearch(new TenantId(tenantId), AUDIT_LOG_BY_USER_ID_CF,
Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId), Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId),
eq(ModelConstants.AUDIT_LOG_USER_ID_PROPERTY, userId.getId())), eq(ModelConstants.AUDIT_LOG_USER_ID_PROPERTY, userId.getId())),
pageLink); pageLink);
@ -339,7 +340,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
if (cursor.isFull() || !cursor.hasNextPartition()) { if (cursor.isFull() || !cursor.hasNextPartition()) {
return cursor.getData(); return cursor.getData();
} else { } else {
cursor.addData(findPageWithTimeSearch(AUDIT_LOG_BY_TENANT_ID_CF, cursor.addData(findPageWithTimeSearch(new TenantId(cursor.getTenantId()), AUDIT_LOG_BY_TENANT_ID_CF,
Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, cursor.getTenantId()), Arrays.asList(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, cursor.getTenantId()),
eq(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY, cursor.getNextPartition())), eq(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY, cursor.getNextPartition())),
cursor.getPageLink())); cursor.getPageLink()));
@ -352,7 +353,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
.where(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId)); .where(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId));
select.and(QueryBuilder.gte(ModelConstants.PARTITION_COLUMN, minPartition)); select.and(QueryBuilder.gte(ModelConstants.PARTITION_COLUMN, minPartition));
select.and(QueryBuilder.lte(ModelConstants.PARTITION_COLUMN, maxPartition)); select.and(QueryBuilder.lte(ModelConstants.PARTITION_COLUMN, maxPartition));
return executeRead(select); return executeRead(new TenantId(tenantId), select);
} }
} }

6
dao/src/main/java/org/thingsboard/server/dao/cache/PreviousDeviceCredentialsIdKeyGenerator.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.cache; package org.thingsboard.server.dao.cache;
import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.KeyGenerator;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceCredentialsService;
@ -28,9 +29,10 @@ public class PreviousDeviceCredentialsIdKeyGenerator implements KeyGenerator {
@Override @Override
public Object generate(Object o, Method method, Object... objects) { public Object generate(Object o, Method method, Object... objects) {
DeviceCredentialsService deviceCredentialsService = (DeviceCredentialsService) o; DeviceCredentialsService deviceCredentialsService = (DeviceCredentialsService) o;
DeviceCredentials deviceCredentials = (DeviceCredentials) objects[0]; TenantId tenantId = (TenantId) objects[0];
DeviceCredentials deviceCredentials = (DeviceCredentials) objects[1];
if (deviceCredentials.getDeviceId() != null) { if (deviceCredentials.getDeviceId() != null) {
DeviceCredentials oldDeviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceCredentials.getDeviceId()); DeviceCredentials oldDeviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, deviceCredentials.getDeviceId());
if (oldDeviceCredentials != null) { if (oldDeviceCredentials != null) {
return oldDeviceCredentials.getCredentialsId(); return oldDeviceCredentials.getCredentialsId();
} }

34
dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java

@ -25,6 +25,8 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.ComponentDescriptorId; import org.thingsboard.server.common.data.id.ComponentDescriptorId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
@ -49,50 +51,50 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic
private ComponentDescriptorDao componentDescriptorDao; private ComponentDescriptorDao componentDescriptorDao;
@Override @Override
public ComponentDescriptor saveComponent(ComponentDescriptor component) { public ComponentDescriptor saveComponent(TenantId tenantId, ComponentDescriptor component) {
componentValidator.validate(component); componentValidator.validate(component, data -> new TenantId(EntityId.NULL_UUID));
Optional<ComponentDescriptor> result = componentDescriptorDao.saveIfNotExist(component); Optional<ComponentDescriptor> result = componentDescriptorDao.saveIfNotExist(tenantId, component);
if (result.isPresent()) { if (result.isPresent()) {
return result.get(); return result.get();
} else { } else {
return componentDescriptorDao.findByClazz(component.getClazz()); return componentDescriptorDao.findByClazz(tenantId, component.getClazz());
} }
} }
@Override @Override
public ComponentDescriptor findById(ComponentDescriptorId componentId) { public ComponentDescriptor findById(TenantId tenantId, ComponentDescriptorId componentId) {
Validator.validateId(componentId, "Incorrect component id for search request."); Validator.validateId(componentId, "Incorrect component id for search request.");
return componentDescriptorDao.findById(componentId); return componentDescriptorDao.findById(tenantId, componentId);
} }
@Override @Override
public ComponentDescriptor findByClazz(String clazz) { public ComponentDescriptor findByClazz(TenantId tenantId, String clazz) {
Validator.validateString(clazz, "Incorrect clazz for search request."); Validator.validateString(clazz, "Incorrect clazz for search request.");
return componentDescriptorDao.findByClazz(clazz); return componentDescriptorDao.findByClazz(tenantId, clazz);
} }
@Override @Override
public TextPageData<ComponentDescriptor> findByTypeAndPageLink(ComponentType type, TextPageLink pageLink) { public TextPageData<ComponentDescriptor> findByTypeAndPageLink(TenantId tenantId, ComponentType type, TextPageLink pageLink) {
Validator.validatePageLink(pageLink, "Incorrect PageLink object for search plugin components request."); Validator.validatePageLink(pageLink, "Incorrect PageLink object for search plugin components request.");
List<ComponentDescriptor> components = componentDescriptorDao.findByTypeAndPageLink(type, pageLink); List<ComponentDescriptor> components = componentDescriptorDao.findByTypeAndPageLink(tenantId, type, pageLink);
return new TextPageData<>(components, pageLink); return new TextPageData<>(components, pageLink);
} }
@Override @Override
public TextPageData<ComponentDescriptor> findByScopeAndTypeAndPageLink(ComponentScope scope, ComponentType type, TextPageLink pageLink) { public TextPageData<ComponentDescriptor> findByScopeAndTypeAndPageLink(TenantId tenantId, ComponentScope scope, ComponentType type, TextPageLink pageLink) {
Validator.validatePageLink(pageLink, "Incorrect PageLink object for search plugin components request."); Validator.validatePageLink(pageLink, "Incorrect PageLink object for search plugin components request.");
List<ComponentDescriptor> components = componentDescriptorDao.findByScopeAndTypeAndPageLink(scope, type, pageLink); List<ComponentDescriptor> components = componentDescriptorDao.findByScopeAndTypeAndPageLink(tenantId, scope, type, pageLink);
return new TextPageData<>(components, pageLink); return new TextPageData<>(components, pageLink);
} }
@Override @Override
public void deleteByClazz(String clazz) { public void deleteByClazz(TenantId tenantId, String clazz) {
Validator.validateString(clazz, "Incorrect clazz for delete request."); Validator.validateString(clazz, "Incorrect clazz for delete request.");
componentDescriptorDao.deleteByClazz(clazz); componentDescriptorDao.deleteByClazz(tenantId, clazz);
} }
@Override @Override
public boolean validate(ComponentDescriptor component, JsonNode configuration) { public boolean validate(TenantId tenantId, ComponentDescriptor component, JsonNode configuration) {
JsonValidator validator = JsonSchemaFactory.byDefault().getValidator(); JsonValidator validator = JsonSchemaFactory.byDefault().getValidator();
try { try {
if (!component.getConfigurationDescriptor().has("schema")) { if (!component.getConfigurationDescriptor().has("schema")) {
@ -109,7 +111,7 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic
private DataValidator<ComponentDescriptor> componentValidator = private DataValidator<ComponentDescriptor> componentValidator =
new DataValidator<ComponentDescriptor>() { new DataValidator<ComponentDescriptor>() {
@Override @Override
protected void validateDataImpl(ComponentDescriptor plugin) { protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) {
if (plugin.getType() == null) { if (plugin.getType() == null) {
throw new DataValidationException("Component type should be specified!."); throw new DataValidationException("Component type should be specified!.");
} }

37
dao/src/main/java/org/thingsboard/server/dao/component/CassandraBaseComponentDescriptorDao.java

@ -23,6 +23,7 @@ import com.datastax.driver.core.utils.UUIDs;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.ComponentDescriptorId; import org.thingsboard.server.common.data.id.ComponentDescriptorId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope; import org.thingsboard.server.common.data.plugin.ComponentScope;
@ -62,10 +63,10 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
} }
@Override @Override
public Optional<ComponentDescriptor> saveIfNotExist(ComponentDescriptor component) { public Optional<ComponentDescriptor> saveIfNotExist(TenantId tenantId, ComponentDescriptor component) {
ComponentDescriptorEntity entity = new ComponentDescriptorEntity(component); ComponentDescriptorEntity entity = new ComponentDescriptorEntity(component);
log.debug("Save component entity [{}]", entity); log.debug("Save component entity [{}]", entity);
Optional<ComponentDescriptor> result = saveIfNotExist(entity); Optional<ComponentDescriptor> result = saveIfNotExist(tenantId, entity);
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("Saved result: [{}] for component entity [{}]", result.isPresent(), result.orElse(null)); log.trace("Saved result: [{}] for component entity [{}]", result.isPresent(), result.orElse(null));
} else { } else {
@ -75,9 +76,9 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
} }
@Override @Override
public ComponentDescriptor findById(ComponentDescriptorId componentId) { public ComponentDescriptor findById(TenantId tenantId, ComponentDescriptorId componentId) {
log.debug("Search component entity by id [{}]", componentId); log.debug("Search component entity by id [{}]", componentId);
ComponentDescriptor componentDescriptor = super.findById(componentId.getId()); ComponentDescriptor componentDescriptor = super.findById(tenantId, componentId.getId());
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("Search result: [{}] for component entity [{}]", componentDescriptor != null, componentDescriptor); log.trace("Search result: [{}] for component entity [{}]", componentDescriptor != null, componentDescriptor);
} else { } else {
@ -87,11 +88,11 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
} }
@Override @Override
public ComponentDescriptor findByClazz(String clazz) { public ComponentDescriptor findByClazz(TenantId tenantId, String clazz) {
log.debug("Search component entity by clazz [{}]", clazz); log.debug("Search component entity by clazz [{}]", clazz);
Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, clazz)); Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, clazz));
log.trace("Execute query [{}]", query); log.trace("Execute query [{}]", query);
ComponentDescriptorEntity entity = findOneByStatement(query); ComponentDescriptorEntity entity = findOneByStatement(tenantId, query);
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("Search result: [{}] for component entity [{}]", entity != null, entity); log.trace("Search result: [{}] for component entity [{}]", entity != null, entity);
} else { } else {
@ -101,9 +102,9 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
} }
@Override @Override
public List<ComponentDescriptor> findByTypeAndPageLink(ComponentType type, TextPageLink pageLink) { public List<ComponentDescriptor> findByTypeAndPageLink(TenantId tenantId, ComponentType type, TextPageLink pageLink) {
log.debug("Try to find component by type [{}] and pageLink [{}]", type, pageLink); log.debug("Try to find component by type [{}] and pageLink [{}]", type, pageLink);
List<ComponentDescriptorEntity> entities = findPageWithTextSearch(ModelConstants.COMPONENT_DESCRIPTOR_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<ComponentDescriptorEntity> entities = findPageWithTextSearch(tenantId, ModelConstants.COMPONENT_DESCRIPTOR_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ModelConstants.COMPONENT_DESCRIPTOR_TYPE_PROPERTY, type)), pageLink); Arrays.asList(eq(ModelConstants.COMPONENT_DESCRIPTOR_TYPE_PROPERTY, type)), pageLink);
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray())); log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray()));
@ -114,9 +115,9 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
} }
@Override @Override
public List<ComponentDescriptor> findByScopeAndTypeAndPageLink(ComponentScope scope, ComponentType type, TextPageLink pageLink) { public List<ComponentDescriptor> findByScopeAndTypeAndPageLink(TenantId tenantId, ComponentScope scope, ComponentType type, TextPageLink pageLink) {
log.debug("Try to find component by scope [{}] and type [{}] and pageLink [{}]", scope, type, pageLink); log.debug("Try to find component by scope [{}] and type [{}] and pageLink [{}]", scope, type, pageLink);
List<ComponentDescriptorEntity> entities = findPageWithTextSearch(ModelConstants.COMPONENT_DESCRIPTOR_BY_SCOPE_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<ComponentDescriptorEntity> entities = findPageWithTextSearch(tenantId, ModelConstants.COMPONENT_DESCRIPTOR_BY_SCOPE_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ModelConstants.COMPONENT_DESCRIPTOR_TYPE_PROPERTY, type), Arrays.asList(eq(ModelConstants.COMPONENT_DESCRIPTOR_TYPE_PROPERTY, type),
eq(ModelConstants.COMPONENT_DESCRIPTOR_SCOPE_PROPERTY, scope.name())), pageLink); eq(ModelConstants.COMPONENT_DESCRIPTOR_SCOPE_PROPERTY, scope.name())), pageLink);
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
@ -127,34 +128,34 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
return DaoUtil.convertDataList(entities); return DaoUtil.convertDataList(entities);
} }
public boolean removeById(UUID key) { public boolean removeById(TenantId tenantId, UUID key) {
Statement delete = QueryBuilder.delete().all().from(ModelConstants.COMPONENT_DESCRIPTOR_BY_ID).where(eq(ModelConstants.ID_PROPERTY, key)); Statement delete = QueryBuilder.delete().all().from(ModelConstants.COMPONENT_DESCRIPTOR_BY_ID).where(eq(ModelConstants.ID_PROPERTY, key));
log.debug("Remove request: {}", delete.toString()); log.debug("Remove request: {}", delete.toString());
return executeWrite(delete).wasApplied(); return executeWrite(tenantId, delete).wasApplied();
} }
@Override @Override
public void deleteById(ComponentDescriptorId id) { public void deleteById(TenantId tenantId, ComponentDescriptorId id) {
log.debug("Delete plugin meta-data entity by id [{}]", id); log.debug("Delete plugin meta-data entity by id [{}]", id);
boolean result = removeById(id.getId()); boolean result = removeById(tenantId, id.getId());
log.debug("Delete result: [{}]", result); log.debug("Delete result: [{}]", result);
} }
@Override @Override
public void deleteByClazz(String clazz) { public void deleteByClazz(TenantId tenantId, String clazz) {
log.debug("Delete plugin meta-data entity by id [{}]", clazz); log.debug("Delete plugin meta-data entity by id [{}]", clazz);
Statement delete = QueryBuilder.delete().all().from(getColumnFamilyName()).where(eq(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, clazz)); Statement delete = QueryBuilder.delete().all().from(getColumnFamilyName()).where(eq(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, clazz));
log.debug("Remove request: {}", delete.toString()); log.debug("Remove request: {}", delete.toString());
ResultSet resultSet = executeWrite(delete); ResultSet resultSet = executeWrite(tenantId, delete);
log.debug("Delete result: [{}]", resultSet.wasApplied()); log.debug("Delete result: [{}]", resultSet.wasApplied());
} }
private Optional<ComponentDescriptor> saveIfNotExist(ComponentDescriptorEntity entity) { private Optional<ComponentDescriptor> saveIfNotExist(TenantId tenantId, ComponentDescriptorEntity entity) {
if (entity.getId() == null) { if (entity.getId() == null) {
entity.setId(UUIDs.timeBased()); entity.setId(UUIDs.timeBased());
} }
ResultSet rs = executeRead(QueryBuilder.insertInto(getColumnFamilyName()) ResultSet rs = executeRead(tenantId, QueryBuilder.insertInto(getColumnFamilyName())
.value(ModelConstants.ID_PROPERTY, entity.getId()) .value(ModelConstants.ID_PROPERTY, entity.getId())
.value(ModelConstants.COMPONENT_DESCRIPTOR_NAME_PROPERTY, entity.getName()) .value(ModelConstants.COMPONENT_DESCRIPTOR_NAME_PROPERTY, entity.getName())
.value(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, entity.getClazz()) .value(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, entity.getClazz())

15
dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorDao.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.component; package org.thingsboard.server.dao.component;
import org.thingsboard.server.common.data.id.ComponentDescriptorId; import org.thingsboard.server.common.data.id.ComponentDescriptorId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope; import org.thingsboard.server.common.data.plugin.ComponentScope;
@ -30,18 +31,18 @@ import java.util.Optional;
*/ */
public interface ComponentDescriptorDao extends Dao<ComponentDescriptor> { public interface ComponentDescriptorDao extends Dao<ComponentDescriptor> {
Optional<ComponentDescriptor> saveIfNotExist(ComponentDescriptor component); Optional<ComponentDescriptor> saveIfNotExist(TenantId tenantId, ComponentDescriptor component);
ComponentDescriptor findById(ComponentDescriptorId componentId); ComponentDescriptor findById(TenantId tenantId, ComponentDescriptorId componentId);
ComponentDescriptor findByClazz(String clazz); ComponentDescriptor findByClazz(TenantId tenantId, String clazz);
List<ComponentDescriptor> findByTypeAndPageLink(ComponentType type, TextPageLink pageLink); List<ComponentDescriptor> findByTypeAndPageLink(TenantId tenantId, ComponentType type, TextPageLink pageLink);
List<ComponentDescriptor> findByScopeAndTypeAndPageLink(ComponentScope scope, ComponentType type, TextPageLink pageLink); List<ComponentDescriptor> findByScopeAndTypeAndPageLink(TenantId tenantId, ComponentScope scope, ComponentType type, TextPageLink pageLink);
void deleteById(ComponentDescriptorId componentId); void deleteById(TenantId tenantId, ComponentDescriptorId componentId);
void deleteByClazz(String clazz); void deleteByClazz(TenantId tenantId, String clazz);
} }

15
dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.component;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.id.ComponentDescriptorId; import org.thingsboard.server.common.data.id.ComponentDescriptorId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
@ -28,18 +29,18 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
*/ */
public interface ComponentDescriptorService { public interface ComponentDescriptorService {
ComponentDescriptor saveComponent(ComponentDescriptor component); ComponentDescriptor saveComponent(TenantId tenantId, ComponentDescriptor component);
ComponentDescriptor findById(ComponentDescriptorId componentId); ComponentDescriptor findById(TenantId tenantId, ComponentDescriptorId componentId);
ComponentDescriptor findByClazz(String clazz); ComponentDescriptor findByClazz(TenantId tenantId, String clazz);
TextPageData<ComponentDescriptor> findByTypeAndPageLink(ComponentType type, TextPageLink pageLink); TextPageData<ComponentDescriptor> findByTypeAndPageLink(TenantId tenantId, ComponentType type, TextPageLink pageLink);
TextPageData<ComponentDescriptor> findByScopeAndTypeAndPageLink(ComponentScope scope, ComponentType type, TextPageLink pageLink); TextPageData<ComponentDescriptor> findByScopeAndTypeAndPageLink(TenantId tenantId, ComponentScope scope, ComponentType type, TextPageLink pageLink);
boolean validate(ComponentDescriptor component, JsonNode configuration); boolean validate(TenantId tenantId, ComponentDescriptor component, JsonNode configuration);
void deleteByClazz(String clazz); void deleteByClazz(TenantId tenantId, String clazz);
} }

8
dao/src/main/java/org/thingsboard/server/dao/customer/CassandraCustomerDao.java

@ -19,6 +19,7 @@ import com.datastax.driver.core.querybuilder.Select;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
@ -36,6 +37,7 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_BY_TENANT_AND_TITLE_VIEW_NAME; import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_BY_TENANT_AND_TITLE_VIEW_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TITLE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TITLE_PROPERTY;
@Component @Component
@Slf4j @Slf4j
@NoSqlDao @NoSqlDao
@ -54,9 +56,9 @@ public class CassandraCustomerDao extends CassandraAbstractSearchTextDao<Custome
@Override @Override
public List<Customer> findCustomersByTenantId(UUID tenantId, TextPageLink pageLink) { public List<Customer> findCustomersByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find customers by tenantId [{}] and pageLink [{}]", tenantId, pageLink); log.debug("Try to find customers by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<CustomerEntity> customerEntities = findPageWithTextSearch(ModelConstants.CUSTOMER_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<CustomerEntity> customerEntities = findPageWithTextSearch(new TenantId(tenantId), ModelConstants.CUSTOMER_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ModelConstants.CUSTOMER_TENANT_ID_PROPERTY, tenantId)), Arrays.asList(eq(ModelConstants.CUSTOMER_TENANT_ID_PROPERTY, tenantId)),
pageLink); pageLink);
log.trace("Found customers [{}] by tenantId [{}] and pageLink [{}]", customerEntities, tenantId, pageLink); log.trace("Found customers [{}] by tenantId [{}] and pageLink [{}]", customerEntities, tenantId, pageLink);
return DaoUtil.convertDataList(customerEntities); return DaoUtil.convertDataList(customerEntities);
} }
@ -67,7 +69,7 @@ public class CassandraCustomerDao extends CassandraAbstractSearchTextDao<Custome
Select.Where query = select.where(); Select.Where query = select.where();
query.and(eq(CUSTOMER_TENANT_ID_PROPERTY, tenantId)); query.and(eq(CUSTOMER_TENANT_ID_PROPERTY, tenantId));
query.and(eq(CUSTOMER_TITLE_PROPERTY, title)); query.and(eq(CUSTOMER_TITLE_PROPERTY, title));
CustomerEntity customerEntity = findOneByStatement(query); CustomerEntity customerEntity = findOneByStatement(new TenantId(tenantId), query);
Customer customer = DaoUtil.getData(customerEntity); Customer customer = DaoUtil.getData(customerEntity);
return Optional.ofNullable(customer); return Optional.ofNullable(customer);
} }

3
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.customer; package org.thingsboard.server.dao.customer;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
@ -34,7 +35,7 @@ public interface CustomerDao extends Dao<Customer> {
* @param customer the customer object * @param customer the customer object
* @return saved customer object * @return saved customer object
*/ */
Customer save(Customer customer); Customer save(TenantId tenantId, Customer customer);
/** /**
* Find customers by tenant id and page link. * Find customers by tenant id and page link.

6
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java

@ -26,15 +26,15 @@ import java.util.Optional;
public interface CustomerService { public interface CustomerService {
Customer findCustomerById(CustomerId customerId); Customer findCustomerById(TenantId tenantId, CustomerId customerId);
Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title); Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title);
ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId); ListenableFuture<Customer> findCustomerByIdAsync(TenantId tenantId, CustomerId customerId);
Customer saveCustomer(Customer customer); Customer saveCustomer(Customer customer);
void deleteCustomer(CustomerId customerId); void deleteCustomer(TenantId tenantId, CustomerId customerId);
Customer findOrCreatePublicCustomer(TenantId tenantId); Customer findOrCreatePublicCustomer(TenantId tenantId);

42
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java

@ -77,10 +77,10 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
private DashboardService dashboardService; private DashboardService dashboardService;
@Override @Override
public Customer findCustomerById(CustomerId customerId) { public Customer findCustomerById(TenantId tenantId, CustomerId customerId) {
log.trace("Executing findCustomerById [{}]", customerId); log.trace("Executing findCustomerById [{}]", customerId);
Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
return customerDao.findById(customerId.getId()); return customerDao.findById(tenantId, customerId.getId());
} }
@Override @Override
@ -91,36 +91,36 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
} }
@Override @Override
public ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId) { public ListenableFuture<Customer> findCustomerByIdAsync(TenantId tenantId, CustomerId customerId) {
log.trace("Executing findCustomerByIdAsync [{}]", customerId); log.trace("Executing findCustomerByIdAsync [{}]", customerId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
return customerDao.findByIdAsync(customerId.getId()); return customerDao.findByIdAsync(tenantId, customerId.getId());
} }
@Override @Override
public Customer saveCustomer(Customer customer) { public Customer saveCustomer(Customer customer) {
log.trace("Executing saveCustomer [{}]", customer); log.trace("Executing saveCustomer [{}]", customer);
customerValidator.validate(customer); customerValidator.validate(customer, Customer::getTenantId);
Customer savedCustomer = customerDao.save(customer); Customer savedCustomer = customerDao.save(customer.getTenantId(), customer);
dashboardService.updateCustomerDashboards(savedCustomer.getId()); dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId());
return savedCustomer; return savedCustomer;
} }
@Override @Override
public void deleteCustomer(CustomerId customerId) { public void deleteCustomer(TenantId tenantId, CustomerId customerId) {
log.trace("Executing deleteCustomer [{}]", customerId); log.trace("Executing deleteCustomer [{}]", customerId);
Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
Customer customer = findCustomerById(customerId); Customer customer = findCustomerById(tenantId, customerId);
if (customer == null) { if (customer == null) {
throw new IncorrectParameterException("Unable to delete non-existent customer."); throw new IncorrectParameterException("Unable to delete non-existent customer.");
} }
dashboardService.unassignCustomerDashboards(customerId); dashboardService.unassignCustomerDashboards(tenantId, customerId);
entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId); entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId);
assetService.unassignCustomerAssets(customer.getTenantId(), customerId); assetService.unassignCustomerAssets(customer.getTenantId(), customerId);
deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
userService.deleteCustomerUsers(customer.getTenantId(), customerId); userService.deleteCustomerUsers(customer.getTenantId(), customerId);
deleteEntityRelations(customerId); deleteEntityRelations(tenantId, customerId);
customerDao.removeById(customerId.getId()); customerDao.removeById(tenantId, customerId.getId());
} }
@Override @Override
@ -139,7 +139,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
} catch (IOException e) { } catch (IOException e) {
throw new IncorrectParameterException("Unable to create public customer.", e); throw new IncorrectParameterException("Unable to create public customer.", e);
} }
return customerDao.save(publicCustomer); return customerDao.save(tenantId, publicCustomer);
} }
} }
@ -156,14 +156,14 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
public void deleteCustomersByTenantId(TenantId tenantId) { public void deleteCustomersByTenantId(TenantId tenantId) {
log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId); log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
customersByTenantRemover.removeEntities(tenantId); customersByTenantRemover.removeEntities(tenantId, tenantId);
} }
private DataValidator<Customer> customerValidator = private DataValidator<Customer> customerValidator =
new DataValidator<Customer>() { new DataValidator<Customer>() {
@Override @Override
protected void validateCreate(Customer customer) { protected void validateCreate(TenantId tenantId, Customer customer) {
customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent(
c -> { c -> {
throw new DataValidationException("Customer with such title already exists!"); throw new DataValidationException("Customer with such title already exists!");
@ -172,7 +172,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
} }
@Override @Override
protected void validateUpdate(Customer customer) { protected void validateUpdate(TenantId tenantId, Customer customer) {
customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent(
c -> { c -> {
if (!c.getId().equals(customer.getId())) { if (!c.getId().equals(customer.getId())) {
@ -183,7 +183,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
} }
@Override @Override
protected void validateDataImpl(Customer customer) { protected void validateDataImpl(TenantId tenantId, Customer customer) {
if (StringUtils.isEmpty(customer.getTitle())) { if (StringUtils.isEmpty(customer.getTitle())) {
throw new DataValidationException("Customer title should be specified!"); throw new DataValidationException("Customer title should be specified!");
} }
@ -196,7 +196,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
if (customer.getTenantId() == null) { if (customer.getTenantId() == null) {
throw new DataValidationException("Customer should be assigned to tenant!"); throw new DataValidationException("Customer should be assigned to tenant!");
} else { } else {
Tenant tenant = tenantDao.findById(customer.getTenantId().getId()); Tenant tenant = tenantDao.findById(tenantId, customer.getTenantId().getId());
if (tenant == null) { if (tenant == null) {
throw new DataValidationException("Customer is referencing to non-existent tenant!"); throw new DataValidationException("Customer is referencing to non-existent tenant!");
} }
@ -208,13 +208,13 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
new PaginatedRemover<TenantId, Customer>() { new PaginatedRemover<TenantId, Customer>() {
@Override @Override
protected List<Customer> findEntities(TenantId id, TextPageLink pageLink) { protected List<Customer> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
return customerDao.findCustomersByTenantId(id.getId(), pageLink); return customerDao.findCustomersByTenantId(id.getId(), pageLink);
} }
@Override @Override
protected void removeEntity(Customer entity) { protected void removeEntity(TenantId tenantId, Customer entity) {
deleteCustomer(new CustomerId(entity.getUuidId())); deleteCustomer(tenantId, new CustomerId(entity.getUuidId()));
} }
}; };
} }

7
dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java

@ -23,6 +23,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
@ -64,7 +65,7 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da
@Override @Override
public List<DashboardInfo> findDashboardsByTenantId(UUID tenantId, TextPageLink pageLink) { public List<DashboardInfo> findDashboardsByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find dashboards by tenantId [{}] and pageLink [{}]", tenantId, pageLink); log.debug("Try to find dashboards by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<DashboardInfoEntity> dashboardEntities = findPageWithTextSearch(DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<DashboardInfoEntity> dashboardEntities = findPageWithTextSearch(new TenantId(tenantId), DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Collections.singletonList(eq(DASHBOARD_TENANT_ID_PROPERTY, tenantId)), Collections.singletonList(eq(DASHBOARD_TENANT_ID_PROPERTY, tenantId)),
pageLink); pageLink);
@ -76,12 +77,12 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da
public ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink) { public ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink) {
log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink);
return Futures.transformAsync(relations, input -> { return Futures.transformAsync(relations, input -> {
List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size()); List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) { for (EntityRelation relation : input) {
dashboardFutures.add(findByIdAsync(relation.getTo().getId())); dashboardFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
} }
return Futures.successfulAsList(dashboardFutures); return Futures.successfulAsList(dashboardFutures);
}); });

3
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.dashboard; package org.thingsboard.server.dao.dashboard;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
/** /**
@ -29,6 +30,6 @@ public interface DashboardDao extends Dao<Dashboard> {
* @param dashboard the dashboard object * @param dashboard the dashboard object
* @return saved dashboard object * @return saved dashboard object
*/ */
Dashboard save(Dashboard dashboard); Dashboard save(TenantId tenantId, Dashboard dashboard);
} }

18
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java

@ -28,21 +28,21 @@ import org.thingsboard.server.common.data.page.TimePageLink;
public interface DashboardService { public interface DashboardService {
Dashboard findDashboardById(DashboardId dashboardId); Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId);
ListenableFuture<Dashboard> findDashboardByIdAsync(DashboardId dashboardId); ListenableFuture<Dashboard> findDashboardByIdAsync(TenantId tenantId, DashboardId dashboardId);
DashboardInfo findDashboardInfoById(DashboardId dashboardId); DashboardInfo findDashboardInfoById(TenantId tenantId, DashboardId dashboardId);
ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(DashboardId dashboardId); ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(TenantId tenantId, DashboardId dashboardId);
Dashboard saveDashboard(Dashboard dashboard); Dashboard saveDashboard(Dashboard dashboard);
Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId); Dashboard assignDashboardToCustomer(TenantId tenantId, DashboardId dashboardId, CustomerId customerId);
Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId); Dashboard unassignDashboardFromCustomer(TenantId tenantId, DashboardId dashboardId, CustomerId customerId);
void deleteDashboard(DashboardId dashboardId); void deleteDashboard(TenantId tenantId, DashboardId dashboardId);
TextPageData<DashboardInfo> findDashboardsByTenantId(TenantId tenantId, TextPageLink pageLink); TextPageData<DashboardInfo> findDashboardsByTenantId(TenantId tenantId, TextPageLink pageLink);
@ -50,8 +50,8 @@ public interface DashboardService {
ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink); ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink);
void unassignCustomerDashboards(CustomerId customerId); void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId);
void updateCustomerDashboards(CustomerId customerId); void updateCustomerDashboards(TenantId tenantId, CustomerId customerId);
} }

78
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java

@ -69,44 +69,44 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
private CustomerDao customerDao; private CustomerDao customerDao;
@Override @Override
public Dashboard findDashboardById(DashboardId dashboardId) { public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardById [{}]", dashboardId); log.trace("Executing findDashboardById [{}]", dashboardId);
Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
return dashboardDao.findById(dashboardId.getId()); return dashboardDao.findById(tenantId, dashboardId.getId());
} }
@Override @Override
public ListenableFuture<Dashboard> findDashboardByIdAsync(DashboardId dashboardId) { public ListenableFuture<Dashboard> findDashboardByIdAsync(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardByIdAsync [{}]", dashboardId); log.trace("Executing findDashboardByIdAsync [{}]", dashboardId);
validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
return dashboardDao.findByIdAsync(dashboardId.getId()); return dashboardDao.findByIdAsync(tenantId, dashboardId.getId());
} }
@Override @Override
public DashboardInfo findDashboardInfoById(DashboardId dashboardId) { public DashboardInfo findDashboardInfoById(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardInfoById [{}]", dashboardId); log.trace("Executing findDashboardInfoById [{}]", dashboardId);
Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
return dashboardInfoDao.findById(dashboardId.getId()); return dashboardInfoDao.findById(tenantId, dashboardId.getId());
} }
@Override @Override
public ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(DashboardId dashboardId) { public ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardInfoByIdAsync [{}]", dashboardId); log.trace("Executing findDashboardInfoByIdAsync [{}]", dashboardId);
validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
return dashboardInfoDao.findByIdAsync(dashboardId.getId()); return dashboardInfoDao.findByIdAsync(tenantId, dashboardId.getId());
} }
@Override @Override
public Dashboard saveDashboard(Dashboard dashboard) { public Dashboard saveDashboard(Dashboard dashboard) {
log.trace("Executing saveDashboard [{}]", dashboard); log.trace("Executing saveDashboard [{}]", dashboard);
dashboardValidator.validate(dashboard); dashboardValidator.validate(dashboard, DashboardInfo::getTenantId);
return dashboardDao.save(dashboard); return dashboardDao.save(dashboard.getTenantId(), dashboard);
} }
@Override @Override
public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId) { public Dashboard assignDashboardToCustomer(TenantId tenantId, DashboardId dashboardId, CustomerId customerId) {
Dashboard dashboard = findDashboardById(dashboardId); Dashboard dashboard = findDashboardById(tenantId, dashboardId);
Customer customer = customerDao.findById(customerId.getId()); Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) { if (customer == null) {
throw new DataValidationException("Can't assign dashboard to non-existent customer!"); throw new DataValidationException("Can't assign dashboard to non-existent customer!");
} }
@ -115,7 +115,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
} }
if (dashboard.addAssignedCustomer(customer)) { if (dashboard.addAssignedCustomer(customer)) {
try { try {
createRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); createRelation(tenantId, new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD));
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create dashboard relation. Customer Id: [{}]", dashboardId, customerId); log.warn("[{}] Failed to create dashboard relation. Customer Id: [{}]", dashboardId, customerId);
throw new RuntimeException(e); throw new RuntimeException(e);
@ -127,15 +127,15 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
} }
@Override @Override
public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) { public Dashboard unassignDashboardFromCustomer(TenantId tenantId, DashboardId dashboardId, CustomerId customerId) {
Dashboard dashboard = findDashboardById(dashboardId); Dashboard dashboard = findDashboardById(tenantId, dashboardId);
Customer customer = customerDao.findById(customerId.getId()); Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) { if (customer == null) {
throw new DataValidationException("Can't unassign dashboard from non-existent customer!"); throw new DataValidationException("Can't unassign dashboard from non-existent customer!");
} }
if (dashboard.removeAssignedCustomer(customer)) { if (dashboard.removeAssignedCustomer(customer)) {
try { try {
deleteRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); deleteRelation(tenantId, new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD));
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete dashboard relation. Customer Id: [{}]", dashboardId, customerId); log.warn("[{}] Failed to delete dashboard relation. Customer Id: [{}]", dashboardId, customerId);
throw new RuntimeException(e); throw new RuntimeException(e);
@ -146,8 +146,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
} }
} }
private Dashboard updateAssignedCustomer(DashboardId dashboardId, Customer customer) { private Dashboard updateAssignedCustomer(TenantId tenantId, DashboardId dashboardId, Customer customer) {
Dashboard dashboard = findDashboardById(dashboardId); Dashboard dashboard = findDashboardById(tenantId, dashboardId);
if (dashboard.updateAssignedCustomer(customer)) { if (dashboard.updateAssignedCustomer(customer)) {
return saveDashboard(dashboard); return saveDashboard(dashboard);
} else { } else {
@ -155,22 +155,22 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
} }
} }
private void deleteRelation(EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { private void deleteRelation(TenantId tenantId, EntityRelation dashboardRelation) throws ExecutionException, InterruptedException {
log.debug("Deleting Dashboard relation: {}", dashboardRelation); log.debug("Deleting Dashboard relation: {}", dashboardRelation);
relationService.deleteRelationAsync(dashboardRelation).get(); relationService.deleteRelationAsync(tenantId, dashboardRelation).get();
} }
private void createRelation(EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { private void createRelation(TenantId tenantId, EntityRelation dashboardRelation) throws ExecutionException, InterruptedException {
log.debug("Creating Dashboard relation: {}", dashboardRelation); log.debug("Creating Dashboard relation: {}", dashboardRelation);
relationService.saveRelationAsync(dashboardRelation).get(); relationService.saveRelationAsync(tenantId, dashboardRelation).get();
} }
@Override @Override
public void deleteDashboard(DashboardId dashboardId) { public void deleteDashboard(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing deleteDashboard [{}]", dashboardId); log.trace("Executing deleteDashboard [{}]", dashboardId);
Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
deleteEntityRelations(dashboardId); deleteEntityRelations(tenantId, dashboardId);
dashboardDao.removeById(dashboardId.getId()); dashboardDao.removeById(tenantId, dashboardId.getId());
} }
@Override @Override
@ -186,7 +186,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
public void deleteDashboardsByTenantId(TenantId tenantId) { public void deleteDashboardsByTenantId(TenantId tenantId) {
log.trace("Executing deleteDashboardsByTenantId, tenantId [{}]", tenantId); log.trace("Executing deleteDashboardsByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantDashboardsRemover.removeEntities(tenantId); tenantDashboardsRemover.removeEntities(tenantId, tenantId);
} }
@Override @Override
@ -207,10 +207,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
} }
@Override @Override
public void unassignCustomerDashboards(CustomerId customerId) { public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerDashboards, customerId [{}]", customerId); log.trace("Executing unassignCustomerDashboards, customerId [{}]", customerId);
Validator.validateId(customerId, "Incorrect customerId " + customerId); Validator.validateId(customerId, "Incorrect customerId " + customerId);
Customer customer = customerDao.findById(customerId.getId()); Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) { if (customer == null) {
throw new DataValidationException("Can't unassign dashboards from non-existent customer!"); throw new DataValidationException("Can't unassign dashboards from non-existent customer!");
} }
@ -218,10 +218,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
} }
@Override @Override
public void updateCustomerDashboards(CustomerId customerId) { public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId) {
log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId); log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId);
Validator.validateId(customerId, "Incorrect customerId " + customerId); Validator.validateId(customerId, "Incorrect customerId " + customerId);
Customer customer = customerDao.findById(customerId.getId()); Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) { if (customer == null) {
throw new DataValidationException("Can't update dashboards for non-existent customer!"); throw new DataValidationException("Can't update dashboards for non-existent customer!");
} }
@ -231,14 +231,14 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
private DataValidator<Dashboard> dashboardValidator = private DataValidator<Dashboard> dashboardValidator =
new DataValidator<Dashboard>() { new DataValidator<Dashboard>() {
@Override @Override
protected void validateDataImpl(Dashboard dashboard) { protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) {
if (StringUtils.isEmpty(dashboard.getTitle())) { if (StringUtils.isEmpty(dashboard.getTitle())) {
throw new DataValidationException("Dashboard title should be specified!"); throw new DataValidationException("Dashboard title should be specified!");
} }
if (dashboard.getTenantId() == null) { if (dashboard.getTenantId() == null) {
throw new DataValidationException("Dashboard should be assigned to tenant!"); throw new DataValidationException("Dashboard should be assigned to tenant!");
} else { } else {
Tenant tenant = tenantDao.findById(dashboard.getTenantId().getId()); Tenant tenant = tenantDao.findById(tenantId, dashboard.getTenantId().getId());
if (tenant == null) { if (tenant == null) {
throw new DataValidationException("Dashboard is referencing to non-existent tenant!"); throw new DataValidationException("Dashboard is referencing to non-existent tenant!");
} }
@ -250,13 +250,13 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
new PaginatedRemover<TenantId, DashboardInfo>() { new PaginatedRemover<TenantId, DashboardInfo>() {
@Override @Override
protected List<DashboardInfo> findEntities(TenantId id, TextPageLink pageLink) { protected List<DashboardInfo> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
return dashboardInfoDao.findDashboardsByTenantId(id.getId(), pageLink); return dashboardInfoDao.findDashboardsByTenantId(id.getId(), pageLink);
} }
@Override @Override
protected void removeEntity(DashboardInfo entity) { protected void removeEntity(TenantId tenantId, DashboardInfo entity) {
deleteDashboard(new DashboardId(entity.getUuidId())); deleteDashboard(tenantId, new DashboardId(entity.getUuidId()));
} }
}; };
@ -280,7 +280,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override @Override
protected void removeEntity(DashboardInfo entity) { protected void removeEntity(DashboardInfo entity) {
unassignDashboardFromCustomer(new DashboardId(entity.getUuidId()), this.customer.getId()); unassignDashboardFromCustomer(customer.getTenantId(), new DashboardId(entity.getUuidId()), this.customer.getId());
} }
} }
@ -305,7 +305,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override @Override
protected void removeEntity(DashboardInfo entity) { protected void removeEntity(DashboardInfo entity) {
updateAssignedCustomer(new DashboardId(entity.getUuidId()), this.customer); updateAssignedCustomer(customer.getTenantId(), new DashboardId(entity.getUuidId()), this.customer);
} }
} }

9
dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceCredentialsDao.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.device;
import com.datastax.driver.core.querybuilder.Select.Where; import com.datastax.driver.core.querybuilder.Select.Where;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
@ -46,23 +47,23 @@ public class CassandraDeviceCredentialsDao extends CassandraAbstractModelDao<Dev
} }
@Override @Override
public DeviceCredentials findByDeviceId(UUID deviceId) { public DeviceCredentials findByDeviceId(TenantId tenantId, UUID deviceId) {
log.debug("Try to find device credentials by deviceId [{}] ", deviceId); log.debug("Try to find device credentials by deviceId [{}] ", deviceId);
Where query = select().from(ModelConstants.DEVICE_CREDENTIALS_BY_DEVICE_COLUMN_FAMILY_NAME) Where query = select().from(ModelConstants.DEVICE_CREDENTIALS_BY_DEVICE_COLUMN_FAMILY_NAME)
.where(eq(ModelConstants.DEVICE_CREDENTIALS_DEVICE_ID_PROPERTY, deviceId)); .where(eq(ModelConstants.DEVICE_CREDENTIALS_DEVICE_ID_PROPERTY, deviceId));
log.trace("Execute query {}", query); log.trace("Execute query {}", query);
DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(query); DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(tenantId, query);
log.trace("Found device credentials [{}] by deviceId [{}]", deviceCredentialsEntity, deviceId); log.trace("Found device credentials [{}] by deviceId [{}]", deviceCredentialsEntity, deviceId);
return DaoUtil.getData(deviceCredentialsEntity); return DaoUtil.getData(deviceCredentialsEntity);
} }
@Override @Override
public DeviceCredentials findByCredentialsId(String credentialsId) { public DeviceCredentials findByCredentialsId(TenantId tenantId, String credentialsId) {
log.debug("Try to find device credentials by credentialsId [{}] ", credentialsId); log.debug("Try to find device credentials by credentialsId [{}] ", credentialsId);
Where query = select().from(ModelConstants.DEVICE_CREDENTIALS_BY_CREDENTIALS_ID_COLUMN_FAMILY_NAME) Where query = select().from(ModelConstants.DEVICE_CREDENTIALS_BY_CREDENTIALS_ID_COLUMN_FAMILY_NAME)
.where(eq(ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_ID_PROPERTY, credentialsId)); .where(eq(ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_ID_PROPERTY, credentialsId));
log.trace("Execute query {}", query); log.trace("Execute query {}", query);
DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(query); DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(tenantId, query);
log.trace("Found device credentials [{}] by credentialsId [{}]", deviceCredentialsEntity, credentialsId); log.trace("Found device credentials [{}] by credentialsId [{}]", deviceCredentialsEntity, credentialsId);
return DaoUtil.getData(deviceCredentialsEntity); return DaoUtil.getData(deviceCredentialsEntity);
} }

23
dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java

@ -28,6 +28,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.EntitySubtypeEntity; import org.thingsboard.server.dao.model.EntitySubtypeEntity;
@ -77,19 +78,19 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
} }
@Override @Override
public Device save(Device domain) { public Device save(TenantId tenantId, Device domain) {
Device savedDevice = super.save(domain); Device savedDevice = super.save(tenantId, domain);
EntitySubtype entitySubtype = new EntitySubtype(savedDevice.getTenantId(), EntityType.DEVICE, savedDevice.getType()); EntitySubtype entitySubtype = new EntitySubtype(savedDevice.getTenantId(), EntityType.DEVICE, savedDevice.getType());
EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype); EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype);
Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity); Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity);
executeWrite(saveStatement); executeWrite(tenantId, saveStatement);
return savedDevice; return savedDevice;
} }
@Override @Override
public List<Device> findDevicesByTenantId(UUID tenantId, TextPageLink pageLink) { public List<Device> findDevicesByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find devices by tenantId [{}] and pageLink [{}]", tenantId, pageLink); log.debug("Try to find devices by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<DeviceEntity> deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Collections.singletonList(eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), pageLink); Collections.singletonList(eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found devices [{}] by tenantId [{}] and pageLink [{}]", deviceEntities, tenantId, pageLink); log.trace("Found devices [{}] by tenantId [{}] and pageLink [{}]", deviceEntities, tenantId, pageLink);
@ -99,7 +100,7 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
@Override @Override
public List<Device> findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) { public List<Device> findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
log.debug("Try to find devices by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink); log.debug("Try to find devices by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<DeviceEntity> deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), pageLink); eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found devices [{}] by tenantId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, type, pageLink); log.trace("Found devices [{}] by tenantId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, type, pageLink);
@ -113,13 +114,13 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
Select.Where query = select.where(); Select.Where query = select.where();
query.and(eq(DEVICE_TENANT_ID_PROPERTY, tenantId)); query.and(eq(DEVICE_TENANT_ID_PROPERTY, tenantId));
query.and(in(ID_PROPERTY, deviceIds)); query.and(in(ID_PROPERTY, deviceIds));
return findListByStatementAsync(query); return findListByStatementAsync(new TenantId(tenantId), query);
} }
@Override @Override
public List<Device> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { public List<Device> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<DeviceEntity> deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId), Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
pageLink); pageLink);
@ -131,7 +132,7 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
@Override @Override
public List<Device> findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { public List<Device> findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) {
log.debug("Try to find devices by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink); log.debug("Try to find devices by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, List<DeviceEntity> deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId), eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
@ -149,7 +150,7 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
query.and(eq(DEVICE_TENANT_ID_PROPERTY, tenantId)); query.and(eq(DEVICE_TENANT_ID_PROPERTY, tenantId));
query.and(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId)); query.and(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId));
query.and(in(ID_PROPERTY, deviceIds)); query.and(in(ID_PROPERTY, deviceIds));
return findListByStatementAsync(query); return findListByStatementAsync(new TenantId(tenantId), query);
} }
@Override @Override
@ -158,7 +159,7 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
Select.Where query = select.where(); Select.Where query = select.where();
query.and(eq(DEVICE_TENANT_ID_PROPERTY, tenantId)); query.and(eq(DEVICE_TENANT_ID_PROPERTY, tenantId));
query.and(eq(DEVICE_NAME_PROPERTY, deviceName)); query.and(eq(DEVICE_NAME_PROPERTY, deviceName));
return Optional.ofNullable(DaoUtil.getData(findOneByStatement(query))); return Optional.ofNullable(DaoUtil.getData(findOneByStatement(new TenantId(tenantId), query)));
} }
@Override @Override
@ -168,7 +169,7 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId)); query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId));
query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.DEVICE)); query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.DEVICE));
query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(query); ResultSetFuture resultSetFuture = executeAsyncRead(new TenantId(tenantId), query);
return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() { return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() {
@Nullable @Nullable
@Override @Override

8
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDao.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.dao.device; package org.thingsboard.server.dao.device;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
@ -28,10 +29,11 @@ public interface DeviceCredentialsDao extends Dao<DeviceCredentials> {
/** /**
* Save or update device credentials object * Save or update device credentials object
* *
* @param tenantId the device tenant id
* @param deviceCredentials the device credentials object * @param deviceCredentials the device credentials object
* @return saved device credentials object * @return saved device credentials object
*/ */
DeviceCredentials save(DeviceCredentials deviceCredentials); DeviceCredentials save(TenantId tenantId, DeviceCredentials deviceCredentials);
/** /**
* Find device credentials by device id. * Find device credentials by device id.
@ -39,7 +41,7 @@ public interface DeviceCredentialsDao extends Dao<DeviceCredentials> {
* @param deviceId the device id * @param deviceId the device id
* @return the device credentials object * @return the device credentials object
*/ */
DeviceCredentials findByDeviceId(UUID deviceId); DeviceCredentials findByDeviceId(TenantId tenantId, UUID deviceId);
/** /**
* Find device credentials by credentials id. * Find device credentials by credentials id.
@ -47,6 +49,6 @@ public interface DeviceCredentialsDao extends Dao<DeviceCredentials> {
* @param credentialsId the credentials id * @param credentialsId the credentials id
* @return the device credentials object * @return the device credentials object
*/ */
DeviceCredentials findByCredentialsId(String credentialsId); DeviceCredentials findByCredentialsId(TenantId tenantId, String credentialsId);
} }

9
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java

@ -16,17 +16,18 @@
package org.thingsboard.server.dao.device; package org.thingsboard.server.dao.device;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
public interface DeviceCredentialsService { public interface DeviceCredentialsService {
DeviceCredentials findDeviceCredentialsByDeviceId(DeviceId deviceId); DeviceCredentials findDeviceCredentialsByDeviceId(TenantId tenantId, DeviceId deviceId);
DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId); DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId);
DeviceCredentials updateDeviceCredentials(DeviceCredentials deviceCredentials); DeviceCredentials updateDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials);
DeviceCredentials createDeviceCredentials(DeviceCredentials deviceCredentials); DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials);
void deleteDeviceCredentials(DeviceCredentials deviceCredentials); void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials);
} }

46
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java

@ -24,6 +24,8 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.EncryptionUtil;
@ -45,38 +47,38 @@ public class DeviceCredentialsServiceImpl implements DeviceCredentialsService {
private DeviceService deviceService; private DeviceService deviceService;
@Override @Override
public DeviceCredentials findDeviceCredentialsByDeviceId(DeviceId deviceId) { public DeviceCredentials findDeviceCredentialsByDeviceId(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceCredentialsByDeviceId [{}]", deviceId); log.trace("Executing findDeviceCredentialsByDeviceId [{}]", deviceId);
validateId(deviceId, "Incorrect deviceId " + deviceId); validateId(deviceId, "Incorrect deviceId " + deviceId);
return deviceCredentialsDao.findByDeviceId(deviceId.getId()); return deviceCredentialsDao.findByDeviceId(tenantId, deviceId.getId());
} }
@Override @Override
@Cacheable(cacheNames = DEVICE_CREDENTIALS_CACHE, unless="#result == null") @Cacheable(cacheNames = DEVICE_CREDENTIALS_CACHE, unless = "#result == null")
public DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId) { public DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId) {
log.trace("Executing findDeviceCredentialsByCredentialsId [{}]", credentialsId); log.trace("Executing findDeviceCredentialsByCredentialsId [{}]", credentialsId);
validateString(credentialsId, "Incorrect credentialsId " + credentialsId); validateString(credentialsId, "Incorrect credentialsId " + credentialsId);
return deviceCredentialsDao.findByCredentialsId(credentialsId); return deviceCredentialsDao.findByCredentialsId(new TenantId(EntityId.NULL_UUID), credentialsId);
} }
@Override @Override
@CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, keyGenerator="previousDeviceCredentialsId", beforeInvocation = true) @CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, keyGenerator = "previousDeviceCredentialsId", beforeInvocation = true)
public DeviceCredentials updateDeviceCredentials(DeviceCredentials deviceCredentials) { public DeviceCredentials updateDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials) {
return saveOrUpdate(deviceCredentials); return saveOrUpdate(tenantId, deviceCredentials);
} }
@Override @Override
public DeviceCredentials createDeviceCredentials(DeviceCredentials deviceCredentials) { public DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials) {
return saveOrUpdate(deviceCredentials); return saveOrUpdate(tenantId, deviceCredentials);
} }
private DeviceCredentials saveOrUpdate(DeviceCredentials deviceCredentials) { private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) {
if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) { if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) {
formatCertData(deviceCredentials); formatCertData(deviceCredentials);
} }
log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials); log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials);
credentialsValidator.validate(deviceCredentials); credentialsValidator.validate(deviceCredentials, id -> tenantId);
return deviceCredentialsDao.save(deviceCredentials); return deviceCredentialsDao.save(tenantId, deviceCredentials);
} }
private void formatCertData(DeviceCredentials deviceCredentials) { private void formatCertData(DeviceCredentials deviceCredentials) {
@ -87,37 +89,37 @@ public class DeviceCredentialsServiceImpl implements DeviceCredentialsService {
} }
@Override @Override
@CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, key="#deviceCredentials.credentialsId") @CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, key = "#deviceCredentials.credentialsId")
public void deleteDeviceCredentials(DeviceCredentials deviceCredentials) { public void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials) {
log.trace("Executing deleteDeviceCredentials [{}]", deviceCredentials); log.trace("Executing deleteDeviceCredentials [{}]", deviceCredentials);
deviceCredentialsDao.removeById(deviceCredentials.getUuidId()); deviceCredentialsDao.removeById(tenantId, deviceCredentials.getUuidId());
} }
private DataValidator<DeviceCredentials> credentialsValidator = private DataValidator<DeviceCredentials> credentialsValidator =
new DataValidator<DeviceCredentials>() { new DataValidator<DeviceCredentials>() {
@Override @Override
protected void validateCreate(DeviceCredentials deviceCredentials) { protected void validateCreate(TenantId tenantId, DeviceCredentials deviceCredentials) {
DeviceCredentials existingCredentialsEntity = deviceCredentialsDao.findByCredentialsId(deviceCredentials.getCredentialsId()); DeviceCredentials existingCredentialsEntity = deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId());
if (existingCredentialsEntity != null) { if (existingCredentialsEntity != null) {
throw new DataValidationException("Create of existent device credentials!"); throw new DataValidationException("Create of existent device credentials!");
} }
} }
@Override @Override
protected void validateUpdate(DeviceCredentials deviceCredentials) { protected void validateUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) {
DeviceCredentials existingCredentials = deviceCredentialsDao.findById(deviceCredentials.getUuidId()); DeviceCredentials existingCredentials = deviceCredentialsDao.findById(tenantId, deviceCredentials.getUuidId());
if (existingCredentials == null) { if (existingCredentials == null) {
throw new DataValidationException("Unable to update non-existent device credentials!"); throw new DataValidationException("Unable to update non-existent device credentials!");
} }
DeviceCredentials sameCredentialsId = deviceCredentialsDao.findByCredentialsId(deviceCredentials.getCredentialsId()); DeviceCredentials sameCredentialsId = deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId());
if (sameCredentialsId != null && !sameCredentialsId.getUuidId().equals(deviceCredentials.getUuidId())) { if (sameCredentialsId != null && !sameCredentialsId.getUuidId().equals(deviceCredentials.getUuidId())) {
throw new DataValidationException("Specified credentials are already registered!"); throw new DataValidationException("Specified credentials are already registered!");
} }
} }
@Override @Override
protected void validateDataImpl(DeviceCredentials deviceCredentials) { protected void validateDataImpl(TenantId tenantId, DeviceCredentials deviceCredentials) {
if (deviceCredentials.getDeviceId() == null) { if (deviceCredentials.getDeviceId() == null) {
throw new DataValidationException("Device credentials should be assigned to device!"); throw new DataValidationException("Device credentials should be assigned to device!");
} }
@ -127,7 +129,7 @@ public class DeviceCredentialsServiceImpl implements DeviceCredentialsService {
if (StringUtils.isEmpty(deviceCredentials.getCredentialsId())) { if (StringUtils.isEmpty(deviceCredentials.getCredentialsId())) {
throw new DataValidationException("Device credentials id should be specified!"); throw new DataValidationException("Device credentials id should be specified!");
} }
Device device = deviceService.findDeviceById(deviceCredentials.getDeviceId()); Device device = deviceService.findDeviceById(tenantId, deviceCredentials.getDeviceId());
if (device == null) { if (device == null) {
throw new DataValidationException("Can't assign device credentials to non-existent device!"); throw new DataValidationException("Can't assign device credentials to non-existent device!");
} }

3
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.device;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
@ -37,7 +38,7 @@ public interface DeviceDao extends Dao<Device> {
* @param device the device object * @param device the device object
* @return saved device object * @return saved device object
*/ */
Device save(Device device); Device save(TenantId tenantId, Device device);
/** /**
* Find devices by tenantId and page link. * Find devices by tenantId and page link.

12
dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -29,19 +29,19 @@ import java.util.List;
public interface DeviceService { public interface DeviceService {
Device findDeviceById(DeviceId deviceId); Device findDeviceById(TenantId tenantId, DeviceId deviceId);
ListenableFuture<Device> findDeviceByIdAsync(DeviceId deviceId); ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId);
Device findDeviceByTenantIdAndName(TenantId tenantId, String name); Device findDeviceByTenantIdAndName(TenantId tenantId, String name);
Device saveDevice(Device device); Device saveDevice(Device device);
Device assignDeviceToCustomer(DeviceId deviceId, CustomerId customerId); Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId);
Device unassignDeviceFromCustomer(DeviceId deviceId); Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId);
void deleteDevice(DeviceId deviceId); void deleteDevice(TenantId tenantId, DeviceId deviceId);
TextPageData<Device> findDevicesByTenantId(TenantId tenantId, TextPageLink pageLink); TextPageData<Device> findDevicesByTenantId(TenantId tenantId, TextPageLink pageLink);
@ -59,7 +59,7 @@ public interface DeviceService {
void unassignCustomerDevices(TenantId tenantId, CustomerId customerId); void unassignCustomerDevices(TenantId tenantId, CustomerId customerId);
ListenableFuture<List<Device>> findDevicesByQuery(DeviceSearchQuery query); ListenableFuture<List<Device>> findDevicesByQuery(TenantId tenantId, DeviceSearchQuery query);
ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId); ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId);

93
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -96,17 +96,17 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
private CacheManager cacheManager; private CacheManager cacheManager;
@Override @Override
public Device findDeviceById(DeviceId deviceId) { public Device findDeviceById(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId); log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
return deviceDao.findById(deviceId.getId()); return deviceDao.findById(tenantId, deviceId.getId());
} }
@Override @Override
public ListenableFuture<Device> findDeviceByIdAsync(DeviceId deviceId) { public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId); log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
return deviceDao.findByIdAsync(deviceId.getId()); return deviceDao.findByIdAsync(tenantId, deviceId.getId());
} }
@Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}") @Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}")
@ -122,38 +122,38 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override @Override
public Device saveDevice(Device device) { public Device saveDevice(Device device) {
log.trace("Executing saveDevice [{}]", device); log.trace("Executing saveDevice [{}]", device);
deviceValidator.validate(device); deviceValidator.validate(device, Device::getTenantId);
Device savedDevice = deviceDao.save(device); Device savedDevice = deviceDao.save(device.getTenantId(), device);
if (device.getId() == null) { if (device.getId() == null) {
DeviceCredentials deviceCredentials = new DeviceCredentials(); DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId())); deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(RandomStringUtils.randomAlphanumeric(20)); deviceCredentials.setCredentialsId(RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(deviceCredentials); deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
} }
return savedDevice; return savedDevice;
} }
@Override @Override
public Device assignDeviceToCustomer(DeviceId deviceId, CustomerId customerId) { public Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId) {
Device device = findDeviceById(deviceId); Device device = findDeviceById(tenantId, deviceId);
device.setCustomerId(customerId); device.setCustomerId(customerId);
return saveDevice(device); return saveDevice(device);
} }
@Override @Override
public Device unassignDeviceFromCustomer(DeviceId deviceId) { public Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId) {
Device device = findDeviceById(deviceId); Device device = findDeviceById(tenantId, deviceId);
device.setCustomerId(null); device.setCustomerId(null);
return saveDevice(device); return saveDevice(device);
} }
@Override @Override
public void deleteDevice(DeviceId deviceId) { public void deleteDevice(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing deleteDevice [{}]", deviceId); log.trace("Executing deleteDevice [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
Device device = deviceDao.findById(deviceId.getId()); Device device = deviceDao.findById(tenantId, deviceId.getId());
try { try {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get(); List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get();
if (entityViews != null && !entityViews.isEmpty()) { if (entityViews != null && !entityViews.isEmpty()) {
@ -164,11 +164,11 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
throw new RuntimeException("Exception while finding entity views for deviceId [" + deviceId + "]", e); throw new RuntimeException("Exception while finding entity views for deviceId [" + deviceId + "]", e);
} }
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, deviceId);
if (deviceCredentials != null) { if (deviceCredentials != null) {
deviceCredentialsService.deleteDeviceCredentials(deviceCredentials); deviceCredentialsService.deleteDeviceCredentials(tenantId, deviceCredentials);
} }
deleteEntityRelations(deviceId); deleteEntityRelations(tenantId, deviceId);
List<Object> list = new ArrayList<>(); List<Object> list = new ArrayList<>();
list.add(device.getTenantId()); list.add(device.getTenantId());
@ -176,7 +176,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
Cache cache = cacheManager.getCache(DEVICE_CACHE); Cache cache = cacheManager.getCache(DEVICE_CACHE);
cache.evict(list); cache.evict(list);
deviceDao.removeById(deviceId.getId()); deviceDao.removeById(tenantId, deviceId.getId());
} }
@Override @Override
@ -211,7 +211,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
public void deleteDevicesByTenantId(TenantId tenantId) { public void deleteDevicesByTenantId(TenantId tenantId) {
log.trace("Executing deleteDevicesByTenantId, tenantId [{}]", tenantId); log.trace("Executing deleteDevicesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantDevicesRemover.removeEntities(tenantId); tenantDevicesRemover.removeEntities(tenantId, tenantId);
} }
@Override @Override
@ -250,19 +250,19 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
log.trace("Executing unassignCustomerDevices, tenantId [{}], customerId [{}]", tenantId, customerId); log.trace("Executing unassignCustomerDevices, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
new CustomerDevicesUnassigner(tenantId).removeEntities(customerId); customerDeviceUnasigner.removeEntities(tenantId, customerId);
} }
@Override @Override
public ListenableFuture<List<Device>> findDevicesByQuery(DeviceSearchQuery query) { public ListenableFuture<List<Device>> findDevicesByQuery(TenantId tenantId, DeviceSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery()); ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery());
ListenableFuture<List<Device>> devices = Futures.transformAsync(relations, r -> { ListenableFuture<List<Device>> devices = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection(); EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<Device>> futures = new ArrayList<>(); List<ListenableFuture<Device>> futures = new ArrayList<>();
for (EntityRelation relation : r) { for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom(); EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
if (entityId.getEntityType() == EntityType.DEVICE) { if (entityId.getEntityType() == EntityType.DEVICE) {
futures.add(findDeviceByIdAsync(new DeviceId(entityId.getId()))); futures.add(findDeviceByIdAsync(tenantId, new DeviceId(entityId.getId())));
} }
} }
return Futures.successfulAsList(futures); return Futures.successfulAsList(futures);
@ -285,7 +285,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
ListenableFuture<List<EntitySubtype>> tenantDeviceTypes = deviceDao.findTenantDeviceTypesAsync(tenantId.getId()); ListenableFuture<List<EntitySubtype>> tenantDeviceTypes = deviceDao.findTenantDeviceTypesAsync(tenantId.getId());
return Futures.transform(tenantDeviceTypes, return Futures.transform(tenantDeviceTypes,
(Function<List<EntitySubtype>, List<EntitySubtype>>) deviceTypes -> { deviceTypes -> {
deviceTypes.sort(Comparator.comparing(EntitySubtype::getType)); deviceTypes.sort(Comparator.comparing(EntitySubtype::getType));
return deviceTypes; return deviceTypes;
}); });
@ -295,7 +295,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
new DataValidator<Device>() { new DataValidator<Device>() {
@Override @Override
protected void validateCreate(Device device) { protected void validateCreate(TenantId tenantId, Device device) {
deviceDao.findDeviceByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent( deviceDao.findDeviceByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> { d -> {
throw new DataValidationException("Device with such name already exists!"); throw new DataValidationException("Device with such name already exists!");
@ -304,7 +304,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
} }
@Override @Override
protected void validateUpdate(Device device) { protected void validateUpdate(TenantId tenantId, Device device) {
deviceDao.findDeviceByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent( deviceDao.findDeviceByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> { d -> {
if (!d.getUuidId().equals(device.getUuidId())) { if (!d.getUuidId().equals(device.getUuidId())) {
@ -315,7 +315,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
} }
@Override @Override
protected void validateDataImpl(Device device) { protected void validateDataImpl(TenantId tenantId, Device device) {
if (StringUtils.isEmpty(device.getType())) { if (StringUtils.isEmpty(device.getType())) {
throw new DataValidationException("Device type should be specified!"); throw new DataValidationException("Device type should be specified!");
} }
@ -325,7 +325,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
if (device.getTenantId() == null) { if (device.getTenantId() == null) {
throw new DataValidationException("Device should be assigned to tenant!"); throw new DataValidationException("Device should be assigned to tenant!");
} else { } else {
Tenant tenant = tenantDao.findById(device.getTenantId().getId()); Tenant tenant = tenantDao.findById(device.getTenantId(), device.getTenantId().getId());
if (tenant == null) { if (tenant == null) {
throw new DataValidationException("Device is referencing to non-existent tenant!"); throw new DataValidationException("Device is referencing to non-existent tenant!");
} }
@ -333,7 +333,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
if (device.getCustomerId() == null) { if (device.getCustomerId() == null) {
device.setCustomerId(new CustomerId(NULL_UUID)); device.setCustomerId(new CustomerId(NULL_UUID));
} else if (!device.getCustomerId().getId().equals(NULL_UUID)) { } else if (!device.getCustomerId().getId().equals(NULL_UUID)) {
Customer customer = customerDao.findById(device.getCustomerId().getId()); Customer customer = customerDao.findById(device.getTenantId(), device.getCustomerId().getId());
if (customer == null) { if (customer == null) {
throw new DataValidationException("Can't assign device to non-existent customer!"); throw new DataValidationException("Can't assign device to non-existent customer!");
} }
@ -345,36 +345,29 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
}; };
private PaginatedRemover<TenantId, Device> tenantDevicesRemover = private PaginatedRemover<TenantId, Device> tenantDevicesRemover =
new PaginatedRemover<TenantId, Device>() { new PaginatedRemover<TenantId, Device>() {
@Override @Override
protected List<Device> findEntities(TenantId id, TextPageLink pageLink) { protected List<Device> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
return deviceDao.findDevicesByTenantId(id.getId(), pageLink); return deviceDao.findDevicesByTenantId(id.getId(), pageLink);
} }
@Override
protected void removeEntity(Device entity) {
deleteDevice(new DeviceId(entity.getUuidId()));
}
};
private class CustomerDevicesUnassigner extends PaginatedRemover<CustomerId, Device> {
private TenantId tenantId; @Override
protected void removeEntity(TenantId tenantId, Device entity) {
deleteDevice(tenantId, new DeviceId(entity.getUuidId()));
}
};
CustomerDevicesUnassigner(TenantId tenantId) { private PaginatedRemover<CustomerId, Device> customerDeviceUnasigner = new PaginatedRemover<CustomerId, Device>() {
this.tenantId = tenantId;
}
@Override @Override
protected List<Device> findEntities(CustomerId id, TextPageLink pageLink) { protected List<Device> findEntities(TenantId tenantId, CustomerId id, TextPageLink pageLink) {
return deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink); return deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
} }
@Override @Override
protected void removeEntity(Device entity) { protected void removeEntity(TenantId tenantId, Device entity) {
unassignDeviceFromCustomer(new DeviceId(entity.getUuidId())); unassignDeviceFromCustomer(tenantId, new DeviceId(entity.getUuidId()));
} }
};
}
} }

5
dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.entity;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
@Slf4j @Slf4j
@ -26,9 +27,9 @@ public abstract class AbstractEntityService {
@Autowired @Autowired
protected RelationService relationService; protected RelationService relationService;
protected void deleteEntityRelations(EntityId entityId) { protected void deleteEntityRelations(TenantId tenantId, EntityId entityId) {
log.trace("Executing deleteEntityRelations [{}]", entityId); log.trace("Executing deleteEntityRelations [{}]", entityId);
relationService.deleteEntityRelations(entityId); relationService.deleteEntityRelations(tenantId, entityId);
} }
} }

24
dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java

@ -69,42 +69,42 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe
private RuleChainService ruleChainService; private RuleChainService ruleChainService;
@Override @Override
public void deleteEntityRelations(EntityId entityId) { public void deleteEntityRelations(TenantId tenantId, EntityId entityId) {
super.deleteEntityRelations(entityId); super.deleteEntityRelations(tenantId, entityId);
} }
@Override @Override
public ListenableFuture<String> fetchEntityNameAsync(EntityId entityId) { public ListenableFuture<String> fetchEntityNameAsync(TenantId tenantId, EntityId entityId) {
log.trace("Executing fetchEntityNameAsync [{}]", entityId); log.trace("Executing fetchEntityNameAsync [{}]", entityId);
ListenableFuture<String> entityName; ListenableFuture<String> entityName;
ListenableFuture<? extends HasName> hasName; ListenableFuture<? extends HasName> hasName;
switch (entityId.getEntityType()) { switch (entityId.getEntityType()) {
case ASSET: case ASSET:
hasName = assetService.findAssetByIdAsync(new AssetId(entityId.getId())); hasName = assetService.findAssetByIdAsync(tenantId, new AssetId(entityId.getId()));
break; break;
case DEVICE: case DEVICE:
hasName = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); hasName = deviceService.findDeviceByIdAsync(tenantId, new DeviceId(entityId.getId()));
break; break;
case ENTITY_VIEW: case ENTITY_VIEW:
hasName = entityViewService.findEntityViewByIdAsync(new EntityViewId(entityId.getId())); hasName = entityViewService.findEntityViewByIdAsync(tenantId, new EntityViewId(entityId.getId()));
break; break;
case TENANT: case TENANT:
hasName = tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); hasName = tenantService.findTenantByIdAsync(tenantId, new TenantId(entityId.getId()));
break; break;
case CUSTOMER: case CUSTOMER:
hasName = customerService.findCustomerByIdAsync(new CustomerId(entityId.getId())); hasName = customerService.findCustomerByIdAsync(tenantId, new CustomerId(entityId.getId()));
break; break;
case USER: case USER:
hasName = userService.findUserByIdAsync(new UserId(entityId.getId())); hasName = userService.findUserByIdAsync(tenantId, new UserId(entityId.getId()));
break; break;
case DASHBOARD: case DASHBOARD:
hasName = dashboardService.findDashboardInfoByIdAsync(new DashboardId(entityId.getId())); hasName = dashboardService.findDashboardInfoByIdAsync(tenantId, new DashboardId(entityId.getId()));
break; break;
case ALARM: case ALARM:
hasName = alarmService.findAlarmByIdAsync(new AlarmId(entityId.getId())); hasName = alarmService.findAlarmByIdAsync(tenantId, new AlarmId(entityId.getId()));
break; break;
case RULE_CHAIN: case RULE_CHAIN:
hasName = ruleChainService.findRuleChainByIdAsync(new RuleChainId(entityId.getId())); hasName = ruleChainService.findRuleChainByIdAsync(tenantId, new RuleChainId(entityId.getId()));
break; break;
default: default:
throw new IllegalStateException("Not Implemented!"); throw new IllegalStateException("Not Implemented!");

5
dao/src/main/java/org/thingsboard/server/dao/entity/EntityService.java

@ -17,11 +17,12 @@ package org.thingsboard.server.dao.entity;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
public interface EntityService { public interface EntityService {
ListenableFuture<String> fetchEntityNameAsync(EntityId entityId); ListenableFuture<String> fetchEntityNameAsync(TenantId tenantId, EntityId entityId);
void deleteEntityRelations(EntityId entityId); void deleteEntityRelations(TenantId tenantId, EntityId entityId);
} }

21
dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java

@ -28,6 +28,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.EntitySubtypeEntity; import org.thingsboard.server.dao.model.EntitySubtypeEntity;
@ -82,12 +83,12 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
} }
@Override @Override
public EntityView save(EntityView domain) { public EntityView save(TenantId tenantId, EntityView domain) {
EntityView savedEntityView = super.save(domain); EntityView savedEntityView = super.save(domain.getTenantId(), domain);
EntitySubtype entitySubtype = new EntitySubtype(savedEntityView.getTenantId(), EntityType.ENTITY_VIEW, savedEntityView.getType()); EntitySubtype entitySubtype = new EntitySubtype(savedEntityView.getTenantId(), EntityType.ENTITY_VIEW, savedEntityView.getType());
EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype); EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype);
Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity); Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity);
executeWrite(saveStatement); executeWrite(tenantId, saveStatement);
return savedEntityView; return savedEntityView;
} }
@ -95,7 +96,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
public List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink) { public List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find entity views by tenantId [{}] and pageLink [{}]", tenantId, pageLink); log.debug("Try to find entity views by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<EntityViewEntity> entityViewEntities = List<EntityViewEntity> entityViewEntities =
findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_CF, findPageWithTextSearch(new TenantId(tenantId), ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_CF,
Collections.singletonList(eq(TENANT_ID_PROPERTY, tenantId)), pageLink); Collections.singletonList(eq(TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found entity views [{}] by tenantId [{}] and pageLink [{}]", log.trace("Found entity views [{}] by tenantId [{}] and pageLink [{}]",
entityViewEntities, tenantId, pageLink); entityViewEntities, tenantId, pageLink);
@ -106,7 +107,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
public List<EntityView> findEntityViewsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) { public List<EntityView> findEntityViewsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
log.debug("Try to find entity views by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink); log.debug("Try to find entity views by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
List<EntityViewEntity> entityViewEntities = List<EntityViewEntity> entityViewEntities =
findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_CF, findPageWithTextSearch(new TenantId(tenantId), ENTITY_VIEW_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_CF,
Arrays.asList(eq(ENTITY_VIEW_TYPE_PROPERTY, type), Arrays.asList(eq(ENTITY_VIEW_TYPE_PROPERTY, type),
eq(TENANT_ID_PROPERTY, tenantId)), pageLink); eq(TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found entity views [{}] by tenantId [{}], type [{}] and pageLink [{}]", log.trace("Found entity views [{}] by tenantId [{}], type [{}] and pageLink [{}]",
@ -119,14 +120,14 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_NAME).where(); Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_NAME).where();
query.and(eq(ENTITY_VIEW_TENANT_ID_PROPERTY, tenantId)); query.and(eq(ENTITY_VIEW_TENANT_ID_PROPERTY, tenantId));
query.and(eq(ENTITY_VIEW_NAME_PROPERTY, name)); query.and(eq(ENTITY_VIEW_NAME_PROPERTY, name));
return Optional.ofNullable(DaoUtil.getData(findOneByStatement(query))); return Optional.ofNullable(DaoUtil.getData(findOneByStatement(new TenantId(tenantId), query)));
} }
@Override @Override
public List<EntityView> findEntityViewsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { public List<EntityView> findEntityViewsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
log.debug("Try to find entity views by tenantId [{}], customerId[{}] and pageLink [{}]", log.debug("Try to find entity views by tenantId [{}], customerId[{}] and pageLink [{}]",
tenantId, customerId, pageLink); tenantId, customerId, pageLink);
List<EntityViewEntity> entityViewEntities = findPageWithTextSearch( List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(new TenantId(tenantId),
ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF, ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF,
Arrays.asList(eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)), Arrays.asList(eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)),
pageLink); pageLink);
@ -139,7 +140,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
public List<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { public List<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) {
log.debug("Try to find entity views by tenantId [{}], customerId[{}], type [{}] and pageLink [{}]", log.debug("Try to find entity views by tenantId [{}], customerId[{}], type [{}] and pageLink [{}]",
tenantId, customerId, type, pageLink); tenantId, customerId, type, pageLink);
List<EntityViewEntity> entityViewEntities = findPageWithTextSearch( List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(new TenantId(tenantId),
ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF, ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF,
Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)), Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)),
pageLink); pageLink);
@ -154,7 +155,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF).where(); Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF).where();
query.and(eq(TENANT_ID_PROPERTY, tenantId)); query.and(eq(TENANT_ID_PROPERTY, tenantId));
query.and(eq(ENTITY_ID_COLUMN, entityId)); query.and(eq(ENTITY_ID_COLUMN, entityId));
return findListByStatementAsync(query); return findListByStatementAsync(new TenantId(tenantId), query);
} }
@Override @Override
@ -164,7 +165,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId)); query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId));
query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.ENTITY_VIEW)); query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.ENTITY_VIEW));
query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(query); ResultSetFuture resultSetFuture = executeAsyncRead(new TenantId(tenantId), query);
return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() { return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() {
@Nullable @Nullable
@Override @Override

3
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java

@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
@ -37,7 +38,7 @@ public interface EntityViewDao extends Dao<EntityView> {
* @param entityView the entity-view object * @param entityView the entity-view object
* @return saved entity-view object * @return saved entity-view object
*/ */
EntityView save(EntityView entityView); EntityView save(TenantId tenantId, EntityView entityView);
/** /**
* Find entity views by tenantId and page link. * Find entity views by tenantId and page link.

13
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.entityview;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -35,13 +36,13 @@ public interface EntityViewService {
EntityView saveEntityView(EntityView entityView); EntityView saveEntityView(EntityView entityView);
EntityView assignEntityViewToCustomer(EntityViewId entityViewId, CustomerId customerId); EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, CustomerId customerId);
EntityView unassignEntityViewFromCustomer(EntityViewId entityViewId); EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId);
void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId); void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId);
EntityView findEntityViewById(EntityViewId entityViewId); EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId);
EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name); EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name);
@ -53,13 +54,13 @@ public interface EntityViewService {
TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, TextPageLink pageLink, String type); TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, TextPageLink pageLink, String type);
ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query); ListenableFuture<List<EntityView>> findEntityViewsByQuery(TenantId tenantId, EntityViewSearchQuery query);
ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId); ListenableFuture<EntityView> findEntityViewByIdAsync(TenantId tenantId, EntityViewId entityViewId);
ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId); ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId);
void deleteEntityView(EntityViewId entityViewId); void deleteEntityView(TenantId tenantId, EntityViewId entityViewId);
void deleteEntityViewsByTenantId(TenantId tenantId); void deleteEntityViewsByTenantId(TenantId tenantId);

85
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java

@ -95,23 +95,23 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
@Override @Override
public EntityView saveEntityView(EntityView entityView) { public EntityView saveEntityView(EntityView entityView) {
log.trace("Executing save entity view [{}]", entityView); log.trace("Executing save entity view [{}]", entityView);
entityViewValidator.validate(entityView); entityViewValidator.validate(entityView, EntityView::getTenantId);
EntityView savedEntityView = entityViewDao.save(entityView); EntityView savedEntityView = entityViewDao.save(entityView.getTenantId(), entityView);
return savedEntityView; return savedEntityView;
} }
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}") @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override @Override
public EntityView assignEntityViewToCustomer(EntityViewId entityViewId, CustomerId customerId) { public EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, CustomerId customerId) {
EntityView entityView = findEntityViewById(entityViewId); EntityView entityView = findEntityViewById(tenantId, entityViewId);
entityView.setCustomerId(customerId); entityView.setCustomerId(customerId);
return saveEntityView(entityView); return saveEntityView(entityView);
} }
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}") @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override @Override
public EntityView unassignEntityViewFromCustomer(EntityViewId entityViewId) { public EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId) {
EntityView entityView = findEntityViewById(entityViewId); EntityView entityView = findEntityViewById(tenantId, entityViewId);
entityView.setCustomerId(null); entityView.setCustomerId(null);
return saveEntityView(entityView); return saveEntityView(entityView);
} }
@ -121,15 +121,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
log.trace("Executing unassignCustomerEntityViews, tenantId [{}], customerId [{}]", tenantId, customerId); log.trace("Executing unassignCustomerEntityViews, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
new CustomerEntityViewsUnAssigner(tenantId).removeEntities(customerId); customerEntityViewsUnAssigner.removeEntities(tenantId, customerId);
} }
@Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}") @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override @Override
public EntityView findEntityViewById(EntityViewId entityViewId) { public EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId) {
log.trace("Executing findEntityViewById [{}]", entityViewId); log.trace("Executing findEntityViewById [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
return entityViewDao.findById(entityViewId.getId()); return entityViewDao.findById(tenantId, entityViewId.getId());
} }
@Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #name}") @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #name}")
@ -187,15 +187,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
} }
@Override @Override
public ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query) { public ListenableFuture<List<EntityView>> findEntityViewsByQuery(TenantId tenantId, EntityViewSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery()); ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery());
ListenableFuture<List<EntityView>> entityViews = Futures.transformAsync(relations, r -> { ListenableFuture<List<EntityView>> entityViews = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection(); EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<EntityView>> futures = new ArrayList<>(); List<ListenableFuture<EntityView>> futures = new ArrayList<>();
for (EntityRelation relation : r) { for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom(); EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
if (entityId.getEntityType() == EntityType.ENTITY_VIEW) { if (entityId.getEntityType() == EntityType.ENTITY_VIEW) {
futures.add(findEntityViewByIdAsync(new EntityViewId(entityId.getId()))); futures.add(findEntityViewByIdAsync(tenantId, new EntityViewId(entityId.getId())));
} }
} }
return Futures.successfulAsList(futures); return Futures.successfulAsList(futures);
@ -213,10 +213,10 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
} }
@Override @Override
public ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId) { public ListenableFuture<EntityView> findEntityViewByIdAsync(TenantId tenantId, EntityViewId entityViewId) {
log.trace("Executing findEntityViewById [{}]", entityViewId); log.trace("Executing findEntityViewById [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
return entityViewDao.findByIdAsync(entityViewId.getId()); return entityViewDao.findByIdAsync(tenantId, entityViewId.getId());
} }
@Override @Override
@ -253,21 +253,21 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}") @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override @Override
public void deleteEntityView(EntityViewId entityViewId) { public void deleteEntityView(TenantId tenantId, EntityViewId entityViewId) {
log.trace("Executing deleteEntityView [{}]", entityViewId); log.trace("Executing deleteEntityView [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
deleteEntityRelations(entityViewId); deleteEntityRelations(tenantId, entityViewId);
EntityView entityView = entityViewDao.findById(entityViewId.getId()); EntityView entityView = entityViewDao.findById(tenantId, entityViewId.getId());
cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getEntityId())); cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getEntityId()));
cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getName())); cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getName()));
entityViewDao.removeById(entityViewId.getId()); entityViewDao.removeById(tenantId, entityViewId.getId());
} }
@Override @Override
public void deleteEntityViewsByTenantId(TenantId tenantId) { public void deleteEntityViewsByTenantId(TenantId tenantId) {
log.trace("Executing deleteEntityViewsByTenantId, tenantId [{}]", tenantId); log.trace("Executing deleteEntityViewsByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantEntityViewRemover.removeEntities(tenantId); tenantEntityViewRemover.removeEntities(tenantId, tenantId);
} }
@Override @Override
@ -286,7 +286,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
new DataValidator<EntityView>() { new DataValidator<EntityView>() {
@Override @Override
protected void validateCreate(EntityView entityView) { protected void validateCreate(TenantId tenantId, EntityView entityView) {
entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName())
.ifPresent(e -> { .ifPresent(e -> {
throw new DataValidationException("Entity view with such name already exists!"); throw new DataValidationException("Entity view with such name already exists!");
@ -294,7 +294,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
} }
@Override @Override
protected void validateUpdate(EntityView entityView) { protected void validateUpdate(TenantId tenantId, EntityView entityView) {
entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName())
.ifPresent(e -> { .ifPresent(e -> {
if (!e.getUuidId().equals(entityView.getUuidId())) { if (!e.getUuidId().equals(entityView.getUuidId())) {
@ -304,7 +304,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
} }
@Override @Override
protected void validateDataImpl(EntityView entityView) { protected void validateDataImpl(TenantId tenantId, EntityView entityView) {
if (StringUtils.isEmpty(entityView.getType())) { if (StringUtils.isEmpty(entityView.getType())) {
throw new DataValidationException("Entity View type should be specified!"); throw new DataValidationException("Entity View type should be specified!");
} }
@ -314,7 +314,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
if (entityView.getTenantId() == null) { if (entityView.getTenantId() == null) {
throw new DataValidationException("Entity view should be assigned to tenant!"); throw new DataValidationException("Entity view should be assigned to tenant!");
} else { } else {
Tenant tenant = tenantDao.findById(entityView.getTenantId().getId()); Tenant tenant = tenantDao.findById(tenantId, entityView.getTenantId().getId());
if (tenant == null) { if (tenant == null) {
throw new DataValidationException("Entity view is referencing to non-existent tenant!"); throw new DataValidationException("Entity view is referencing to non-existent tenant!");
} }
@ -322,7 +322,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
if (entityView.getCustomerId() == null) { if (entityView.getCustomerId() == null) {
entityView.setCustomerId(new CustomerId(NULL_UUID)); entityView.setCustomerId(new CustomerId(NULL_UUID));
} else if (!entityView.getCustomerId().getId().equals(NULL_UUID)) { } else if (!entityView.getCustomerId().getId().equals(NULL_UUID)) {
Customer customer = customerDao.findById(entityView.getCustomerId().getId()); Customer customer = customerDao.findById(tenantId, entityView.getCustomerId().getId());
if (customer == null) { if (customer == null) {
throw new DataValidationException("Can't assign entity view to non-existent customer!"); throw new DataValidationException("Can't assign entity view to non-existent customer!");
} }
@ -333,36 +333,27 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
} }
}; };
private PaginatedRemover<TenantId, EntityView> tenantEntityViewRemover = private PaginatedRemover<TenantId, EntityView> tenantEntityViewRemover = new PaginatedRemover<TenantId, EntityView>() {
new PaginatedRemover<TenantId, EntityView>() { @Override
protected List<EntityView> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
@Override return entityViewDao.findEntityViewsByTenantId(id.getId(), pageLink);
protected List<EntityView> findEntities(TenantId id, TextPageLink pageLink) { }
return entityViewDao.findEntityViewsByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(EntityView entity) {
deleteEntityView(new EntityViewId(entity.getUuidId()));
}
};
private class CustomerEntityViewsUnAssigner extends PaginatedRemover<CustomerId, EntityView> {
private TenantId tenantId;
CustomerEntityViewsUnAssigner(TenantId tenantId) { @Override
this.tenantId = tenantId; protected void removeEntity(TenantId tenantId, EntityView entity) {
deleteEntityView(tenantId, new EntityViewId(entity.getUuidId()));
} }
};
private PaginatedRemover<CustomerId, EntityView> customerEntityViewsUnAssigner = new PaginatedRemover<CustomerId, EntityView>() {
@Override @Override
protected List<EntityView> findEntities(CustomerId id, TextPageLink pageLink) { protected List<EntityView> findEntities(TenantId tenantId, CustomerId id, TextPageLink pageLink) {
return entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink); return entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
} }
@Override @Override
protected void removeEntity(EntityView entity) { protected void removeEntity(TenantId tenantId, EntityView entity) {
unassignEntityViewFromCustomer(new EntityViewId(entity.getUuidId())); unassignEntityViewFromCustomer(tenantId, new EntityViewId(entity.getUuidId()));
} }
} };
} }

10
dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java

@ -40,19 +40,19 @@ public class BaseEventService implements EventService {
@Override @Override
public Event save(Event event) { public Event save(Event event) {
eventValidator.validate(event); eventValidator.validate(event, Event::getTenantId);
return eventDao.save(event); return eventDao.save(event.getTenantId(), event);
} }
@Override @Override
public ListenableFuture<Event> saveAsync(Event event) { public ListenableFuture<Event> saveAsync(Event event) {
eventValidator.validate(event); eventValidator.validate(event, Event::getTenantId);
return eventDao.saveAsync(event); return eventDao.saveAsync(event);
} }
@Override @Override
public Optional<Event> saveIfNotExists(Event event) { public Optional<Event> saveIfNotExists(Event event) {
eventValidator.validate(event); eventValidator.validate(event, Event::getTenantId);
if (StringUtils.isEmpty(event.getUid())) { if (StringUtils.isEmpty(event.getUid())) {
throw new DataValidationException("Event uid should be specified!."); throw new DataValidationException("Event uid should be specified!.");
} }
@ -97,7 +97,7 @@ public class BaseEventService implements EventService {
private DataValidator<Event> eventValidator = private DataValidator<Event> eventValidator =
new DataValidator<Event>() { new DataValidator<Event>() {
@Override @Override
protected void validateDataImpl(Event event) { protected void validateDataImpl(TenantId tenantId, Event event) {
if (event.getEntityId() == null) { if (event.getEntityId() == null) {
throw new DataValidationException("Entity id should be specified!."); throw new DataValidationException("Entity id should be specified!.");
} }

22
dao/src/main/java/org/thingsboard/server/dao/event/CassandraBaseEventDao.java

@ -64,7 +64,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
} }
@Override @Override
public Event save(Event event) { public Event save(TenantId tenantId, Event event) {
try { try {
return saveAsync(event).get(); return saveAsync(event).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
@ -85,7 +85,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
if (StringUtils.isEmpty(event.getUid())) { if (StringUtils.isEmpty(event.getUid())) {
event.setUid(event.getId().toString()); event.setUid(event.getId().toString());
} }
ListenableFuture<Optional<Event>> optionalSave = saveAsync(new EventEntity(event), false); ListenableFuture<Optional<Event>> optionalSave = saveAsync(event.getTenantId(), new EventEntity(event), false);
return Futures.transform(optionalSave, opt -> opt.orElse(null)); return Futures.transform(optionalSave, opt -> opt.orElse(null));
} }
@ -98,7 +98,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
if (event.getId() == null) { if (event.getId() == null) {
event.setId(new EventId(UUIDs.timeBased())); event.setId(new EventId(UUIDs.timeBased()));
} }
return save(new EventEntity(event), true); return save(event.getTenantId(), new EventEntity(event), true);
} }
@Override @Override
@ -111,7 +111,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
.and(eq(ModelConstants.EVENT_TYPE_PROPERTY, eventType)) .and(eq(ModelConstants.EVENT_TYPE_PROPERTY, eventType))
.and(eq(ModelConstants.EVENT_UID_PROPERTY, eventUid)); .and(eq(ModelConstants.EVENT_UID_PROPERTY, eventUid));
log.trace("Execute query [{}]", query); log.trace("Execute query [{}]", query);
EventEntity entity = findOneByStatement(query); EventEntity entity = findOneByStatement(new TenantId(tenantId), query);
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("Search result: [{}] for event entity [{}]", entity != null, entity); log.trace("Search result: [{}] for event entity [{}]", entity != null, entity);
} else { } else {
@ -123,7 +123,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
@Override @Override
public List<Event> findEvents(UUID tenantId, EntityId entityId, TimePageLink pageLink) { public List<Event> findEvents(UUID tenantId, EntityId entityId, TimePageLink pageLink) {
log.trace("Try to find events by tenant [{}], entity [{}]and pageLink [{}]", tenantId, entityId, pageLink); log.trace("Try to find events by tenant [{}], entity [{}]and pageLink [{}]", tenantId, entityId, pageLink);
List<EventEntity> entities = findPageWithTimeSearch(EVENT_BY_ID_VIEW_NAME, List<EventEntity> entities = findPageWithTimeSearch(new TenantId(tenantId), EVENT_BY_ID_VIEW_NAME,
Arrays.asList(eq(ModelConstants.EVENT_TENANT_ID_PROPERTY, tenantId), Arrays.asList(eq(ModelConstants.EVENT_TENANT_ID_PROPERTY, tenantId),
eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType()), eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType()),
eq(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entityId.getId())), eq(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entityId.getId())),
@ -135,7 +135,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
@Override @Override
public List<Event> findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink) { public List<Event> findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink) {
log.trace("Try to find events by tenant [{}], entity [{}], type [{}] and pageLink [{}]", tenantId, entityId, eventType, pageLink); log.trace("Try to find events by tenant [{}], entity [{}], type [{}] and pageLink [{}]", tenantId, entityId, eventType, pageLink);
List<EventEntity> entities = findPageWithTimeSearch(EVENT_BY_TYPE_AND_ID_VIEW_NAME, List<EventEntity> entities = findPageWithTimeSearch(new TenantId(tenantId), EVENT_BY_TYPE_AND_ID_VIEW_NAME,
Arrays.asList(eq(ModelConstants.EVENT_TENANT_ID_PROPERTY, tenantId), Arrays.asList(eq(ModelConstants.EVENT_TENANT_ID_PROPERTY, tenantId),
eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType()), eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType()),
eq(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entityId.getId()), eq(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entityId.getId()),
@ -158,19 +158,19 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
query.and(eq(ModelConstants.EVENT_TYPE_PROPERTY, eventType)); query.and(eq(ModelConstants.EVENT_TYPE_PROPERTY, eventType));
query.limit(limit); query.limit(limit);
query.orderBy(QueryBuilder.desc(ModelConstants.EVENT_TYPE_PROPERTY), QueryBuilder.desc(ModelConstants.ID_PROPERTY)); query.orderBy(QueryBuilder.desc(ModelConstants.EVENT_TYPE_PROPERTY), QueryBuilder.desc(ModelConstants.ID_PROPERTY));
List<EventEntity> entities = findListByStatement(query); List<EventEntity> entities = findListByStatement(new TenantId(tenantId), query);
return DaoUtil.convertDataList(entities); return DaoUtil.convertDataList(entities);
} }
private Optional<Event> save(EventEntity entity, boolean ifNotExists) { private Optional<Event> save(TenantId tenantId, EventEntity entity, boolean ifNotExists) {
try { try {
return saveAsync(entity, ifNotExists).get(); return saveAsync(tenantId, entity, ifNotExists).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException("Could not save EventEntity", e); throw new IllegalStateException("Could not save EventEntity", e);
} }
} }
private ListenableFuture<Optional<Event>> saveAsync(EventEntity entity, boolean ifNotExists) { private ListenableFuture<Optional<Event>> saveAsync(TenantId tenantId, EventEntity entity, boolean ifNotExists) {
if (entity.getId() == null) { if (entity.getId() == null) {
entity.setId(UUIDs.timeBased()); entity.setId(UUIDs.timeBased());
} }
@ -185,7 +185,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
if (ifNotExists) { if (ifNotExists) {
insert = insert.ifNotExists(); insert = insert.ifNotExists();
} }
ResultSetFuture resultSetFuture = executeAsyncWrite(insert); ResultSetFuture resultSetFuture = executeAsyncWrite(tenantId, insert);
return Futures.transform(resultSetFuture, rs -> { return Futures.transform(resultSetFuture, rs -> {
if (rs.wasApplied()) { if (rs.wasApplied()) {
return Optional.of(DaoUtil.getData(entity)); return Optional.of(DaoUtil.getData(entity));

3
dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.event;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
@ -36,7 +37,7 @@ public interface EventDao extends Dao<Event> {
* @param event the event object * @param event the event object
* @return saved event object * @return saved event object
*/ */
Event save(Event event); Event save(TenantId tenantId, Event event);
/** /**
* Save or update event object async * Save or update event object async

25
dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java

@ -27,6 +27,7 @@ import com.datastax.driver.core.TypeCodec;
import com.datastax.driver.core.exceptions.CodecNotFoundException; import com.datastax.driver.core.exceptions.CodecNotFoundException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.cassandra.CassandraCluster;
import org.thingsboard.server.dao.model.type.AuthorityCodec; import org.thingsboard.server.dao.model.type.AuthorityCodec;
import org.thingsboard.server.dao.model.type.ComponentLifecycleStateCodec; import org.thingsboard.server.dao.model.type.ComponentLifecycleStateCodec;
@ -84,37 +85,37 @@ public abstract class CassandraAbstractDao {
} }
} }
protected ResultSet executeRead(Statement statement) { protected ResultSet executeRead(TenantId tenantId, Statement statement) {
return execute(statement, defaultReadLevel); return execute(tenantId, statement, defaultReadLevel);
} }
protected ResultSet executeWrite(Statement statement) { protected ResultSet executeWrite(TenantId tenantId, Statement statement) {
return execute(statement, defaultWriteLevel); return execute(tenantId, statement, defaultWriteLevel);
} }
protected ResultSetFuture executeAsyncRead(Statement statement) { protected ResultSetFuture executeAsyncRead(TenantId tenantId, Statement statement) {
return executeAsync(statement, defaultReadLevel); return executeAsync(tenantId, statement, defaultReadLevel);
} }
protected ResultSetFuture executeAsyncWrite(Statement statement) { protected ResultSetFuture executeAsyncWrite(TenantId tenantId, Statement statement) {
return executeAsync(statement, defaultWriteLevel); return executeAsync(tenantId, statement, defaultWriteLevel);
} }
private ResultSet execute(Statement statement, ConsistencyLevel level) { private ResultSet execute(TenantId tenantId, Statement statement, ConsistencyLevel level) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Execute cassandra statement {}", statementToString(statement)); log.debug("Execute cassandra statement {}", statementToString(statement));
} }
return executeAsync(statement, level).getUninterruptibly(); return executeAsync(tenantId, statement, level).getUninterruptibly();
} }
private ResultSetFuture executeAsync(Statement statement, ConsistencyLevel level) { private ResultSetFuture executeAsync(TenantId tenantId, Statement statement, ConsistencyLevel level) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Execute cassandra async statement {}", statementToString(statement)); log.debug("Execute cassandra async statement {}", statementToString(statement));
} }
if (statement.getConsistencyLevel() == null) { if (statement.getConsistencyLevel() == null) {
statement.setConsistencyLevel(level); statement.setConsistencyLevel(level);
} }
return rateLimiter.submit(new CassandraStatementTask(getSession(), statement)); return rateLimiter.submit(new CassandraStatementTask(tenantId, getSession(), statement));
} }
private static String statementToString(Statement statement) { private static String statementToString(Statement statement) {

54
dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractModelDao.java

@ -27,6 +27,7 @@ import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseEntity;
@ -56,11 +57,11 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
return cluster.getMapper(getColumnFamilyClass()); return cluster.getMapper(getColumnFamilyClass());
} }
protected List<E> findListByStatement(Statement statement) { protected List<E> findListByStatement(TenantId tenantId, Statement statement) {
List<E> list = Collections.emptyList(); List<E> list = Collections.emptyList();
if (statement != null) { if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSet resultSet = executeRead(statement); ResultSet resultSet = executeRead(tenantId, statement);
Result<E> result = getMapper().map(resultSet); Result<E> result = getMapper().map(resultSet);
if (result != null) { if (result != null) {
list = result.all(); list = result.all();
@ -69,10 +70,10 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
return list; return list;
} }
protected ListenableFuture<List<D>> findListByStatementAsync(Statement statement) { protected ListenableFuture<List<D>> findListByStatementAsync(TenantId tenantId, Statement statement) {
if (statement != null) { if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(statement); ResultSetFuture resultSetFuture = executeAsyncRead(tenantId, statement);
return Futures.transform(resultSetFuture, new Function<ResultSet, List<D>>() { return Futures.transform(resultSetFuture, new Function<ResultSet, List<D>>() {
@Nullable @Nullable
@Override @Override
@ -90,11 +91,11 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
return Futures.immediateFuture(Collections.emptyList()); return Futures.immediateFuture(Collections.emptyList());
} }
protected E findOneByStatement(Statement statement) { protected E findOneByStatement(TenantId tenantId, Statement statement) {
E object = null; E object = null;
if (statement != null) { if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSet resultSet = executeRead(statement); ResultSet resultSet = executeRead(tenantId, statement);
Result<E> result = getMapper().map(resultSet); Result<E> result = getMapper().map(resultSet);
if (result != null) { if (result != null) {
object = result.one(); object = result.one();
@ -103,10 +104,10 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
return object; return object;
} }
protected ListenableFuture<D> findOneByStatementAsync(Statement statement) { protected ListenableFuture<D> findOneByStatementAsync(TenantId tenantId, Statement statement) {
if (statement != null) { if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(statement); ResultSetFuture resultSetFuture = executeAsyncRead(tenantId, statement);
return Futures.transform(resultSetFuture, new Function<ResultSet, D>() { return Futures.transform(resultSetFuture, new Function<ResultSet, D>() {
@Nullable @Nullable
@Override @Override
@ -128,16 +129,16 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
return getMapper().saveQuery(dto); return getMapper().saveQuery(dto);
} }
protected EntityResultSet<E> saveWithResult(E entity) { protected EntityResultSet<E> saveWithResult(TenantId tenantId, E entity) {
log.debug("Save entity {}", entity); log.debug("Save entity {}", entity);
if (entity.getId() == null) { if (entity.getId() == null) {
entity.setId(UUIDs.timeBased()); entity.setId(UUIDs.timeBased());
} else if (isDeleteOnSave()) { } else if (isDeleteOnSave()) {
removeById(entity.getId()); removeById(tenantId, entity.getId());
} }
Statement saveStatement = getSaveQuery(entity); Statement saveStatement = getSaveQuery(entity);
saveStatement.setConsistencyLevel(cluster.getDefaultWriteConsistencyLevel()); saveStatement.setConsistencyLevel(cluster.getDefaultWriteConsistencyLevel());
ResultSet resultSet = executeWrite(saveStatement); ResultSet resultSet = executeWrite(tenantId, saveStatement);
return new EntityResultSet<>(resultSet, entity); return new EntityResultSet<>(resultSet, entity);
} }
@ -146,7 +147,7 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
} }
@Override @Override
public D save(D domain) { public D save(TenantId tenantId, D domain) {
E entity; E entity;
try { try {
entity = getColumnFamilyClass().getConstructor(domain.getClass()).newInstance(domain); entity = getColumnFamilyClass().getConstructor(domain.getClass()).newInstance(domain);
@ -156,48 +157,39 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
} }
entity = updateSearchTextIfPresent(entity); entity = updateSearchTextIfPresent(entity);
log.debug("Saving entity {}", entity); log.debug("Saving entity {}", entity);
entity = saveWithResult(entity).getEntity(); entity = saveWithResult(tenantId, entity).getEntity();
return DaoUtil.getData(entity); return DaoUtil.getData(entity);
} }
@Override @Override
public D findById(UUID key) { public D findById(TenantId tenantId, UUID key) {
log.debug("Get entity by key {}", key); log.debug("Get entity by key {}", key);
Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key)); Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key));
log.trace("Execute query {}", query); log.trace("Execute query {}", query);
E entity = findOneByStatement(query); E entity = findOneByStatement(tenantId, query);
return DaoUtil.getData(entity); return DaoUtil.getData(entity);
} }
@Override @Override
public ListenableFuture<D> findByIdAsync(UUID key) { public ListenableFuture<D> findByIdAsync(TenantId tenantId, UUID key) {
log.debug("Get entity by key {}", key); log.debug("Get entity by key {}", key);
Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key)); Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key));
log.trace("Execute query {}", query); log.trace("Execute query {}", query);
return findOneByStatementAsync(query); return findOneByStatementAsync(tenantId, query);
} }
@Override @Override
public boolean removeById(UUID key) { public boolean removeById(TenantId tenantId, UUID key) {
Statement delete = QueryBuilder.delete().all().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key)); Statement delete = QueryBuilder.delete().all().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key));
log.debug("Remove request: {}", delete.toString()); log.debug("Remove request: {}", delete.toString());
return executeWrite(delete).wasApplied(); return executeWrite(tenantId, delete).wasApplied();
} }
@Override @Override
public List<D> find() { public List<D> find(TenantId tenantId) {
log.debug("Get all entities from column family {}", getColumnFamilyName()); log.debug("Get all entities from column family {}", getColumnFamilyName());
List<E> entities = findListByStatement(QueryBuilder.select().all().from(getColumnFamilyName()).setConsistencyLevel(cluster.getDefaultReadConsistencyLevel())); List<E> entities = findListByStatement(tenantId, QueryBuilder.select().all().from(getColumnFamilyName()).setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()));
return DaoUtil.convertDataList(entities); return DaoUtil.convertDataList(entities);
} }
protected static <T> Function<BaseEntity<T>, T> toDataFunction() {
return new Function<BaseEntity<T>, T>() {
@Nullable
@Override
public T apply(@Nullable BaseEntity<T> entity) {
return entity != null ? entity.toData() : null;
}
};
}
} }

15
dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractSearchTextDao.java

@ -21,6 +21,7 @@ import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Select.Where; import com.datastax.driver.core.querybuilder.Select.Where;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.SearchTextEntity;
@ -43,38 +44,38 @@ public abstract class CassandraAbstractSearchTextDao<E extends SearchTextEntity<
return entity; return entity;
} }
protected List<E> findPageWithTextSearch(String searchView, List<Clause> clauses, TextPageLink pageLink) { protected List<E> findPageWithTextSearch(TenantId tenantId, String searchView, List<Clause> clauses, TextPageLink pageLink) {
Select select = select().from(searchView); Select select = select().from(searchView);
Where query = select.where(); Where query = select.where();
for (Clause clause : clauses) { for (Clause clause : clauses) {
query.and(clause); query.and(clause);
} }
query.limit(pageLink.getLimit()); query.limit(pageLink.getLimit());
if (!StringUtils.isEmpty(pageLink.getTextOffset())) { if (!StringUtils.isEmpty(pageLink.getTextOffset())) {
query.and(eq(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextOffset())); query.and(eq(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextOffset()));
query.and(QueryBuilder.lt(ModelConstants.ID_PROPERTY, pageLink.getIdOffset())); query.and(QueryBuilder.lt(ModelConstants.ID_PROPERTY, pageLink.getIdOffset()));
List<E> result = findListByStatement(query); List<E> result = findListByStatement(tenantId, query);
if (result.size() < pageLink.getLimit()) { if (result.size() < pageLink.getLimit()) {
select = select().from(searchView); select = select().from(searchView);
query = select.where(); query = select.where();
for (Clause clause : clauses) { for (Clause clause : clauses) {
query.and(clause); query.and(clause);
} }
query.and(QueryBuilder.gt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextOffset())); query.and(QueryBuilder.gt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextOffset()));
if (!StringUtils.isEmpty(pageLink.getTextSearch())) { if (!StringUtils.isEmpty(pageLink.getTextSearch())) {
query.and(QueryBuilder.lt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearchBound())); query.and(QueryBuilder.lt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearchBound()));
} }
int limit = pageLink.getLimit() - result.size(); int limit = pageLink.getLimit() - result.size();
query.limit(limit); query.limit(limit);
result.addAll(findListByStatement(query)); result.addAll(findListByStatement(tenantId, query));
} }
return result; return result;
} else if (!StringUtils.isEmpty(pageLink.getTextSearch())) { } else if (!StringUtils.isEmpty(pageLink.getTextSearch())) {
query.and(QueryBuilder.gte(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearch())); query.and(QueryBuilder.gte(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearch()));
query.and(QueryBuilder.lt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearchBound())); query.and(QueryBuilder.lt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearchBound()));
return findListByStatement(query); return findListByStatement(tenantId, query);
} else { } else {
return findListByStatement(query); return findListByStatement(tenantId, query);
} }
} }

21
dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractSearchTimeDao.java

@ -21,6 +21,7 @@ import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Select.Where; import com.datastax.driver.core.querybuilder.Select.Where;
import com.datastax.driver.core.utils.UUIDs; import com.datastax.driver.core.utils.UUIDs;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseEntity;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
@ -35,24 +36,24 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
public abstract class CassandraAbstractSearchTimeDao<E extends BaseEntity<D>, D> extends CassandraAbstractModelDao<E, D> { public abstract class CassandraAbstractSearchTimeDao<E extends BaseEntity<D>, D> extends CassandraAbstractModelDao<E, D> {
protected List<E> findPageWithTimeSearch(String searchView, List<Clause> clauses, TimePageLink pageLink) { protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, TimePageLink pageLink) {
return findPageWithTimeSearch(searchView, clauses, Collections.emptyList(), pageLink); return findPageWithTimeSearch(tenantId, searchView, clauses, Collections.emptyList(), pageLink);
} }
protected List<E> findPageWithTimeSearch(String searchView, List<Clause> clauses, Ordering ordering, TimePageLink pageLink) { protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, Ordering ordering, TimePageLink pageLink) {
return findPageWithTimeSearch(searchView, clauses, Collections.singletonList(ordering), pageLink); return findPageWithTimeSearch(tenantId, searchView, clauses, Collections.singletonList(ordering), pageLink);
} }
protected List<E> findPageWithTimeSearch(String searchView, List<Clause> clauses, List<Ordering> topLevelOrderings, TimePageLink pageLink) { protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, List<Ordering> topLevelOrderings, TimePageLink pageLink) {
return findPageWithTimeSearch(searchView, clauses, topLevelOrderings, pageLink, ModelConstants.ID_PROPERTY); return findPageWithTimeSearch(tenantId, searchView, clauses, topLevelOrderings, pageLink, ModelConstants.ID_PROPERTY);
} }
protected List<E> findPageWithTimeSearch(String searchView, List<Clause> clauses, TimePageLink pageLink, String idColumn) { protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, TimePageLink pageLink, String idColumn) {
return findPageWithTimeSearch(searchView, clauses, Collections.emptyList(), pageLink, idColumn); return findPageWithTimeSearch(tenantId, searchView, clauses, Collections.emptyList(), pageLink, idColumn);
} }
protected List<E> findPageWithTimeSearch(String searchView, List<Clause> clauses, List<Ordering> topLevelOrderings, TimePageLink pageLink, String idColumn) { protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, List<Ordering> topLevelOrderings, TimePageLink pageLink, String idColumn) {
return findListByStatement(buildQuery(searchView, clauses, topLevelOrderings, pageLink, idColumn)); return findListByStatement(tenantId, buildQuery(searchView, clauses, topLevelOrderings, pageLink, idColumn));
} }
public static Where buildQuery(String searchView, List<Clause> clauses, TimePageLink pageLink, String idColumn) { public static Where buildQuery(String searchView, List<Clause> clauses, TimePageLink pageLink, String idColumn) {

15
dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java

@ -22,11 +22,16 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor; import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor;
import org.thingsboard.server.dao.util.AsyncTaskContext; import org.thingsboard.server.dao.util.AsyncTaskContext;
import org.thingsboard.server.dao.util.NoSqlAnyDao; import org.thingsboard.server.dao.util.NoSqlAnyDao;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/** /**
* Created by ashvayka on 24.10.18. * Created by ashvayka on 24.10.18.
@ -42,17 +47,19 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<
@Value("${cassandra.query.permit_max_wait_time}") long maxWaitTime, @Value("${cassandra.query.permit_max_wait_time}") long maxWaitTime,
@Value("${cassandra.query.dispatcher_threads:2}") int dispatcherThreads, @Value("${cassandra.query.dispatcher_threads:2}") int dispatcherThreads,
@Value("${cassandra.query.callback_threads:2}") int callbackThreads, @Value("${cassandra.query.callback_threads:2}") int callbackThreads,
@Value("${cassandra.query.poll_ms:50}") long pollMs) { @Value("${cassandra.query.poll_ms:50}") long pollMs,
super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs); @Value("${cassandra.query.tenant_rate_limits.enabled}") boolean tenantRateLimitsEnabled,
@Value("${cassandra.query.tenant_rate_limits.configuration}") String tenantRateLimitsConfiguration) {
super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, tenantRateLimitsEnabled, tenantRateLimitsConfiguration);
} }
@Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}") @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}")
public void printStats() { public void printStats() {
log.info("Permits queueSize [{}] totalAdded [{}] totalLaunched [{}] totalReleased [{}] totalFailed [{}] totalExpired [{}] totalRejected [{}] currBuffer [{}] ", log.info("Permits queueSize [{}] totalAdded [{}] totalLaunched [{}] totalReleased [{}] totalFailed [{}] totalExpired [{}] totalRejected [{}] totalRateLimited [{}] currBuffer [{}] ",
getQueueSize(), getQueueSize(),
totalAdded.getAndSet(0), totalLaunched.getAndSet(0), totalReleased.getAndSet(0), totalAdded.getAndSet(0), totalLaunched.getAndSet(0), totalReleased.getAndSet(0),
totalFailed.getAndSet(0), totalExpired.getAndSet(0), totalRejected.getAndSet(0), totalFailed.getAndSet(0), totalExpired.getAndSet(0), totalRejected.getAndSet(0),
concurrencyLevel.get()); totalRateLimited.getAndSet(0), concurrencyLevel.get());
} }
@PreDestroy @PreDestroy

2
dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraStatementTask.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.nosql;
import com.datastax.driver.core.Session; import com.datastax.driver.core.Session;
import com.datastax.driver.core.Statement; import com.datastax.driver.core.Statement;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.util.AsyncTask; import org.thingsboard.server.dao.util.AsyncTask;
/** /**
@ -26,6 +27,7 @@ import org.thingsboard.server.dao.util.AsyncTask;
@Data @Data
public class CassandraStatementTask implements AsyncTask { public class CassandraStatementTask implements AsyncTask {
private final TenantId tenantId;
private final Session session; private final Session session;
private final Statement statement; private final Statement statement;

77
dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java

@ -29,6 +29,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
@ -83,45 +84,45 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findAllByFrom(EntityId from, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findAllByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) {
BoundStatement stmt = getFindAllByFromStmt().bind() BoundStatement stmt = getFindAllByFromStmt().bind()
.setUUID(0, from.getId()) .setUUID(0, from.getId())
.setString(1, from.getEntityType().name()) .setString(1, from.getEntityType().name())
.set(2, typeGroup, relationTypeGroupCodec); .set(2, typeGroup, relationTypeGroupCodec);
return executeAsyncRead(from, stmt); return executeAsyncRead(tenantId, from, stmt);
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findAllByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findAllByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getFindAllByFromAndTypeStmt().bind() BoundStatement stmt = getFindAllByFromAndTypeStmt().bind()
.setUUID(0, from.getId()) .setUUID(0, from.getId())
.setString(1, from.getEntityType().name()) .setString(1, from.getEntityType().name())
.set(2, typeGroup, relationTypeGroupCodec) .set(2, typeGroup, relationTypeGroupCodec)
.setString(3, relationType); .setString(3, relationType);
return executeAsyncRead(from, stmt); return executeAsyncRead(tenantId, from, stmt);
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findAllByTo(EntityId to, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findAllByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup) {
BoundStatement stmt = getFindAllByToStmt().bind() BoundStatement stmt = getFindAllByToStmt().bind()
.setUUID(0, to.getId()) .setUUID(0, to.getId())
.setString(1, to.getEntityType().name()) .setString(1, to.getEntityType().name())
.set(2, typeGroup, relationTypeGroupCodec); .set(2, typeGroup, relationTypeGroupCodec);
return executeAsyncRead(to, stmt); return executeAsyncRead(tenantId, to, stmt);
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findAllByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findAllByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getFindAllByToAndTypeStmt().bind() BoundStatement stmt = getFindAllByToAndTypeStmt().bind()
.setUUID(0, to.getId()) .setUUID(0, to.getId())
.setString(1, to.getEntityType().name()) .setString(1, to.getEntityType().name())
.set(2, typeGroup, relationTypeGroupCodec) .set(2, typeGroup, relationTypeGroupCodec)
.setString(3, relationType); .setString(3, relationType);
return executeAsyncRead(to, stmt); return executeAsyncRead(tenantId, to, stmt);
} }
@Override @Override
public ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<Boolean> checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getCheckRelationStmt().bind() BoundStatement stmt = getCheckRelationStmt().bind()
.setUUID(0, from.getId()) .setUUID(0, from.getId())
.setString(1, from.getEntityType().name()) .setString(1, from.getEntityType().name())
@ -129,11 +130,11 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
.setString(3, to.getEntityType().name()) .setString(3, to.getEntityType().name())
.set(4, typeGroup, relationTypeGroupCodec) .set(4, typeGroup, relationTypeGroupCodec)
.setString(5, relationType); .setString(5, relationType);
return getFuture(executeAsyncRead(stmt), rs -> rs != null ? rs.one() != null : false); return getFuture(executeAsyncRead(tenantId, stmt), rs -> rs != null ? rs.one() != null : false);
} }
@Override @Override
public ListenableFuture<EntityRelation> getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<EntityRelation> getRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getCheckRelationStmt().bind() BoundStatement stmt = getCheckRelationStmt().bind()
.setUUID(0, from.getId()) .setUUID(0, from.getId())
.setString(1, from.getEntityType().name()) .setString(1, from.getEntityType().name())
@ -141,24 +142,24 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
.setString(3, to.getEntityType().name()) .setString(3, to.getEntityType().name())
.set(4, typeGroup, relationTypeGroupCodec) .set(4, typeGroup, relationTypeGroupCodec)
.setString(5, relationType); .setString(5, relationType);
return getFuture(executeAsyncRead(stmt), rs -> rs != null ? getEntityRelation(rs.one()) : null); return getFuture(executeAsyncRead(tenantId, stmt), rs -> rs != null ? getEntityRelation(rs.one()) : null);
} }
@Override @Override
public boolean saveRelation(EntityRelation relation) { public boolean saveRelation(TenantId tenantId, EntityRelation relation) {
BoundStatement stmt = getSaveRelationStatement(relation); BoundStatement stmt = getSaveRelationStatement(tenantId, relation);
ResultSet rs = executeWrite(stmt); ResultSet rs = executeWrite(tenantId, stmt);
return rs.wasApplied(); return rs.wasApplied();
} }
@Override @Override
public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) { public ListenableFuture<Boolean> saveRelationAsync(TenantId tenantId, EntityRelation relation) {
BoundStatement stmt = getSaveRelationStatement(relation); BoundStatement stmt = getSaveRelationStatement(tenantId, relation);
ResultSetFuture future = executeAsyncWrite(stmt); ResultSetFuture future = executeAsyncWrite(tenantId, stmt);
return getBooleanListenableFuture(future); return getBooleanListenableFuture(future);
} }
private BoundStatement getSaveRelationStatement(EntityRelation relation) { private BoundStatement getSaveRelationStatement(TenantId tenantId, EntityRelation relation) {
BoundStatement stmt = getSaveStmt().bind() BoundStatement stmt = getSaveStmt().bind()
.setUUID(0, relation.getFrom().getId()) .setUUID(0, relation.getFrom().getId())
.setString(1, relation.getFrom().getEntityType().name()) .setString(1, relation.getFrom().getEntityType().name())
@ -171,30 +172,30 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
} }
@Override @Override
public boolean deleteRelation(EntityRelation relation) { public boolean deleteRelation(TenantId tenantId, EntityRelation relation) {
return deleteRelation(relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup()); return deleteRelation(tenantId, relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup());
} }
@Override @Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) { public ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityRelation relation) {
return deleteRelationAsync(relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup()); return deleteRelationAsync(tenantId, relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup());
} }
@Override @Override
public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public boolean deleteRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getDeleteRelationStatement(from, to, relationType, typeGroup); BoundStatement stmt = getDeleteRelationStatement(tenantId, from, to, relationType, typeGroup);
ResultSet rs = executeWrite(stmt); ResultSet rs = executeWrite(tenantId, stmt);
return rs.wasApplied(); return rs.wasApplied();
} }
@Override @Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getDeleteRelationStatement(from, to, relationType, typeGroup); BoundStatement stmt = getDeleteRelationStatement(tenantId, from, to, relationType, typeGroup);
ResultSetFuture future = executeAsyncWrite(stmt); ResultSetFuture future = executeAsyncWrite(tenantId, stmt);
return getBooleanListenableFuture(future); return getBooleanListenableFuture(future);
} }
private BoundStatement getDeleteRelationStatement(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { private BoundStatement getDeleteRelationStatement(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getDeleteStmt().bind() BoundStatement stmt = getDeleteStmt().bind()
.setUUID(0, from.getId()) .setUUID(0, from.getId())
.setString(1, from.getEntityType().name()) .setString(1, from.getEntityType().name())
@ -206,26 +207,26 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
} }
@Override @Override
public boolean deleteOutboundRelations(EntityId entity) { public boolean deleteOutboundRelations(TenantId tenantId, EntityId entity) {
BoundStatement stmt = getDeleteAllByEntityStmt().bind() BoundStatement stmt = getDeleteAllByEntityStmt().bind()
.setUUID(0, entity.getId()) .setUUID(0, entity.getId())
.setString(1, entity.getEntityType().name()); .setString(1, entity.getEntityType().name());
ResultSet rs = executeWrite(stmt); ResultSet rs = executeWrite(tenantId, stmt);
return rs.wasApplied(); return rs.wasApplied();
} }
@Override @Override
public ListenableFuture<Boolean> deleteOutboundRelationsAsync(EntityId entity) { public ListenableFuture<Boolean> deleteOutboundRelationsAsync(TenantId tenantId, EntityId entity) {
BoundStatement stmt = getDeleteAllByEntityStmt().bind() BoundStatement stmt = getDeleteAllByEntityStmt().bind()
.setUUID(0, entity.getId()) .setUUID(0, entity.getId())
.setString(1, entity.getEntityType().name()); .setString(1, entity.getEntityType().name());
ResultSetFuture future = executeAsyncWrite(stmt); ResultSetFuture future = executeAsyncWrite(tenantId, stmt);
return getBooleanListenableFuture(future); return getBooleanListenableFuture(future);
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findRelations(EntityId from, String relationType, RelationTypeGroup typeGroup, EntityType childType, TimePageLink pageLink) { public ListenableFuture<List<EntityRelation>> findRelations(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup, EntityType childType, TimePageLink pageLink) {
Select.Where query = CassandraAbstractSearchTimeDao.buildQuery(ModelConstants.RELATION_BY_TYPE_AND_CHILD_TYPE_VIEW_NAME, Select.Where query = CassandraAbstractSearchTimeDao.buildQuery(ModelConstants.RELATION_BY_TYPE_AND_CHILD_TYPE_VIEW_NAME,
Arrays.asList(eq(ModelConstants.RELATION_FROM_ID_PROPERTY, from.getId()), Arrays.asList(eq(ModelConstants.RELATION_FROM_ID_PROPERTY, from.getId()),
eq(ModelConstants.RELATION_FROM_TYPE_PROPERTY, from.getEntityType().name()), eq(ModelConstants.RELATION_FROM_TYPE_PROPERTY, from.getEntityType().name()),
@ -241,7 +242,7 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY) QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)
), ),
pageLink, ModelConstants.RELATION_TO_ID_PROPERTY); pageLink, ModelConstants.RELATION_TO_ID_PROPERTY);
return getFuture(executeAsyncRead(query), this::getEntityRelations); return getFuture(executeAsyncRead(tenantId, query), this::getEntityRelations);
} }
private PreparedStatement getSaveStmt() { private PreparedStatement getSaveStmt() {
@ -347,9 +348,9 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
return EntityIdFactory.getByTypeAndUuid(row.getString(typeColumn), row.getUUID(uuidColumn)); return EntityIdFactory.getByTypeAndUuid(row.getString(typeColumn), row.getUUID(uuidColumn));
} }
private ListenableFuture<List<EntityRelation>> executeAsyncRead(EntityId from, BoundStatement stmt) { private ListenableFuture<List<EntityRelation>> executeAsyncRead(TenantId tenantId, EntityId from, BoundStatement stmt) {
log.debug("Generated query [{}] for entity {}", stmt, from); log.debug("Generated query [{}] for entity {}", stmt, from);
return getFuture(executeAsyncRead(stmt), rs -> getEntityRelations(rs)); return getFuture(executeAsyncRead(tenantId, stmt), rs -> getEntityRelations(rs));
} }
private ListenableFuture<Boolean> getBooleanListenableFuture(ResultSetFuture rsFuture) { private ListenableFuture<Boolean> getBooleanListenableFuture(ResultSetFuture rsFuture) {

132
dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java

@ -26,7 +26,9 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching; import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
@ -66,27 +68,27 @@ public class BaseRelationService implements RelationService {
private CacheManager cacheManager; private CacheManager cacheManager;
@Override @Override
public ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<Boolean> checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
log.trace("Executing checkRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); log.trace("Executing checkRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup);
validate(from, to, relationType, typeGroup); validate(from, to, relationType, typeGroup);
return relationDao.checkRelation(from, to, relationType, typeGroup); return relationDao.checkRelation(tenantId, from, to, relationType, typeGroup);
} }
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}") @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}")
@Override @Override
public EntityRelation getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public EntityRelation getRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
try { try {
return getRelationAsync(from, to, relationType, typeGroup).get(); return getRelationAsync(tenantId, from, to, relationType, typeGroup).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
public ListenableFuture<EntityRelation> getRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<EntityRelation> getRelationAsync(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
log.trace("Executing EntityRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); log.trace("Executing EntityRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup);
validate(from, to, relationType, typeGroup); validate(from, to, relationType, typeGroup);
return relationDao.getRelation(from, to, relationType, typeGroup); return relationDao.getRelation(tenantId, from, to, relationType, typeGroup);
} }
@Caching(evict = { @Caching(evict = {
@ -97,10 +99,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}") @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
}) })
@Override @Override
public boolean saveRelation(EntityRelation relation) { public boolean saveRelation(TenantId tenantId, EntityRelation relation) {
log.trace("Executing saveRelation [{}]", relation); log.trace("Executing saveRelation [{}]", relation);
validate(relation); validate(relation);
return relationDao.saveRelation(relation); return relationDao.saveRelation(tenantId, relation);
} }
@Caching(evict = { @Caching(evict = {
@ -111,10 +113,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}") @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
}) })
@Override @Override
public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) { public ListenableFuture<Boolean> saveRelationAsync(TenantId tenantId, EntityRelation relation) {
log.trace("Executing saveRelationAsync [{}]", relation); log.trace("Executing saveRelationAsync [{}]", relation);
validate(relation); validate(relation);
return relationDao.saveRelationAsync(relation); return relationDao.saveRelationAsync(tenantId, relation);
} }
@Caching(evict = { @Caching(evict = {
@ -125,10 +127,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}") @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
}) })
@Override @Override
public boolean deleteRelation(EntityRelation relation) { public boolean deleteRelation(TenantId tenantId, EntityRelation relation) {
log.trace("Executing deleteRelation [{}]", relation); log.trace("Executing deleteRelation [{}]", relation);
validate(relation); validate(relation);
return relationDao.deleteRelation(relation); return relationDao.deleteRelation(tenantId, relation);
} }
@Caching(evict = { @Caching(evict = {
@ -139,10 +141,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}") @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
}) })
@Override @Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) { public ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityRelation relation) {
log.trace("Executing deleteRelationAsync [{}]", relation); log.trace("Executing deleteRelationAsync [{}]", relation);
validate(relation); validate(relation);
return relationDao.deleteRelationAsync(relation); return relationDao.deleteRelationAsync(tenantId, relation);
} }
@Caching(evict = { @Caching(evict = {
@ -153,10 +155,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}") @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
}) })
@Override @Override
public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public boolean deleteRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
log.trace("Executing deleteRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); log.trace("Executing deleteRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup);
validate(from, to, relationType, typeGroup); validate(from, to, relationType, typeGroup);
return relationDao.deleteRelation(from, to, relationType, typeGroup); return relationDao.deleteRelation(tenantId, from, to, relationType, typeGroup);
} }
@Caching(evict = { @Caching(evict = {
@ -167,69 +169,69 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}") @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
}) })
@Override @Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
log.trace("Executing deleteRelationAsync [{}][{}][{}][{}]", from, to, relationType, typeGroup); log.trace("Executing deleteRelationAsync [{}][{}][{}][{}]", from, to, relationType, typeGroup);
validate(from, to, relationType, typeGroup); validate(from, to, relationType, typeGroup);
return relationDao.deleteRelationAsync(from, to, relationType, typeGroup); return relationDao.deleteRelationAsync(tenantId, from, to, relationType, typeGroup);
} }
@Override @Override
public void deleteEntityRelations(EntityId entityId) { public void deleteEntityRelations(TenantId tenantId, EntityId entityId) {
try { try {
deleteEntityRelationsAsync(entityId).get(); deleteEntityRelationsAsync(tenantId, entityId).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
public ListenableFuture<Void> deleteEntityRelationsAsync(EntityId entityId) { public ListenableFuture<Void> deleteEntityRelationsAsync(TenantId tenantId, EntityId entityId) {
Cache cache = cacheManager.getCache(RELATIONS_CACHE); Cache cache = cacheManager.getCache(RELATIONS_CACHE);
log.trace("Executing deleteEntityRelationsAsync [{}]", entityId); log.trace("Executing deleteEntityRelationsAsync [{}]", entityId);
validate(entityId); validate(entityId);
List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>(); List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>();
for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) {
inboundRelationsList.add(relationDao.findAllByTo(entityId, typeGroup)); inboundRelationsList.add(relationDao.findAllByTo(tenantId, entityId, typeGroup));
} }
ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList); ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList);
List<ListenableFuture<List<EntityRelation>>> outboundRelationsList = new ArrayList<>(); List<ListenableFuture<List<EntityRelation>>> outboundRelationsList = new ArrayList<>();
for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) {
outboundRelationsList.add(relationDao.findAllByFrom(entityId, typeGroup)); outboundRelationsList.add(relationDao.findAllByFrom(tenantId, entityId, typeGroup));
} }
ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList); ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList);
ListenableFuture<List<Boolean>> inboundDeletions = Futures.transformAsync(inboundRelations, ListenableFuture<List<Boolean>> inboundDeletions = Futures.transformAsync(inboundRelations,
relations -> { relations -> {
List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(relations, cache, true); List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(tenantId, relations, cache, true);
return Futures.allAsList(results); return Futures.allAsList(results);
}); });
ListenableFuture<List<Boolean>> outboundDeletions = Futures.transformAsync(outboundRelations, ListenableFuture<List<Boolean>> outboundDeletions = Futures.transformAsync(outboundRelations,
relations -> { relations -> {
List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(relations, cache, false); List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(tenantId, relations, cache, false);
return Futures.allAsList(results); return Futures.allAsList(results);
}); });
ListenableFuture<List<List<Boolean>>> deletionsFuture = Futures.allAsList(inboundDeletions, outboundDeletions); ListenableFuture<List<List<Boolean>>> deletionsFuture = Futures.allAsList(inboundDeletions, outboundDeletions);
return Futures.transform(Futures.transformAsync(deletionsFuture, (deletions) -> relationDao.deleteOutboundRelationsAsync(entityId)), result -> null); return Futures.transform(Futures.transformAsync(deletionsFuture, (deletions) -> relationDao.deleteOutboundRelationsAsync(tenantId, entityId)), result -> null);
} }
private List<ListenableFuture<Boolean>> deleteRelationGroupsAsync(List<List<EntityRelation>> relations, Cache cache, boolean deleteFromDb) { private List<ListenableFuture<Boolean>> deleteRelationGroupsAsync(TenantId tenantId, List<List<EntityRelation>> relations, Cache cache, boolean deleteFromDb) {
List<ListenableFuture<Boolean>> results = new ArrayList<>(); List<ListenableFuture<Boolean>> results = new ArrayList<>();
for (List<EntityRelation> relationList : relations) { for (List<EntityRelation> relationList : relations) {
relationList.forEach(relation -> results.add(deleteAsync(cache, relation, deleteFromDb))); relationList.forEach(relation -> results.add(deleteAsync(tenantId, cache, relation, deleteFromDb)));
} }
return results; return results;
} }
private ListenableFuture<Boolean> deleteAsync(Cache cache, EntityRelation relation, boolean deleteFromDb) { private ListenableFuture<Boolean> deleteAsync(TenantId tenantId, Cache cache, EntityRelation relation, boolean deleteFromDb) {
cacheEviction(relation, cache); cacheEviction(relation, cache);
if (deleteFromDb) { if (deleteFromDb) {
return relationDao.deleteRelationAsync(relation); return relationDao.deleteRelationAsync(tenantId, relation);
} else { } else {
return Futures.immediateFuture(false); return Futures.immediateFuture(false);
} }
@ -272,18 +274,18 @@ public class BaseRelationService implements RelationService {
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}") @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}")
@Override @Override
public List<EntityRelation> findByFrom(EntityId from, RelationTypeGroup typeGroup) { public List<EntityRelation> findByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) {
validate(from); validate(from);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
try { try {
return relationDao.findAllByFrom(from, typeGroup).get(); return relationDao.findAllByFrom(tenantId, from, typeGroup).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findByFromAsync(EntityId from, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findByFromAsync(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) {
log.trace("Executing findByFrom [{}][{}]", from, typeGroup); log.trace("Executing findByFrom [{}][{}]", from, typeGroup);
validate(from); validate(from);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
@ -298,7 +300,7 @@ public class BaseRelationService implements RelationService {
if (fromCache != null) { if (fromCache != null) {
return Futures.immediateFuture(fromCache); return Futures.immediateFuture(fromCache);
} else { } else {
ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByFrom(from, typeGroup); ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByFrom(tenantId, from, typeGroup);
Futures.addCallback(relationsFuture, Futures.addCallback(relationsFuture,
new FutureCallback<List<EntityRelation>>() { new FutureCallback<List<EntityRelation>>() {
@Override @Override
@ -313,16 +315,16 @@ public class BaseRelationService implements RelationService {
} }
@Override @Override
public ListenableFuture<List<EntityRelationInfo>> findInfoByFrom(EntityId from, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelationInfo>> findInfoByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) {
log.trace("Executing findInfoByFrom [{}][{}]", from, typeGroup); log.trace("Executing findInfoByFrom [{}][{}]", from, typeGroup);
validate(from); validate(from);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(from, typeGroup); ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(tenantId, from, typeGroup);
return Futures.transformAsync(relations, return Futures.transformAsync(relations,
relations1 -> { relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.forEach(relation -> relations1.forEach(relation ->
futures.add(fetchRelationInfoAsync(relation, futures.add(fetchRelationInfoAsync(tenantId, relation,
EntityRelation::getTo, EntityRelation::getTo,
EntityRelationInfo::setToName)) EntityRelationInfo::setToName))
); );
@ -332,37 +334,37 @@ public class BaseRelationService implements RelationService {
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}") @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}")
@Override @Override
public List<EntityRelation> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { public List<EntityRelation> findByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup) {
try { try {
return findByFromAndTypeAsync(from, relationType, typeGroup).get(); return findByFromAndTypeAsync(tenantId, from, relationType, typeGroup).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findByFromAndTypeAsync(EntityId from, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findByFromAndTypeAsync(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup) {
log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup); log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup);
validate(from); validate(from);
validateType(relationType); validateType(relationType);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
return relationDao.findAllByFromAndType(from, relationType, typeGroup); return relationDao.findAllByFromAndType(tenantId, from, relationType, typeGroup);
} }
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}") @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}")
@Override @Override
public List<EntityRelation> findByTo(EntityId to, RelationTypeGroup typeGroup) { public List<EntityRelation> findByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup) {
validate(to); validate(to);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
try { try {
return relationDao.findAllByTo(to, typeGroup).get(); return relationDao.findAllByTo(tenantId, to, typeGroup).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findByToAsync(EntityId to, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findByToAsync(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup) {
log.trace("Executing findByTo [{}][{}]", to, typeGroup); log.trace("Executing findByTo [{}][{}]", to, typeGroup);
validate(to); validate(to);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
@ -377,7 +379,7 @@ public class BaseRelationService implements RelationService {
if (fromCache != null) { if (fromCache != null) {
return Futures.immediateFuture(fromCache); return Futures.immediateFuture(fromCache);
} else { } else {
ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByTo(to, typeGroup); ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByTo(tenantId, to, typeGroup);
Futures.addCallback(relationsFuture, Futures.addCallback(relationsFuture,
new FutureCallback<List<EntityRelation>>() { new FutureCallback<List<EntityRelation>>() {
@Override @Override
@ -392,16 +394,16 @@ public class BaseRelationService implements RelationService {
} }
@Override @Override
public ListenableFuture<List<EntityRelationInfo>> findInfoByTo(EntityId to, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelationInfo>> findInfoByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup) {
log.trace("Executing findInfoByTo [{}][{}]", to, typeGroup); log.trace("Executing findInfoByTo [{}][{}]", to, typeGroup);
validate(to); validate(to);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByTo(to, typeGroup); ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByTo(tenantId, to, typeGroup);
return Futures.transformAsync(relations, return Futures.transformAsync(relations,
relations1 -> { relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.forEach(relation -> relations1.forEach(relation ->
futures.add(fetchRelationInfoAsync(relation, futures.add(fetchRelationInfoAsync(tenantId, relation,
EntityRelation::getFrom, EntityRelation::getFrom,
EntityRelationInfo::setFromName)) EntityRelationInfo::setFromName))
); );
@ -409,10 +411,10 @@ public class BaseRelationService implements RelationService {
}); });
} }
private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation, private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(TenantId tenantId, EntityRelation relation,
Function<EntityRelation, EntityId> entityIdGetter, Function<EntityRelation, EntityId> entityIdGetter,
BiConsumer<EntityRelationInfo, String> entityNameSetter) { BiConsumer<EntityRelationInfo, String> entityNameSetter) {
ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(entityIdGetter.apply(relation)); ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(tenantId, entityIdGetter.apply(relation));
return Futures.transform(entityName, entityName1 -> { return Futures.transform(entityName, entityName1 -> {
EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation); EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation);
entityNameSetter.accept(entityRelationInfo1, entityName1); entityNameSetter.accept(entityRelationInfo1, entityName1);
@ -422,25 +424,25 @@ public class BaseRelationService implements RelationService {
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}") @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
@Override @Override
public List<EntityRelation> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { public List<EntityRelation> findByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup) {
try { try {
return findByToAndTypeAsync(to, relationType, typeGroup).get(); return findByToAndTypeAsync(tenantId, to, relationType, typeGroup).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findByToAndTypeAsync(EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findByToAndTypeAsync(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup) {
log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup); log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup);
validate(to); validate(to);
validateType(relationType); validateType(relationType);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
return relationDao.findAllByToAndType(to, relationType, typeGroup); return relationDao.findAllByToAndType(tenantId, to, relationType, typeGroup);
} }
@Override @Override
public ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query) { public ListenableFuture<List<EntityRelation>> findByQuery(TenantId tenantId, EntityRelationsQuery query) {
log.trace("Executing findByQuery [{}]", query); log.trace("Executing findByQuery [{}]", query);
RelationsSearchParameters params = query.getParameters(); RelationsSearchParameters params = query.getParameters();
final List<EntityTypeFilter> filters = query.getFilters(); final List<EntityTypeFilter> filters = query.getFilters();
@ -451,7 +453,7 @@ public class BaseRelationService implements RelationService {
int maxLvl = params.getMaxLevel() > 0 ? params.getMaxLevel() : Integer.MAX_VALUE; int maxLvl = params.getMaxLevel() > 0 ? params.getMaxLevel() : Integer.MAX_VALUE;
try { try {
ListenableFuture<Set<EntityRelation>> relationSet = findRelationsRecursively(params.getEntityId(), params.getDirection(), params.getRelationTypeGroup(), maxLvl, new ConcurrentHashMap<>()); ListenableFuture<Set<EntityRelation>> relationSet = findRelationsRecursively(tenantId, params.getEntityId(), params.getDirection(), params.getRelationTypeGroup(), maxLvl, new ConcurrentHashMap<>());
return Futures.transform(relationSet, input -> { return Futures.transform(relationSet, input -> {
List<EntityRelation> relations = new ArrayList<>(); List<EntityRelation> relations = new ArrayList<>();
if (filters == null || filters.isEmpty()) { if (filters == null || filters.isEmpty()) {
@ -472,15 +474,15 @@ public class BaseRelationService implements RelationService {
} }
@Override @Override
public ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(EntityRelationsQuery query) { public ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query) {
log.trace("Executing findInfoByQuery [{}]", query); log.trace("Executing findInfoByQuery [{}]", query);
ListenableFuture<List<EntityRelation>> relations = findByQuery(query); ListenableFuture<List<EntityRelation>> relations = findByQuery(tenantId, query);
EntitySearchDirection direction = query.getParameters().getDirection(); EntitySearchDirection direction = query.getParameters().getDirection();
return Futures.transformAsync(relations, return Futures.transformAsync(relations,
relations1 -> { relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.forEach(relation -> relations1.forEach(relation ->
futures.add(fetchRelationInfoAsync(relation, futures.add(fetchRelationInfoAsync(tenantId, relation,
relation2 -> direction == EntitySearchDirection.FROM ? relation2.getTo() : relation2.getFrom(), relation2 -> direction == EntitySearchDirection.FROM ? relation2.getTo() : relation2.getFrom(),
(EntityRelationInfo relationInfo, String entityName) -> { (EntityRelationInfo relationInfo, String entityName) -> {
if (direction == EntitySearchDirection.FROM) { if (direction == EntitySearchDirection.FROM) {
@ -567,7 +569,7 @@ public class BaseRelationService implements RelationService {
} }
} }
private ListenableFuture<Set<EntityRelation>> findRelationsRecursively(final EntityId rootId, final EntitySearchDirection direction, private ListenableFuture<Set<EntityRelation>> findRelationsRecursively(final TenantId tenantId, final EntityId rootId, final EntitySearchDirection direction,
RelationTypeGroup relationTypeGroup, int lvl, RelationTypeGroup relationTypeGroup, int lvl,
final ConcurrentHashMap<EntityId, Boolean> uniqueMap) throws Exception { final ConcurrentHashMap<EntityId, Boolean> uniqueMap) throws Exception {
if (lvl == 0) { if (lvl == 0) {
@ -575,7 +577,7 @@ public class BaseRelationService implements RelationService {
} }
lvl--; lvl--;
//TODO: try to remove this blocking operation //TODO: try to remove this blocking operation
Set<EntityRelation> children = new HashSet<>(findRelations(rootId, direction, relationTypeGroup).get()); Set<EntityRelation> children = new HashSet<>(findRelations(tenantId, rootId, direction, relationTypeGroup).get());
Set<EntityId> childrenIds = new HashSet<>(); Set<EntityId> childrenIds = new HashSet<>();
for (EntityRelation childRelation : children) { for (EntityRelation childRelation : children) {
log.trace("Found Relation: {}", childRelation); log.trace("Found Relation: {}", childRelation);
@ -594,7 +596,7 @@ public class BaseRelationService implements RelationService {
} }
List<ListenableFuture<Set<EntityRelation>>> futures = new ArrayList<>(); List<ListenableFuture<Set<EntityRelation>>> futures = new ArrayList<>();
for (EntityId entityId : childrenIds) { for (EntityId entityId : childrenIds) {
futures.add(findRelationsRecursively(entityId, direction, relationTypeGroup, lvl, uniqueMap)); futures.add(findRelationsRecursively(tenantId, entityId, direction, relationTypeGroup, lvl, uniqueMap));
} }
//TODO: try to remove this blocking operation //TODO: try to remove this blocking operation
List<Set<EntityRelation>> relations = Futures.successfulAsList(futures).get(); List<Set<EntityRelation>> relations = Futures.successfulAsList(futures).get();
@ -602,15 +604,15 @@ public class BaseRelationService implements RelationService {
return Futures.immediateFuture(children); return Futures.immediateFuture(children);
} }
private ListenableFuture<List<EntityRelation>> findRelations(final EntityId rootId, final EntitySearchDirection direction, RelationTypeGroup relationTypeGroup) { private ListenableFuture<List<EntityRelation>> findRelations(final TenantId tenantId, final EntityId rootId, final EntitySearchDirection direction, RelationTypeGroup relationTypeGroup) {
ListenableFuture<List<EntityRelation>> relations; ListenableFuture<List<EntityRelation>> relations;
if (relationTypeGroup == null) { if (relationTypeGroup == null) {
relationTypeGroup = RelationTypeGroup.COMMON; relationTypeGroup = RelationTypeGroup.COMMON;
} }
if (direction == EntitySearchDirection.FROM) { if (direction == EntitySearchDirection.FROM) {
relations = findByFromAsync(rootId, relationTypeGroup); relations = findByFromAsync(tenantId, rootId, relationTypeGroup);
} else { } else {
relations = findByToAsync(rootId, relationTypeGroup); relations = findByToAsync(tenantId, rootId, relationTypeGroup);
} }
return relations; return relations;
} }

31
dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.relation;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
@ -29,34 +30,34 @@ import java.util.List;
*/ */
public interface RelationDao { public interface RelationDao {
ListenableFuture<List<EntityRelation>> findAllByFrom(EntityId from, RelationTypeGroup typeGroup); ListenableFuture<List<EntityRelation>> findAllByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findAllByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup); ListenableFuture<List<EntityRelation>> findAllByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findAllByTo(EntityId to, RelationTypeGroup typeGroup); ListenableFuture<List<EntityRelation>> findAllByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findAllByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture<List<EntityRelation>> findAllByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture<Boolean> checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<EntityRelation> getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture<EntityRelation> getRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);
boolean saveRelation(EntityRelation relation); boolean saveRelation(TenantId tenantId, EntityRelation relation);
ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation); ListenableFuture<Boolean> saveRelationAsync(TenantId tenantId, EntityRelation relation);
boolean deleteRelation(EntityRelation relation); boolean deleteRelation(TenantId tenantId, EntityRelation relation);
ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation); ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityRelation relation);
boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); boolean deleteRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);
boolean deleteOutboundRelations(EntityId entity); boolean deleteOutboundRelations(TenantId tenantId, EntityId entity);
ListenableFuture<Boolean> deleteOutboundRelationsAsync(EntityId entity); ListenableFuture<Boolean> deleteOutboundRelationsAsync(TenantId tenantId, EntityId entity);
ListenableFuture<List<EntityRelation>> findRelations(EntityId from, String relationType, RelationTypeGroup typeGroup, EntityType toType, TimePageLink pageLink); ListenableFuture<List<EntityRelation>> findRelations(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup, EntityType toType, TimePageLink pageLink);
} }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save