Browse Source

Refactoring of DAO layer to support rate limiting by tenant

pull/1236/head
Andrew Shvayka 8 years ago
parent
commit
104450c102
  1. 3
      application/src/main/resources/thingsboard.yml
  2. 4
      common/message/pom.xml
  3. 8
      common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java
  4. 4
      common/transport/transport-api/pom.xml
  5. 13
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java
  6. 11
      dao/src/main/java/org/thingsboard/server/dao/Dao.java
  7. 6
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  8. 12
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  9. 92
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  10. 16
      dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java
  11. 3
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
  12. 12
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
  13. 88
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  14. 23
      dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java
  15. 11
      dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java
  16. 11
      dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java
  17. 21
      dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java
  18. 25
      dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java
  19. 6
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java
  20. 25
      dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java
  21. 6
      dao/src/main/java/org/thingsboard/server/dao/cache/PreviousDeviceCredentialsIdKeyGenerator.java
  22. 34
      dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java
  23. 37
      dao/src/main/java/org/thingsboard/server/dao/component/CassandraBaseComponentDescriptorDao.java
  24. 15
      dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorDao.java
  25. 15
      dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorService.java
  26. 8
      dao/src/main/java/org/thingsboard/server/dao/customer/CassandraCustomerDao.java
  27. 3
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java
  28. 6
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
  29. 42
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  30. 7
      dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java
  31. 3
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java
  32. 18
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
  33. 78
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  34. 9
      dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceCredentialsDao.java
  35. 23
      dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
  36. 8
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDao.java
  37. 9
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java
  38. 46
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
  39. 3
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  40. 12
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  41. 93
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  42. 5
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  43. 24
      dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java
  44. 5
      dao/src/main/java/org/thingsboard/server/dao/entity/EntityService.java
  45. 21
      dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
  46. 3
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
  47. 13
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
  48. 85
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
  49. 10
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  50. 22
      dao/src/main/java/org/thingsboard/server/dao/event/CassandraBaseEventDao.java
  51. 3
      dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java
  52. 25
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java
  53. 54
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractModelDao.java
  54. 15
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractSearchTextDao.java
  55. 21
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractSearchTimeDao.java
  56. 15
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java
  57. 2
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraStatementTask.java
  58. 77
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
  59. 132
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
  60. 31
      dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java
  61. 47
      dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java
  62. 124
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  63. 3
      dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java
  64. 20
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  65. 39
      dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java
  66. 11
      dao/src/main/java/org/thingsboard/server/dao/service/PaginatedRemover.java
  67. 5
      dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java
  68. 7
      dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java
  69. 26
      dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java
  70. 5
      dao/src/main/java/org/thingsboard/server/dao/settings/CassandraAdminSettingsDao.java
  71. 11
      dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java
  72. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  73. 11
      dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java
  74. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java
  75. 21
      dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java
  76. 5
      dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java
  77. 5
      dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceCredentialsDao.java
  78. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java
  79. 31
      dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java
  80. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/settings/JpaAdminSettingsDao.java
  81. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java
  82. 33
      dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
  83. 7
      dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java
  84. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java
  85. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java
  86. 5
      dao/src/main/java/org/thingsboard/server/dao/tenant/CassandraTenantDao.java
  87. 5
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java
  88. 2
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java
  89. 27
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  90. 53
      dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
  91. 103
      dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java
  92. 19
      dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java
  93. 13
      dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java
  94. 13
      dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserCredentialsDao.java
  95. 11
      dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java
  96. 9
      dao/src/main/java/org/thingsboard/server/dao/user/UserCredentialsDao.java
  97. 5
      dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java
  98. 20
      dao/src/main/java/org/thingsboard/server/dao/user/UserService.java
  99. 174
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
  100. 41
      dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java

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

@ -143,6 +143,9 @@ cassandra:
concurrent_limit: "${CASSANDRA_QUERY_CONCURRENT_LIMIT:1000}"
permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}"
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:

4
common/message/pom.xml

@ -60,6 +60,10 @@
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<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
* 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.Bucket4j;
@ -25,10 +25,10 @@ import java.time.Duration;
/**
* Created by ashvayka on 22.10.18.
*/
class TbTransportRateLimits {
public class TbRateLimits {
private final LocalBucket bucket;
public TbTransportRateLimits(String limitsConfiguration) {
public TbRateLimits(String limitsConfiguration) {
LocalBucketBuilder builder = Bucket4j.builder();
boolean initialized = false;
for (String limitSrc : limitsConfiguration.split(",")) {
@ -46,7 +46,7 @@ class TbTransportRateLimits {
}
boolean tryConsume() {
public boolean tryConsume() {
return bucket.tryConsume(1);
}

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

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

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.id.DeviceId;
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.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
@ -58,8 +59,8 @@ public abstract class AbstractTransportService implements TransportService {
private ConcurrentMap<UUID, SessionMetaData> sessions = new ConcurrentHashMap<>();
//TODO: Implement cleanup of this maps.
private ConcurrentMap<TenantId, TbTransportRateLimits> perTenantLimits = new ConcurrentHashMap<>();
private ConcurrentMap<DeviceId, TbTransportRateLimits> perDeviceLimits = new ConcurrentHashMap<>();
private ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
private ConcurrentMap<DeviceId, TbRateLimits> perDeviceLimits = new ConcurrentHashMap<>();
@Override
public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) {
@ -204,7 +205,7 @@ public abstract class AbstractTransportService implements TransportService {
return true;
}
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 (callback != null) {
callback.onError(new TbRateLimitsException(EntityType.TENANT));
@ -215,7 +216,7 @@ public abstract class AbstractTransportService implements TransportService {
return false;
}
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 (callback != null) {
callback.onError(new TbRateLimitsException(EntityType.DEVICE));
@ -271,8 +272,8 @@ public abstract class AbstractTransportService implements TransportService {
public void init() {
if (rateLimitEnabled) {
//Just checking the configuration parameters
new TbTransportRateLimits(perTenantLimitsConf);
new TbTransportRateLimits(perDevicesLimitsConf);
new TbRateLimits(perTenantLimitsConf);
new TbRateLimits(perDevicesLimitsConf);
}
this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor();
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;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List;
import java.util.UUID;
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> 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);
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);
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
public Alarm createOrUpdateAlarm(Alarm alarm) {
alarmDataValidator.validate(alarm);
alarmDataValidator.validate(alarm, Alarm::getTenantId);
try {
if (alarm.getStartTs() == 0L) {
alarm.setStartTs(System.currentTimeMillis());
@ -120,7 +120,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
private Alarm createAlarm(Alarm alarm) throws InterruptedException, ExecutionException {
log.debug("New Alarm : {}", alarm);
Alarm saved = alarmDao.save(alarm);
Alarm saved = alarmDao.save(alarm.getTenantId(), alarm);
createAlarmRelations(saved);
return saved;
}
@ -129,17 +129,17 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm.isPropagate()) {
EntityRelationsQuery query = new EntityRelationsQuery();
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) {
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) {
alarmDataValidator.validate(update);
return getAndUpdate(update.getId(), new Function<Alarm, Alarm>() {
alarmDataValidator.validate(update, Alarm::getTenantId);
return getAndUpdate(update.getTenantId(), update.getId(), new Function<Alarm, Alarm>() {
@Nullable
@Override
public Alarm apply(@Nullable Alarm alarm) {
@ -157,7 +157,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
AlarmStatus newStatus = newAlarm.getStatus();
boolean oldPropagate = oldAlarm.isPropagate();
boolean newPropagate = newAlarm.isPropagate();
Alarm result = alarmDao.save(merge(oldAlarm, newAlarm));
Alarm result = alarmDao.save(newAlarm.getTenantId(), merge(oldAlarm, newAlarm));
if (!oldPropagate && newPropagate) {
try {
createAlarmRelations(result);
@ -172,8 +172,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
@Override
public ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTime) {
return getAndUpdate(alarmId, new Function<Alarm, Boolean>() {
public ListenableFuture<Boolean> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTime) {
return getAndUpdate(tenantId, alarmId, new Function<Alarm, Boolean>() {
@Nullable
@Override
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;
alarm.setStatus(newStatus);
alarm.setAckTs(ackTime);
alarmDao.save(alarm);
alarmDao.save(alarm.getTenantId(), alarm);
updateRelations(alarm, oldStatus, newStatus);
return true;
}
@ -193,8 +193,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
@Override
public ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, JsonNode details, long clearTime) {
return getAndUpdate(alarmId, new Function<Alarm, Boolean>() {
public ListenableFuture<Boolean> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTime) {
return getAndUpdate(tenantId, alarmId, new Function<Alarm, Boolean>() {
@Nullable
@Override
public Boolean apply(@Nullable Alarm alarm) {
@ -208,7 +208,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (details != null) {
alarm.setDetails(details);
}
alarmDao.save(alarm);
alarmDao.save(alarm.getTenantId(), alarm);
updateRelations(alarm, oldStatus, newStatus);
return true;
}
@ -217,21 +217,21 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
@Override
public ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId) {
public ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId) {
log.trace("Executing findAlarmById [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId);
return alarmDao.findAlarmByIdAsync(alarmId.getId());
return alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId());
}
@Override
public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) {
public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId) {
log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId);
return Futures.transformAsync(alarmDao.findAlarmByIdAsync(alarmId.getId()),
return Futures.transformAsync(alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()),
a -> {
AlarmInfo alarmInfo = new AlarmInfo(a);
return Futures.transform(
entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), originatorName -> {
entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {
alarmInfo.setOriginatorName(originatorName);
return alarmInfo;
}
@ -240,14 +240,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
@Override
public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) {
ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query);
public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) {
ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(tenantId, query);
if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
alarms = Futures.transformAsync(alarms, input -> {
List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
for (AlarmInfo alarmInfo : input) {
alarmFutures.add(Futures.transform(
entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), originatorName -> {
entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {
if (originatorName == null) {
originatorName = "Deleted";
}
@ -269,7 +269,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
@Override
public AlarmSeverity findHighestAlarmSeverity(EntityId entityId, AlarmSearchStatus alarmSearchStatus,
public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
AlarmStatus alarmStatus) {
TimePageLink nextPageLink = new TimePageLink(100);
boolean hasNext = true;
@ -279,7 +279,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
query = new AlarmQuery(entityId, nextPageLink, alarmSearchStatus, alarmStatus, false);
List<AlarmInfo> alarms;
try {
alarms = alarmDao.findAlarms(query).get();
alarms = alarmDao.findAlarms(tenantId, query).get();
} catch (ExecutionException | InterruptedException e) {
log.warn("Failed to find highest alarm severity. 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);
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);
relationService.saveRelationAsync(alarmRelation).get();
relationService.saveRelationAsync(tenantId, alarmRelation).get();
}
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) {
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());
for (EntityId parentId : parents) {
updateAlarmRelation(parentId, alarm.getId(), oldStatus, newStatus);
updateAlarmRelation(alarm.getTenantId(), parentId, alarm.getId(), oldStatus, newStatus);
}
} catch (ExecutionException | InterruptedException e) {
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 {
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(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.name(), RelationTypeGroup.ALARM));
createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create relation. Status: [{}]", alarmId, status);
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 {
deleteRelation(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(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete relation. Status: [{}]", alarmId, status);
throw new RuntimeException(e);
}
}
private void updateAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) {
deleteAlarmRelation(entityId, alarmId, oldStatus);
createAlarmRelation(entityId, alarmId, newStatus, false);
private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) {
deleteAlarmRelation(tenantId, entityId, alarmId, oldStatus);
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!");
ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId());
ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId());
return Futures.transform(entity, function, readResultsProcessingExecutor);
}
@ -395,7 +395,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
new DataValidator<Alarm>() {
@Override
protected void validateDataImpl(Alarm alarm) {
protected void validateDataImpl(TenantId tenantId, Alarm alarm) {
if (StringUtils.isEmpty(alarm.getType())) {
throw new DataValidationException("Alarm type should be specified!");
}
@ -411,7 +411,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm.getTenantId() == null) {
throw new DataValidationException("Alarm should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(alarm.getTenantId().getId());
Tenant tenant = tenantDao.findById(alarm.getTenantId(), alarm.getTenantId().getId());
if (tenant == null) {
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
public Alarm save(Alarm alarm) {
public Alarm save(TenantId tenantId, Alarm alarm) {
log.debug("Save asset [{}] ", alarm);
return super.save(alarm);
return super.save(tenantId, alarm);
}
@Override
@ -88,11 +88,11 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
query.and(eq(ALARM_TYPE_PROPERTY, type));
query.limit(1);
query.orderBy(QueryBuilder.asc(ModelConstants.ALARM_TYPE_PROPERTY), QueryBuilder.desc(ModelConstants.ID_PROPERTY));
return findOneByStatementAsync(query);
return findOneByStatementAsync(tenantId, query);
}
@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());
EntityId affectedEntity = query.getAffectedEntityId();
String searchStatusName;
@ -104,12 +104,12 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
searchStatusName = query.getStatus().name();
}
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 -> {
List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
alarmFutures.add(Futures.transform(
findAlarmByIdAsync(relation.getTo().getId()),
findAlarmByIdAsync(tenantId, relation.getTo().getId()),
AlarmInfo::new));
}
return Futures.successfulAsList(alarmFutures);
@ -117,11 +117,11 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
}
@Override
public ListenableFuture<Alarm> findAlarmByIdAsync(UUID key) {
public ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key) {
log.debug("Get alarm by id {}", key);
Select.Where query = select().from(ALARM_BY_ID_VIEW_NAME).where(eq(ModelConstants.ID_PROPERTY, key));
query.limit(1);
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 org.thingsboard.server.common.data.EntitySubtype;
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.dao.Dao;
@ -37,7 +38,7 @@ public interface AssetDao extends Dao<Asset> {
* @param asset the asset object
* @return saved asset object
*/
Asset save(Asset asset);
Asset save(TenantId tenantId, Asset asset);
/**
* 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 {
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 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);
@ -60,7 +60,7 @@ public interface AssetService {
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);
}

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;
@Override
public Asset findAssetById(AssetId assetId) {
public Asset findAssetById(TenantId tenantId, AssetId assetId) {
log.trace("Executing findAssetById [{}]", assetId);
validateId(assetId, INCORRECT_ASSET_ID + assetId);
return assetDao.findById(assetId.getId());
return assetDao.findById(tenantId, assetId.getId());
}
@Override
public ListenableFuture<Asset> findAssetByIdAsync(AssetId assetId) {
public ListenableFuture<Asset> findAssetByIdAsync(TenantId tenantId, AssetId assetId) {
log.trace("Executing findAssetById [{}]", 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}")
@ -112,31 +112,31 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
@Override
public Asset saveAsset(Asset asset) {
log.trace("Executing saveAsset [{}]", asset);
assetValidator.validate(asset);
return assetDao.save(asset);
assetValidator.validate(asset, Asset::getTenantId);
return assetDao.save(asset.getTenantId(), asset);
}
@Override
public Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId) {
Asset asset = findAssetById(assetId);
public Asset assignAssetToCustomer(TenantId tenantId, AssetId assetId, CustomerId customerId) {
Asset asset = findAssetById(tenantId, assetId);
asset.setCustomerId(customerId);
return saveAsset(asset);
}
@Override
public Asset unassignAssetFromCustomer(AssetId assetId) {
Asset asset = findAssetById(assetId);
public Asset unassignAssetFromCustomer(TenantId tenantId, AssetId assetId) {
Asset asset = findAssetById(tenantId, assetId);
asset.setCustomerId(null);
return saveAsset(asset);
}
@Override
public void deleteAsset(AssetId assetId) {
public void deleteAsset(TenantId tenantId, AssetId assetId) {
log.trace("Executing deleteAsset [{}]", 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 {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get();
if (entityViews != null && !entityViews.isEmpty()) {
@ -153,7 +153,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
Cache cache = cacheManager.getCache(ASSET_CACHE);
cache.evict(list);
assetDao.removeById(assetId.getId());
assetDao.removeById(tenantId, assetId.getId());
}
@Override
@ -187,7 +187,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
public void deleteAssetsByTenantId(TenantId tenantId) {
log.trace("Executing deleteAssetsByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantAssetsRemover.removeEntities(tenantId);
tenantAssetsRemover.removeEntities(tenantId, tenantId);
}
@Override
@ -225,24 +225,24 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
log.trace("Executing unassignCustomerAssets, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
new CustomerAssetsUnassigner(tenantId).removeEntities(customerId);
customerAssetsUnasigner.removeEntities(tenantId, customerId);
}
@Override
public ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery());
public ListenableFuture<List<Asset>> findAssetsByQuery(TenantId tenantId, AssetSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery());
ListenableFuture<List<Asset>> assets = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<Asset>> futures = new ArrayList<>();
for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
if (entityId.getEntityType() == EntityType.ASSET) {
futures.add(findAssetByIdAsync(new AssetId(entityId.getId())));
futures.add(findAssetByIdAsync(tenantId, new AssetId(entityId.getId())));
}
}
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())
);
return assets;
@ -254,7 +254,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
ListenableFuture<List<EntitySubtype>> tenantAssetTypes = assetDao.findTenantAssetTypesAsync(tenantId.getId());
return Futures.transform(tenantAssetTypes,
(Function<List<EntitySubtype>, List<EntitySubtype>>) assetTypes -> {
assetTypes -> {
assetTypes.sort(Comparator.comparing(EntitySubtype::getType));
return assetTypes;
});
@ -264,7 +264,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
new DataValidator<Asset>() {
@Override
protected void validateCreate(Asset asset) {
protected void validateCreate(TenantId tenantId, Asset asset) {
assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
d -> {
throw new DataValidationException("Asset with such name already exists!");
@ -273,7 +273,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
}
@Override
protected void validateUpdate(Asset asset) {
protected void validateUpdate(TenantId tenantId, Asset asset) {
assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
d -> {
if (!d.getId().equals(asset.getId())) {
@ -284,7 +284,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
}
@Override
protected void validateDataImpl(Asset asset) {
protected void validateDataImpl(TenantId tenantId, Asset asset) {
if (StringUtils.isEmpty(asset.getType())) {
throw new DataValidationException("Asset type should be specified!");
}
@ -294,7 +294,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
if (asset.getTenantId() == null) {
throw new DataValidationException("Asset should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(asset.getTenantId().getId());
Tenant tenant = tenantDao.findById(tenantId, asset.getTenantId().getId());
if (tenant == null) {
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) {
asset.setCustomerId(new CustomerId(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) {
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 =
new PaginatedRemover<TenantId, Asset>() {
new PaginatedRemover<TenantId, Asset>() {
@Override
protected List<Asset> findEntities(TenantId id, TextPageLink pageLink) {
return assetDao.findAssetsByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(Asset entity) {
deleteAsset(new AssetId(entity.getId().getId()));
}
};
@Override
protected List<Asset> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
return assetDao.findAssetsByTenantId(id.getId(), pageLink);
}
class CustomerAssetsUnassigner extends PaginatedRemover<CustomerId, Asset> {
@Override
protected void removeEntity(TenantId tenantId, Asset entity) {
deleteAsset(tenantId, new AssetId(entity.getId().getId()));
}
};
private TenantId tenantId;
CustomerAssetsUnassigner(TenantId tenantId) {
this.tenantId = tenantId;
}
private PaginatedRemover<CustomerId, Asset> customerAssetsUnasigner = new PaginatedRemover<CustomerId, Asset>() {
@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);
}
@Override
protected void removeEntity(Asset entity) {
unassignAssetFromCustomer(new AssetId(entity.getId().getId()));
protected void removeEntity(TenantId tenantId, Asset entity) {
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.EntityType;
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.dao.DaoUtil;
import org.thingsboard.server.dao.model.EntitySubtypeEntity;
@ -77,19 +78,19 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
}
@Override
public Asset save(Asset domain) {
Asset savedAsset = super.save(domain);
public Asset save(TenantId tenantId, Asset domain) {
Asset savedAsset = super.save(tenantId, domain);
EntitySubtype entitySubtype = new EntitySubtype(savedAsset.getTenantId(), EntityType.ASSET, savedAsset.getType());
EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype);
Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity);
executeWrite(saveStatement);
executeWrite(tenantId, saveStatement);
return savedAsset;
}
@Override
public List<Asset> findAssetsByTenantId(UUID tenantId, TextPageLink 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);
log.trace("Found assets [{}] by tenantId [{}] and pageLink [{}]", assetEntities, tenantId, pageLink);
@ -99,7 +100,7 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
@Override
public List<Asset> findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink 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),
eq(ASSET_TENANT_ID_PROPERTY, tenantId)), 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();
query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
query.and(in(ID_PROPERTY, assetIds));
return findListByStatementAsync(query);
return findListByStatementAsync(new TenantId(tenantId), query);
}
@Override
public List<Asset> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink 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),
eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
pageLink);
@ -130,7 +131,7 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
@Override
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);
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),
eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
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_CUSTOMER_ID_PROPERTY, customerId));
query.and(in(ID_PROPERTY, assetIds));
return findListByStatementAsync(query);
return findListByStatementAsync(new TenantId(tenantId), query);
}
@Override
@ -157,7 +158,7 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
Select.Where query = select.where();
query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
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));
}
@ -168,7 +169,7 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId));
query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.ASSET));
query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(query);
ResultSetFuture resultSetFuture = executeAsyncRead(new TenantId(tenantId), query);
return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() {
@Nullable
@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 org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import java.util.Collection;
@ -28,13 +29,13 @@ import java.util.Optional;
*/
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 org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import java.util.Collection;
@ -28,13 +29,13 @@ import java.util.Optional;
*/
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.stereotype.Service;
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.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.service.Validator;
@ -39,40 +40,40 @@ public class BaseAttributesService implements AttributesService {
private AttributesDao attributesDao;
@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);
Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey);
return attributesDao.find(entityId, scope, attributeKey);
return attributesDao.find(tenantId, entityId, scope, attributeKey);
}
@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);
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey));
return attributesDao.find(entityId, scope, attributeKeys);
return attributesDao.find(tenantId, entityId, scope, attributeKeys);
}
@Override
public ListenableFuture<List<AttributeKvEntry>> findAll(EntityId entityId, String scope) {
public ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String scope) {
validate(entityId, scope);
return attributesDao.findAll(entityId, scope);
return attributesDao.findAll(tenantId, entityId, scope);
}
@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);
attributes.forEach(attribute -> validate(attribute));
List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(attributes.size());
for (AttributeKvEntry attribute : attributes) {
futures.add(attributesDao.save(entityId, scope, attribute));
futures.add(attributesDao.save(tenantId, entityId, scope, attribute));
}
return Futures.allAsList(futures);
}
@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);
return attributesDao.removeAll(entityId, scope, keys);
return attributesDao.removeAll(tenantId, entityId, scope, keys);
}
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 org.springframework.stereotype.Component;
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.BaseAttributeKvEntry;
import org.thingsboard.server.dao.model.ModelConstants;
@ -73,22 +74,22 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem
}
@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)
.where(eq(ENTITY_TYPE_COLUMN, entityId.getEntityType()))
.and(eq(ENTITY_ID_COLUMN, entityId.getId()))
.and(eq(ATTRIBUTE_TYPE_COLUMN, attributeType))
.and(eq(ATTRIBUTE_KEY_COLUMN, 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()))
, readResultsProcessingExecutor);
}
@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<>();
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 -> {
List<AttributeKvEntry> result = new ArrayList<>();
input.stream().filter(opt -> opt.isPresent()).forEach(opt -> result.add(opt.get()));
@ -98,19 +99,19 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem
@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)
.where(eq(ENTITY_TYPE_COLUMN, entityId.getEntityType()))
.and(eq(ENTITY_ID_COLUMN, entityId.getId()))
.and(eq(ATTRIBUTE_TYPE_COLUMN, 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)
, readResultsProcessingExecutor);
}
@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();
stmt.setString(0, entityId.getEntityType().name());
stmt.setUUID(1, entityId.getId());
@ -137,26 +138,26 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem
stmt.setToNull(8);
}
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
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
.stream()
.map(key -> delete(entityId, attributeType, key))
.map(key -> delete(tenantId, entityId, attributeType, key))
.collect(Collectors.toList());
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)
.where(eq(ENTITY_TYPE_COLUMN, entityId.getEntityType()))
.and(eq(ENTITY_ID_COLUMN, entityId.getId()))
.and(eq(ATTRIBUTE_TYPE_COLUMN, attributeType))
.and(eq(ATTRIBUTE_KEY_COLUMN, key));
log.debug("Remove request: {}", delete.toString());
return getFuture(executeAsyncWrite(delete), rs -> null);
return getFuture(executeAsyncWrite(tenantId, delete), rs -> null);
}
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();
} else {
try {
entityName = entityService.fetchEntityNameAsync(entityId).get();
entityName = entityService.fetchEntityNameAsync(tenantId, entityId).get();
} catch (Exception ex) {}
}
if (e != null) {
@ -315,7 +315,7 @@ public class AuditLogServiceImpl implements AuditLogService {
AuditLog auditLogEntry = createAuditLogEntry(tenantId, entityId, entityName, customerId, userId, userName,
actionType, actionData, actionStatus, actionFailureDetails);
log.trace("Executing logAction [{}]", auditLogEntry);
auditLogValidator.validate(auditLogEntry);
auditLogValidator.validate(auditLogEntry, AuditLog::getTenantId);
List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(INSERTS_PER_ENTRY);
futures.add(auditLogDao.savePartitionsByTenantId(auditLogEntry));
futures.add(auditLogDao.saveByTenantId(auditLogEntry));
@ -331,7 +331,7 @@ public class AuditLogServiceImpl implements AuditLogService {
private DataValidator<AuditLog> auditLogValidator =
new DataValidator<AuditLog>() {
@Override
protected void validateDataImpl(AuditLog auditLog) {
protected void validateDataImpl(TenantId tenantId, AuditLog auditLog) {
if (auditLog.getEntityId() == null) {
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.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.UserId;
import org.thingsboard.server.common.data.page.TimePageLink;
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());
BoundStatement stmt = getSaveByTenantStmt().bind();
stmt = setSaveStmtVariables(stmt, auditLog, partition);
return getFuture(executeAsyncWrite(stmt), rs -> null);
return getFuture(executeAsyncWrite(auditLog.getTenantId(), stmt), rs -> null);
}
@Override
@ -151,7 +152,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
BoundStatement stmt = getSaveByTenantIdAndEntityIdStmt().bind();
stmt = setSaveStmtVariables(stmt, auditLog, -1);
return getFuture(executeAsyncWrite(stmt), rs -> null);
return getFuture(executeAsyncWrite(auditLog.getTenantId(), stmt), rs -> null);
}
@Override
@ -160,7 +161,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
BoundStatement stmt = getSaveByTenantIdAndCustomerIdStmt().bind();
stmt = setSaveStmtVariables(stmt, auditLog, -1);
return getFuture(executeAsyncWrite(stmt), rs -> null);
return getFuture(executeAsyncWrite(auditLog.getTenantId(), stmt), rs -> null);
}
@Override
@ -169,11 +170,11 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
BoundStatement stmt = getSaveByTenantIdAndUserIdStmt().bind();
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) {
stmt.setUUID(0, auditLog.getId().getId())
stmt.setUUID(0, auditLog.getId().getId())
.setUUID(1, auditLog.getTenantId().getId())
.setUUID(2, auditLog.getCustomerId().getId())
.setUUID(3, auditLog.getEntityId().getId())
@ -200,7 +201,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
BoundStatement stmt = getPartitionInsertStmt().bind();
stmt = stmt.setUUID(0, auditLog.getTenantId().getId())
.setLong(1, partition);
return getFuture(executeAsyncWrite(stmt), rs -> null);
return getFuture(executeAsyncWrite(auditLog.getTenantId(), stmt), rs -> null);
}
private PreparedStatement getSaveByTenantStmt() {
@ -249,7 +250,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
columnsList.add(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY);
}
StringJoiner values = new StringJoiner(",");
for (int i=0;i<columnsList.size();i++) {
for (int i = 0; i < columnsList.size(); i++) {
values.add("?");
}
String statementString = INSERT_INTO + cfName + " (" + String.join(",", columnsList) + ") VALUES (" + values.toString() + ")";
@ -274,7 +275,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
@Override
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);
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),
eq(ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY, entityId.getEntityType()),
eq(ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY, entityId.getId())),
@ -286,7 +287,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
@Override
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);
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),
eq(ModelConstants.AUDIT_LOG_CUSTOMER_ID_PROPERTY, customerId.getId())),
pageLink);
@ -297,7 +298,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
@Override
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);
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),
eq(ModelConstants.AUDIT_LOG_USER_ID_PROPERTY, userId.getId())),
pageLink);
@ -339,7 +340,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
if (cursor.isFull() || !cursor.hasNextPartition()) {
return cursor.getData();
} 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()),
eq(ModelConstants.AUDIT_LOG_PARTITION_PROPERTY, cursor.getNextPartition())),
cursor.getPageLink()));
@ -352,7 +353,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
.where(eq(ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY, tenantId));
select.and(QueryBuilder.gte(ModelConstants.PARTITION_COLUMN, minPartition));
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;
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.dao.device.DeviceCredentialsService;
@ -28,9 +29,10 @@ public class PreviousDeviceCredentialsIdKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object o, Method method, Object... objects) {
DeviceCredentialsService deviceCredentialsService = (DeviceCredentialsService) o;
DeviceCredentials deviceCredentials = (DeviceCredentials) objects[0];
TenantId tenantId = (TenantId) objects[0];
DeviceCredentials deviceCredentials = (DeviceCredentials) objects[1];
if (deviceCredentials.getDeviceId() != null) {
DeviceCredentials oldDeviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceCredentials.getDeviceId());
DeviceCredentials oldDeviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, deviceCredentials.getDeviceId());
if (oldDeviceCredentials != null) {
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.stereotype.Service;
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.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
@ -49,50 +51,50 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic
private ComponentDescriptorDao componentDescriptorDao;
@Override
public ComponentDescriptor saveComponent(ComponentDescriptor component) {
componentValidator.validate(component);
Optional<ComponentDescriptor> result = componentDescriptorDao.saveIfNotExist(component);
public ComponentDescriptor saveComponent(TenantId tenantId, ComponentDescriptor component) {
componentValidator.validate(component, data -> new TenantId(EntityId.NULL_UUID));
Optional<ComponentDescriptor> result = componentDescriptorDao.saveIfNotExist(tenantId, component);
if (result.isPresent()) {
return result.get();
} else {
return componentDescriptorDao.findByClazz(component.getClazz());
return componentDescriptorDao.findByClazz(tenantId, component.getClazz());
}
}
@Override
public ComponentDescriptor findById(ComponentDescriptorId componentId) {
public ComponentDescriptor findById(TenantId tenantId, ComponentDescriptorId componentId) {
Validator.validateId(componentId, "Incorrect component id for search request.");
return componentDescriptorDao.findById(componentId);
return componentDescriptorDao.findById(tenantId, componentId);
}
@Override
public ComponentDescriptor findByClazz(String clazz) {
public ComponentDescriptor findByClazz(TenantId tenantId, String clazz) {
Validator.validateString(clazz, "Incorrect clazz for search request.");
return componentDescriptorDao.findByClazz(clazz);
return componentDescriptorDao.findByClazz(tenantId, clazz);
}
@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.");
List<ComponentDescriptor> components = componentDescriptorDao.findByTypeAndPageLink(type, pageLink);
List<ComponentDescriptor> components = componentDescriptorDao.findByTypeAndPageLink(tenantId, type, pageLink);
return new TextPageData<>(components, pageLink);
}
@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.");
List<ComponentDescriptor> components = componentDescriptorDao.findByScopeAndTypeAndPageLink(scope, type, pageLink);
List<ComponentDescriptor> components = componentDescriptorDao.findByScopeAndTypeAndPageLink(tenantId, scope, type, pageLink);
return new TextPageData<>(components, pageLink);
}
@Override
public void deleteByClazz(String clazz) {
public void deleteByClazz(TenantId tenantId, String clazz) {
Validator.validateString(clazz, "Incorrect clazz for delete request.");
componentDescriptorDao.deleteByClazz(clazz);
componentDescriptorDao.deleteByClazz(tenantId, clazz);
}
@Override
public boolean validate(ComponentDescriptor component, JsonNode configuration) {
public boolean validate(TenantId tenantId, ComponentDescriptor component, JsonNode configuration) {
JsonValidator validator = JsonSchemaFactory.byDefault().getValidator();
try {
if (!component.getConfigurationDescriptor().has("schema")) {
@ -109,7 +111,7 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic
private DataValidator<ComponentDescriptor> componentValidator =
new DataValidator<ComponentDescriptor>() {
@Override
protected void validateDataImpl(ComponentDescriptor plugin) {
protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) {
if (plugin.getType() == null) {
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 org.springframework.stereotype.Component;
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.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope;
@ -62,10 +63,10 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
}
@Override
public Optional<ComponentDescriptor> saveIfNotExist(ComponentDescriptor component) {
public Optional<ComponentDescriptor> saveIfNotExist(TenantId tenantId, ComponentDescriptor component) {
ComponentDescriptorEntity entity = new ComponentDescriptorEntity(component);
log.debug("Save component entity [{}]", entity);
Optional<ComponentDescriptor> result = saveIfNotExist(entity);
Optional<ComponentDescriptor> result = saveIfNotExist(tenantId, entity);
if (log.isTraceEnabled()) {
log.trace("Saved result: [{}] for component entity [{}]", result.isPresent(), result.orElse(null));
} else {
@ -75,9 +76,9 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
}
@Override
public ComponentDescriptor findById(ComponentDescriptorId componentId) {
public ComponentDescriptor findById(TenantId tenantId, ComponentDescriptorId 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()) {
log.trace("Search result: [{}] for component entity [{}]", componentDescriptor != null, componentDescriptor);
} else {
@ -87,11 +88,11 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
}
@Override
public ComponentDescriptor findByClazz(String clazz) {
public ComponentDescriptor findByClazz(TenantId tenantId, String clazz) {
log.debug("Search component entity by clazz [{}]", clazz);
Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, clazz));
log.trace("Execute query [{}]", query);
ComponentDescriptorEntity entity = findOneByStatement(query);
ComponentDescriptorEntity entity = findOneByStatement(tenantId, query);
if (log.isTraceEnabled()) {
log.trace("Search result: [{}] for component entity [{}]", entity != null, entity);
} else {
@ -101,9 +102,9 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
}
@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);
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);
if (log.isTraceEnabled()) {
log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray()));
@ -114,9 +115,9 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
}
@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);
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),
eq(ModelConstants.COMPONENT_DESCRIPTOR_SCOPE_PROPERTY, scope.name())), pageLink);
if (log.isTraceEnabled()) {
@ -127,34 +128,34 @@ public class CassandraBaseComponentDescriptorDao extends CassandraAbstractSearch
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));
log.debug("Remove request: {}", delete.toString());
return executeWrite(delete).wasApplied();
return executeWrite(tenantId, delete).wasApplied();
}
@Override
public void deleteById(ComponentDescriptorId id) {
public void deleteById(TenantId tenantId, ComponentDescriptorId 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);
}
@Override
public void deleteByClazz(String clazz) {
public void deleteByClazz(TenantId tenantId, String 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));
log.debug("Remove request: {}", delete.toString());
ResultSet resultSet = executeWrite(delete);
ResultSet resultSet = executeWrite(tenantId, delete);
log.debug("Delete result: [{}]", resultSet.wasApplied());
}
private Optional<ComponentDescriptor> saveIfNotExist(ComponentDescriptorEntity entity) {
private Optional<ComponentDescriptor> saveIfNotExist(TenantId tenantId, ComponentDescriptorEntity entity) {
if (entity.getId() == null) {
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.COMPONENT_DESCRIPTOR_NAME_PROPERTY, entity.getName())
.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;
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.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope;
@ -30,18 +31,18 @@ import java.util.Optional;
*/
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 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.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
@ -28,18 +29,18 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
*/
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 org.springframework.stereotype.Component;
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.dao.DaoUtil;
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_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TITLE_PROPERTY;
@Component
@Slf4j
@NoSqlDao
@ -54,9 +56,9 @@ public class CassandraCustomerDao extends CassandraAbstractSearchTextDao<Custome
@Override
public List<Customer> findCustomersByTenantId(UUID tenantId, TextPageLink 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)),
pageLink);
pageLink);
log.trace("Found customers [{}] by tenantId [{}] and pageLink [{}]", customerEntities, tenantId, pageLink);
return DaoUtil.convertDataList(customerEntities);
}
@ -67,7 +69,7 @@ public class CassandraCustomerDao extends CassandraAbstractSearchTextDao<Custome
Select.Where query = select.where();
query.and(eq(CUSTOMER_TENANT_ID_PROPERTY, tenantId));
query.and(eq(CUSTOMER_TITLE_PROPERTY, title));
CustomerEntity customerEntity = findOneByStatement(query);
CustomerEntity customerEntity = findOneByStatement(new TenantId(tenantId), query);
Customer customer = DaoUtil.getData(customerEntity);
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;
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.dao.Dao;
@ -34,7 +35,7 @@ public interface CustomerDao extends Dao<Customer> {
* @param customer the customer object
* @return saved customer object
*/
Customer save(Customer customer);
Customer save(TenantId tenantId, Customer customer);
/**
* 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 {
Customer findCustomerById(CustomerId customerId);
Customer findCustomerById(TenantId tenantId, CustomerId customerId);
Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title);
ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId);
ListenableFuture<Customer> findCustomerByIdAsync(TenantId tenantId, CustomerId customerId);
Customer saveCustomer(Customer customer);
void deleteCustomer(CustomerId customerId);
void deleteCustomer(TenantId tenantId, CustomerId customerId);
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;
@Override
public Customer findCustomerById(CustomerId customerId) {
public Customer findCustomerById(TenantId tenantId, CustomerId customerId) {
log.trace("Executing findCustomerById [{}]", customerId);
Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
return customerDao.findById(customerId.getId());
return customerDao.findById(tenantId, customerId.getId());
}
@Override
@ -91,36 +91,36 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
}
@Override
public ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId) {
public ListenableFuture<Customer> findCustomerByIdAsync(TenantId tenantId, CustomerId customerId) {
log.trace("Executing findCustomerByIdAsync [{}]", customerId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
return customerDao.findByIdAsync(customerId.getId());
return customerDao.findByIdAsync(tenantId, customerId.getId());
}
@Override
public Customer saveCustomer(Customer customer) {
log.trace("Executing saveCustomer [{}]", customer);
customerValidator.validate(customer);
Customer savedCustomer = customerDao.save(customer);
dashboardService.updateCustomerDashboards(savedCustomer.getId());
customerValidator.validate(customer, Customer::getTenantId);
Customer savedCustomer = customerDao.save(customer.getTenantId(), customer);
dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId());
return savedCustomer;
}
@Override
public void deleteCustomer(CustomerId customerId) {
public void deleteCustomer(TenantId tenantId, CustomerId customerId) {
log.trace("Executing deleteCustomer [{}]", customerId);
Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
Customer customer = findCustomerById(customerId);
Customer customer = findCustomerById(tenantId, customerId);
if (customer == null) {
throw new IncorrectParameterException("Unable to delete non-existent customer.");
}
dashboardService.unassignCustomerDashboards(customerId);
dashboardService.unassignCustomerDashboards(tenantId, customerId);
entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId);
assetService.unassignCustomerAssets(customer.getTenantId(), customerId);
deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
userService.deleteCustomerUsers(customer.getTenantId(), customerId);
deleteEntityRelations(customerId);
customerDao.removeById(customerId.getId());
deleteEntityRelations(tenantId, customerId);
customerDao.removeById(tenantId, customerId.getId());
}
@Override
@ -139,7 +139,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
} catch (IOException 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) {
log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
customersByTenantRemover.removeEntities(tenantId);
customersByTenantRemover.removeEntities(tenantId, tenantId);
}
private DataValidator<Customer> customerValidator =
new DataValidator<Customer>() {
@Override
protected void validateCreate(Customer customer) {
protected void validateCreate(TenantId tenantId, Customer customer) {
customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent(
c -> {
throw new DataValidationException("Customer with such title already exists!");
@ -172,7 +172,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
}
@Override
protected void validateUpdate(Customer customer) {
protected void validateUpdate(TenantId tenantId, Customer customer) {
customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent(
c -> {
if (!c.getId().equals(customer.getId())) {
@ -183,7 +183,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
}
@Override
protected void validateDataImpl(Customer customer) {
protected void validateDataImpl(TenantId tenantId, Customer customer) {
if (StringUtils.isEmpty(customer.getTitle())) {
throw new DataValidationException("Customer title should be specified!");
}
@ -196,7 +196,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
if (customer.getTenantId() == null) {
throw new DataValidationException("Customer should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(customer.getTenantId().getId());
Tenant tenant = tenantDao.findById(tenantId, customer.getTenantId().getId());
if (tenant == null) {
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>() {
@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);
}
@Override
protected void removeEntity(Customer entity) {
deleteCustomer(new CustomerId(entity.getUuidId()));
protected void removeEntity(TenantId tenantId, Customer entity) {
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.EntityType;
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.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
@ -64,7 +65,7 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da
@Override
public List<DashboardInfo> findDashboardsByTenantId(UUID tenantId, TextPageLink 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)),
pageLink);
@ -76,12 +77,12 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da
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);
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 -> {
List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
dashboardFutures.add(findByIdAsync(relation.getTo().getId()));
dashboardFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
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;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.Dao;
/**
@ -29,6 +30,6 @@ public interface DashboardDao extends Dao<Dashboard> {
* @param dashboard the 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 {
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 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);
@ -50,8 +50,8 @@ public interface DashboardService {
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;
@Override
public Dashboard findDashboardById(DashboardId dashboardId) {
public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardById [{}]", dashboardId);
Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
return dashboardDao.findById(dashboardId.getId());
return dashboardDao.findById(tenantId, dashboardId.getId());
}
@Override
public ListenableFuture<Dashboard> findDashboardByIdAsync(DashboardId dashboardId) {
public ListenableFuture<Dashboard> findDashboardByIdAsync(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardByIdAsync [{}]", dashboardId);
validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
return dashboardDao.findByIdAsync(dashboardId.getId());
return dashboardDao.findByIdAsync(tenantId, dashboardId.getId());
}
@Override
public DashboardInfo findDashboardInfoById(DashboardId dashboardId) {
public DashboardInfo findDashboardInfoById(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardInfoById [{}]", dashboardId);
Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
return dashboardInfoDao.findById(dashboardId.getId());
return dashboardInfoDao.findById(tenantId, dashboardId.getId());
}
@Override
public ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(DashboardId dashboardId) {
public ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardInfoByIdAsync [{}]", dashboardId);
validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
return dashboardInfoDao.findByIdAsync(dashboardId.getId());
return dashboardInfoDao.findByIdAsync(tenantId, dashboardId.getId());
}
@Override
public Dashboard saveDashboard(Dashboard dashboard) {
log.trace("Executing saveDashboard [{}]", dashboard);
dashboardValidator.validate(dashboard);
return dashboardDao.save(dashboard);
dashboardValidator.validate(dashboard, DashboardInfo::getTenantId);
return dashboardDao.save(dashboard.getTenantId(), dashboard);
}
@Override
public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId) {
Dashboard dashboard = findDashboardById(dashboardId);
Customer customer = customerDao.findById(customerId.getId());
public Dashboard assignDashboardToCustomer(TenantId tenantId, DashboardId dashboardId, CustomerId customerId) {
Dashboard dashboard = findDashboardById(tenantId, dashboardId);
Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) {
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)) {
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) {
log.warn("[{}] Failed to create dashboard relation. Customer Id: [{}]", dashboardId, customerId);
throw new RuntimeException(e);
@ -127,15 +127,15 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
@Override
public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) {
Dashboard dashboard = findDashboardById(dashboardId);
Customer customer = customerDao.findById(customerId.getId());
public Dashboard unassignDashboardFromCustomer(TenantId tenantId, DashboardId dashboardId, CustomerId customerId) {
Dashboard dashboard = findDashboardById(tenantId, dashboardId);
Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) {
throw new DataValidationException("Can't unassign dashboard from non-existent customer!");
}
if (dashboard.removeAssignedCustomer(customer)) {
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) {
log.warn("[{}] Failed to delete dashboard relation. Customer Id: [{}]", dashboardId, customerId);
throw new RuntimeException(e);
@ -146,8 +146,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
}
private Dashboard updateAssignedCustomer(DashboardId dashboardId, Customer customer) {
Dashboard dashboard = findDashboardById(dashboardId);
private Dashboard updateAssignedCustomer(TenantId tenantId, DashboardId dashboardId, Customer customer) {
Dashboard dashboard = findDashboardById(tenantId, dashboardId);
if (dashboard.updateAssignedCustomer(customer)) {
return saveDashboard(dashboard);
} 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);
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);
relationService.saveRelationAsync(dashboardRelation).get();
relationService.saveRelationAsync(tenantId, dashboardRelation).get();
}
@Override
public void deleteDashboard(DashboardId dashboardId) {
public void deleteDashboard(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing deleteDashboard [{}]", dashboardId);
Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
deleteEntityRelations(dashboardId);
dashboardDao.removeById(dashboardId.getId());
deleteEntityRelations(tenantId, dashboardId);
dashboardDao.removeById(tenantId, dashboardId.getId());
}
@Override
@ -186,7 +186,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
public void deleteDashboardsByTenantId(TenantId tenantId) {
log.trace("Executing deleteDashboardsByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantDashboardsRemover.removeEntities(tenantId);
tenantDashboardsRemover.removeEntities(tenantId, tenantId);
}
@Override
@ -207,10 +207,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
@Override
public void unassignCustomerDashboards(CustomerId customerId) {
public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerDashboards, customerId [{}]", customerId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
Customer customer = customerDao.findById(customerId.getId());
Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) {
throw new DataValidationException("Can't unassign dashboards from non-existent customer!");
}
@ -218,10 +218,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
@Override
public void updateCustomerDashboards(CustomerId customerId) {
public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId) {
log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
Customer customer = customerDao.findById(customerId.getId());
Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) {
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 =
new DataValidator<Dashboard>() {
@Override
protected void validateDataImpl(Dashboard dashboard) {
protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) {
if (StringUtils.isEmpty(dashboard.getTitle())) {
throw new DataValidationException("Dashboard title should be specified!");
}
if (dashboard.getTenantId() == null) {
throw new DataValidationException("Dashboard should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(dashboard.getTenantId().getId());
Tenant tenant = tenantDao.findById(tenantId, dashboard.getTenantId().getId());
if (tenant == null) {
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>() {
@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);
}
@Override
protected void removeEntity(DashboardInfo entity) {
deleteDashboard(new DashboardId(entity.getUuidId()));
protected void removeEntity(TenantId tenantId, DashboardInfo entity) {
deleteDashboard(tenantId, new DashboardId(entity.getUuidId()));
}
};
@ -280,7 +280,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
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
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 lombok.extern.slf4j.Slf4j;
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.dao.DaoUtil;
import org.thingsboard.server.dao.model.ModelConstants;
@ -46,23 +47,23 @@ public class CassandraDeviceCredentialsDao extends CassandraAbstractModelDao<Dev
}
@Override
public DeviceCredentials findByDeviceId(UUID deviceId) {
public DeviceCredentials findByDeviceId(TenantId tenantId, UUID deviceId) {
log.debug("Try to find device credentials by deviceId [{}] ", deviceId);
Where query = select().from(ModelConstants.DEVICE_CREDENTIALS_BY_DEVICE_COLUMN_FAMILY_NAME)
.where(eq(ModelConstants.DEVICE_CREDENTIALS_DEVICE_ID_PROPERTY, deviceId));
log.trace("Execute query {}", query);
DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(query);
DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(tenantId, query);
log.trace("Found device credentials [{}] by deviceId [{}]", deviceCredentialsEntity, deviceId);
return DaoUtil.getData(deviceCredentialsEntity);
}
@Override
public DeviceCredentials findByCredentialsId(String credentialsId) {
public DeviceCredentials findByCredentialsId(TenantId tenantId, String 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(eq(ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_ID_PROPERTY, credentialsId));
log.trace("Execute query {}", query);
DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(query);
DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(tenantId, query);
log.trace("Found device credentials [{}] by credentialsId [{}]", deviceCredentialsEntity, credentialsId);
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.EntitySubtype;
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.dao.DaoUtil;
import org.thingsboard.server.dao.model.EntitySubtypeEntity;
@ -77,19 +78,19 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
}
@Override
public Device save(Device domain) {
Device savedDevice = super.save(domain);
public Device save(TenantId tenantId, Device domain) {
Device savedDevice = super.save(tenantId, domain);
EntitySubtype entitySubtype = new EntitySubtype(savedDevice.getTenantId(), EntityType.DEVICE, savedDevice.getType());
EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype);
Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity);
executeWrite(saveStatement);
executeWrite(tenantId, saveStatement);
return savedDevice;
}
@Override
public List<Device> findDevicesByTenantId(UUID tenantId, TextPageLink 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);
log.trace("Found devices [{}] by tenantId [{}] and pageLink [{}]", deviceEntities, tenantId, pageLink);
@ -99,7 +100,7 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
@Override
public List<Device> findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink 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),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), 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();
query.and(eq(DEVICE_TENANT_ID_PROPERTY, tenantId));
query.and(in(ID_PROPERTY, deviceIds));
return findListByStatementAsync(query);
return findListByStatementAsync(new TenantId(tenantId), query);
}
@Override
public List<Device> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink 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),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
pageLink);
@ -131,7 +132,7 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
@Override
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);
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),
eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
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_CUSTOMER_ID_PROPERTY, customerId));
query.and(in(ID_PROPERTY, deviceIds));
return findListByStatementAsync(query);
return findListByStatementAsync(new TenantId(tenantId), query);
}
@Override
@ -158,7 +159,7 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
Select.Where query = select.where();
query.and(eq(DEVICE_TENANT_ID_PROPERTY, tenantId));
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
@ -168,7 +169,7 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId));
query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.DEVICE));
query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(query);
ResultSetFuture resultSetFuture = executeAsyncRead(new TenantId(tenantId), query);
return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() {
@Nullable
@Override

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

@ -15,6 +15,7 @@
*/
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.dao.Dao;
@ -28,10 +29,11 @@ public interface DeviceCredentialsDao extends Dao<DeviceCredentials> {
/**
* Save or update device credentials object
*
* @param tenantId the device tenant id
* @param deviceCredentials the device credentials object
* @return saved device credentials object
*/
DeviceCredentials save(DeviceCredentials deviceCredentials);
DeviceCredentials save(TenantId tenantId, DeviceCredentials deviceCredentials);
/**
* Find device credentials by device id.
@ -39,7 +41,7 @@ public interface DeviceCredentialsDao extends Dao<DeviceCredentials> {
* @param deviceId the device id
* @return the device credentials object
*/
DeviceCredentials findByDeviceId(UUID deviceId);
DeviceCredentials findByDeviceId(TenantId tenantId, UUID deviceId);
/**
* Find device credentials by credentials id.
@ -47,6 +49,6 @@ public interface DeviceCredentialsDao extends Dao<DeviceCredentials> {
* @param credentialsId the credentials id
* @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;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
public interface DeviceCredentialsService {
DeviceCredentials findDeviceCredentialsByDeviceId(DeviceId deviceId);
DeviceCredentials findDeviceCredentialsByDeviceId(TenantId tenantId, DeviceId deviceId);
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.thingsboard.server.common.data.Device;
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.DeviceCredentialsType;
import org.thingsboard.server.common.msg.EncryptionUtil;
@ -45,38 +47,38 @@ public class DeviceCredentialsServiceImpl implements DeviceCredentialsService {
private DeviceService deviceService;
@Override
public DeviceCredentials findDeviceCredentialsByDeviceId(DeviceId deviceId) {
public DeviceCredentials findDeviceCredentialsByDeviceId(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceCredentialsByDeviceId [{}]", deviceId);
validateId(deviceId, "Incorrect deviceId " + deviceId);
return deviceCredentialsDao.findByDeviceId(deviceId.getId());
return deviceCredentialsDao.findByDeviceId(tenantId, deviceId.getId());
}
@Override
@Cacheable(cacheNames = DEVICE_CREDENTIALS_CACHE, unless="#result == null")
@Cacheable(cacheNames = DEVICE_CREDENTIALS_CACHE, unless = "#result == null")
public DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId) {
log.trace("Executing findDeviceCredentialsByCredentialsId [{}]", credentialsId);
validateString(credentialsId, "Incorrect credentialsId " + credentialsId);
return deviceCredentialsDao.findByCredentialsId(credentialsId);
return deviceCredentialsDao.findByCredentialsId(new TenantId(EntityId.NULL_UUID), credentialsId);
}
@Override
@CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, keyGenerator="previousDeviceCredentialsId", beforeInvocation = true)
public DeviceCredentials updateDeviceCredentials(DeviceCredentials deviceCredentials) {
return saveOrUpdate(deviceCredentials);
@CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, keyGenerator = "previousDeviceCredentialsId", beforeInvocation = true)
public DeviceCredentials updateDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials) {
return saveOrUpdate(tenantId, deviceCredentials);
}
@Override
public DeviceCredentials createDeviceCredentials(DeviceCredentials deviceCredentials) {
return saveOrUpdate(deviceCredentials);
public DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials) {
return saveOrUpdate(tenantId, deviceCredentials);
}
private DeviceCredentials saveOrUpdate(DeviceCredentials deviceCredentials) {
private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) {
if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) {
formatCertData(deviceCredentials);
}
log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials);
credentialsValidator.validate(deviceCredentials);
return deviceCredentialsDao.save(deviceCredentials);
credentialsValidator.validate(deviceCredentials, id -> tenantId);
return deviceCredentialsDao.save(tenantId, deviceCredentials);
}
private void formatCertData(DeviceCredentials deviceCredentials) {
@ -87,37 +89,37 @@ public class DeviceCredentialsServiceImpl implements DeviceCredentialsService {
}
@Override
@CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, key="#deviceCredentials.credentialsId")
public void deleteDeviceCredentials(DeviceCredentials deviceCredentials) {
@CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, key = "#deviceCredentials.credentialsId")
public void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials) {
log.trace("Executing deleteDeviceCredentials [{}]", deviceCredentials);
deviceCredentialsDao.removeById(deviceCredentials.getUuidId());
deviceCredentialsDao.removeById(tenantId, deviceCredentials.getUuidId());
}
private DataValidator<DeviceCredentials> credentialsValidator =
new DataValidator<DeviceCredentials>() {
@Override
protected void validateCreate(DeviceCredentials deviceCredentials) {
DeviceCredentials existingCredentialsEntity = deviceCredentialsDao.findByCredentialsId(deviceCredentials.getCredentialsId());
protected void validateCreate(TenantId tenantId, DeviceCredentials deviceCredentials) {
DeviceCredentials existingCredentialsEntity = deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId());
if (existingCredentialsEntity != null) {
throw new DataValidationException("Create of existent device credentials!");
}
}
@Override
protected void validateUpdate(DeviceCredentials deviceCredentials) {
DeviceCredentials existingCredentials = deviceCredentialsDao.findById(deviceCredentials.getUuidId());
protected void validateUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) {
DeviceCredentials existingCredentials = deviceCredentialsDao.findById(tenantId, deviceCredentials.getUuidId());
if (existingCredentials == null) {
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())) {
throw new DataValidationException("Specified credentials are already registered!");
}
}
@Override
protected void validateDataImpl(DeviceCredentials deviceCredentials) {
protected void validateDataImpl(TenantId tenantId, DeviceCredentials deviceCredentials) {
if (deviceCredentials.getDeviceId() == null) {
throw new DataValidationException("Device credentials should be assigned to device!");
}
@ -127,7 +129,7 @@ public class DeviceCredentialsServiceImpl implements DeviceCredentialsService {
if (StringUtils.isEmpty(deviceCredentials.getCredentialsId())) {
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) {
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 org.thingsboard.server.common.data.Device;
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.dao.Dao;
@ -37,7 +38,7 @@ public interface DeviceDao extends Dao<Device> {
* @param device the device object
* @return saved device object
*/
Device save(Device device);
Device save(TenantId tenantId, Device device);
/**
* 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 {
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 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);
@ -59,7 +59,7 @@ public interface DeviceService {
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);

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;
@Override
public Device findDeviceById(DeviceId deviceId) {
public Device findDeviceById(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
return deviceDao.findById(deviceId.getId());
return deviceDao.findById(tenantId, deviceId.getId());
}
@Override
public ListenableFuture<Device> findDeviceByIdAsync(DeviceId deviceId) {
public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", 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}")
@ -122,38 +122,38 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override
public Device saveDevice(Device device) {
log.trace("Executing saveDevice [{}]", device);
deviceValidator.validate(device);
Device savedDevice = deviceDao.save(device);
deviceValidator.validate(device, Device::getTenantId);
Device savedDevice = deviceDao.save(device.getTenantId(), device);
if (device.getId() == null) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(deviceCredentials);
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
}
return savedDevice;
}
@Override
public Device assignDeviceToCustomer(DeviceId deviceId, CustomerId customerId) {
Device device = findDeviceById(deviceId);
public Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId) {
Device device = findDeviceById(tenantId, deviceId);
device.setCustomerId(customerId);
return saveDevice(device);
}
@Override
public Device unassignDeviceFromCustomer(DeviceId deviceId) {
Device device = findDeviceById(deviceId);
public Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId) {
Device device = findDeviceById(tenantId, deviceId);
device.setCustomerId(null);
return saveDevice(device);
}
@Override
public void deleteDevice(DeviceId deviceId) {
public void deleteDevice(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing deleteDevice [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
Device device = deviceDao.findById(deviceId.getId());
Device device = deviceDao.findById(tenantId, deviceId.getId());
try {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get();
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);
}
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, deviceId);
if (deviceCredentials != null) {
deviceCredentialsService.deleteDeviceCredentials(deviceCredentials);
deviceCredentialsService.deleteDeviceCredentials(tenantId, deviceCredentials);
}
deleteEntityRelations(deviceId);
deleteEntityRelations(tenantId, deviceId);
List<Object> list = new ArrayList<>();
list.add(device.getTenantId());
@ -176,7 +176,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
Cache cache = cacheManager.getCache(DEVICE_CACHE);
cache.evict(list);
deviceDao.removeById(deviceId.getId());
deviceDao.removeById(tenantId, deviceId.getId());
}
@Override
@ -211,7 +211,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
public void deleteDevicesByTenantId(TenantId tenantId) {
log.trace("Executing deleteDevicesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantDevicesRemover.removeEntities(tenantId);
tenantDevicesRemover.removeEntities(tenantId, tenantId);
}
@Override
@ -250,19 +250,19 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
log.trace("Executing unassignCustomerDevices, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
new CustomerDevicesUnassigner(tenantId).removeEntities(customerId);
customerDeviceUnasigner.removeEntities(tenantId, customerId);
}
@Override
public ListenableFuture<List<Device>> findDevicesByQuery(DeviceSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery());
public ListenableFuture<List<Device>> findDevicesByQuery(TenantId tenantId, DeviceSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery());
ListenableFuture<List<Device>> devices = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<Device>> futures = new ArrayList<>();
for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
if (entityId.getEntityType() == EntityType.DEVICE) {
futures.add(findDeviceByIdAsync(new DeviceId(entityId.getId())));
futures.add(findDeviceByIdAsync(tenantId, new DeviceId(entityId.getId())));
}
}
return Futures.successfulAsList(futures);
@ -285,7 +285,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
ListenableFuture<List<EntitySubtype>> tenantDeviceTypes = deviceDao.findTenantDeviceTypesAsync(tenantId.getId());
return Futures.transform(tenantDeviceTypes,
(Function<List<EntitySubtype>, List<EntitySubtype>>) deviceTypes -> {
deviceTypes -> {
deviceTypes.sort(Comparator.comparing(EntitySubtype::getType));
return deviceTypes;
});
@ -295,7 +295,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
new DataValidator<Device>() {
@Override
protected void validateCreate(Device device) {
protected void validateCreate(TenantId tenantId, Device device) {
deviceDao.findDeviceByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> {
throw new DataValidationException("Device with such name already exists!");
@ -304,7 +304,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
}
@Override
protected void validateUpdate(Device device) {
protected void validateUpdate(TenantId tenantId, Device device) {
deviceDao.findDeviceByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> {
if (!d.getUuidId().equals(device.getUuidId())) {
@ -315,7 +315,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
}
@Override
protected void validateDataImpl(Device device) {
protected void validateDataImpl(TenantId tenantId, Device device) {
if (StringUtils.isEmpty(device.getType())) {
throw new DataValidationException("Device type should be specified!");
}
@ -325,7 +325,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
if (device.getTenantId() == null) {
throw new DataValidationException("Device should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(device.getTenantId().getId());
Tenant tenant = tenantDao.findById(device.getTenantId(), device.getTenantId().getId());
if (tenant == null) {
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) {
device.setCustomerId(new CustomerId(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) {
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 =
new PaginatedRemover<TenantId, Device>() {
new PaginatedRemover<TenantId, Device>() {
@Override
protected List<Device> findEntities(TenantId id, TextPageLink 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> {
@Override
protected List<Device> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
return deviceDao.findDevicesByTenantId(id.getId(), pageLink);
}
private TenantId tenantId;
@Override
protected void removeEntity(TenantId tenantId, Device entity) {
deleteDevice(tenantId, new DeviceId(entity.getUuidId()));
}
};
CustomerDevicesUnassigner(TenantId tenantId) {
this.tenantId = tenantId;
}
private PaginatedRemover<CustomerId, Device> customerDeviceUnasigner = new PaginatedRemover<CustomerId, Device>() {
@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);
}
@Override
protected void removeEntity(Device entity) {
unassignDeviceFromCustomer(new DeviceId(entity.getUuidId()));
protected void removeEntity(TenantId tenantId, Device entity) {
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 org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.relation.RelationService;
@Slf4j
@ -26,9 +27,9 @@ public abstract class AbstractEntityService {
@Autowired
protected RelationService relationService;
protected void deleteEntityRelations(EntityId entityId) {
protected void deleteEntityRelations(TenantId tenantId, EntityId 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;
@Override
public void deleteEntityRelations(EntityId entityId) {
super.deleteEntityRelations(entityId);
public void deleteEntityRelations(TenantId tenantId, EntityId entityId) {
super.deleteEntityRelations(tenantId, entityId);
}
@Override
public ListenableFuture<String> fetchEntityNameAsync(EntityId entityId) {
public ListenableFuture<String> fetchEntityNameAsync(TenantId tenantId, EntityId entityId) {
log.trace("Executing fetchEntityNameAsync [{}]", entityId);
ListenableFuture<String> entityName;
ListenableFuture<? extends HasName> hasName;
switch (entityId.getEntityType()) {
case ASSET:
hasName = assetService.findAssetByIdAsync(new AssetId(entityId.getId()));
hasName = assetService.findAssetByIdAsync(tenantId, new AssetId(entityId.getId()));
break;
case DEVICE:
hasName = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
hasName = deviceService.findDeviceByIdAsync(tenantId, new DeviceId(entityId.getId()));
break;
case ENTITY_VIEW:
hasName = entityViewService.findEntityViewByIdAsync(new EntityViewId(entityId.getId()));
hasName = entityViewService.findEntityViewByIdAsync(tenantId, new EntityViewId(entityId.getId()));
break;
case TENANT:
hasName = tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
hasName = tenantService.findTenantByIdAsync(tenantId, new TenantId(entityId.getId()));
break;
case CUSTOMER:
hasName = customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));
hasName = customerService.findCustomerByIdAsync(tenantId, new CustomerId(entityId.getId()));
break;
case USER:
hasName = userService.findUserByIdAsync(new UserId(entityId.getId()));
hasName = userService.findUserByIdAsync(tenantId, new UserId(entityId.getId()));
break;
case DASHBOARD:
hasName = dashboardService.findDashboardInfoByIdAsync(new DashboardId(entityId.getId()));
hasName = dashboardService.findDashboardInfoByIdAsync(tenantId, new DashboardId(entityId.getId()));
break;
case ALARM:
hasName = alarmService.findAlarmByIdAsync(new AlarmId(entityId.getId()));
hasName = alarmService.findAlarmByIdAsync(tenantId, new AlarmId(entityId.getId()));
break;
case RULE_CHAIN:
hasName = ruleChainService.findRuleChainByIdAsync(new RuleChainId(entityId.getId()));
hasName = ruleChainService.findRuleChainByIdAsync(tenantId, new RuleChainId(entityId.getId()));
break;
default:
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 org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
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.EntityType;
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.dao.DaoUtil;
import org.thingsboard.server.dao.model.EntitySubtypeEntity;
@ -82,12 +83,12 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
}
@Override
public EntityView save(EntityView domain) {
EntityView savedEntityView = super.save(domain);
public EntityView save(TenantId tenantId, EntityView domain) {
EntityView savedEntityView = super.save(domain.getTenantId(), domain);
EntitySubtype entitySubtype = new EntitySubtype(savedEntityView.getTenantId(), EntityType.ENTITY_VIEW, savedEntityView.getType());
EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype);
Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity);
executeWrite(saveStatement);
executeWrite(tenantId, saveStatement);
return savedEntityView;
}
@ -95,7 +96,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
public List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find entity views by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
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);
log.trace("Found entity views [{}] by tenantId [{}] and pageLink [{}]",
entityViewEntities, tenantId, pageLink);
@ -106,7 +107,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
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);
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),
eq(TENANT_ID_PROPERTY, tenantId)), 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();
query.and(eq(ENTITY_VIEW_TENANT_ID_PROPERTY, tenantId));
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
public List<EntityView> findEntityViewsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
log.debug("Try to find entity views by tenantId [{}], customerId[{}] and pageLink [{}]",
tenantId, customerId, pageLink);
List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(
List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(new TenantId(tenantId),
ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF,
Arrays.asList(eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)),
pageLink);
@ -139,7 +140,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
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 [{}]",
tenantId, customerId, type, pageLink);
List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(
List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(new TenantId(tenantId),
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)),
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();
query.and(eq(TENANT_ID_PROPERTY, tenantId));
query.and(eq(ENTITY_ID_COLUMN, entityId));
return findListByStatementAsync(query);
return findListByStatementAsync(new TenantId(tenantId), query);
}
@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_ENTITY_TYPE_PROPERTY, EntityType.ENTITY_VIEW));
query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(query);
ResultSetFuture resultSetFuture = executeAsyncRead(new TenantId(tenantId), query);
return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() {
@Nullable
@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.EntitySubtype;
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.dao.Dao;
@ -37,7 +38,7 @@ public interface EntityViewDao extends Dao<EntityView> {
* @param entityView the 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.

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 org.thingsboard.server.common.data.EntitySubtype;
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.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
@ -35,13 +36,13 @@ public interface EntityViewService {
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);
EntityView findEntityViewById(EntityViewId entityViewId);
EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId);
EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name);
@ -53,13 +54,13 @@ public interface EntityViewService {
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);
void deleteEntityView(EntityViewId entityViewId);
void deleteEntityView(TenantId tenantId, EntityViewId entityViewId);
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
public EntityView saveEntityView(EntityView entityView) {
log.trace("Executing save entity view [{}]", entityView);
entityViewValidator.validate(entityView);
EntityView savedEntityView = entityViewDao.save(entityView);
entityViewValidator.validate(entityView, EntityView::getTenantId);
EntityView savedEntityView = entityViewDao.save(entityView.getTenantId(), entityView);
return savedEntityView;
}
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override
public EntityView assignEntityViewToCustomer(EntityViewId entityViewId, CustomerId customerId) {
EntityView entityView = findEntityViewById(entityViewId);
public EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, CustomerId customerId) {
EntityView entityView = findEntityViewById(tenantId, entityViewId);
entityView.setCustomerId(customerId);
return saveEntityView(entityView);
}
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override
public EntityView unassignEntityViewFromCustomer(EntityViewId entityViewId) {
EntityView entityView = findEntityViewById(entityViewId);
public EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId) {
EntityView entityView = findEntityViewById(tenantId, entityViewId);
entityView.setCustomerId(null);
return saveEntityView(entityView);
}
@ -121,15 +121,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
log.trace("Executing unassignCustomerEntityViews, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
new CustomerEntityViewsUnAssigner(tenantId).removeEntities(customerId);
customerEntityViewsUnAssigner.removeEntities(tenantId, customerId);
}
@Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override
public EntityView findEntityViewById(EntityViewId entityViewId) {
public EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId) {
log.trace("Executing findEntityViewById [{}]", 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}")
@ -187,15 +187,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
}
@Override
public ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery());
public ListenableFuture<List<EntityView>> findEntityViewsByQuery(TenantId tenantId, EntityViewSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery());
ListenableFuture<List<EntityView>> entityViews = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<EntityView>> futures = new ArrayList<>();
for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
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);
@ -213,10 +213,10 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
}
@Override
public ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId) {
public ListenableFuture<EntityView> findEntityViewByIdAsync(TenantId tenantId, EntityViewId entityViewId) {
log.trace("Executing findEntityViewById [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
return entityViewDao.findByIdAsync(entityViewId.getId());
return entityViewDao.findByIdAsync(tenantId, entityViewId.getId());
}
@Override
@ -253,21 +253,21 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override
public void deleteEntityView(EntityViewId entityViewId) {
public void deleteEntityView(TenantId tenantId, EntityViewId entityViewId) {
log.trace("Executing deleteEntityView [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
deleteEntityRelations(entityViewId);
EntityView entityView = entityViewDao.findById(entityViewId.getId());
deleteEntityRelations(tenantId, entityViewId);
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.getName()));
entityViewDao.removeById(entityViewId.getId());
entityViewDao.removeById(tenantId, entityViewId.getId());
}
@Override
public void deleteEntityViewsByTenantId(TenantId tenantId) {
log.trace("Executing deleteEntityViewsByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantEntityViewRemover.removeEntities(tenantId);
tenantEntityViewRemover.removeEntities(tenantId, tenantId);
}
@Override
@ -286,7 +286,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
new DataValidator<EntityView>() {
@Override
protected void validateCreate(EntityView entityView) {
protected void validateCreate(TenantId tenantId, EntityView entityView) {
entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName())
.ifPresent(e -> {
throw new DataValidationException("Entity view with such name already exists!");
@ -294,7 +294,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
}
@Override
protected void validateUpdate(EntityView entityView) {
protected void validateUpdate(TenantId tenantId, EntityView entityView) {
entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName())
.ifPresent(e -> {
if (!e.getUuidId().equals(entityView.getUuidId())) {
@ -304,7 +304,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
}
@Override
protected void validateDataImpl(EntityView entityView) {
protected void validateDataImpl(TenantId tenantId, EntityView entityView) {
if (StringUtils.isEmpty(entityView.getType())) {
throw new DataValidationException("Entity View type should be specified!");
}
@ -314,7 +314,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
if (entityView.getTenantId() == null) {
throw new DataValidationException("Entity view should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(entityView.getTenantId().getId());
Tenant tenant = tenantDao.findById(tenantId, entityView.getTenantId().getId());
if (tenant == null) {
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) {
entityView.setCustomerId(new CustomerId(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) {
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 =
new PaginatedRemover<TenantId, EntityView>() {
@Override
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;
private PaginatedRemover<TenantId, EntityView> tenantEntityViewRemover = new PaginatedRemover<TenantId, EntityView>() {
@Override
protected List<EntityView> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
return entityViewDao.findEntityViewsByTenantId(id.getId(), pageLink);
}
CustomerEntityViewsUnAssigner(TenantId tenantId) {
this.tenantId = tenantId;
@Override
protected void removeEntity(TenantId tenantId, EntityView entity) {
deleteEntityView(tenantId, new EntityViewId(entity.getUuidId()));
}
};
private PaginatedRemover<CustomerId, EntityView> customerEntityViewsUnAssigner = new PaginatedRemover<CustomerId, EntityView>() {
@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);
}
@Override
protected void removeEntity(EntityView entity) {
unassignEntityViewFromCustomer(new EntityViewId(entity.getUuidId()));
protected void removeEntity(TenantId tenantId, EntityView entity) {
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
public Event save(Event event) {
eventValidator.validate(event);
return eventDao.save(event);
eventValidator.validate(event, Event::getTenantId);
return eventDao.save(event.getTenantId(), event);
}
@Override
public ListenableFuture<Event> saveAsync(Event event) {
eventValidator.validate(event);
eventValidator.validate(event, Event::getTenantId);
return eventDao.saveAsync(event);
}
@Override
public Optional<Event> saveIfNotExists(Event event) {
eventValidator.validate(event);
eventValidator.validate(event, Event::getTenantId);
if (StringUtils.isEmpty(event.getUid())) {
throw new DataValidationException("Event uid should be specified!.");
}
@ -97,7 +97,7 @@ public class BaseEventService implements EventService {
private DataValidator<Event> eventValidator =
new DataValidator<Event>() {
@Override
protected void validateDataImpl(Event event) {
protected void validateDataImpl(TenantId tenantId, Event event) {
if (event.getEntityId() == null) {
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
public Event save(Event event) {
public Event save(TenantId tenantId, Event event) {
try {
return saveAsync(event).get();
} catch (InterruptedException | ExecutionException e) {
@ -85,7 +85,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
if (StringUtils.isEmpty(event.getUid())) {
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));
}
@ -98,7 +98,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
if (event.getId() == null) {
event.setId(new EventId(UUIDs.timeBased()));
}
return save(new EventEntity(event), true);
return save(event.getTenantId(), new EventEntity(event), true);
}
@Override
@ -111,7 +111,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
.and(eq(ModelConstants.EVENT_TYPE_PROPERTY, eventType))
.and(eq(ModelConstants.EVENT_UID_PROPERTY, eventUid));
log.trace("Execute query [{}]", query);
EventEntity entity = findOneByStatement(query);
EventEntity entity = findOneByStatement(new TenantId(tenantId), query);
if (log.isTraceEnabled()) {
log.trace("Search result: [{}] for event entity [{}]", entity != null, entity);
} else {
@ -123,7 +123,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
@Override
public List<Event> findEvents(UUID tenantId, EntityId entityId, TimePageLink 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),
eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType()),
eq(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entityId.getId())),
@ -135,7 +135,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
@Override
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);
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),
eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType()),
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.limit(limit);
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);
}
private Optional<Event> save(EventEntity entity, boolean ifNotExists) {
private Optional<Event> save(TenantId tenantId, EventEntity entity, boolean ifNotExists) {
try {
return saveAsync(entity, ifNotExists).get();
return saveAsync(tenantId, entity, ifNotExists).get();
} catch (InterruptedException | ExecutionException 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) {
entity.setId(UUIDs.timeBased());
}
@ -185,7 +185,7 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao<EventE
if (ifNotExists) {
insert = insert.ifNotExists();
}
ResultSetFuture resultSetFuture = executeAsyncWrite(insert);
ResultSetFuture resultSetFuture = executeAsyncWrite(tenantId, insert);
return Futures.transform(resultSetFuture, rs -> {
if (rs.wasApplied()) {
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 org.thingsboard.server.common.data.Event;
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.dao.Dao;
@ -36,7 +37,7 @@ public interface EventDao extends Dao<Event> {
* @param event the event object
* @return saved event object
*/
Event save(Event event);
Event save(TenantId tenantId, Event event);
/**
* 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 lombok.extern.slf4j.Slf4j;
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.model.type.AuthorityCodec;
import org.thingsboard.server.dao.model.type.ComponentLifecycleStateCodec;
@ -84,37 +85,37 @@ public abstract class CassandraAbstractDao {
}
}
protected ResultSet executeRead(Statement statement) {
return execute(statement, defaultReadLevel);
protected ResultSet executeRead(TenantId tenantId, Statement statement) {
return execute(tenantId, statement, defaultReadLevel);
}
protected ResultSet executeWrite(Statement statement) {
return execute(statement, defaultWriteLevel);
protected ResultSet executeWrite(TenantId tenantId, Statement statement) {
return execute(tenantId, statement, defaultWriteLevel);
}
protected ResultSetFuture executeAsyncRead(Statement statement) {
return executeAsync(statement, defaultReadLevel);
protected ResultSetFuture executeAsyncRead(TenantId tenantId, Statement statement) {
return executeAsync(tenantId, statement, defaultReadLevel);
}
protected ResultSetFuture executeAsyncWrite(Statement statement) {
return executeAsync(statement, defaultWriteLevel);
protected ResultSetFuture executeAsyncWrite(TenantId tenantId, Statement statement) {
return executeAsync(tenantId, statement, defaultWriteLevel);
}
private ResultSet execute(Statement statement, ConsistencyLevel level) {
private ResultSet execute(TenantId tenantId, Statement statement, ConsistencyLevel level) {
if (log.isDebugEnabled()) {
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()) {
log.debug("Execute cassandra async statement {}", statementToString(statement));
}
if (statement.getConsistencyLevel() == null) {
statement.setConsistencyLevel(level);
}
return rateLimiter.submit(new CassandraStatementTask(getSession(), statement));
return rateLimiter.submit(new CassandraStatementTask(tenantId, getSession(), 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.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.DaoUtil;
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());
}
protected List<E> findListByStatement(Statement statement) {
protected List<E> findListByStatement(TenantId tenantId, Statement statement) {
List<E> list = Collections.emptyList();
if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSet resultSet = executeRead(statement);
ResultSet resultSet = executeRead(tenantId, statement);
Result<E> result = getMapper().map(resultSet);
if (result != null) {
list = result.all();
@ -69,10 +70,10 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
return list;
}
protected ListenableFuture<List<D>> findListByStatementAsync(Statement statement) {
protected ListenableFuture<List<D>> findListByStatementAsync(TenantId tenantId, Statement statement) {
if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(statement);
ResultSetFuture resultSetFuture = executeAsyncRead(tenantId, statement);
return Futures.transform(resultSetFuture, new Function<ResultSet, List<D>>() {
@Nullable
@Override
@ -90,11 +91,11 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
return Futures.immediateFuture(Collections.emptyList());
}
protected E findOneByStatement(Statement statement) {
protected E findOneByStatement(TenantId tenantId, Statement statement) {
E object = null;
if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSet resultSet = executeRead(statement);
ResultSet resultSet = executeRead(tenantId, statement);
Result<E> result = getMapper().map(resultSet);
if (result != null) {
object = result.one();
@ -103,10 +104,10 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
return object;
}
protected ListenableFuture<D> findOneByStatementAsync(Statement statement) {
protected ListenableFuture<D> findOneByStatementAsync(TenantId tenantId, Statement statement) {
if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = executeAsyncRead(statement);
ResultSetFuture resultSetFuture = executeAsyncRead(tenantId, statement);
return Futures.transform(resultSetFuture, new Function<ResultSet, D>() {
@Nullable
@Override
@ -128,16 +129,16 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
return getMapper().saveQuery(dto);
}
protected EntityResultSet<E> saveWithResult(E entity) {
protected EntityResultSet<E> saveWithResult(TenantId tenantId, E entity) {
log.debug("Save entity {}", entity);
if (entity.getId() == null) {
entity.setId(UUIDs.timeBased());
} else if (isDeleteOnSave()) {
removeById(entity.getId());
removeById(tenantId, entity.getId());
}
Statement saveStatement = getSaveQuery(entity);
saveStatement.setConsistencyLevel(cluster.getDefaultWriteConsistencyLevel());
ResultSet resultSet = executeWrite(saveStatement);
ResultSet resultSet = executeWrite(tenantId, saveStatement);
return new EntityResultSet<>(resultSet, entity);
}
@ -146,7 +147,7 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
}
@Override
public D save(D domain) {
public D save(TenantId tenantId, D domain) {
E entity;
try {
entity = getColumnFamilyClass().getConstructor(domain.getClass()).newInstance(domain);
@ -156,48 +157,39 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
}
entity = updateSearchTextIfPresent(entity);
log.debug("Saving entity {}", entity);
entity = saveWithResult(entity).getEntity();
entity = saveWithResult(tenantId, entity).getEntity();
return DaoUtil.getData(entity);
}
@Override
public D findById(UUID key) {
public D findById(TenantId tenantId, UUID key) {
log.debug("Get entity by key {}", key);
Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key));
log.trace("Execute query {}", query);
E entity = findOneByStatement(query);
E entity = findOneByStatement(tenantId, query);
return DaoUtil.getData(entity);
}
@Override
public ListenableFuture<D> findByIdAsync(UUID key) {
public ListenableFuture<D> findByIdAsync(TenantId tenantId, UUID key) {
log.debug("Get entity by key {}", key);
Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key));
log.trace("Execute query {}", query);
return findOneByStatementAsync(query);
return findOneByStatementAsync(tenantId, query);
}
@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));
log.debug("Remove request: {}", delete.toString());
return executeWrite(delete).wasApplied();
return executeWrite(tenantId, delete).wasApplied();
}
@Override
public List<D> find() {
public List<D> find(TenantId tenantId) {
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);
}
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 lombok.extern.slf4j.Slf4j;
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.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.SearchTextEntity;
@ -43,38 +44,38 @@ public abstract class CassandraAbstractSearchTextDao<E extends SearchTextEntity<
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);
Where query = select.where();
for (Clause clause : clauses) {
query.and(clause);
}
}
query.limit(pageLink.getLimit());
if (!StringUtils.isEmpty(pageLink.getTextOffset())) {
query.and(eq(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextOffset()));
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()) {
select = select().from(searchView);
query = select.where();
for (Clause clause : clauses) {
query.and(clause);
}
}
query.and(QueryBuilder.gt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextOffset()));
if (!StringUtils.isEmpty(pageLink.getTextSearch())) {
query.and(QueryBuilder.lt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearchBound()));
}
int limit = pageLink.getLimit() - result.size();
query.limit(limit);
result.addAll(findListByStatement(query));
result.addAll(findListByStatement(tenantId, query));
}
return result;
} else if (!StringUtils.isEmpty(pageLink.getTextSearch())) {
query.and(QueryBuilder.gte(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearch()));
query.and(QueryBuilder.lt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearchBound()));
return findListByStatement(query);
return findListByStatement(tenantId, query);
} 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.Where;
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.dao.model.BaseEntity;
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> {
protected List<E> findPageWithTimeSearch(String searchView, List<Clause> clauses, TimePageLink pageLink) {
return findPageWithTimeSearch(searchView, clauses, Collections.emptyList(), pageLink);
protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, TimePageLink pageLink) {
return findPageWithTimeSearch(tenantId, searchView, clauses, Collections.emptyList(), pageLink);
}
protected List<E> findPageWithTimeSearch(String searchView, List<Clause> clauses, Ordering ordering, TimePageLink pageLink) {
return findPageWithTimeSearch(searchView, clauses, Collections.singletonList(ordering), pageLink);
protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, Ordering ordering, TimePageLink pageLink) {
return findPageWithTimeSearch(tenantId, searchView, clauses, Collections.singletonList(ordering), pageLink);
}
protected List<E> findPageWithTimeSearch(String searchView, List<Clause> clauses, List<Ordering> topLevelOrderings, TimePageLink pageLink) {
return findPageWithTimeSearch(searchView, clauses, topLevelOrderings, pageLink, ModelConstants.ID_PROPERTY);
protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, List<Ordering> topLevelOrderings, TimePageLink pageLink) {
return findPageWithTimeSearch(tenantId, searchView, clauses, topLevelOrderings, pageLink, ModelConstants.ID_PROPERTY);
}
protected List<E> findPageWithTimeSearch(String searchView, List<Clause> clauses, TimePageLink pageLink, String idColumn) {
return findPageWithTimeSearch(searchView, clauses, Collections.emptyList(), pageLink, idColumn);
protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, TimePageLink pageLink, String 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) {
return findListByStatement(buildQuery(searchView, clauses, topLevelOrderings, pageLink, idColumn));
protected List<E> findPageWithTimeSearch(TenantId tenantId, String searchView, List<Clause> clauses, List<Ordering> topLevelOrderings, TimePageLink pageLink, String idColumn) {
return findListByStatement(tenantId, buildQuery(searchView, clauses, topLevelOrderings, pageLink, 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.scheduling.annotation.Scheduled;
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.AsyncTaskContext;
import org.thingsboard.server.dao.util.NoSqlAnyDao;
import javax.annotation.PreDestroy;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 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.dispatcher_threads:2}") int dispatcherThreads,
@Value("${cassandra.query.callback_threads:2}") int callbackThreads,
@Value("${cassandra.query.poll_ms:50}") long pollMs) {
super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs);
@Value("${cassandra.query.poll_ms:50}") long 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}")
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(),
totalAdded.getAndSet(0), totalLaunched.getAndSet(0), totalReleased.getAndSet(0),
totalFailed.getAndSet(0), totalExpired.getAndSet(0), totalRejected.getAndSet(0),
concurrencyLevel.get());
totalRateLimited.getAndSet(0), concurrencyLevel.get());
}
@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.Statement;
import lombok.Data;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.util.AsyncTask;
/**
@ -26,6 +27,7 @@ import org.thingsboard.server.dao.util.AsyncTask;
@Data
public class CassandraStatementTask implements AsyncTask {
private final TenantId tenantId;
private final Session session;
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.id.EntityId;
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.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
@ -83,45 +84,45 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
}
@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()
.setUUID(0, from.getId())
.setString(1, from.getEntityType().name())
.set(2, typeGroup, relationTypeGroupCodec);
return executeAsyncRead(from, stmt);
return executeAsyncRead(tenantId, from, stmt);
}
@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()
.setUUID(0, from.getId())
.setString(1, from.getEntityType().name())
.set(2, typeGroup, relationTypeGroupCodec)
.setString(3, relationType);
return executeAsyncRead(from, stmt);
return executeAsyncRead(tenantId, from, stmt);
}
@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()
.setUUID(0, to.getId())
.setString(1, to.getEntityType().name())
.set(2, typeGroup, relationTypeGroupCodec);
return executeAsyncRead(to, stmt);
return executeAsyncRead(tenantId, to, stmt);
}
@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()
.setUUID(0, to.getId())
.setString(1, to.getEntityType().name())
.set(2, typeGroup, relationTypeGroupCodec)
.setString(3, relationType);
return executeAsyncRead(to, stmt);
return executeAsyncRead(tenantId, to, stmt);
}
@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()
.setUUID(0, from.getId())
.setString(1, from.getEntityType().name())
@ -129,11 +130,11 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
.setString(3, to.getEntityType().name())
.set(4, typeGroup, relationTypeGroupCodec)
.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
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()
.setUUID(0, from.getId())
.setString(1, from.getEntityType().name())
@ -141,24 +142,24 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
.setString(3, to.getEntityType().name())
.set(4, typeGroup, relationTypeGroupCodec)
.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
public boolean saveRelation(EntityRelation relation) {
BoundStatement stmt = getSaveRelationStatement(relation);
ResultSet rs = executeWrite(stmt);
public boolean saveRelation(TenantId tenantId, EntityRelation relation) {
BoundStatement stmt = getSaveRelationStatement(tenantId, relation);
ResultSet rs = executeWrite(tenantId, stmt);
return rs.wasApplied();
}
@Override
public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) {
BoundStatement stmt = getSaveRelationStatement(relation);
ResultSetFuture future = executeAsyncWrite(stmt);
public ListenableFuture<Boolean> saveRelationAsync(TenantId tenantId, EntityRelation relation) {
BoundStatement stmt = getSaveRelationStatement(tenantId, relation);
ResultSetFuture future = executeAsyncWrite(tenantId, stmt);
return getBooleanListenableFuture(future);
}
private BoundStatement getSaveRelationStatement(EntityRelation relation) {
private BoundStatement getSaveRelationStatement(TenantId tenantId, EntityRelation relation) {
BoundStatement stmt = getSaveStmt().bind()
.setUUID(0, relation.getFrom().getId())
.setString(1, relation.getFrom().getEntityType().name())
@ -171,30 +172,30 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
}
@Override
public boolean deleteRelation(EntityRelation relation) {
return deleteRelation(relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup());
public boolean deleteRelation(TenantId tenantId, EntityRelation relation) {
return deleteRelation(tenantId, relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup());
}
@Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) {
return deleteRelationAsync(relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup());
public ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityRelation relation) {
return deleteRelationAsync(tenantId, relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup());
}
@Override
public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getDeleteRelationStatement(from, to, relationType, typeGroup);
ResultSet rs = executeWrite(stmt);
public boolean deleteRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getDeleteRelationStatement(tenantId, from, to, relationType, typeGroup);
ResultSet rs = executeWrite(tenantId, stmt);
return rs.wasApplied();
}
@Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getDeleteRelationStatement(from, to, relationType, typeGroup);
ResultSetFuture future = executeAsyncWrite(stmt);
public ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
BoundStatement stmt = getDeleteRelationStatement(tenantId, from, to, relationType, typeGroup);
ResultSetFuture future = executeAsyncWrite(tenantId, stmt);
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()
.setUUID(0, from.getId())
.setString(1, from.getEntityType().name())
@ -206,26 +207,26 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
}
@Override
public boolean deleteOutboundRelations(EntityId entity) {
public boolean deleteOutboundRelations(TenantId tenantId, EntityId entity) {
BoundStatement stmt = getDeleteAllByEntityStmt().bind()
.setUUID(0, entity.getId())
.setString(1, entity.getEntityType().name());
ResultSet rs = executeWrite(stmt);
ResultSet rs = executeWrite(tenantId, stmt);
return rs.wasApplied();
}
@Override
public ListenableFuture<Boolean> deleteOutboundRelationsAsync(EntityId entity) {
public ListenableFuture<Boolean> deleteOutboundRelationsAsync(TenantId tenantId, EntityId entity) {
BoundStatement stmt = getDeleteAllByEntityStmt().bind()
.setUUID(0, entity.getId())
.setString(1, entity.getEntityType().name());
ResultSetFuture future = executeAsyncWrite(stmt);
ResultSetFuture future = executeAsyncWrite(tenantId, stmt);
return getBooleanListenableFuture(future);
}
@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,
Arrays.asList(eq(ModelConstants.RELATION_FROM_ID_PROPERTY, from.getId()),
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)
),
pageLink, ModelConstants.RELATION_TO_ID_PROPERTY);
return getFuture(executeAsyncRead(query), this::getEntityRelations);
return getFuture(executeAsyncRead(tenantId, query), this::getEntityRelations);
}
private PreparedStatement getSaveStmt() {
@ -347,9 +348,9 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
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);
return getFuture(executeAsyncRead(stmt), rs -> getEntityRelations(rs));
return getFuture(executeAsyncRead(tenantId, stmt), rs -> getEntityRelations(rs));
}
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.stereotype.Service;
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.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
@ -66,27 +68,27 @@ public class BaseRelationService implements RelationService {
private CacheManager cacheManager;
@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);
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}")
@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 {
return getRelationAsync(from, to, relationType, typeGroup).get();
return getRelationAsync(tenantId, from, to, relationType, typeGroup).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@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);
validate(from, to, relationType, typeGroup);
return relationDao.getRelation(from, to, relationType, typeGroup);
return relationDao.getRelation(tenantId, from, to, relationType, typeGroup);
}
@Caching(evict = {
@ -97,10 +99,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
})
@Override
public boolean saveRelation(EntityRelation relation) {
public boolean saveRelation(TenantId tenantId, EntityRelation relation) {
log.trace("Executing saveRelation [{}]", relation);
validate(relation);
return relationDao.saveRelation(relation);
return relationDao.saveRelation(tenantId, relation);
}
@Caching(evict = {
@ -111,10 +113,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
})
@Override
public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) {
public ListenableFuture<Boolean> saveRelationAsync(TenantId tenantId, EntityRelation relation) {
log.trace("Executing saveRelationAsync [{}]", relation);
validate(relation);
return relationDao.saveRelationAsync(relation);
return relationDao.saveRelationAsync(tenantId, relation);
}
@Caching(evict = {
@ -125,10 +127,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
})
@Override
public boolean deleteRelation(EntityRelation relation) {
public boolean deleteRelation(TenantId tenantId, EntityRelation relation) {
log.trace("Executing deleteRelation [{}]", relation);
validate(relation);
return relationDao.deleteRelation(relation);
return relationDao.deleteRelation(tenantId, relation);
}
@Caching(evict = {
@ -139,10 +141,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}")
})
@Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) {
public ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityRelation relation) {
log.trace("Executing deleteRelationAsync [{}]", relation);
validate(relation);
return relationDao.deleteRelationAsync(relation);
return relationDao.deleteRelationAsync(tenantId, relation);
}
@Caching(evict = {
@ -153,10 +155,10 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
})
@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);
validate(from, to, relationType, typeGroup);
return relationDao.deleteRelation(from, to, relationType, typeGroup);
return relationDao.deleteRelation(tenantId, from, to, relationType, typeGroup);
}
@Caching(evict = {
@ -167,69 +169,69 @@ public class BaseRelationService implements RelationService {
@CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
})
@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);
validate(from, to, relationType, typeGroup);
return relationDao.deleteRelationAsync(from, to, relationType, typeGroup);
return relationDao.deleteRelationAsync(tenantId, from, to, relationType, typeGroup);
}
@Override
public void deleteEntityRelations(EntityId entityId) {
public void deleteEntityRelations(TenantId tenantId, EntityId entityId) {
try {
deleteEntityRelationsAsync(entityId).get();
deleteEntityRelationsAsync(tenantId, entityId).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@Override
public ListenableFuture<Void> deleteEntityRelationsAsync(EntityId entityId) {
public ListenableFuture<Void> deleteEntityRelationsAsync(TenantId tenantId, EntityId entityId) {
Cache cache = cacheManager.getCache(RELATIONS_CACHE);
log.trace("Executing deleteEntityRelationsAsync [{}]", entityId);
validate(entityId);
List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>();
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);
List<ListenableFuture<List<EntityRelation>>> outboundRelationsList = new ArrayList<>();
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<Boolean>> inboundDeletions = Futures.transformAsync(inboundRelations,
relations -> {
List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(relations, cache, true);
List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(tenantId, relations, cache, true);
return Futures.allAsList(results);
});
ListenableFuture<List<Boolean>> outboundDeletions = Futures.transformAsync(outboundRelations,
relations -> {
List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(relations, cache, false);
List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(tenantId, relations, cache, false);
return Futures.allAsList(results);
});
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<>();
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;
}
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);
if (deleteFromDb) {
return relationDao.deleteRelationAsync(relation);
return relationDao.deleteRelationAsync(tenantId, relation);
} else {
return Futures.immediateFuture(false);
}
@ -272,18 +274,18 @@ public class BaseRelationService implements RelationService {
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}")
@Override
public List<EntityRelation> findByFrom(EntityId from, RelationTypeGroup typeGroup) {
public List<EntityRelation> findByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) {
validate(from);
validateTypeGroup(typeGroup);
try {
return relationDao.findAllByFrom(from, typeGroup).get();
return relationDao.findAllByFrom(tenantId, from, typeGroup).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@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);
validate(from);
validateTypeGroup(typeGroup);
@ -298,7 +300,7 @@ public class BaseRelationService implements RelationService {
if (fromCache != null) {
return Futures.immediateFuture(fromCache);
} else {
ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByFrom(from, typeGroup);
ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByFrom(tenantId, from, typeGroup);
Futures.addCallback(relationsFuture,
new FutureCallback<List<EntityRelation>>() {
@Override
@ -313,16 +315,16 @@ public class BaseRelationService implements RelationService {
}
@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);
validate(from);
validateTypeGroup(typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(from, typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(tenantId, from, typeGroup);
return Futures.transformAsync(relations,
relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.forEach(relation ->
futures.add(fetchRelationInfoAsync(relation,
futures.add(fetchRelationInfoAsync(tenantId, relation,
EntityRelation::getTo,
EntityRelationInfo::setToName))
);
@ -332,37 +334,37 @@ public class BaseRelationService implements RelationService {
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}")
@Override
public List<EntityRelation> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) {
public List<EntityRelation> findByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup) {
try {
return findByFromAndTypeAsync(from, relationType, typeGroup).get();
return findByFromAndTypeAsync(tenantId, from, relationType, typeGroup).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@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);
validate(from);
validateType(relationType);
validateTypeGroup(typeGroup);
return relationDao.findAllByFromAndType(from, relationType, typeGroup);
return relationDao.findAllByFromAndType(tenantId, from, relationType, typeGroup);
}
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}")
@Override
public List<EntityRelation> findByTo(EntityId to, RelationTypeGroup typeGroup) {
public List<EntityRelation> findByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup) {
validate(to);
validateTypeGroup(typeGroup);
try {
return relationDao.findAllByTo(to, typeGroup).get();
return relationDao.findAllByTo(tenantId, to, typeGroup).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@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);
validate(to);
validateTypeGroup(typeGroup);
@ -377,7 +379,7 @@ public class BaseRelationService implements RelationService {
if (fromCache != null) {
return Futures.immediateFuture(fromCache);
} else {
ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByTo(to, typeGroup);
ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByTo(tenantId, to, typeGroup);
Futures.addCallback(relationsFuture,
new FutureCallback<List<EntityRelation>>() {
@Override
@ -392,16 +394,16 @@ public class BaseRelationService implements RelationService {
}
@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);
validate(to);
validateTypeGroup(typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByTo(to, typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByTo(tenantId, to, typeGroup);
return Futures.transformAsync(relations,
relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.forEach(relation ->
futures.add(fetchRelationInfoAsync(relation,
futures.add(fetchRelationInfoAsync(tenantId, relation,
EntityRelation::getFrom,
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,
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 -> {
EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation);
entityNameSetter.accept(entityRelationInfo1, entityName1);
@ -422,25 +424,25 @@ public class BaseRelationService implements RelationService {
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
@Override
public List<EntityRelation> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) {
public List<EntityRelation> findByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup) {
try {
return findByToAndTypeAsync(to, relationType, typeGroup).get();
return findByToAndTypeAsync(tenantId, to, relationType, typeGroup).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@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);
validate(to);
validateType(relationType);
validateTypeGroup(typeGroup);
return relationDao.findAllByToAndType(to, relationType, typeGroup);
return relationDao.findAllByToAndType(tenantId, to, relationType, typeGroup);
}
@Override
public ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query) {
public ListenableFuture<List<EntityRelation>> findByQuery(TenantId tenantId, EntityRelationsQuery query) {
log.trace("Executing findByQuery [{}]", query);
RelationsSearchParameters params = query.getParameters();
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;
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 -> {
List<EntityRelation> relations = new ArrayList<>();
if (filters == null || filters.isEmpty()) {
@ -472,15 +474,15 @@ public class BaseRelationService implements RelationService {
}
@Override
public ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(EntityRelationsQuery query) {
public ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query) {
log.trace("Executing findInfoByQuery [{}]", query);
ListenableFuture<List<EntityRelation>> relations = findByQuery(query);
ListenableFuture<List<EntityRelation>> relations = findByQuery(tenantId, query);
EntitySearchDirection direction = query.getParameters().getDirection();
return Futures.transformAsync(relations,
relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.forEach(relation ->
futures.add(fetchRelationInfoAsync(relation,
futures.add(fetchRelationInfoAsync(tenantId, relation,
relation2 -> direction == EntitySearchDirection.FROM ? relation2.getTo() : relation2.getFrom(),
(EntityRelationInfo relationInfo, String entityName) -> {
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,
final ConcurrentHashMap<EntityId, Boolean> uniqueMap) throws Exception {
if (lvl == 0) {
@ -575,7 +577,7 @@ public class BaseRelationService implements RelationService {
}
lvl--;
//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<>();
for (EntityRelation childRelation : children) {
log.trace("Found Relation: {}", childRelation);
@ -594,7 +596,7 @@ public class BaseRelationService implements RelationService {
}
List<ListenableFuture<Set<EntityRelation>>> futures = new ArrayList<>();
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
List<Set<EntityRelation>> relations = Futures.successfulAsList(futures).get();
@ -602,15 +604,15 @@ public class BaseRelationService implements RelationService {
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;
if (relationTypeGroup == null) {
relationTypeGroup = RelationTypeGroup.COMMON;
}
if (direction == EntitySearchDirection.FROM) {
relations = findByFromAsync(rootId, relationTypeGroup);
relations = findByFromAsync(tenantId, rootId, relationTypeGroup);
} else {
relations = findByToAsync(rootId, relationTypeGroup);
relations = findByToAsync(tenantId, rootId, relationTypeGroup);
}
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 org.thingsboard.server.common.data.EntityType;
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.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
@ -29,34 +30,34 @@ import java.util.List;
*/
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);
}

47
dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.relation;
import com.google.common.util.concurrent.ListenableFuture;
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.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
@ -30,51 +31,51 @@ import java.util.concurrent.ExecutionException;
*/
public interface RelationService {
ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<Boolean> checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);
EntityRelation getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);
EntityRelation getRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<EntityRelation> getRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<EntityRelation> getRelationAsync(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);
void deleteEntityRelations(EntityId entity);
void deleteEntityRelations(TenantId tenantId, EntityId entity);
ListenableFuture<Void> deleteEntityRelationsAsync(EntityId entity);
ListenableFuture<Void> deleteEntityRelationsAsync(TenantId tenantId, EntityId entity);
List<EntityRelation> findByFrom(EntityId from, RelationTypeGroup typeGroup);
List<EntityRelation> findByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByFromAsync(EntityId from, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByFromAsync(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelationInfo>> findInfoByFrom(EntityId from, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelationInfo>> findInfoByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup);
List<EntityRelation> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup);
List<EntityRelation> findByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByFromAndTypeAsync(EntityId from, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByFromAndTypeAsync(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup);
List<EntityRelation> findByTo(EntityId to, RelationTypeGroup typeGroup);
List<EntityRelation> findByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByToAsync(EntityId to, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByToAsync(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelationInfo>> findInfoByTo(EntityId to, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelationInfo>> findInfoByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup);
List<EntityRelation> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup);
List<EntityRelation> findByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByToAndTypeAsync(EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByToAndTypeAsync(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query);
ListenableFuture<List<EntityRelation>> findByQuery(TenantId tenantId, EntityRelationsQuery query);
ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(EntityRelationsQuery query);
ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query);
// TODO: This method may be useful for some validations in the future
// ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);

124
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -67,11 +67,11 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override
public RuleChain saveRuleChain(RuleChain ruleChain) {
ruleChainValidator.validate(ruleChain);
RuleChain savedRuleChain = ruleChainDao.save(ruleChain);
ruleChainValidator.validate(ruleChain, RuleChain::getTenantId);
RuleChain savedRuleChain = ruleChainDao.save(ruleChain.getTenantId(), ruleChain);
if (ruleChain.isRoot() && ruleChain.getId() == null) {
try {
createRelation(new EntityRelation(savedRuleChain.getTenantId(), savedRuleChain.getId(),
createRelation(ruleChain.getTenantId(), new EntityRelation(savedRuleChain.getTenantId(), savedRuleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create tenant to root rule chain relation. from: [{}], to: [{}]",
@ -83,20 +83,20 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
public boolean setRootRuleChain(RuleChainId ruleChainId) {
RuleChain ruleChain = ruleChainDao.findById(ruleChainId.getId());
public boolean setRootRuleChain(TenantId tenantId, RuleChainId ruleChainId) {
RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId());
if (!ruleChain.isRoot()) {
RuleChain previousRootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId());
if (!previousRootRuleChain.getId().equals(ruleChain.getId())) {
try {
deleteRelation(new EntityRelation(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
deleteRelation(tenantId, new EntityRelation(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
previousRootRuleChain.setRoot(false);
ruleChainDao.save(previousRootRuleChain);
createRelation(new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
ruleChainDao.save(tenantId, previousRootRuleChain);
createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
ruleChain.setRoot(true);
ruleChainDao.save(ruleChain);
ruleChainDao.save(tenantId, ruleChain);
return true;
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to set root rule chain, ruleChainId: [{}]", ruleChainId);
@ -108,9 +108,9 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
public RuleChainMetaData saveRuleChainMetaData(RuleChainMetaData ruleChainMetaData) {
public RuleChainMetaData saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData) {
Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id.");
RuleChain ruleChain = findRuleChainById(ruleChainMetaData.getRuleChainId());
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainMetaData.getRuleChainId());
if (ruleChain == null) {
return null;
}
@ -130,9 +130,9 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
}
List<RuleNode> existingRuleNodes = getRuleChainNodes(ruleChainMetaData.getRuleChainId());
List<RuleNode> existingRuleNodes = getRuleChainNodes(tenantId, ruleChainMetaData.getRuleChainId());
for (RuleNode existingNode : existingRuleNodes) {
deleteEntityRelations(existingNode.getId());
deleteEntityRelations(tenantId, existingNode.getId());
Integer index = ruleNodeIndexMap.get(existingNode.getId());
if (index != null) {
toAddOrUpdate.add(ruleChainMetaData.getNodes().get(index));
@ -142,9 +142,9 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
for (RuleNode node : toAddOrUpdate) {
node.setRuleChainId(ruleChain.getId());
RuleNode savedNode = ruleNodeDao.save(node);
RuleNode savedNode = ruleNodeDao.save(tenantId, node);
try {
createRelation(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(),
createRelation(tenantId, new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create rule chain to rule node relation. from: [{}], to: [{}]",
@ -156,7 +156,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
ruleNodeIndexMap.put(savedNode.getId(), index);
}
for (RuleNode node : toDelete) {
deleteRuleNode(node.getId());
deleteRuleNode(tenantId, node.getId());
}
RuleNodeId firstRuleNodeId = null;
if (ruleChainMetaData.getFirstNodeIndex() != null) {
@ -165,7 +165,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
if ((ruleChain.getFirstRuleNodeId() != null && !ruleChain.getFirstRuleNodeId().equals(firstRuleNodeId))
|| (ruleChain.getFirstRuleNodeId() == null && firstRuleNodeId != null)) {
ruleChain.setFirstRuleNodeId(firstRuleNodeId);
ruleChainDao.save(ruleChain);
ruleChainDao.save(tenantId, ruleChain);
}
if (ruleChainMetaData.getConnections() != null) {
for (NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) {
@ -173,7 +173,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
EntityId to = nodes.get(nodeConnection.getToIndex()).getId();
String type = nodeConnection.getType();
try {
createRelation(new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE));
createRelation(tenantId, new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create rule node relation. from: [{}], to: [{}]", from, to);
throw new RuntimeException(e);
@ -186,7 +186,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
EntityId to = nodeToRuleChainConnection.getTargetRuleChainId();
String type = nodeToRuleChainConnection.getType();
try {
createRelation(new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE, nodeToRuleChainConnection.getAdditionalInfo()));
createRelation(tenantId, new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE, nodeToRuleChainConnection.getAdditionalInfo()));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create rule node to rule chain relation. from: [{}], to: [{}]", from, to);
throw new RuntimeException(e);
@ -194,19 +194,19 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
}
return loadRuleChainMetaData(ruleChainMetaData.getRuleChainId());
return loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId());
}
@Override
public RuleChainMetaData loadRuleChainMetaData(RuleChainId ruleChainId) {
public RuleChainMetaData loadRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id.");
RuleChain ruleChain = findRuleChainById(ruleChainId);
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
if (ruleChain == null) {
return null;
}
RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
ruleChainMetaData.setRuleChainId(ruleChainId);
List<RuleNode> ruleNodes = getRuleChainNodes(ruleChainId);
List<RuleNode> ruleNodes = getRuleChainNodes(tenantId, ruleChainId);
Map<RuleNodeId, Integer> ruleNodeIndexMap = new HashMap<>();
for (RuleNode node : ruleNodes) {
ruleNodeIndexMap.put(node.getId(), ruleNodes.indexOf(node));
@ -217,7 +217,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
for (RuleNode node : ruleNodes) {
int fromIndex = ruleNodeIndexMap.get(node.getId());
List<EntityRelation> nodeRelations = getRuleNodeRelations(node.getId());
List<EntityRelation> nodeRelations = getRuleNodeRelations(tenantId, node.getId());
for (EntityRelation nodeRelation : nodeRelations) {
String type = nodeRelation.getType();
if (nodeRelation.getTo().getEntityType() == EntityType.RULE_NODE) {
@ -234,54 +234,54 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
public RuleChain findRuleChainById(RuleChainId ruleChainId) {
public RuleChain findRuleChainById(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id for search request.");
return ruleChainDao.findById(ruleChainId.getId());
return ruleChainDao.findById(tenantId, ruleChainId.getId());
}
@Override
public RuleNode findRuleNodeById(RuleNodeId ruleNodeId) {
public RuleNode findRuleNodeById(TenantId tenantId, RuleNodeId ruleNodeId) {
Validator.validateId(ruleNodeId, "Incorrect rule node id for search request.");
return ruleNodeDao.findById(ruleNodeId.getId());
return ruleNodeDao.findById(tenantId, ruleNodeId.getId());
}
@Override
public ListenableFuture<RuleChain> findRuleChainByIdAsync(RuleChainId ruleChainId) {
public ListenableFuture<RuleChain> findRuleChainByIdAsync(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id for search request.");
return ruleChainDao.findByIdAsync(ruleChainId.getId());
return ruleChainDao.findByIdAsync(tenantId, ruleChainId.getId());
}
@Override
public ListenableFuture<RuleNode> findRuleNodeByIdAsync(RuleNodeId ruleNodeId) {
public ListenableFuture<RuleNode> findRuleNodeByIdAsync(TenantId tenantId, RuleNodeId ruleNodeId) {
Validator.validateId(ruleNodeId, "Incorrect rule node id for search request.");
return ruleNodeDao.findByIdAsync(ruleNodeId.getId());
return ruleNodeDao.findByIdAsync(tenantId, ruleNodeId.getId());
}
@Override
public RuleChain getRootTenantRuleChain(TenantId tenantId) {
Validator.validateId(tenantId, "Incorrect tenant id for search request.");
List<EntityRelation> relations = relationService.findByFrom(tenantId, RelationTypeGroup.RULE_CHAIN);
List<EntityRelation> relations = relationService.findByFrom(tenantId, tenantId, RelationTypeGroup.RULE_CHAIN);
if (relations != null && !relations.isEmpty()) {
EntityRelation relation = relations.get(0);
RuleChainId ruleChainId = new RuleChainId(relation.getTo().getId());
return findRuleChainById(ruleChainId);
return findRuleChainById(tenantId, ruleChainId);
} else {
return null;
}
}
@Override
public List<RuleNode> getRuleChainNodes(RuleChainId ruleChainId) {
public List<RuleNode> getRuleChainNodes(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id for search request.");
List<EntityRelation> relations = getRuleChainToNodeRelations(ruleChainId);
List<RuleNode> ruleNodes = relations.stream().map(relation -> ruleNodeDao.findById(relation.getTo().getId())).collect(Collectors.toList());
List<EntityRelation> relations = getRuleChainToNodeRelations(tenantId, ruleChainId);
List<RuleNode> ruleNodes = relations.stream().map(relation -> ruleNodeDao.findById(tenantId, relation.getTo().getId())).collect(Collectors.toList());
return ruleNodes;
}
@Override
public List<EntityRelation> getRuleNodeRelations(RuleNodeId ruleNodeId) {
public List<EntityRelation> getRuleNodeRelations(TenantId tenantId, RuleNodeId ruleNodeId) {
Validator.validateId(ruleNodeId, "Incorrect rule node id for search request.");
return relationService.findByFrom(ruleNodeId, RelationTypeGroup.RULE_NODE);
return relationService.findByFrom(tenantId, ruleNodeId, RelationTypeGroup.RULE_NODE);
}
@Override
@ -293,60 +293,60 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
public void deleteRuleChainById(RuleChainId ruleChainId) {
public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request.");
RuleChain ruleChain = ruleChainDao.findById(ruleChainId.getId());
RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId());
if (ruleChain != null && ruleChain.isRoot()) {
throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!");
}
checkRuleNodesAndDelete(ruleChainId);
checkRuleNodesAndDelete(tenantId, ruleChainId);
}
@Override
public void deleteRuleChainsByTenantId(TenantId tenantId) {
Validator.validateId(tenantId, "Incorrect tenant id for delete rule chains request.");
tenantRuleChainsRemover.removeEntities(tenantId);
tenantRuleChainsRemover.removeEntities(tenantId, tenantId);
}
private void checkRuleNodesAndDelete(RuleChainId ruleChainId) {
List<EntityRelation> nodeRelations = getRuleChainToNodeRelations(ruleChainId);
private void checkRuleNodesAndDelete(TenantId tenantId, RuleChainId ruleChainId) {
List<EntityRelation> nodeRelations = getRuleChainToNodeRelations(tenantId, ruleChainId);
for (EntityRelation relation : nodeRelations) {
deleteRuleNode(relation.getTo());
deleteRuleNode(tenantId, relation.getTo());
}
deleteEntityRelations(ruleChainId);
ruleChainDao.removeById(ruleChainId.getId());
deleteEntityRelations(tenantId, ruleChainId);
ruleChainDao.removeById(tenantId, ruleChainId.getId());
}
private List<EntityRelation> getRuleChainToNodeRelations(RuleChainId ruleChainId) {
return relationService.findByFrom(ruleChainId, RelationTypeGroup.RULE_CHAIN);
private List<EntityRelation> getRuleChainToNodeRelations(TenantId tenantId, RuleChainId ruleChainId) {
return relationService.findByFrom(tenantId, ruleChainId, RelationTypeGroup.RULE_CHAIN);
}
private void deleteRuleNode(EntityId entityId) {
deleteEntityRelations(entityId);
ruleNodeDao.removeById(entityId.getId());
private void deleteRuleNode(TenantId tenantId, EntityId entityId) {
deleteEntityRelations(tenantId, entityId);
ruleNodeDao.removeById(tenantId, entityId.getId());
}
private void createRelation(EntityRelation relation) throws ExecutionException, InterruptedException {
private void createRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException {
log.debug("Creating relation: {}", relation);
relationService.saveRelation(relation);
relationService.saveRelation(tenantId, relation);
}
private void deleteRelation(EntityRelation relation) throws ExecutionException, InterruptedException {
private void deleteRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException {
log.debug("Deleting relation: {}", relation);
relationService.deleteRelation(relation);
relationService.deleteRelation(tenantId, relation);
}
private DataValidator<RuleChain> ruleChainValidator =
new DataValidator<RuleChain>() {
@Override
protected void validateDataImpl(RuleChain ruleChain) {
protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) {
if (StringUtils.isEmpty(ruleChain.getName())) {
throw new DataValidationException("Rule chain name should be specified!.");
}
if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) {
throw new DataValidationException("Rule chain should be assigned to tenant!");
}
Tenant tenant = tenantDao.findById(ruleChain.getTenantId().getId());
Tenant tenant = tenantDao.findById(tenantId, ruleChain.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Rule chain is referencing to non-existent tenant!");
}
@ -363,13 +363,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
new PaginatedRemover<TenantId, RuleChain>() {
@Override
protected List<RuleChain> findEntities(TenantId id, TextPageLink pageLink) {
protected List<RuleChain> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
return ruleChainDao.findRuleChainsByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(RuleChain entity) {
checkRuleNodesAndDelete(entity.getId());
protected void removeEntity(TenantId tenantId, RuleChain entity) {
checkRuleNodesAndDelete(tenantId, entity.getId());
}
};
}

3
dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.rule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.DaoUtil;
@ -51,7 +52,7 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao<RuleCh
@Override
public List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find rule chains by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<RuleChainEntity> ruleChainEntities = findPageWithTextSearch(RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
List<RuleChainEntity> ruleChainEntities = findPageWithTextSearch(new TenantId(tenantId), RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Collections.singletonList(eq(RULE_CHAIN_TENANT_ID_PROPERTY, tenantId)),
pageLink);

20
dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java

@ -36,29 +36,29 @@ public interface RuleChainService {
RuleChain saveRuleChain(RuleChain ruleChain);
boolean setRootRuleChain(RuleChainId ruleChainId);
boolean setRootRuleChain(TenantId tenantId, RuleChainId ruleChainId);
RuleChainMetaData saveRuleChainMetaData(RuleChainMetaData ruleChainMetaData);
RuleChainMetaData saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData);
RuleChainMetaData loadRuleChainMetaData(RuleChainId ruleChainId);
RuleChainMetaData loadRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId);
RuleChain findRuleChainById(RuleChainId ruleChainId);
RuleChain findRuleChainById(TenantId tenantId, RuleChainId ruleChainId);
RuleNode findRuleNodeById(RuleNodeId ruleNodeId);
RuleNode findRuleNodeById(TenantId tenantId, RuleNodeId ruleNodeId);
ListenableFuture<RuleChain> findRuleChainByIdAsync(RuleChainId ruleChainId);
ListenableFuture<RuleChain> findRuleChainByIdAsync(TenantId tenantId, RuleChainId ruleChainId);
ListenableFuture<RuleNode> findRuleNodeByIdAsync(RuleNodeId ruleNodeId);
ListenableFuture<RuleNode> findRuleNodeByIdAsync(TenantId tenantId, RuleNodeId ruleNodeId);
RuleChain getRootTenantRuleChain(TenantId tenantId);
List<RuleNode> getRuleChainNodes(RuleChainId ruleChainId);
List<RuleNode> getRuleChainNodes(TenantId tenantId, RuleChainId ruleChainId);
List<EntityRelation> getRuleNodeRelations(RuleNodeId ruleNodeId);
List<EntityRelation> getRuleNodeRelations(TenantId tenantId, RuleNodeId ruleNodeId);
TextPageData<RuleChain> findTenantRuleChains(TenantId tenantId, TextPageLink pageLink);
void deleteRuleChainById(RuleChainId ruleChainId);
void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId);
void deleteRuleChainsByTenantId(TenantId tenantId);

39
dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java

@ -19,70 +19,73 @@ import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.validator.routines.EmailValidator;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Function;
@Slf4j
public abstract class DataValidator<D extends BaseData<?>> {
private static EmailValidator emailValidator = EmailValidator.getInstance();
public void validate(D data) {
public void validate(D data, Function<D, TenantId> tenantIdFunction) {
try {
if (data == null) {
throw new DataValidationException("Data object can't be null!");
}
validateDataImpl(data);
TenantId tenantId = tenantIdFunction.apply(data);
validateDataImpl(tenantId, data);
if (data.getId() == null) {
validateCreate(data);
validateCreate(tenantId, data);
} else {
validateUpdate(data);
validateUpdate(tenantId, data);
}
} catch (DataValidationException e) {
log.error("Data object is invalid: [{}]", e.getMessage());
throw e;
}
}
protected void validateDataImpl(D data) {
protected void validateDataImpl(TenantId tenantId, D data) {
}
protected void validateCreate(D data) {
protected void validateCreate(TenantId tenantId, D data) {
}
protected void validateUpdate(D data) {
protected void validateUpdate(TenantId tenantId, D data) {
}
protected boolean isSameData(D existentData, D actualData) {
return actualData.getId() != null && existentData.getId().equals(actualData.getId());
}
protected static void validateEmail(String email) {
if (!emailValidator.isValid(email)) {
throw new DataValidationException("Invalid email address format '" + email + "'!");
}
}
protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) {
Set<String> expectedFields = new HashSet<>();
Set<String> expectedFields = new HashSet<>();
Iterator<String> fieldsIterator = expectedNode.fieldNames();
while (fieldsIterator.hasNext()) {
expectedFields.add(fieldsIterator.next());
}
Set<String> actualFields = new HashSet<>();
Set<String> actualFields = new HashSet<>();
fieldsIterator = actualNode.fieldNames();
while (fieldsIterator.hasNext()) {
actualFields.add(fieldsIterator.next());
}
if (!expectedFields.containsAll(actualFields) || !actualFields.containsAll(expectedFields)) {
throw new DataValidationException("Provided json structure is different from stored one '" + actualNode + "'!");
}
for (String field : actualFields) {
if (!actualNode.get(field).isTextual()) {
throw new DataValidationException("Provided json structure can't contain non-text values '" + actualNode + "'!");

11
dao/src/main/java/org/thingsboard/server/dao/service/PaginatedRemover.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.service;
import org.thingsboard.server.common.data.id.IdBased;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import java.util.List;
@ -25,13 +26,13 @@ public abstract class PaginatedRemover<I, D extends IdBased<?>> {
private static final int DEFAULT_LIMIT = 100;
public void removeEntities(I id) {
public void removeEntities(TenantId tenantId, I id) {
TextPageLink pageLink = new TextPageLink(DEFAULT_LIMIT);
boolean hasNext = true;
while (hasNext) {
List<D> entities = findEntities(id, pageLink);
List<D> entities = findEntities(tenantId, id, pageLink);
for (D entity : entities) {
removeEntity(entity);
removeEntity(tenantId, entity);
}
hasNext = entities.size() == pageLink.getLimit();
if (hasNext) {
@ -42,8 +43,8 @@ public abstract class PaginatedRemover<I, D extends IdBased<?>> {
}
}
protected abstract List<D> findEntities(I id, TextPageLink pageLink);
protected abstract List<D> findEntities(TenantId tenantId,I id, TextPageLink pageLink);
protected abstract void removeEntity(D entity);
protected abstract void removeEntity(TenantId tenantId, D entity);
}

5
dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.settings;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.Dao;
public interface AdminSettingsDao extends Dao<AdminSettings> {
@ -26,7 +27,7 @@ public interface AdminSettingsDao extends Dao<AdminSettings> {
* @param adminSettings the admin settings object
* @return saved admin settings object
*/
AdminSettings save(AdminSettings adminSettings);
AdminSettings save(TenantId tenantId, AdminSettings adminSettings);
/**
* Find admin settings by key.
@ -34,6 +35,6 @@ public interface AdminSettingsDao extends Dao<AdminSettings> {
* @param key the key
* @return the admin settings object
*/
AdminSettings findByKey(String key);
AdminSettings findByKey(TenantId tenantId, String key);
}

7
dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java

@ -17,13 +17,14 @@ package org.thingsboard.server.dao.settings;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.id.AdminSettingsId;
import org.thingsboard.server.common.data.id.TenantId;
public interface AdminSettingsService {
AdminSettings findAdminSettingsById(AdminSettingsId adminSettingsId);
AdminSettings findAdminSettingsById(TenantId tenantId, AdminSettingsId adminSettingsId);
AdminSettings findAdminSettingsByKey(String key);
AdminSettings findAdminSettingsByKey(TenantId tenantId, String key);
AdminSettings saveAdminSettings(AdminSettings adminSettings);
AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings);
}

26
dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java

@ -20,7 +20,9 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.AdminSettingsId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.Validator;
@ -33,40 +35,40 @@ public class AdminSettingsServiceImpl implements AdminSettingsService {
private AdminSettingsDao adminSettingsDao;
@Override
public AdminSettings findAdminSettingsById(AdminSettingsId adminSettingsId) {
public AdminSettings findAdminSettingsById(TenantId tenantId, AdminSettingsId adminSettingsId) {
log.trace("Executing findAdminSettingsById [{}]", adminSettingsId);
Validator.validateId(adminSettingsId, "Incorrect adminSettingsId " + adminSettingsId);
return adminSettingsDao.findById(adminSettingsId.getId());
return adminSettingsDao.findById(tenantId, adminSettingsId.getId());
}
@Override
public AdminSettings findAdminSettingsByKey(String key) {
public AdminSettings findAdminSettingsByKey(TenantId tenantId, String key) {
log.trace("Executing findAdminSettingsByKey [{}]", key);
Validator.validateString(key, "Incorrect key " + key);
return adminSettingsDao.findByKey(key);
return adminSettingsDao.findByKey(tenantId, key);
}
@Override
public AdminSettings saveAdminSettings(AdminSettings adminSettings) {
public AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings) {
log.trace("Executing saveAdminSettings [{}]", adminSettings);
adminSettingsValidator.validate(adminSettings);
return adminSettingsDao.save(adminSettings);
adminSettingsValidator.validate(adminSettings, data -> tenantId);
return adminSettingsDao.save(tenantId, adminSettings);
}
private DataValidator<AdminSettings> adminSettingsValidator =
new DataValidator<AdminSettings>() {
@Override
protected void validateCreate(AdminSettings adminSettings) {
AdminSettings existentAdminSettingsWithKey = findAdminSettingsByKey(adminSettings.getKey());
protected void validateCreate(TenantId tenantId, AdminSettings adminSettings) {
AdminSettings existentAdminSettingsWithKey = findAdminSettingsByKey(tenantId, adminSettings.getKey());
if (existentAdminSettingsWithKey != null) {
throw new DataValidationException("Admin settings with such name already exists!");
}
}
@Override
protected void validateUpdate(AdminSettings adminSettings) {
AdminSettings existentAdminSettings = findAdminSettingsById(adminSettings.getId());
protected void validateUpdate(TenantId tenantId, AdminSettings adminSettings) {
AdminSettings existentAdminSettings = findAdminSettingsById(tenantId, adminSettings.getId());
if (existentAdminSettings != null) {
if (!existentAdminSettings.getKey().equals(adminSettings.getKey())) {
throw new DataValidationException("Changing key of admin settings entry is prohibited!");
@ -77,7 +79,7 @@ public class AdminSettingsServiceImpl implements AdminSettingsService {
@Override
protected void validateDataImpl(AdminSettings adminSettings) {
protected void validateDataImpl(TenantId tenantId, AdminSettings adminSettings) {
if (StringUtils.isEmpty(adminSettings.getKey())) {
throw new DataValidationException("Key should be specified!");
}

5
dao/src/main/java/org/thingsboard/server/dao/settings/CassandraAdminSettingsDao.java

@ -19,6 +19,7 @@ import com.datastax.driver.core.querybuilder.Select.Where;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.nosql.AdminSettingsEntity;
import org.thingsboard.server.dao.nosql.CassandraAbstractModelDao;
@ -46,11 +47,11 @@ public class CassandraAdminSettingsDao extends CassandraAbstractModelDao<AdminSe
}
@Override
public AdminSettings findByKey(String key) {
public AdminSettings findByKey(TenantId tenantId, String key) {
log.debug("Try to find admin settings by key [{}] ", key);
Where query = select().from(ADMIN_SETTINGS_BY_KEY_COLUMN_FAMILY_NAME).where(eq(ADMIN_SETTINGS_KEY_PROPERTY, key));
log.trace("Execute query {}", query);
AdminSettingsEntity adminSettingsEntity = findOneByStatement(query);
AdminSettingsEntity adminSettingsEntity = findOneByStatement(tenantId, query);
log.trace("Found admin settings [{}] by key [{}]", adminSettingsEntity, key);
return DaoUtil.getData(adminSettingsEntity);
}

11
dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java

@ -21,6 +21,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.BaseEntity;
@ -46,7 +47,7 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
@Override
@Transactional
public D save(D domain) {
public D save(TenantId tenantId, D domain) {
E entity;
try {
entity = getEntityClass().getConstructor(domain.getClass()).newInstance(domain);
@ -64,21 +65,21 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
}
@Override
public D findById(UUID key) {
public D findById(TenantId tenantId, UUID key) {
log.debug("Get entity by key {}", key);
E entity = getCrudRepository().findOne(fromTimeUUID(key));
return DaoUtil.getData(entity);
}
@Override
public ListenableFuture<D> findByIdAsync(UUID key) {
public ListenableFuture<D> findByIdAsync(TenantId tenantId, UUID key) {
log.debug("Get entity by key async {}", key);
return service.submit(() -> DaoUtil.getData(getCrudRepository().findOne(fromTimeUUID(key))));
}
@Override
@Transactional
public boolean removeById(UUID id) {
public boolean removeById(TenantId tenantId, UUID id) {
String key = fromTimeUUID(id);
getCrudRepository().delete(key);
log.debug("Remove request: {}", key);
@ -86,7 +87,7 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
}
@Override
public List<D> find() {
public List<D> find(TenantId tenantId) {
List<E> entities = Lists.newArrayList(getCrudRepository().findAll());
return DaoUtil.convertDataList(entities);
}

10
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java

@ -82,12 +82,12 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
}
@Override
public ListenableFuture<Alarm> findAlarmByIdAsync(UUID key) {
return findByIdAsync(key);
public ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key) {
return findByIdAsync(tenantId, key);
}
@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 [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink());
EntityId affectedEntity = query.getAffectedEntityId();
String searchStatusName;
@ -99,12 +99,12 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
searchStatusName = query.getStatus().name();
}
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 -> {
List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
alarmFutures.add(Futures.transform(
findAlarmByIdAsync(relation.getTo().getId()),
findAlarmByIdAsync(tenantId, relation.getTo().getId()),
AlarmInfo::new));
}
return Futures.successfulAsList(alarmFutures);

11
dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java

@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.UUIDConverter;
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.dao.DaoUtil;
import org.thingsboard.server.dao.attributes.AttributesDao;
@ -47,7 +48,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
private AttributeKvRepository attributeKvRepository;
@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) {
AttributeKvCompositeKey compositeKey =
getAttributeKvCompositeKey(entityId, attributeType, attributeKey);
return Futures.immediateFuture(
@ -55,7 +56,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
}
@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<AttributeKvCompositeKey> compositeKeys =
attributeKeys
.stream()
@ -67,7 +68,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
}
@Override
public ListenableFuture<List<AttributeKvEntry>> findAll(EntityId entityId, String attributeType) {
public ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String attributeType) {
return Futures.immediateFuture(
DaoUtil.convertDataList(Lists.newArrayList(
attributeKvRepository.findAllByEntityTypeAndEntityIdAndAttributeType(
@ -77,7 +78,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
}
@Override
public ListenableFuture<Void> save(EntityId entityId, String attributeType, AttributeKvEntry attribute) {
public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute) {
AttributeKvEntity entity = new AttributeKvEntity();
entity.setId(new AttributeKvCompositeKey(entityId.getEntityType(), fromTimeUUID(entityId.getId()), attributeType, attribute.getKey()));
entity.setLastUpdateTs(attribute.getLastUpdateTs());
@ -92,7 +93,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
}
@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<AttributeKvEntity> entitiesToDelete = keys
.stream()
.map(key -> {

2
dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java

@ -75,7 +75,7 @@ public class JpaAuditLogDao extends JpaAbstractDao<AuditLogEntity, AuditLog> imp
@Override
public ListenableFuture<Void> saveByTenantId(AuditLog auditLog) {
return insertService.submit(() -> {
save(auditLog);
save(auditLog.getTenantId(), auditLog);
return null;
});
}

21
dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java

@ -23,6 +23,7 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.UUIDConverter;
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.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope;
@ -61,28 +62,28 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao<Comp
}
@Override
public Optional<ComponentDescriptor> saveIfNotExist(ComponentDescriptor component) {
public Optional<ComponentDescriptor> saveIfNotExist(TenantId tenantId, ComponentDescriptor component) {
if (component.getId() == null) {
component.setId(new ComponentDescriptorId(UUIDs.timeBased()));
}
if (componentDescriptorRepository.findOne(UUIDConverter.fromTimeUUID(component.getId().getId())) == null) {
return Optional.of(save(component));
return Optional.of(save(tenantId, component));
}
return Optional.empty();
}
@Override
public ComponentDescriptor findById(ComponentDescriptorId componentId) {
return findById(componentId.getId());
public ComponentDescriptor findById(TenantId tenantId, ComponentDescriptorId componentId) {
return findById(tenantId, componentId.getId());
}
@Override
public ComponentDescriptor findByClazz(String clazz) {
public ComponentDescriptor findByClazz(TenantId tenantId, String clazz) {
return DaoUtil.getData(componentDescriptorRepository.findByClazz(clazz));
}
@Override
public List<ComponentDescriptor> findByTypeAndPageLink(ComponentType type, TextPageLink pageLink) {
public List<ComponentDescriptor> findByTypeAndPageLink(TenantId tenantId, ComponentType type, TextPageLink pageLink) {
return DaoUtil.convertDataList(componentDescriptorRepository
.findByType(
type,
@ -92,7 +93,7 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao<Comp
}
@Override
public List<ComponentDescriptor> findByScopeAndTypeAndPageLink(ComponentScope scope, ComponentType type, TextPageLink pageLink) {
public List<ComponentDescriptor> findByScopeAndTypeAndPageLink(TenantId tenantId, ComponentScope scope, ComponentType type, TextPageLink pageLink) {
return DaoUtil.convertDataList(componentDescriptorRepository
.findByScopeAndType(
type,
@ -104,13 +105,13 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao<Comp
@Override
@Transactional
public void deleteById(ComponentDescriptorId componentId) {
removeById(componentId.getId());
public void deleteById(TenantId tenantId, ComponentDescriptorId componentId) {
removeById(tenantId, componentId.getId());
}
@Override
@Transactional
public void deleteByClazz(String clazz) {
public void deleteByClazz(TenantId tenantId, String clazz) {
componentDescriptorRepository.deleteByClazz(clazz);
}
}

5
dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.UUIDConverter;
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.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
@ -82,12 +83,12 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoE
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);
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 -> {
List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
dashboardFutures.add(findByIdAsync(relation.getTo().getId()));
dashboardFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
return Futures.successfulAsList(dashboardFutures);
});

5
dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceCredentialsDao.java

@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.device.DeviceCredentialsDao;
@ -49,12 +50,12 @@ public class JpaDeviceCredentialsDao extends JpaAbstractDao<DeviceCredentialsEnt
}
@Override
public DeviceCredentials findByDeviceId(UUID deviceId) {
public DeviceCredentials findByDeviceId(TenantId tenantId, UUID deviceId) {
return DaoUtil.getData(deviceCredentialsRepository.findByDeviceId(UUIDConverter.fromTimeUUID(deviceId)));
}
@Override
public DeviceCredentials findByCredentialsId(String credentialsId) {
public DeviceCredentials findByCredentialsId(TenantId tenantId, String credentialsId) {
return DaoUtil.getData(deviceCredentialsRepository.findByCredentialsId(credentialsId));
}
}

3
dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java

@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EventId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.event.EventDao;
@ -71,7 +72,7 @@ public class JpaBaseEventDao extends JpaAbstractSearchTimeDao<EventEntity, Event
}
@Override
public Event save(Event event) {
public Event save(TenantId tenantId, Event event) {
log.debug("Save event [{}] ", event);
if (event.getId() == null) {
event.setId(new EventId(UUIDs.timeBased()));

31
dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java

@ -26,6 +26,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.UUIDConverter;
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.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
@ -56,7 +57,7 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
private RelationRepository relationRepository;
@Override
public ListenableFuture<List<EntityRelation>> findAllByFrom(EntityId from, RelationTypeGroup typeGroup) {
public ListenableFuture<List<EntityRelation>> findAllByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) {
return service.submit(() -> DaoUtil.convertDataList(
relationRepository.findAllByFromIdAndFromTypeAndRelationTypeGroup(
UUIDConverter.fromTimeUUID(from.getId()),
@ -65,7 +66,7 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@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) {
return service.submit(() -> DaoUtil.convertDataList(
relationRepository.findAllByFromIdAndFromTypeAndRelationTypeAndRelationTypeGroup(
UUIDConverter.fromTimeUUID(from.getId()),
@ -75,7 +76,7 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@Override
public ListenableFuture<List<EntityRelation>> findAllByTo(EntityId to, RelationTypeGroup typeGroup) {
public ListenableFuture<List<EntityRelation>> findAllByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup) {
return service.submit(() -> DaoUtil.convertDataList(
relationRepository.findAllByToIdAndToTypeAndRelationTypeGroup(
UUIDConverter.fromTimeUUID(to.getId()),
@ -84,7 +85,7 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@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) {
return service.submit(() -> DaoUtil.convertDataList(
relationRepository.findAllByToIdAndToTypeAndRelationTypeAndRelationTypeGroup(
UUIDConverter.fromTimeUUID(to.getId()),
@ -94,13 +95,13 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@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) {
RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup);
return service.submit(() -> relationRepository.findOne(key) != null);
}
@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) {
RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup);
return service.submit(() -> DaoUtil.getData(relationRepository.findOne(key)));
}
@ -115,36 +116,36 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@Override
public boolean saveRelation(EntityRelation relation) {
public boolean saveRelation(TenantId tenantId, EntityRelation relation) {
return relationRepository.save(new RelationEntity(relation)) != null;
}
@Override
public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) {
public ListenableFuture<Boolean> saveRelationAsync(TenantId tenantId, EntityRelation relation) {
return service.submit(() -> relationRepository.save(new RelationEntity(relation)) != null);
}
@Override
public boolean deleteRelation(EntityRelation relation) {
public boolean deleteRelation(TenantId tenantId, EntityRelation relation) {
RelationCompositeKey key = new RelationCompositeKey(relation);
return deleteRelationIfExists(key);
}
@Override
public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) {
public ListenableFuture<Boolean> deleteRelationAsync(TenantId tenantId, EntityRelation relation) {
RelationCompositeKey key = new RelationCompositeKey(relation);
return service.submit(
() -> deleteRelationIfExists(key));
}
@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) {
RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup);
return deleteRelationIfExists(key);
}
@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) {
RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup);
return service.submit(
() -> deleteRelationIfExists(key));
@ -159,7 +160,7 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@Override
public boolean deleteOutboundRelations(EntityId entity) {
public boolean deleteOutboundRelations(TenantId tenantId, EntityId entity) {
boolean relationExistsBeforeDelete = relationRepository
.findAllByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name())
.size() > 0;
@ -170,7 +171,7 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@Override
public ListenableFuture<Boolean> deleteOutboundRelationsAsync(EntityId entity) {
public ListenableFuture<Boolean> deleteOutboundRelationsAsync(TenantId tenantId, EntityId entity) {
return service.submit(
() -> {
boolean relationExistsBeforeDelete = relationRepository
@ -184,7 +185,7 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@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) {
Specification<RelationEntity> timeSearchSpec = JpaAbstractSearchTimeDao.getTimeSearchPageSpec(pageLink, "toId");
Specification<RelationEntity> fieldsSpec = getEntityFieldsSpec(from, relationType, typeGroup, childType);
Sort.Direction sortDirection = pageLink.isAscOrder() ? Sort.Direction.ASC : Sort.Direction.DESC;

3
dao/src/main/java/org/thingsboard/server/dao/sql/settings/JpaAdminSettingsDao.java

@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.AdminSettingsEntity;
import org.thingsboard.server.dao.settings.AdminSettingsDao;
@ -45,7 +46,7 @@ public class JpaAdminSettingsDao extends JpaAbstractDao<AdminSettingsEntity, Adm
}
@Override
public AdminSettings findByKey(String key) {
public AdminSettings findByKey(TenantId tenantId, String key) {
return DaoUtil.getData(adminSettingsRepository.findByKey(key));
}
}

3
dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java

@ -21,6 +21,7 @@ import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.TenantEntity;
@ -54,7 +55,7 @@ public class JpaTenantDao extends JpaAbstractSearchTextDao<TenantEntity, Tenant>
}
@Override
public List<Tenant> findTenantsByRegion(String region, TextPageLink pageLink) {
public List<Tenant> findTenantsByRegion(TenantId tenantId, String region, TextPageLink pageLink) {
return DaoUtil.convertDataList(tenantRepository
.findByRegionNextPage(
region,

33
dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java

@ -31,6 +31,7 @@ import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -110,10 +111,10 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}
@Override
public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries) {
public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
List<ListenableFuture<List<TsKvEntry>>> futures = queries
.stream()
.map(query -> findAllAsync(entityId, query))
.map(query -> findAllAsync(tenantId, entityId, query))
.collect(Collectors.toList());
return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() {
@Nullable
@ -129,7 +130,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}, service);
}
private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) {
private ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
if (query.getAggregation() == Aggregation.NONE) {
return findAllAsyncWithLimit(entityId, query);
} else {
@ -251,7 +252,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}
@Override
public ListenableFuture<TsKvEntry> findLatest(EntityId entityId, String key) {
public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
TsKvLatestCompositeKey compositeKey =
new TsKvLatestCompositeKey(
entityId.getEntityType(),
@ -268,7 +269,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}
@Override
public ListenableFuture<List<TsKvEntry>> findAllLatest(EntityId entityId) {
public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
return Futures.immediateFuture(
DaoUtil.convertDataList(Lists.newArrayList(
tsKvLatestRepository.findAllByEntityTypeAndEntityId(
@ -277,7 +278,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}
@Override
public ListenableFuture<Void> save(EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
TsKvEntity entity = new TsKvEntity();
entity.setEntityType(entityId.getEntityType());
entity.setEntityId(fromTimeUUID(entityId.getId()));
@ -295,12 +296,12 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}
@Override
public ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl) {
public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
return insertService.submit(() -> null);
}
@Override
public ListenableFuture<Void> saveLatest(EntityId entityId, TsKvEntry tsKvEntry) {
public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
TsKvLatestEntity latestEntity = new TsKvLatestEntity();
latestEntity.setEntityType(entityId.getEntityType());
latestEntity.setEntityId(fromTimeUUID(entityId.getId()));
@ -317,7 +318,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}
@Override
public ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query) {
public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
return service.submit(() -> {
tsKvRepository.delete(
fromTimeUUID(entityId.getId()),
@ -330,8 +331,8 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}
@Override
public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) {
ListenableFuture<TsKvEntry> latestFuture = findLatest(entityId, query.getKey());
public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
ListenableFuture<TsKvEntry> latestFuture = findLatest(tenantId, entityId, query.getKey());
ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> {
long ts = tsKvEntry.getTs();
@ -359,7 +360,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
if (query.getRewriteLatestIfDeleted()) {
ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
if (isRemove) {
return getNewLatestEntryFuture(entityId, query);
return getNewLatestEntryFuture(tenantId, entityId, query);
}
return Futures.immediateFuture(null);
}, service);
@ -382,16 +383,16 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
return resultFuture;
}
private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {
private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
long startTs = 0;
long endTs = query.getStartTs() - 1;
ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
Aggregation.NONE, DESC_ORDER);
ListenableFuture<List<TsKvEntry>> future = findAllAsync(entityId, findNewLatestQuery);
ListenableFuture<List<TsKvEntry>> future = findAllAsync(tenantId, entityId, findNewLatestQuery);
return Futures.transformAsync(future, entryList -> {
if (entryList.size() == 1) {
return saveLatest(entityId, entryList.get(0));
return saveLatest(tenantId, entityId, entryList.get(0));
} else {
log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
}
@ -400,7 +401,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
}
@Override
public ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query) {
public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
return service.submit(() -> null);
}

7
dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java

@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.UserCredentialsEntity;
@ -49,17 +50,17 @@ public class JpaUserCredentialsDao extends JpaAbstractDao<UserCredentialsEntity,
}
@Override
public UserCredentials findByUserId(UUID userId) {
public UserCredentials findByUserId(TenantId tenantId, UUID userId) {
return DaoUtil.getData(userCredentialsRepository.findByUserId(UUIDConverter.fromTimeUUID(userId)));
}
@Override
public UserCredentials findByActivateToken(String activateToken) {
public UserCredentials findByActivateToken(TenantId tenantId, String activateToken) {
return DaoUtil.getData(userCredentialsRepository.findByActivateToken(activateToken));
}
@Override
public UserCredentials findByResetToken(String resetToken) {
public UserCredentials findByResetToken(TenantId tenantId, String resetToken) {
return DaoUtil.getData(userCredentialsRepository.findByResetToken(resetToken));
}
}

3
dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java

@ -20,6 +20,7 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.DaoUtil;
@ -56,7 +57,7 @@ public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> imple
}
@Override
public User findByEmail(String email) {
public User findByEmail(TenantId tenantId, String email) {
return DaoUtil.getData(userRepository.findByEmail(email));
}

3
dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java

@ -20,6 +20,7 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.DaoUtil;
@ -60,7 +61,7 @@ public class JpaWidgetsBundleDao extends JpaAbstractSearchTextDao<WidgetsBundleE
}
@Override
public List<WidgetsBundle> findSystemWidgetsBundles(TextPageLink pageLink) {
public List<WidgetsBundle> findSystemWidgetsBundles(TenantId tenantId, TextPageLink pageLink) {
return DaoUtil.convertDataList(
widgetsBundleRepository
.findSystemWidgetsBundles(

5
dao/src/main/java/org/thingsboard/server/dao/tenant/CassandraTenantDao.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.tenant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.nosql.TenantEntity;
@ -48,9 +49,9 @@ public class CassandraTenantDao extends CassandraAbstractSearchTextDao<TenantEnt
}
@Override
public List<Tenant> findTenantsByRegion(String region, TextPageLink pageLink) {
public List<Tenant> findTenantsByRegion(TenantId tenantId, String region, TextPageLink pageLink) {
log.debug("Try to find tenants by region [{}] and pageLink [{}]", region, pageLink);
List<TenantEntity> tenantEntities = findPageWithTextSearch(TENANT_BY_REGION_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
List<TenantEntity> tenantEntities = findPageWithTextSearch(tenantId, TENANT_BY_REGION_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(TENANT_REGION_PROPERTY, region)),
pageLink);
log.trace("Found tenants [{}] by region [{}] and pageLink [{}]", tenantEntities, region, pageLink);

5
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.tenant;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao;
@ -29,7 +30,7 @@ public interface TenantDao extends Dao<Tenant> {
* @param tenant the tenant object
* @return saved tenant object
*/
Tenant save(Tenant tenant);
Tenant save(TenantId tenantId, Tenant tenant);
/**
* Find tenants by region and page link.
@ -38,6 +39,6 @@ public interface TenantDao extends Dao<Tenant> {
* @param pageLink the page link
* @return the list of tenant objects
*/
List<Tenant> findTenantsByRegion(String region, TextPageLink pageLink);
List<Tenant> findTenantsByRegion(TenantId tenantId, String region, TextPageLink pageLink);
}

2
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java

@ -25,7 +25,7 @@ public interface TenantService {
Tenant findTenantById(TenantId tenantId);
ListenableFuture<Tenant> findTenantByIdAsync(TenantId customerId);
ListenableFuture<Tenant> findTenantByIdAsync(TenantId callerId, TenantId tenantId);
Tenant saveTenant(Tenant tenant);

27
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java

@ -21,6 +21,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Tenant;
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.TextPageLink;
@ -80,22 +81,22 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
public Tenant findTenantById(TenantId tenantId) {
log.trace("Executing findTenantById [{}]", tenantId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
return tenantDao.findById(tenantId.getId());
return tenantDao.findById(tenantId, tenantId.getId());
}
@Override
public ListenableFuture<Tenant> findTenantByIdAsync(TenantId tenantId) {
public ListenableFuture<Tenant> findTenantByIdAsync(TenantId callerId, TenantId tenantId) {
log.trace("Executing TenantIdAsync [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
return tenantDao.findByIdAsync(tenantId.getId());
return tenantDao.findByIdAsync(callerId, tenantId.getId());
}
@Override
public Tenant saveTenant(Tenant tenant) {
log.trace("Executing saveTenant [{}]", tenant);
tenant.setRegion(DEFAULT_TENANT_REGION);
tenantValidator.validate(tenant);
return tenantDao.save(tenant);
tenantValidator.validate(tenant, Tenant::getId);
return tenantDao.save(tenant.getId(), tenant);
}
@Override
@ -110,28 +111,28 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
deviceService.deleteDevicesByTenantId(tenantId);
userService.deleteTenantAdmins(tenantId);
ruleChainService.deleteRuleChainsByTenantId(tenantId);
tenantDao.removeById(tenantId.getId());
deleteEntityRelations(tenantId);
tenantDao.removeById(tenantId, tenantId.getId());
deleteEntityRelations(tenantId, tenantId);
}
@Override
public TextPageData<Tenant> findTenants(TextPageLink pageLink) {
log.trace("Executing findTenants pageLink [{}]", pageLink);
Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<Tenant> tenants = tenantDao.findTenantsByRegion(DEFAULT_TENANT_REGION, pageLink);
List<Tenant> tenants = tenantDao.findTenantsByRegion(new TenantId(EntityId.NULL_UUID), DEFAULT_TENANT_REGION, pageLink);
return new TextPageData<>(tenants, pageLink);
}
@Override
public void deleteTenants() {
log.trace("Executing deleteTenants");
tenantsRemover.removeEntities(DEFAULT_TENANT_REGION);
tenantsRemover.removeEntities(new TenantId(EntityId.NULL_UUID),DEFAULT_TENANT_REGION);
}
private DataValidator<Tenant> tenantValidator =
new DataValidator<Tenant>() {
@Override
protected void validateDataImpl(Tenant tenant) {
protected void validateDataImpl(TenantId tenantId, Tenant tenant) {
if (StringUtils.isEmpty(tenant.getTitle())) {
throw new DataValidationException("Tenant title should be specified!");
}
@ -145,12 +146,12 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
new PaginatedRemover<String, Tenant>() {
@Override
protected List<Tenant> findEntities(String region, TextPageLink pageLink) {
return tenantDao.findTenantsByRegion(region, pageLink);
protected List<Tenant> findEntities(TenantId tenantId, String region, TextPageLink pageLink) {
return tenantDao.findTenantsByRegion(tenantId, region, pageLink);
}
@Override
protected void removeEntity(Tenant entity) {
protected void removeEntity(TenantId tenantId, Tenant entity) {
deleteTenant(new TenantId(entity.getUuidId()));
}
};

53
dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
@ -62,27 +63,27 @@ public class BaseTimeseriesService implements TimeseriesService {
private EntityViewService entityViewService;
@Override
public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries) {
public ListenableFuture<List<TsKvEntry>> findAll(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
validate(entityId);
queries.forEach(this::validate);
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId);
EntityView entityView = entityViewService.findEntityViewById(tenantId, (EntityViewId) entityId);
List<ReadTsKvQuery> filteredQueries =
queries.stream()
.filter(query -> entityView.getKeys().getTimeseries().isEmpty() || entityView.getKeys().getTimeseries().contains(query.getKey()))
.collect(Collectors.toList());
return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, filteredQueries));
return timeseriesDao.findAllAsync(tenantId, entityView.getEntityId(), updateQueriesForEntityView(entityView, filteredQueries));
}
return timeseriesDao.findAllAsync(entityId, queries);
return timeseriesDao.findAllAsync(tenantId, entityId, queries);
}
@Override
public ListenableFuture<List<TsKvEntry>> findLatest(EntityId entityId, Collection<String> keys) {
public ListenableFuture<List<TsKvEntry>> findLatest(TenantId tenantId, EntityId entityId, Collection<String> keys) {
validate(entityId);
List<ListenableFuture<TsKvEntry>> futures = Lists.newArrayListWithExpectedSize(keys.size());
keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key));
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId);
EntityView entityView = entityViewService.findEntityViewById(tenantId, (EntityViewId) entityId);
List<String> filteredKeys = new ArrayList<>(keys);
if (entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null &&
!entityView.getKeys().getTimeseries().isEmpty()) {
@ -97,61 +98,61 @@ public class BaseTimeseriesService implements TimeseriesService {
.collect(Collectors.toList());
if (queries.size() > 0) {
return timeseriesDao.findAllAsync(entityView.getEntityId(), queries);
return timeseriesDao.findAllAsync(tenantId, entityView.getEntityId(), queries);
} else {
return Futures.immediateFuture(new ArrayList<>());
}
}
keys.forEach(key -> futures.add(timeseriesDao.findLatest(entityId, key)));
keys.forEach(key -> futures.add(timeseriesDao.findLatest(tenantId, entityId, key)));
return Futures.allAsList(futures);
}
@Override
public ListenableFuture<List<TsKvEntry>> findAllLatest(EntityId entityId) {
public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
validate(entityId);
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId);
EntityView entityView = entityViewService.findEntityViewById(tenantId, (EntityViewId) entityId);
if (entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null &&
!entityView.getKeys().getTimeseries().isEmpty()) {
return findLatest(entityId, entityView.getKeys().getTimeseries());
return findLatest(tenantId, entityId, entityView.getKeys().getTimeseries());
} else {
return Futures.immediateFuture(new ArrayList<>());
}
} else {
return timeseriesDao.findAllLatest(entityId);
return timeseriesDao.findAllLatest(tenantId, entityId);
}
}
@Override
public ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry) {
public ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
validate(entityId);
if (tsKvEntry == null) {
throw new IncorrectParameterException("Key value entry can't be null");
}
List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(INSERTS_PER_ENTRY);
saveAndRegisterFutures(futures, entityId, tsKvEntry, 0L);
saveAndRegisterFutures(tenantId, futures, entityId, tsKvEntry, 0L);
return Futures.allAsList(futures);
}
@Override
public ListenableFuture<List<Void>> save(EntityId entityId, List<TsKvEntry> tsKvEntries, long ttl) {
public ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntries, long ttl) {
List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(tsKvEntries.size() * INSERTS_PER_ENTRY);
for (TsKvEntry tsKvEntry : tsKvEntries) {
if (tsKvEntry == null) {
throw new IncorrectParameterException("Key value entry can't be null");
}
saveAndRegisterFutures(futures, entityId, tsKvEntry, ttl);
saveAndRegisterFutures(tenantId, futures, entityId, tsKvEntry, ttl);
}
return Futures.allAsList(futures);
}
private void saveAndRegisterFutures(List<ListenableFuture<Void>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
private void saveAndRegisterFutures(TenantId tenantId, List<ListenableFuture<Void>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
throw new IncorrectParameterException("Telemetry data can't be stored for entity view. Only read only");
}
futures.add(timeseriesDao.savePartition(entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl));
futures.add(timeseriesDao.saveLatest(entityId, tsKvEntry));
futures.add(timeseriesDao.save(entityId, tsKvEntry, ttl));
futures.add(timeseriesDao.savePartition(tenantId, entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl));
futures.add(timeseriesDao.saveLatest(tenantId, entityId, tsKvEntry));
futures.add(timeseriesDao.save(tenantId, entityId, tsKvEntry, ttl));
}
private List<ReadTsKvQuery> updateQueriesForEntityView(EntityView entityView, List<ReadTsKvQuery> queries) {
@ -174,20 +175,20 @@ public class BaseTimeseriesService implements TimeseriesService {
}
@Override
public ListenableFuture<List<Void>> remove(EntityId entityId, List<DeleteTsKvQuery> deleteTsKvQueries) {
public ListenableFuture<List<Void>> remove(TenantId tenantId, EntityId entityId, List<DeleteTsKvQuery> deleteTsKvQueries) {
validate(entityId);
deleteTsKvQueries.forEach(BaseTimeseriesService::validate);
List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(deleteTsKvQueries.size() * DELETES_PER_ENTRY);
for (DeleteTsKvQuery tsKvQuery : deleteTsKvQueries) {
deleteAndRegisterFutures(futures, entityId, tsKvQuery);
deleteAndRegisterFutures(tenantId, futures, entityId, tsKvQuery);
}
return Futures.allAsList(futures);
}
private void deleteAndRegisterFutures(List<ListenableFuture<Void>> futures, EntityId entityId, DeleteTsKvQuery query) {
futures.add(timeseriesDao.remove(entityId, query));
futures.add(timeseriesDao.removeLatest(entityId, query));
futures.add(timeseriesDao.removePartition(entityId, query));
private void deleteAndRegisterFutures(TenantId tenantId, List<ListenableFuture<Void>> futures, EntityId entityId, DeleteTsKvQuery query) {
futures.add(timeseriesDao.remove(tenantId, entityId, query));
futures.add(timeseriesDao.removeLatest(tenantId, entityId, query));
futures.add(timeseriesDao.removePartition(tenantId, entityId, query));
}
private static void validate(EntityId entityId) {

103
dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java

@ -34,6 +34,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -131,8 +132,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
@Override
public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries) {
List<ListenableFuture<List<TsKvEntry>>> futures = queries.stream().map(query -> findAllAsync(entityId, query)).collect(Collectors.toList());
public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
List<ListenableFuture<List<TsKvEntry>>> futures = queries.stream().map(query -> findAllAsync(tenantId, entityId, query)).collect(Collectors.toList());
return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() {
@Nullable
@Override
@ -148,9 +149,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) {
private ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
if (query.getAggregation() == Aggregation.NONE) {
return findAllAsyncWithLimit(entityId, query);
return findAllAsyncWithLimit(tenantId, entityId, query);
} else {
long step = Math.max(query.getInterval(), MIN_AGGREGATION_STEP_MS);
long stepTs = query.getStartTs();
@ -159,7 +160,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
long startTs = stepTs;
long endTs = stepTs + step;
ReadTsKvQuery subQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, step, 1, query.getAggregation(), query.getOrderBy());
futures.add(findAndAggregateAsync(entityId, subQuery, toPartitionTs(startTs), toPartitionTs(endTs)));
futures.add(findAndAggregateAsync(tenantId, entityId, subQuery, toPartitionTs(startTs), toPartitionTs(endTs)));
stepTs = endTs;
}
ListenableFuture<List<Optional<TsKvEntry>>> future = Futures.allAsList(futures);
@ -177,25 +178,25 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
return tsFormat.getTruncateUnit().equals(TsPartitionDate.EPOCH_START);
}
private ListenableFuture<List<Long>> getPartitionsFuture(ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {
private ListenableFuture<List<Long>> getPartitionsFuture(TenantId tenantId, ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {
if (isFixedPartitioning()) { //no need to fetch partitions from DB
return Futures.immediateFuture(FIXED_PARTITION);
}
ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition);
ResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
return Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
}
private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
long minPartition = toPartitionTs(query.getStartTs());
long maxPartition = toPartitionTs(query.getEndTs());
final ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition);
final ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(tenantId, query, entityId, minPartition, maxPartition);
final SimpleListenableFuture<List<TsKvEntry>> resultFuture = new SimpleListenableFuture<>();
Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
@Override
public void onSuccess(@Nullable List<Long> partitions) {
TsKvQueryCursor cursor = new TsKvQueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitions);
findAllAsyncSequentiallyWithLimit(cursor, resultFuture);
findAllAsyncSequentiallyWithLimit(tenantId, cursor, resultFuture);
}
@Override
@ -212,7 +213,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
return tsFormat.truncatedTo(time).toInstant(ZoneOffset.UTC).toEpochMilli();
}
private void findAllAsyncSequentiallyWithLimit(final TsKvQueryCursor cursor, final SimpleListenableFuture<List<TsKvEntry>> resultFuture) {
private void findAllAsyncSequentiallyWithLimit(TenantId tenantId, final TsKvQueryCursor cursor, final SimpleListenableFuture<List<TsKvEntry>> resultFuture) {
if (cursor.isFull() || !cursor.hasNextPartition()) {
resultFuture.set(cursor.getData());
} else {
@ -226,11 +227,11 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
stmt.setLong(5, cursor.getEndTs());
stmt.setInt(6, cursor.getCurrentLimit());
Futures.addCallback(executeAsyncRead(stmt), new FutureCallback<ResultSet>() {
Futures.addCallback(executeAsyncRead(tenantId, stmt), new FutureCallback<ResultSet>() {
@Override
public void onSuccess(@Nullable ResultSet result) {
cursor.addData(convertResultToTsKvEntryList(result == null ? Collections.emptyList() : result.all()));
findAllAsyncSequentiallyWithLimit(cursor, resultFuture);
findAllAsyncSequentiallyWithLimit(tenantId, cursor, resultFuture);
}
@Override
@ -241,15 +242,15 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
}
private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, ReadTsKvQuery query, long minPartition, long maxPartition) {
private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query, long minPartition, long maxPartition) {
final Aggregation aggregation = query.getAggregation();
final String key = query.getKey();
final long startTs = query.getStartTs();
final long endTs = query.getEndTs();
final long ts = startTs + (endTs - startTs) / 2;
ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition);
ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(tenantId, query, entityId, minPartition, maxPartition);
ListenableFuture<List<ResultSet>> aggregationChunks = Futures.transformAsync(partitionsListFuture,
getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor);
getFetchChunksAsyncFunction(tenantId, entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor);
return Futures.transform(aggregationChunks, new AggregatePartitionsFunction(aggregation, key, ts), readResultsProcessingExecutor);
}
@ -259,7 +260,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
.map(row -> row.getLong(ModelConstants.PARTITION_COLUMN)).collect(Collectors.toList());
}
private AsyncFunction<List<Long>, List<ResultSet>> getFetchChunksAsyncFunction(EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) {
private AsyncFunction<List<Long>, List<ResultSet>> getFetchChunksAsyncFunction(TenantId tenantId, EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) {
return partitions -> {
try {
PreparedStatement proto = getFetchStmt(aggregation, DESC_ORDER);
@ -274,7 +275,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
stmt.setLong(4, startTs);
stmt.setLong(5, endTs);
log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());
futures.add(executeAsyncRead(stmt));
futures.add(executeAsyncRead(tenantId, stmt));
}
return Futures.allAsList(futures);
} catch (Throwable e) {
@ -285,26 +286,26 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
@Override
public ListenableFuture<TsKvEntry> findLatest(EntityId entityId, String key) {
public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
BoundStatement stmt = getFindLatestStmt().bind();
stmt.setString(0, entityId.getEntityType().name());
stmt.setUUID(1, entityId.getId());
stmt.setString(2, key);
log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());
return getFuture(executeAsyncRead(stmt), rs -> convertResultToTsKvEntry(key, rs.one()));
return getFuture(executeAsyncRead(tenantId, stmt), rs -> convertResultToTsKvEntry(key, rs.one()));
}
@Override
public ListenableFuture<List<TsKvEntry>> findAllLatest(EntityId entityId) {
public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
BoundStatement stmt = getFindAllLatestStmt().bind();
stmt.setString(0, entityId.getEntityType().name());
stmt.setUUID(1, entityId.getId());
log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());
return getFuture(executeAsyncRead(stmt), rs -> convertResultToTsKvEntryList(rs.all()));
return getFuture(executeAsyncRead(tenantId, stmt), rs -> convertResultToTsKvEntryList(rs.all()));
}
@Override
public ListenableFuture<Void> save(EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
ttl = computeTtl(ttl);
long partition = toPartitionTs(tsKvEntry.getTs());
DataType type = tsKvEntry.getDataType();
@ -318,11 +319,11 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
if (ttl > 0) {
stmt.setInt(6, (int) ttl);
}
return getFuture(executeAsyncWrite(stmt), rs -> null);
return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
}
@Override
public ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl) {
public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
if (isFixedPartitioning()) {
return Futures.immediateFuture(null);
}
@ -337,7 +338,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
if (ttl > 0) {
stmt.setInt(4, (int) ttl);
}
return getFuture(executeAsyncWrite(stmt), rs -> null);
return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
}
private long computeTtl(long ttl) {
@ -352,7 +353,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
@Override
public ListenableFuture<Void> saveLatest(EntityId entityId, TsKvEntry tsKvEntry) {
public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
BoundStatement stmt = getLatestStmt().bind()
.setString(0, entityId.getEntityType().name())
.setUUID(1, entityId.getId())
@ -362,15 +363,15 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
.set(5, tsKvEntry.getStrValue().orElse(null), String.class)
.set(6, tsKvEntry.getLongValue().orElse(null), Long.class)
.set(7, tsKvEntry.getDoubleValue().orElse(null), Double.class);
return getFuture(executeAsyncWrite(stmt), rs -> null);
return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
}
@Override
public ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query) {
public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
long minPartition = toPartitionTs(query.getStartTs());
long maxPartition = toPartitionTs(query.getEndTs());
ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition);
ResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
final ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
@ -379,7 +380,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
@Override
public void onSuccess(@Nullable List<Long> partitions) {
QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitions);
deleteAsync(cursor, resultFuture);
deleteAsync(tenantId, cursor, resultFuture);
}
@Override
@ -390,7 +391,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
return resultFuture;
}
private void deleteAsync(final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
private void deleteAsync(TenantId tenantId, final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
if (!cursor.hasNextPartition()) {
resultFuture.set(null);
} else {
@ -403,10 +404,10 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
stmt.setLong(4, cursor.getStartTs());
stmt.setLong(5, cursor.getEndTs());
Futures.addCallback(executeAsyncWrite(stmt), new FutureCallback<ResultSet>() {
Futures.addCallback(executeAsyncWrite(tenantId, stmt), new FutureCallback<ResultSet>() {
@Override
public void onSuccess(@Nullable ResultSet result) {
deleteAsync(cursor, resultFuture);
deleteAsync(tenantId, cursor, resultFuture);
}
@Override
@ -431,8 +432,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
@Override
public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) {
ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey());
public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(tenantId, entityId, query.getKey());
ListenableFuture<Boolean> booleanFuture = Futures.transform(latestEntryFuture, latestEntry -> {
long ts = latestEntry.getTs();
@ -446,7 +447,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
if (isRemove) {
return deleteLatest(entityId, query.getKey());
return deleteLatest(tenantId, entityId, query.getKey());
}
return Futures.immediateFuture(null);
}, readResultsProcessingExecutor);
@ -458,7 +459,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
if (query.getRewriteLatestIfDeleted()) {
ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
if (isRemove) {
return getNewLatestEntryFuture(entityId, query);
return getNewLatestEntryFuture(tenantId, entityId, query);
}
return Futures.immediateFuture(null);
}, readResultsProcessingExecutor);
@ -481,16 +482,16 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
return resultFuture;
}
private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {
private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
long startTs = 0;
long endTs = query.getStartTs() - 1;
ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
Aggregation.NONE, DESC_ORDER);
ListenableFuture<List<TsKvEntry>> future = findAllAsync(entityId, findNewLatestQuery);
ListenableFuture<List<TsKvEntry>> future = findAllAsync(tenantId, entityId, findNewLatestQuery);
return Futures.transformAsync(future, entryList -> {
if (entryList.size() == 1) {
return saveLatest(entityId, entryList.get(0));
return saveLatest(tenantId, entityId, entryList.get(0));
} else {
log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
}
@ -498,23 +499,23 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}, readResultsProcessingExecutor);
}
private ListenableFuture<Void> deleteLatest(EntityId entityId, String key) {
private ListenableFuture<Void> deleteLatest(TenantId tenantId, EntityId entityId, String key) {
Statement delete = QueryBuilder.delete().all().from(ModelConstants.TS_KV_LATEST_CF)
.where(eq(ModelConstants.ENTITY_TYPE_COLUMN, entityId.getEntityType()))
.and(eq(ModelConstants.ENTITY_ID_COLUMN, entityId.getId()))
.and(eq(ModelConstants.KEY_COLUMN, key));
log.debug("Remove request: {}", delete.toString());
return getFuture(executeAsyncWrite(delete), rs -> null);
return getFuture(executeAsyncWrite(tenantId, delete), rs -> null);
}
@Override
public ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query) {
public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
long minPartition = toPartitionTs(query.getStartTs());
long maxPartition = toPartitionTs(query.getEndTs());
if (minPartition == maxPartition) {
return Futures.immediateFuture(null);
} else {
ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition);
ResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
final ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
@ -531,7 +532,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
partitionsToDelete.add(partitions.get(i));
}
QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitionsToDelete);
deletePartitionAsync(cursor, resultFuture);
deletePartitionAsync(tenantId, cursor, resultFuture);
}
@Override
@ -543,7 +544,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
}
}
private void deletePartitionAsync(final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
private void deletePartitionAsync(TenantId tenantId, final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
if (!cursor.hasNextPartition()) {
resultFuture.set(null);
} else {
@ -554,10 +555,10 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
stmt.setLong(2, cursor.getNextPartition());
stmt.setString(3, cursor.getKey());
Futures.addCallback(executeAsyncWrite(stmt), new FutureCallback<ResultSet>() {
Futures.addCallback(executeAsyncWrite(tenantId, stmt), new FutureCallback<ResultSet>() {
@Override
public void onSuccess(@Nullable ResultSet result) {
deletePartitionAsync(cursor, resultFuture);
deletePartitionAsync(tenantId, cursor, resultFuture);
}
@Override
@ -632,12 +633,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
* Select existing partitions from the table
* <code>{@link ModelConstants#TS_KV_PARTITIONS_CF}</code> for the given entity
*/
private ResultSetFuture fetchPartitions(EntityId entityId, String key, long minPartition, long maxPartition) {
private ResultSetFuture fetchPartitions(TenantId tenantId, EntityId entityId, String key, long minPartition, long maxPartition) {
Select.Where select = QueryBuilder.select(ModelConstants.PARTITION_COLUMN).from(ModelConstants.TS_KV_PARTITIONS_CF).where(eq(ModelConstants.ENTITY_TYPE_COLUMN, entityId.getEntityType().name()))
.and(eq(ModelConstants.ENTITY_ID_COLUMN, entityId.getId())).and(eq(ModelConstants.KEY_COLUMN, key));
select.and(QueryBuilder.gte(ModelConstants.PARTITION_COLUMN, minPartition));
select.and(QueryBuilder.lte(ModelConstants.PARTITION_COLUMN, maxPartition));
return executeAsyncRead(select);
return executeAsyncRead(tenantId, select);
}
private PreparedStatement getSaveStmt(DataType dataType) {

19
dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.timeseries;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
@ -28,21 +29,21 @@ import java.util.List;
*/
public interface TimeseriesDao {
ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries);
ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries);
ListenableFuture<TsKvEntry> findLatest(EntityId entityId, String key);
ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key);
ListenableFuture<List<TsKvEntry>> findAllLatest(EntityId entityId);
ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId);
ListenableFuture<Void> save(EntityId entityId, TsKvEntry tsKvEntry, long ttl);
ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl);
ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl);
ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl);
ListenableFuture<Void> saveLatest(EntityId entityId, TsKvEntry tsKvEntry);
ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry);
ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query);
ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query);
ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query);
ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
}

13
dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.timeseries;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
@ -29,15 +30,15 @@ import java.util.List;
*/
public interface TimeseriesService {
ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries);
ListenableFuture<List<TsKvEntry>> findAll(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries);
ListenableFuture<List<TsKvEntry>> findLatest(EntityId entityId, Collection<String> keys);
ListenableFuture<List<TsKvEntry>> findLatest(TenantId tenantId, EntityId entityId, Collection<String> keys);
ListenableFuture<List<TsKvEntry>> findAllLatest(EntityId entityId);
ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId);
ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry);
ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry);
ListenableFuture<List<Void>> save(EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl);
ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl);
ListenableFuture<List<Void>> remove(EntityId entityId, List<DeleteTsKvQuery> queries);
ListenableFuture<List<Void>> remove(TenantId tenantId, EntityId entityId, List<DeleteTsKvQuery> queries);
}

13
dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserCredentialsDao.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.user;
import com.datastax.driver.core.querybuilder.Select.Where;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.ModelConstants;
@ -48,33 +49,33 @@ public class CassandraUserCredentialsDao extends CassandraAbstractModelDao<UserC
}
@Override
public UserCredentials findByUserId(UUID userId) {
public UserCredentials findByUserId(TenantId tenantId, UUID userId) {
log.debug("Try to find user credentials by userId [{}] ", userId);
Where query = select().from(ModelConstants.USER_CREDENTIALS_BY_USER_COLUMN_FAMILY_NAME).where(eq(ModelConstants.USER_CREDENTIALS_USER_ID_PROPERTY, userId));
log.trace(EXECUTE_QUERY, query);
UserCredentialsEntity userCredentialsEntity = findOneByStatement(query);
UserCredentialsEntity userCredentialsEntity = findOneByStatement(tenantId, query);
log.trace("Found user credentials [{}] by userId [{}]", userCredentialsEntity, userId);
return DaoUtil.getData(userCredentialsEntity);
}
@Override
public UserCredentials findByActivateToken(String activateToken) {
public UserCredentials findByActivateToken(TenantId tenantId, String activateToken) {
log.debug("Try to find user credentials by activateToken [{}] ", activateToken);
Where query = select().from(ModelConstants.USER_CREDENTIALS_BY_ACTIVATE_TOKEN_COLUMN_FAMILY_NAME)
.where(eq(ModelConstants.USER_CREDENTIALS_ACTIVATE_TOKEN_PROPERTY, activateToken));
log.trace(EXECUTE_QUERY, query);
UserCredentialsEntity userCredentialsEntity = findOneByStatement(query);
UserCredentialsEntity userCredentialsEntity = findOneByStatement(tenantId, query);
log.trace("Found user credentials [{}] by activateToken [{}]", userCredentialsEntity, activateToken);
return DaoUtil.getData(userCredentialsEntity);
}
@Override
public UserCredentials findByResetToken(String resetToken) {
public UserCredentials findByResetToken(TenantId tenantId, String resetToken) {
log.debug("Try to find user credentials by resetToken [{}] ", resetToken);
Where query = select().from(ModelConstants.USER_CREDENTIALS_BY_RESET_TOKEN_COLUMN_FAMILY_NAME)
.where(eq(ModelConstants.USER_CREDENTIALS_RESET_TOKEN_PROPERTY, resetToken));
log.trace(EXECUTE_QUERY, query);
UserCredentialsEntity userCredentialsEntity = findOneByStatement(query);
UserCredentialsEntity userCredentialsEntity = findOneByStatement(tenantId, query);
log.trace("Found user credentials [{}] by resetToken [{}]", userCredentialsEntity, resetToken);
return DaoUtil.getData(userCredentialsEntity);
}

11
dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java

@ -19,6 +19,7 @@ import com.datastax.driver.core.querybuilder.Select.Where;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.DaoUtil;
@ -50,11 +51,11 @@ public class CassandraUserDao extends CassandraAbstractSearchTextDao<UserEntity,
}
@Override
public User findByEmail(String email) {
public User findByEmail(TenantId tenantId, String email) {
log.debug("Try to find user by email [{}] ", email);
Where query = select().from(ModelConstants.USER_BY_EMAIL_COLUMN_FAMILY_NAME).where(eq(ModelConstants.USER_EMAIL_PROPERTY, email));
log.trace("Execute query {}", query);
UserEntity userEntity = findOneByStatement(query);
UserEntity userEntity = findOneByStatement(tenantId, query);
log.trace("Found user [{}] by email [{}]", userEntity, email);
return DaoUtil.getData(userEntity);
}
@ -62,7 +63,8 @@ public class CassandraUserDao extends CassandraAbstractSearchTextDao<UserEntity,
@Override
public List<User> findTenantAdmins(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find tenant admin users by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<UserEntity> userEntities = findPageWithTextSearch(ModelConstants.USER_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
List<UserEntity> userEntities = findPageWithTextSearch(new TenantId(tenantId),
ModelConstants.USER_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ModelConstants.USER_TENANT_ID_PROPERTY, tenantId),
eq(ModelConstants.USER_CUSTOMER_ID_PROPERTY, ModelConstants.NULL_UUID),
eq(ModelConstants.USER_AUTHORITY_PROPERTY, Authority.TENANT_ADMIN.name())),
@ -74,7 +76,8 @@ public class CassandraUserDao extends CassandraAbstractSearchTextDao<UserEntity,
@Override
public List<User> findCustomerUsers(UUID tenantId, UUID customerId, TextPageLink pageLink) {
log.debug("Try to find customer users by tenantId [{}], customerId [{}] and pageLink [{}]", tenantId, customerId, pageLink);
List<UserEntity> userEntities = findPageWithTextSearch(ModelConstants.USER_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
List<UserEntity> userEntities = findPageWithTextSearch(new TenantId(tenantId),
ModelConstants.USER_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ModelConstants.USER_TENANT_ID_PROPERTY, tenantId),
eq(ModelConstants.USER_CUSTOMER_ID_PROPERTY, customerId),
eq(ModelConstants.USER_AUTHORITY_PROPERTY, Authority.CUSTOMER_USER.name())),

9
dao/src/main/java/org/thingsboard/server/dao/user/UserCredentialsDao.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.dao.user;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.Dao;
@ -31,7 +32,7 @@ public interface UserCredentialsDao extends Dao<UserCredentials> {
* @param userCredentials the user credentials object
* @return saved user credentials object
*/
UserCredentials save(UserCredentials userCredentials);
UserCredentials save(TenantId tenantId, UserCredentials userCredentials);
/**
* Find user credentials by user id.
@ -39,7 +40,7 @@ public interface UserCredentialsDao extends Dao<UserCredentials> {
* @param userId the user id
* @return the user credentials object
*/
UserCredentials findByUserId(UUID userId);
UserCredentials findByUserId(TenantId tenantId, UUID userId);
/**
* Find user credentials by activate token.
@ -47,7 +48,7 @@ public interface UserCredentialsDao extends Dao<UserCredentials> {
* @param activateToken the activate token
* @return the user credentials object
*/
UserCredentials findByActivateToken(String activateToken);
UserCredentials findByActivateToken(TenantId tenantId, String activateToken);
/**
* Find user credentials by reset token.
@ -55,6 +56,6 @@ public interface UserCredentialsDao extends Dao<UserCredentials> {
* @param resetToken the reset token
* @return the user credentials object
*/
UserCredentials findByResetToken(String resetToken);
UserCredentials findByResetToken(TenantId tenantId, String resetToken);
}

5
dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.user;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao;
@ -30,7 +31,7 @@ public interface UserDao extends Dao<User> {
* @param user the user object
* @return saved user entity
*/
User save(User user);
User save(TenantId tenantId, User user);
/**
* Find user by email.
@ -38,7 +39,7 @@ public interface UserDao extends Dao<User> {
* @param email the email
* @return the user entity
*/
User findByEmail(String email);
User findByEmail(TenantId tenantId, String email);
/**
* Find tenant admin users by tenantId and page link.

20
dao/src/main/java/org/thingsboard/server/dao/user/UserService.java

@ -26,27 +26,27 @@ import org.thingsboard.server.common.data.security.UserCredentials;
public interface UserService {
User findUserById(UserId userId);
User findUserById(TenantId tenantId, UserId userId);
ListenableFuture<User> findUserByIdAsync(UserId userId);
ListenableFuture<User> findUserByIdAsync(TenantId tenantId, UserId userId);
User findUserByEmail(String email);
User findUserByEmail(TenantId tenantId, String email);
User saveUser(User user);
UserCredentials findUserCredentialsByUserId(UserId userId);
UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId);
UserCredentials findUserCredentialsByActivateToken(String activateToken);
UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken);
UserCredentials findUserCredentialsByResetToken(String resetToken);
UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken);
UserCredentials saveUserCredentials(UserCredentials userCredentials);
UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials);
UserCredentials activateUserCredentials(String activateToken, String password);
UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password);
UserCredentials requestPasswordReset(String email);
UserCredentials requestPasswordReset(TenantId tenantId, String email);
void deleteUser(UserId userId);
void deleteUser(TenantId tenantId, UserId userId);
TextPageData<User> findTenantAdmins(TenantId tenantId, TextPageLink pageLink);

174
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

@ -56,86 +56,86 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
@Autowired
private UserDao userDao;
@Autowired
private UserCredentialsDao userCredentialsDao;
@Autowired
private TenantDao tenantDao;
@Autowired
private CustomerDao customerDao;
@Override
public User findUserByEmail(String email) {
log.trace("Executing findUserByEmail [{}]", email);
validateString(email, "Incorrect email " + email);
return userDao.findByEmail(email);
}
@Override
public User findUserById(UserId userId) {
log.trace("Executing findUserById [{}]", userId);
validateId(userId, INCORRECT_USER_ID + userId);
return userDao.findById(userId.getId());
}
@Override
public ListenableFuture<User> findUserByIdAsync(UserId userId) {
public User findUserByEmail(TenantId tenantId, String email) {
log.trace("Executing findUserByEmail [{}]", email);
validateString(email, "Incorrect email " + email);
return userDao.findByEmail(tenantId, email);
}
@Override
public User findUserById(TenantId tenantId, UserId userId) {
log.trace("Executing findUserById [{}]", userId);
validateId(userId, INCORRECT_USER_ID + userId);
return userDao.findById(tenantId, userId.getId());
}
@Override
public ListenableFuture<User> findUserByIdAsync(TenantId tenantId, UserId userId) {
log.trace("Executing findUserByIdAsync [{}]", userId);
validateId(userId, INCORRECT_USER_ID + userId);
return userDao.findByIdAsync(userId.getId());
return userDao.findByIdAsync(tenantId, userId.getId());
}
@Override
public User saveUser(User user) {
log.trace("Executing saveUser [{}]", user);
userValidator.validate(user);
User savedUser = userDao.save(user);
userValidator.validate(user, User::getTenantId);
User savedUser = userDao.save(user.getTenantId(), user);
if (user.getId() == null) {
UserCredentials userCredentials = new UserCredentials();
userCredentials.setEnabled(false);
userCredentials.setActivateToken(RandomStringUtils.randomAlphanumeric(DEFAULT_TOKEN_LENGTH));
userCredentials.setUserId(new UserId(savedUser.getUuidId()));
userCredentialsDao.save(userCredentials);
}
userCredentialsDao.save(user.getTenantId(), userCredentials);
}
return savedUser;
}
@Override
public UserCredentials findUserCredentialsByUserId(UserId userId) {
public UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId) {
log.trace("Executing findUserCredentialsByUserId [{}]", userId);
validateId(userId, INCORRECT_USER_ID + userId);
return userCredentialsDao.findByUserId(userId.getId());
return userCredentialsDao.findByUserId(tenantId, userId.getId());
}
@Override
public UserCredentials findUserCredentialsByActivateToken(String activateToken) {
public UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken) {
log.trace("Executing findUserCredentialsByActivateToken [{}]", activateToken);
validateString(activateToken, "Incorrect activateToken " + activateToken);
return userCredentialsDao.findByActivateToken(activateToken);
return userCredentialsDao.findByActivateToken(tenantId, activateToken);
}
@Override
public UserCredentials findUserCredentialsByResetToken(String resetToken) {
public UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken) {
log.trace("Executing findUserCredentialsByResetToken [{}]", resetToken);
validateString(resetToken, "Incorrect resetToken " + resetToken);
return userCredentialsDao.findByResetToken(resetToken);
return userCredentialsDao.findByResetToken(tenantId, resetToken);
}
@Override
public UserCredentials saveUserCredentials(UserCredentials userCredentials) {
public UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials) {
log.trace("Executing saveUserCredentials [{}]", userCredentials);
userCredentialsValidator.validate(userCredentials);
return userCredentialsDao.save(userCredentials);
userCredentialsValidator.validate(userCredentials, data -> tenantId);
return userCredentialsDao.save(tenantId, userCredentials);
}
@Override
public UserCredentials activateUserCredentials(String activateToken, String password) {
public UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password) {
log.trace("Executing activateUserCredentials activateToken [{}], password [{}]", activateToken, password);
validateString(activateToken, "Incorrect activateToken " + activateToken);
validateString(password, "Incorrect password " + password);
UserCredentials userCredentials = userCredentialsDao.findByActivateToken(activateToken);
UserCredentials userCredentials = userCredentialsDao.findByActivateToken(tenantId, activateToken);
if (userCredentials == null) {
throw new IncorrectParameterException(String.format("Unable to find user credentials by activateToken [%s]", activateToken));
}
@ -145,35 +145,35 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
userCredentials.setEnabled(true);
userCredentials.setActivateToken(null);
userCredentials.setPassword(password);
return saveUserCredentials(userCredentials);
return saveUserCredentials(tenantId, userCredentials);
}
@Override
public UserCredentials requestPasswordReset(String email) {
public UserCredentials requestPasswordReset(TenantId tenantId, String email) {
log.trace("Executing requestPasswordReset email [{}]", email);
validateString(email, "Incorrect email " + email);
User user = userDao.findByEmail(email);
User user = userDao.findByEmail(tenantId, email);
if (user == null) {
throw new IncorrectParameterException(String.format("Unable to find user by email [%s]", email));
}
UserCredentials userCredentials = userCredentialsDao.findByUserId(user.getUuidId());
UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, user.getUuidId());
if (!userCredentials.isEnabled()) {
throw new IncorrectParameterException("Unable to reset password for inactive user");
}
userCredentials.setResetToken(RandomStringUtils.randomAlphanumeric(DEFAULT_TOKEN_LENGTH));
return saveUserCredentials(userCredentials);
return saveUserCredentials(tenantId, userCredentials);
}
@Override
public void deleteUser(UserId userId) {
public void deleteUser(TenantId tenantId, UserId userId) {
log.trace("Executing deleteUser [{}]", userId);
validateId(userId, INCORRECT_USER_ID + userId);
UserCredentials userCredentials = userCredentialsDao.findByUserId(userId.getId());
userCredentialsDao.removeById(userCredentials.getUuidId());
deleteEntityRelations(userId);
userDao.removeById(userId.getId());
UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId());
userCredentialsDao.removeById(tenantId, userCredentials.getUuidId());
deleteEntityRelations(tenantId, userId);
userDao.removeById(tenantId, userId.getId());
}
@Override
@ -189,7 +189,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
public void deleteTenantAdmins(TenantId tenantId) {
log.trace("Executing deleteTenantAdmins, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantAdminsRemover.removeEntities(tenantId);
tenantAdminsRemover.removeEntities(tenantId, tenantId);
}
@Override
@ -207,19 +207,19 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
log.trace("Executing deleteCustomerUsers, customerId [{}]", customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, "Incorrect customerId " + customerId);
new CustomerUsersRemover(tenantId).removeEntities(customerId);
customerUsersRemover.removeEntities(tenantId, customerId);
}
private DataValidator<User> userValidator =
new DataValidator<User>() {
@Override
protected void validateDataImpl(User user) {
protected void validateDataImpl(TenantId requestTenantId, User user) {
if (StringUtils.isEmpty(user.getEmail())) {
throw new DataValidationException("User email should be specified!");
}
validateEmail(user.getEmail());
Authority authority = user.getAuthority();
if (authority == null) {
throw new DataValidationException("User authority isn't defined!");
@ -234,12 +234,12 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
customerId = new CustomerId(ModelConstants.NULL_UUID);
user.setCustomerId(customerId);
}
switch (authority) {
case SYS_ADMIN:
if (!tenantId.getId().equals(ModelConstants.NULL_UUID)
|| !customerId.getId().equals(ModelConstants.NULL_UUID)) {
throw new DataValidationException("System administrator can't be assigned neither to tenant nor to customer!");
throw new DataValidationException("System administrator can't be assigned neither to tenant nor to customer!");
}
break;
case TENANT_ADMIN:
@ -251,46 +251,46 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
break;
case CUSTOMER_USER:
if (tenantId.getId().equals(ModelConstants.NULL_UUID)
|| customerId.getId().equals(ModelConstants.NULL_UUID) ) {
|| customerId.getId().equals(ModelConstants.NULL_UUID)) {
throw new DataValidationException("Customer user should be assigned to customer!");
}
break;
default:
break;
}
User existentUserWithEmail = findUserByEmail(user.getEmail());
User existentUserWithEmail = findUserByEmail(tenantId, user.getEmail());
if (existentUserWithEmail != null && !isSameData(existentUserWithEmail, user)) {
throw new DataValidationException("User with email '" + user.getEmail() + "' "
+ " already present in database!");
}
if (!tenantId.getId().equals(ModelConstants.NULL_UUID)) {
Tenant tenant = tenantDao.findById(user.getTenantId().getId());
Tenant tenant = tenantDao.findById(tenantId, user.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("User is referencing to non-existent tenant!");
}
}
if (!customerId.getId().equals(ModelConstants.NULL_UUID)) {
Customer customer = customerDao.findById(user.getCustomerId().getId());
Customer customer = customerDao.findById(tenantId, user.getCustomerId().getId());
if (customer == null) {
throw new DataValidationException("User is referencing to non-existent customer!");
} else if (!customer.getTenantId().getId().equals(tenantId.getId())) {
throw new DataValidationException("User can't be assigned to customer from different tenant!");
}
}
}
};
private DataValidator<UserCredentials> userCredentialsValidator =
}
};
private DataValidator<UserCredentials> userCredentialsValidator =
new DataValidator<UserCredentials>() {
@Override
protected void validateCreate(UserCredentials userCredentials) {
protected void validateCreate(TenantId tenantId, UserCredentials userCredentials) {
throw new IncorrectParameterException("Creation of new user credentials is prohibited.");
}
@Override
protected void validateDataImpl(UserCredentials userCredentials) {
protected void validateDataImpl(TenantId tenantId, UserCredentials userCredentials) {
if (userCredentials.getUserId() == null) {
throw new DataValidationException("User credentials should be assigned to user!");
}
@ -302,50 +302,40 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
throw new DataValidationException("Enabled user credentials can't have activate token!");
}
}
UserCredentials existingUserCredentialsEntity = userCredentialsDao.findById(userCredentials.getId().getId());
UserCredentials existingUserCredentialsEntity = userCredentialsDao.findById(tenantId, userCredentials.getId().getId());
if (existingUserCredentialsEntity == null) {
throw new DataValidationException("Unable to update non-existent user credentials!");
}
User user = findUserById(userCredentials.getUserId());
User user = findUserById(tenantId, userCredentials.getUserId());
if (user == null) {
throw new DataValidationException("Can't assign user credentials to non-existent user!");
}
}
};
private PaginatedRemover<TenantId, User> tenantAdminsRemover =
new PaginatedRemover<TenantId, User>() {
};
private PaginatedRemover<TenantId, User> tenantAdminsRemover = new PaginatedRemover<TenantId, User>() {
@Override
protected List<User> findEntities(TenantId id, TextPageLink pageLink) {
protected List<User> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) {
return userDao.findTenantAdmins(id.getId(), pageLink);
}
@Override
protected void removeEntity(User entity) {
deleteUser(new UserId(entity.getUuidId()));
protected void removeEntity(TenantId tenantId, User entity) {
deleteUser(tenantId, new UserId(entity.getUuidId()));
}
};
private class CustomerUsersRemover extends PaginatedRemover<CustomerId, User> {
private TenantId tenantId;
CustomerUsersRemover(TenantId tenantId) {
this.tenantId = tenantId;
}
private PaginatedRemover<CustomerId, User> customerUsersRemover = new PaginatedRemover<CustomerId, User>() {
@Override
protected List<User> findEntities(CustomerId id, TextPageLink pageLink) {
protected List<User> findEntities(TenantId tenantId, CustomerId id, TextPageLink pageLink) {
return userDao.findCustomerUsers(tenantId.getId(), id.getId(), pageLink);
}
@Override
protected void removeEntity(User entity) {
deleteUser(new UserId(entity.getUuidId()));
protected void removeEntity(TenantId tenantId, User entity) {
deleteUser(tenantId, new UserId(entity.getUuidId()));
}
}
};
}

41
dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java

@ -20,10 +20,15 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import javax.annotation.Nullable;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
@ -45,6 +50,9 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
private final ExecutorService callbackExecutor;
private final ScheduledExecutorService timeoutExecutor;
private final int concurrencyLimit;
private final boolean perTenantLimitsEnabled;
private final String perTenantLimitsConfiguration;
private final ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
protected final AtomicInteger concurrencyLevel = new AtomicInteger();
protected final AtomicInteger totalAdded = new AtomicInteger();
@ -53,8 +61,10 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
protected final AtomicInteger totalFailed = new AtomicInteger();
protected final AtomicInteger totalExpired = new AtomicInteger();
protected final AtomicInteger totalRejected = new AtomicInteger();
protected final AtomicInteger totalRateLimited = new AtomicInteger();
public AbstractBufferedRateExecutor(int queueLimit, int concurrencyLimit, long maxWaitTime, int dispatcherThreads, int callbackThreads, long pollMs) {
public AbstractBufferedRateExecutor(int queueLimit, int concurrencyLimit, long maxWaitTime, int dispatcherThreads, int callbackThreads, long pollMs,
boolean perTenantLimitsEnabled, String perTenantLimitsConfiguration) {
this.maxWaitTime = maxWaitTime;
this.pollMs = pollMs;
this.concurrencyLimit = concurrencyLimit;
@ -62,6 +72,8 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads);
this.callbackExecutor = Executors.newFixedThreadPool(callbackThreads);
this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
this.perTenantLimitsEnabled = perTenantLimitsEnabled;
this.perTenantLimitsConfiguration = perTenantLimitsConfiguration;
for (int i = 0; i < dispatcherThreads; i++) {
dispatcherExecutor.submit(this::dispatch);
}
@ -71,12 +83,27 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
public F submit(T task) {
SettableFuture<V> settableFuture = create();
F result = wrap(task, settableFuture);
try {
totalAdded.incrementAndGet();
queue.add(new AsyncTaskContext<>(UUID.randomUUID(), task, settableFuture, System.currentTimeMillis()));
} catch (IllegalStateException e) {
totalRejected.incrementAndGet();
settableFuture.setException(e);
boolean perTenantLimitReached = false;
if (perTenantLimitsEnabled) {
if (task.getTenantId() == null) {
log.info("Invalid task received: {}", task);
} else if (!task.getTenantId().isNullUid()) {
TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(task.getTenantId(), id -> new TbRateLimits(perTenantLimitsConfiguration));
if (!rateLimits.tryConsume()) {
totalRateLimited.incrementAndGet();
settableFuture.setException(new TenantRateLimitException());
perTenantLimitReached = true;
}
}
}
if (!perTenantLimitReached) {
try {
totalAdded.incrementAndGet();
queue.add(new AsyncTaskContext<>(UUID.randomUUID(), task, settableFuture, System.currentTimeMillis()));
} catch (IllegalStateException e) {
totalRejected.incrementAndGet();
settableFuture.setException(e);
}
}
return result;
}

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

Loading…
Cancel
Save