Commit ed3c57d1eacfb82016f9efef52be49609b0cceb5

Authored by Vladyslav_Prykhodko
2 parents b29a610f 9b76d785

Merge remote-tracking branch 'upstream/master' into feature/rule-chain/open-rule-chain-node

Showing 150 changed files with 2130 additions and 1317 deletions
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
18 19 import com.fasterxml.jackson.databind.ObjectMapper;
19 20 import com.fasterxml.jackson.databind.node.ArrayNode;
20 21 import com.fasterxml.jackson.databind.node.ObjectNode;
... ... @@ -26,26 +27,27 @@ import org.springframework.beans.factory.annotation.Value;
26 27 import org.springframework.security.core.Authentication;
27 28 import org.springframework.security.core.context.SecurityContextHolder;
28 29 import org.springframework.web.bind.annotation.ExceptionHandler;
29   -import org.thingsboard.server.common.data.*;
30 30 import org.thingsboard.server.common.data.Customer;
31 31 import org.thingsboard.server.common.data.Dashboard;
32 32 import org.thingsboard.server.common.data.DashboardInfo;
33 33 import org.thingsboard.server.common.data.DataConstants;
34 34 import org.thingsboard.server.common.data.Device;
  35 +import org.thingsboard.server.common.data.DeviceInfo;
35 36 import org.thingsboard.server.common.data.EntityType;
36 37 import org.thingsboard.server.common.data.EntityView;
  38 +import org.thingsboard.server.common.data.EntityViewInfo;
37 39 import org.thingsboard.server.common.data.HasName;
38 40 import org.thingsboard.server.common.data.HasTenantId;
39 41 import org.thingsboard.server.common.data.Tenant;
40 42 import org.thingsboard.server.common.data.User;
41 43 import org.thingsboard.server.common.data.alarm.Alarm;
42   -import org.thingsboard.server.common.data.id.AlarmId;
43 44 import org.thingsboard.server.common.data.alarm.AlarmInfo;
44 45 import org.thingsboard.server.common.data.asset.Asset;
45 46 import org.thingsboard.server.common.data.asset.AssetInfo;
46 47 import org.thingsboard.server.common.data.audit.ActionType;
47 48 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
48 49 import org.thingsboard.server.common.data.exception.ThingsboardException;
  50 +import org.thingsboard.server.common.data.id.AlarmId;
49 51 import org.thingsboard.server.common.data.id.AssetId;
50 52 import org.thingsboard.server.common.data.id.CustomerId;
51 53 import org.thingsboard.server.common.data.id.DashboardId;
... ... @@ -73,7 +75,6 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle;
73 75 import org.thingsboard.server.common.msg.TbMsg;
74 76 import org.thingsboard.server.common.msg.TbMsgDataType;
75 77 import org.thingsboard.server.common.msg.TbMsgMetaData;
76   -import org.thingsboard.server.dao.alarm.AlarmService;
77 78 import org.thingsboard.server.dao.asset.AssetService;
78 79 import org.thingsboard.server.dao.attributes.AttributesService;
79 80 import org.thingsboard.server.dao.audit.AuditLogService;
... ... @@ -640,6 +641,12 @@ public abstract class BaseController {
640 641 case ALARM_CLEAR:
641 642 msgType = DataConstants.ALARM_CLEAR;
642 643 break;
  644 + case ASSIGNED_FROM_TENANT:
  645 + msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT;
  646 + break;
  647 + case ASSIGNED_TO_TENANT:
  648 + msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT;
  649 + break;
643 650 }
644 651 if (!StringUtils.isEmpty(msgType)) {
645 652 try {
... ... @@ -659,6 +666,16 @@ public abstract class BaseController {
659 666 String strCustomerName = extractParameter(String.class, 2, additionalInfo);
660 667 metaData.putValue("unassignedCustomerId", strCustomerId);
661 668 metaData.putValue("unassignedCustomerName", strCustomerName);
  669 + } else if (actionType == ActionType.ASSIGNED_FROM_TENANT) {
  670 + String strTenantId = extractParameter(String.class, 0, additionalInfo);
  671 + String strTenantName = extractParameter(String.class, 1, additionalInfo);
  672 + metaData.putValue("assignedFromTenantId", strTenantId);
  673 + metaData.putValue("assignedFromTenantName", strTenantName);
  674 + } else if (actionType == ActionType.ASSIGNED_TO_TENANT) {
  675 + String strTenantId = extractParameter(String.class, 0, additionalInfo);
  676 + String strTenantName = extractParameter(String.class, 1, additionalInfo);
  677 + metaData.putValue("assignedToTenantId", strTenantId);
  678 + metaData.putValue("assignedToTenantName", strTenantName);
662 679 }
663 680 ObjectNode entityNode;
664 681 if (entity != null) {
... ... @@ -722,5 +739,13 @@ public abstract class BaseController {
722 739 return result;
723 740 }
724 741
  742 + protected <E extends HasName> String entityToStr(E entity) {
  743 + try {
  744 + return json.writeValueAsString(json.valueToTree(entity));
  745 + } catch (JsonProcessingException e) {
  746 + log.warn("[{}] Failed to convert entity to string!", entity, e);
  747 + }
  748 + return null;
  749 + }
725 750
726 751 }
... ...
... ... @@ -40,8 +40,10 @@ import org.thingsboard.server.common.data.Device;
40 40 import org.thingsboard.server.common.data.DeviceInfo;
41 41 import org.thingsboard.server.common.data.EntitySubtype;
42 42 import org.thingsboard.server.common.data.EntityType;
  43 +import org.thingsboard.server.common.data.Tenant;
43 44 import org.thingsboard.server.common.data.audit.ActionType;
44 45 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
  46 +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
45 47 import org.thingsboard.server.common.data.exception.ThingsboardException;
46 48 import org.thingsboard.server.common.data.id.CustomerId;
47 49 import org.thingsboard.server.common.data.id.DeviceId;
... ... @@ -49,6 +51,9 @@ import org.thingsboard.server.common.data.id.TenantId;
49 51 import org.thingsboard.server.common.data.page.PageData;
50 52 import org.thingsboard.server.common.data.page.PageLink;
51 53 import org.thingsboard.server.common.data.security.DeviceCredentials;
  54 +import org.thingsboard.server.common.msg.TbMsg;
  55 +import org.thingsboard.server.common.msg.TbMsgDataType;
  56 +import org.thingsboard.server.common.msg.TbMsgMetaData;
52 57 import org.thingsboard.server.dao.device.claim.ClaimResponse;
53 58 import org.thingsboard.server.dao.device.claim.ClaimResult;
54 59 import org.thingsboard.server.dao.exception.IncorrectParameterException;
... ... @@ -71,6 +76,7 @@ public class DeviceController extends BaseController {
71 76
72 77 private static final String DEVICE_ID = "deviceId";
73 78 private static final String DEVICE_NAME = "deviceName";
  79 + private static final String TENANT_ID = "tenantId";
74 80
75 81 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
76 82 @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET)
... ... @@ -547,4 +553,54 @@ public class DeviceController extends BaseController {
547 553 }
548 554 return DataConstants.DEFAULT_SECRET_KEY;
549 555 }
  556 +
  557 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  558 + @RequestMapping(value = "/tenant/{tenantId}/device/{deviceId}", method = RequestMethod.POST)
  559 + @ResponseBody
  560 + public Device assignDeviceToTenant(@PathVariable(TENANT_ID) String strTenantId,
  561 + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
  562 + checkParameter(TENANT_ID, strTenantId);
  563 + checkParameter(DEVICE_ID, strDeviceId);
  564 + try {
  565 + DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
  566 + Device device = checkDeviceId(deviceId, Operation.ASSIGN_TO_TENANT);
  567 +
  568 + TenantId newTenantId = new TenantId(toUUID(strTenantId));
  569 + Tenant newTenant = tenantService.findTenantById(newTenantId);
  570 + if (newTenant == null) {
  571 + throw new ThingsboardException("Could not find the specified Tenant!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
  572 + }
  573 +
  574 + Device assignedDevice = deviceService.assignDeviceToTenant(newTenantId, device);
  575 +
  576 + logEntityAction(getCurrentUser(), deviceId, assignedDevice,
  577 + assignedDevice.getCustomerId(),
  578 + ActionType.ASSIGNED_TO_TENANT, null, strTenantId, newTenant.getName());
  579 +
  580 + Tenant currentTenant = tenantService.findTenantById(getTenantId());
  581 + pushAssignedFromNotification(currentTenant, newTenantId, assignedDevice);
  582 +
  583 + return assignedDevice;
  584 + } catch (Exception e) {
  585 + logEntityAction(getCurrentUser(), emptyId(EntityType.DEVICE), null,
  586 + null,
  587 + ActionType.ASSIGNED_TO_TENANT, e, strTenantId);
  588 + throw handleException(e);
  589 + }
  590 + }
  591 +
  592 + private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) {
  593 + String data = entityToStr(assignedDevice);
  594 + if (data != null) {
  595 + TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data);
  596 + tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null);
  597 + }
  598 + }
  599 +
  600 + private TbMsgMetaData getMetaDataForAssignedFrom(Tenant tenant) {
  601 + TbMsgMetaData metaData = new TbMsgMetaData();
  602 + metaData.putValue("assignedFromTenantId", tenant.getId().getId().toString());
  603 + metaData.putValue("assignedFromTenantName", tenant.getName());
  604 + return metaData;
  605 + }
550 606 }
... ...
... ... @@ -28,7 +28,9 @@ import org.thingsboard.server.service.install.DatabaseTsUpgradeService;
28 28 import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
29 29 import org.thingsboard.server.service.install.SystemDataLoaderService;
30 30 import org.thingsboard.server.service.install.TsDatabaseSchemaService;
  31 +import org.thingsboard.server.service.install.TsLatestDatabaseSchemaService;
31 32 import org.thingsboard.server.service.install.migrate.EntitiesMigrateService;
  33 +import org.thingsboard.server.service.install.migrate.TsLatestMigrateService;
32 34 import org.thingsboard.server.service.install.update.DataUpdateService;
33 35
34 36 @Service
... ... @@ -51,6 +53,9 @@ public class ThingsboardInstallService {
51 53 @Autowired
52 54 private TsDatabaseSchemaService tsDatabaseSchemaService;
53 55
  56 + @Autowired(required = false)
  57 + private TsLatestDatabaseSchemaService tsLatestDatabaseSchemaService;
  58 +
54 59 @Autowired
55 60 private DatabaseEntitiesUpgradeService databaseEntitiesUpgradeService;
56 61
... ... @@ -72,6 +77,9 @@ public class ThingsboardInstallService {
72 77 @Autowired(required = false)
73 78 private EntitiesMigrateService entitiesMigrateService;
74 79
  80 + @Autowired(required = false)
  81 + private TsLatestMigrateService latestMigrateService;
  82 +
75 83 public void performInstall() {
76 84 try {
77 85 if (isUpgrade) {
... ... @@ -82,6 +90,10 @@ public class ThingsboardInstallService {
82 90 entitiesMigrateService.migrate();
83 91 log.info("Updating system data...");
84 92 systemDataLoaderService.updateSystemWidgets();
  93 + } else if ("3.0.1-cassandra".equals(upgradeFromVersion)) {
  94 + log.info("Migrating ThingsBoard latest timeseries data from cassandra to SQL database ...");
  95 + latestMigrateService.migrate();
  96 + log.info("Updating system data...");
85 97 } else {
86 98 switch (upgradeFromVersion) {
87 99 case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
... ... @@ -181,6 +193,10 @@ public class ThingsboardInstallService {
181 193
182 194 tsDatabaseSchemaService.createDatabaseSchema();
183 195
  196 + if (tsLatestDatabaseSchemaService != null) {
  197 + tsLatestDatabaseSchemaService.createDatabaseSchema();
  198 + }
  199 +
184 200 log.info("Loading system data...");
185 201
186 202 componentDiscoveryService.discoverComponents();
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install;
  17 +
  18 +import org.springframework.context.annotation.Profile;
  19 +import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.dao.util.NoSqlTsLatestDao;
  21 +
  22 +@Service
  23 +@NoSqlTsLatestDao
  24 +@Profile("install")
  25 +public class CassandraTsLatestDatabaseSchemaService extends CassandraAbstractDatabaseSchemaService
  26 + implements TsLatestDatabaseSchemaService {
  27 + public CassandraTsLatestDatabaseSchemaService() {
  28 + super("schema-ts-latest.cql");
  29 + }
  30 +}
... ...
... ... @@ -18,11 +18,9 @@ package org.thingsboard.server.service.install;
18 18 import org.springframework.context.annotation.Profile;
19 19 import org.springframework.stereotype.Service;
20 20 import org.thingsboard.server.dao.util.HsqlDao;
21   -import org.thingsboard.server.dao.util.SqlDao;
22 21
23 22 @Service
24 23 @HsqlDao
25   -@SqlDao
26 24 @Profile("install")
27 25 public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
28 26 implements EntityDatabaseSchemaService {
... ...
... ... @@ -18,10 +18,8 @@ package org.thingsboard.server.service.install;
18 18 import org.springframework.context.annotation.Profile;
19 19 import org.springframework.stereotype.Service;
20 20 import org.thingsboard.server.dao.util.PsqlDao;
21   -import org.thingsboard.server.dao.util.SqlDao;
22 21
23 22 @Service
24   -@SqlDao
25 23 @PsqlDao
26 24 @Profile("install")
27 25 public class PsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
... ...
... ... @@ -21,7 +21,6 @@ import org.springframework.beans.factory.annotation.Value;
21 21 import org.springframework.context.annotation.Profile;
22 22 import org.springframework.stereotype.Service;
23 23 import org.thingsboard.server.dao.dashboard.DashboardService;
24   -import org.thingsboard.server.dao.util.SqlDao;
25 24 import org.thingsboard.server.service.install.sql.SqlDbHelper;
26 25
27 26 import java.nio.charset.Charset;
... ... @@ -58,7 +57,6 @@ import static org.thingsboard.server.service.install.DatabaseHelper.TYPE;
58 57 @Service
59 58 @Profile("install")
60 59 @Slf4j
61   -@SqlDao
62 60 public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService {
63 61
64 62 private static final String SCHEMA_UPDATE_SQL = "schema_update.sql";
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install;
  17 +
  18 +public interface TsLatestDatabaseSchemaService extends DatabaseSchemaService {
  19 +}
... ...
... ... @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.EntityType;
24 24 import org.thingsboard.server.common.data.UUIDConverter;
25 25 import org.thingsboard.server.dao.cassandra.CassandraCluster;
26 26 import org.thingsboard.server.dao.util.NoSqlAnyDao;
27   -import org.thingsboard.server.dao.util.SqlDao;
28 27 import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
29 28
30 29 import java.sql.Connection;
... ... @@ -42,7 +41,6 @@ import static org.thingsboard.server.service.install.migrate.CassandraToSqlColum
42 41
43 42 @Service
44 43 @Profile("install")
45   -@SqlDao
46 44 @NoSqlAnyDao
47 45 @Slf4j
48 46 public class CassandraEntitiesToSqlMigrateService implements EntitiesMigrateService {
... ...
... ... @@ -128,7 +128,7 @@ public class CassandraToSqlTable {
128 128 return this.validateColumnData(data);
129 129 }
130 130
131   - private CassandraToSqlColumnData[] validateColumnData(CassandraToSqlColumnData[] data) {
  131 + protected CassandraToSqlColumnData[] validateColumnData(CassandraToSqlColumnData[] data) {
132 132 for (int i=0;i<data.length;i++) {
133 133 CassandraToSqlColumn column = this.columns.get(i);
134 134 if (column.getType() == CassandraToSqlColumnType.STRING) {
... ... @@ -148,7 +148,7 @@ public class CassandraToSqlTable {
148 148 return data;
149 149 }
150 150
151   - private void batchInsert(List<CassandraToSqlColumnData[]> batchData, Connection conn) throws SQLException {
  151 + protected void batchInsert(List<CassandraToSqlColumnData[]> batchData, Connection conn) throws SQLException {
152 152 boolean retry = false;
153 153 for (CassandraToSqlColumnData[] data : batchData) {
154 154 for (CassandraToSqlColumn column: this.columns) {
... ... @@ -269,7 +269,7 @@ public class CassandraToSqlTable {
269 269 return Optional.empty();
270 270 }
271 271
272   - private Statement createCassandraSelectStatement() {
  272 + protected Statement createCassandraSelectStatement() {
273 273 StringBuilder selectStatementBuilder = new StringBuilder();
274 274 selectStatementBuilder.append("SELECT ");
275 275 for (CassandraToSqlColumn column : columns) {
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install.migrate;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.apache.commons.lang3.StringUtils;
  20 +import org.hibernate.exception.ConstraintViolationException;
  21 +import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.beans.factory.annotation.Value;
  23 +import org.springframework.context.annotation.Profile;
  24 +import org.springframework.stereotype.Service;
  25 +import org.thingsboard.server.common.data.UUIDConverter;
  26 +import org.thingsboard.server.dao.cassandra.CassandraCluster;
  27 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
  28 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
  29 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
  30 +import org.thingsboard.server.dao.sqlts.dictionary.TsKvDictionaryRepository;
  31 +import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;
  32 +import org.thingsboard.server.dao.util.NoSqlAnyDao;
  33 +import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
  34 +
  35 +import java.sql.Connection;
  36 +import java.sql.DriverManager;
  37 +import java.util.Arrays;
  38 +import java.util.List;
  39 +import java.util.Optional;
  40 +import java.util.concurrent.ConcurrentHashMap;
  41 +import java.util.concurrent.ConcurrentMap;
  42 +import java.util.concurrent.locks.ReentrantLock;
  43 +import java.util.stream.Collectors;
  44 +
  45 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.bigintColumn;
  46 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.booleanColumn;
  47 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.doubleColumn;
  48 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.idColumn;
  49 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.jsonColumn;
  50 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.stringColumn;
  51 +
  52 +@Service
  53 +@Profile("install")
  54 +@NoSqlAnyDao
  55 +@Slf4j
  56 +public class CassandraTsLatestToSqlMigrateService implements TsLatestMigrateService {
  57 +
  58 + @Autowired
  59 + private EntityDatabaseSchemaService entityDatabaseSchemaService;
  60 +
  61 + @Autowired
  62 + private InsertLatestTsRepository insertLatestTsRepository;
  63 +
  64 + @Autowired
  65 + protected CassandraCluster cluster;
  66 +
  67 + @Autowired
  68 + protected TsKvDictionaryRepository dictionaryRepository;
  69 +
  70 + @Value("${spring.datasource.url}")
  71 + protected String dbUrl;
  72 +
  73 + @Value("${spring.datasource.username}")
  74 + protected String dbUserName;
  75 +
  76 + @Value("${spring.datasource.password}")
  77 + protected String dbPassword;
  78 +
  79 + private final ConcurrentMap<String, Integer> tsKvDictionaryMap = new ConcurrentHashMap<>();
  80 +
  81 + protected static final ReentrantLock tsCreationLock = new ReentrantLock();
  82 +
  83 + @Override
  84 + public void migrate() throws Exception {
  85 + log.info("Performing migration of latest timeseries data from cassandra to SQL database ...");
  86 + entityDatabaseSchemaService.createDatabaseSchema(false);
  87 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  88 + conn.setAutoCommit(false);
  89 + for (CassandraToSqlTable table : tables) {
  90 + table.migrateToSql(cluster.getSession(), conn);
  91 + }
  92 + } catch (Exception e) {
  93 + log.error("Unexpected error during ThingsBoard entities data migration!", e);
  94 + throw e;
  95 + }
  96 + entityDatabaseSchemaService.createDatabaseIndexes();
  97 + }
  98 +
  99 + private List<CassandraToSqlTable> tables = Arrays.asList(
  100 + new CassandraToSqlTable("ts_kv_latest_cf",
  101 + idColumn("entity_id"),
  102 + stringColumn("key"),
  103 + bigintColumn("ts"),
  104 + booleanColumn("bool_v"),
  105 + stringColumn("str_v"),
  106 + bigintColumn("long_v"),
  107 + doubleColumn("dbl_v"),
  108 + jsonColumn("json_v")) {
  109 +
  110 + @Override
  111 + protected void batchInsert(List<CassandraToSqlColumnData[]> batchData, Connection conn) {
  112 + insertLatestTsRepository
  113 + .saveOrUpdate(batchData.stream().map(data -> getTsKvLatestEntity(data)).collect(Collectors.toList()));
  114 + }
  115 +
  116 + @Override
  117 + protected CassandraToSqlColumnData[] validateColumnData(CassandraToSqlColumnData[] data) {
  118 + return data;
  119 + }
  120 + });
  121 +
  122 + private TsKvLatestEntity getTsKvLatestEntity(CassandraToSqlColumnData[] data) {
  123 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  124 + latestEntity.setEntityId(UUIDConverter.fromString(data[0].getValue()));
  125 + latestEntity.setKey(getOrSaveKeyId(data[1].getValue()));
  126 + latestEntity.setTs(Long.parseLong(data[2].getValue()));
  127 +
  128 + String strV = data[4].getValue();
  129 + if (strV != null) {
  130 + latestEntity.setStrValue(strV);
  131 + } else {
  132 + Long longV = null;
  133 + try {
  134 + longV = Long.parseLong(data[5].getValue());
  135 + } catch (Exception e) {
  136 + }
  137 + if (longV != null) {
  138 + latestEntity.setLongValue(longV);
  139 + } else {
  140 + Double doubleV = null;
  141 + try {
  142 + doubleV = Double.parseDouble(data[6].getValue());
  143 + } catch (Exception e) {
  144 + }
  145 + if (doubleV != null) {
  146 + latestEntity.setDoubleValue(doubleV);
  147 + } else {
  148 +
  149 + String jsonV = data[7].getValue();
  150 + if (StringUtils.isNoneEmpty(jsonV)) {
  151 + latestEntity.setJsonValue(jsonV);
  152 + } else {
  153 + Boolean boolV = null;
  154 + try {
  155 + boolV = Boolean.parseBoolean(data[3].getValue());
  156 + } catch (Exception e) {
  157 + }
  158 + if (boolV != null) {
  159 + latestEntity.setBooleanValue(boolV);
  160 + } else {
  161 + log.warn("All values in key-value row are nullable ");
  162 + }
  163 + }
  164 + }
  165 + }
  166 + }
  167 + return latestEntity;
  168 + }
  169 +
  170 + protected Integer getOrSaveKeyId(String strKey) {
  171 + Integer keyId = tsKvDictionaryMap.get(strKey);
  172 + if (keyId == null) {
  173 + Optional<TsKvDictionary> tsKvDictionaryOptional;
  174 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  175 + if (!tsKvDictionaryOptional.isPresent()) {
  176 + tsCreationLock.lock();
  177 + try {
  178 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  179 + if (!tsKvDictionaryOptional.isPresent()) {
  180 + TsKvDictionary tsKvDictionary = new TsKvDictionary();
  181 + tsKvDictionary.setKey(strKey);
  182 + try {
  183 + TsKvDictionary saved = dictionaryRepository.save(tsKvDictionary);
  184 + tsKvDictionaryMap.put(saved.getKey(), saved.getKeyId());
  185 + keyId = saved.getKeyId();
  186 + } catch (ConstraintViolationException e) {
  187 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  188 + TsKvDictionary dictionary = tsKvDictionaryOptional.orElseThrow(() -> new RuntimeException("Failed to get TsKvDictionary entity from DB!"));
  189 + tsKvDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId());
  190 + keyId = dictionary.getKeyId();
  191 + }
  192 + } else {
  193 + keyId = tsKvDictionaryOptional.get().getKeyId();
  194 + }
  195 + } finally {
  196 + tsCreationLock.unlock();
  197 + }
  198 + } else {
  199 + keyId = tsKvDictionaryOptional.get().getKeyId();
  200 + tsKvDictionaryMap.put(strKey, keyId);
  201 + }
  202 + }
  203 + return keyId;
  204 + }
  205 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install.migrate;
  17 +
  18 +public interface TsLatestMigrateService {
  19 +
  20 + void migrate() throws Exception;
  21 +}
... ...
... ... @@ -28,7 +28,6 @@ import org.thingsboard.server.common.msg.TbActorMsg;
28 28 import org.thingsboard.server.common.msg.queue.ServiceType;
29 29 import org.thingsboard.server.common.msg.queue.TbCallback;
30 30 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
31   -import org.thingsboard.server.gen.transport.TransportProtos;
32 31 import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
33 32 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
34 33 import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto;
... ... @@ -61,7 +60,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra
61 60
62 61 import javax.annotation.PostConstruct;
63 62 import javax.annotation.PreDestroy;
64   -import java.util.ArrayList;
65 63 import java.util.List;
66 64 import java.util.Optional;
67 65 import java.util.UUID;
... ...
... ... @@ -18,6 +18,6 @@ package org.thingsboard.server.service.security.permission;
18 18 public enum Operation {
19 19
20 20 ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL,
21   - READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES
  21 + READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES, ASSIGN_TO_TENANT
22 22
23 23 }
... ...
... ... @@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Value;
20 20 import org.springframework.scheduling.annotation.Scheduled;
21 21 import org.springframework.stereotype.Service;
22 22 import org.thingsboard.server.dao.util.PsqlDao;
23   -import org.thingsboard.server.dao.util.SqlDao;
24 23 import org.thingsboard.server.service.ttl.AbstractCleanUpService;
25 24
26 25 import java.sql.Connection;
... ... @@ -28,7 +27,6 @@ import java.sql.DriverManager;
28 27 import java.sql.SQLException;
29 28
30 29 @PsqlDao
31   -@SqlDao
32 30 @Slf4j
33 31 @Service
34 32 public class EventsCleanUpService extends AbstractCleanUpService {
... ...
... ... @@ -19,11 +19,13 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.beans.factory.annotation.Value;
20 20 import org.springframework.stereotype.Service;
21 21 import org.thingsboard.server.dao.model.ModelConstants;
22   -import org.thingsboard.server.dao.util.PsqlTsDao;
  22 +import org.thingsboard.server.dao.util.PsqlDao;
  23 +import org.thingsboard.server.dao.util.SqlTsDao;
23 24
24 25 import java.sql.Connection;
25 26
26   -@PsqlTsDao
  27 +@SqlTsDao
  28 +@PsqlDao
27 29 @Service
28 30 @Slf4j
29 31 public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService {
... ... @@ -33,9 +35,9 @@ public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpServi
33 35
34 36 @Override
35 37 protected void doCleanUp(Connection connection) {
36   - long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);");
37   - log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved);
38   - long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);");
39   - log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved);
  38 + long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);");
  39 + log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved);
  40 + long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);");
  41 + log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved);
40 42 }
41 43 }
\ No newline at end of file
... ...
... ... @@ -178,8 +178,8 @@ database:
178 178 ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records
179 179 ts:
180 180 type: "${DATABASE_TS_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale)
181   - latest_ts:
182   - type: "${DATABASE_TS_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale)
  181 + ts_latest:
  182 + type: "${DATABASE_TS_LATEST_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale)
183 183
184 184 # note: timescale works only with postgreSQL database for DATABASE_ENTITIES_TYPE.
185 185
... ...
... ... @@ -15,74 +15,17 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
18   -import com.fasterxml.jackson.core.type.TypeReference;
19   -import com.fasterxml.jackson.databind.JsonNode;
20   -import com.fasterxml.jackson.databind.ObjectMapper;
21   -import io.jsonwebtoken.Claims;
22   -import io.jsonwebtoken.Header;
23   -import io.jsonwebtoken.Jwt;
24   -import io.jsonwebtoken.Jwts;
25 18 import lombok.extern.slf4j.Slf4j;
26   -import org.apache.commons.lang3.StringUtils;
27   -import org.hamcrest.Matcher;
28   -import org.junit.After;
29   -import org.junit.Assert;
30   -import org.junit.Before;
31   -import org.junit.Rule;
32   -import org.junit.rules.TestRule;
33   -import org.junit.rules.TestWatcher;
34   -import org.junit.runner.Description;
35 19 import org.junit.runner.RunWith;
36   -import org.springframework.beans.factory.annotation.Autowired;
37 20 import org.springframework.boot.test.context.SpringBootContextLoader;
38 21 import org.springframework.boot.test.context.SpringBootTest;
39 22 import org.springframework.context.annotation.ComponentScan;
40 23 import org.springframework.context.annotation.Configuration;
41   -import org.springframework.http.HttpHeaders;
42   -import org.springframework.http.MediaType;
43   -import org.springframework.http.converter.HttpMessageConverter;
44   -import org.springframework.http.converter.StringHttpMessageConverter;
45   -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
46   -import org.springframework.mock.http.MockHttpInputMessage;
47   -import org.springframework.mock.http.MockHttpOutputMessage;
48 24 import org.springframework.test.annotation.DirtiesContext;
49 25 import org.springframework.test.context.ActiveProfiles;
50 26 import org.springframework.test.context.ContextConfiguration;
51 27 import org.springframework.test.context.junit4.SpringRunner;
52 28 import org.springframework.test.context.web.WebAppConfiguration;
53   -import org.springframework.test.web.servlet.MockMvc;
54   -import org.springframework.test.web.servlet.MvcResult;
55   -import org.springframework.test.web.servlet.ResultActions;
56   -import org.springframework.test.web.servlet.ResultMatcher;
57   -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
58   -import org.springframework.util.LinkedMultiValueMap;
59   -import org.springframework.util.MultiValueMap;
60   -import org.springframework.web.context.WebApplicationContext;
61   -import org.thingsboard.server.common.data.BaseData;
62   -import org.thingsboard.server.common.data.Customer;
63   -import org.thingsboard.server.common.data.Tenant;
64   -import org.thingsboard.server.common.data.User;
65   -import org.thingsboard.server.common.data.id.TenantId;
66   -import org.thingsboard.server.common.data.id.UUIDBased;
67   -import org.thingsboard.server.common.data.page.PageLink;
68   -import org.thingsboard.server.common.data.page.SortOrder;
69   -import org.thingsboard.server.common.data.page.TimePageLink;
70   -import org.thingsboard.server.common.data.security.Authority;
71   -import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
72   -import org.thingsboard.server.service.mail.TestMailService;
73   -import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest;
74   -import org.thingsboard.server.service.security.auth.rest.LoginRequest;
75   -
76   -import java.io.IOException;
77   -import java.util.ArrayList;
78   -import java.util.Arrays;
79   -import java.util.Comparator;
80   -import java.util.List;
81   -
82   -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
83   -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
84   -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
85   -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
86 29
87 30 @ActiveProfiles("test")
88 31 @RunWith(SpringRunner.class)
... ...
... ... @@ -218,6 +218,7 @@ public abstract class AbstractWebTest {
218 218 }
219 219
220 220 private Tenant savedDifferentTenant;
  221 +
221 222 protected void loginDifferentTenant() throws Exception {
222 223 loginSysAdmin();
223 224 Tenant tenant = new Tenant();
... ... @@ -313,6 +314,10 @@ public abstract class AbstractWebTest {
313 314 return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
314 315 }
315 316
  317 + protected <T> T doGet(String urlTemplate, Class<T> responseClass, ResultMatcher resultMatcher, Object... urlVariables) throws Exception {
  318 + return readResponse(doGet(urlTemplate, urlVariables).andExpect(resultMatcher), responseClass);
  319 + }
  320 +
316 321 protected <T> T doGetAsync(String urlTemplate, Class<T> responseClass, Object... urlVariables) throws Exception {
317 322 return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
318 323 }
... ... @@ -352,9 +357,9 @@ public abstract class AbstractWebTest {
352 357 return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType);
353 358 }
354 359
355   - protected <T> T doGetTypedWithTimePageLink(String urlTemplate, TypeReference<T> responseType,
356   - TimePageLink pageLink,
357   - Object... urlVariables) throws Exception {
  360 + protected <T> T doGetTypedWithTimePageLink(String urlTemplate, TypeReference<T> responseType,
  361 + TimePageLink pageLink,
  362 + Object... urlVariables) throws Exception {
358 363 List<Object> pageLinkVariables = new ArrayList<>();
359 364 urlTemplate += "pageSize={pageSize}&page={page}";
360 365 pageLinkVariables.add(pageLink.getPageSize());
... ... @@ -395,11 +400,11 @@ public abstract class AbstractWebTest {
395 400 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
396 401 }
397 402
398   - protected <T,R> R doPostWithResponse(String urlTemplate, T content, Class<R> responseClass, String... params) throws Exception {
  403 + protected <T, R> R doPostWithResponse(String urlTemplate, T content, Class<R> responseClass, String... params) throws Exception {
399 404 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
400 405 }
401 406
402   - protected <T,R> R doPostWithTypedResponse(String urlTemplate, T content, TypeReference<R> responseType, String... params) throws Exception {
  407 + protected <T, R> R doPostWithTypedResponse(String urlTemplate, T content, TypeReference<R> responseType, String... params) throws Exception {
403 408 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseType);
404 409 }
405 410
... ... @@ -430,7 +435,7 @@ public abstract class AbstractWebTest {
430 435 return mockMvc.perform(postRequest);
431 436 }
432 437
433   - protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception {
  438 + protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception {
434 439 MockHttpServletRequestBuilder postRequest = post(urlTemplate, params);
435 440 setJwtToken(postRequest);
436 441 String json = json(content);
... ...
... ... @@ -15,74 +15,79 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
18   -import static org.hamcrest.Matchers.containsString;
19   -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
20   -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
21   -
22   -import java.util.ArrayList;
23   -import java.util.Collections;
24   -import java.util.List;
25   -
26 18 import com.datastax.oss.driver.api.core.uuid.Uuids;
  19 +import com.fasterxml.jackson.core.type.TypeReference;
27 20 import org.apache.commons.lang3.RandomStringUtils;
28   -import org.thingsboard.server.common.data.*;
  21 +import org.junit.After;
  22 +import org.junit.Assert;
  23 +import org.junit.Before;
  24 +import org.junit.Test;
  25 +import org.thingsboard.server.common.data.Customer;
  26 +import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.EntitySubtype;
  28 +import org.thingsboard.server.common.data.Tenant;
  29 +import org.thingsboard.server.common.data.User;
29 30 import org.thingsboard.server.common.data.id.CustomerId;
30 31 import org.thingsboard.server.common.data.id.DeviceCredentialsId;
31 32 import org.thingsboard.server.common.data.id.DeviceId;
32 33 import org.thingsboard.server.common.data.page.PageData;
33 34 import org.thingsboard.server.common.data.page.PageLink;
  35 +import org.thingsboard.server.common.data.relation.EntityRelation;
  36 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
34 37 import org.thingsboard.server.common.data.security.Authority;
35 38 import org.thingsboard.server.common.data.security.DeviceCredentials;
36 39 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
37 40 import org.thingsboard.server.dao.model.ModelConstants;
38   -import org.junit.After;
39   -import org.junit.Assert;
40   -import org.junit.Before;
41   -import org.junit.Test;
42 41
43   -import com.fasterxml.jackson.core.type.TypeReference;
  42 +import java.util.ArrayList;
  43 +import java.util.Collections;
  44 +import java.util.List;
  45 +
  46 +import static org.hamcrest.Matchers.containsString;
  47 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  48 +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
44 49
45 50 public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
46   -
  51 +
47 52 private IdComparator<Device> idComparator = new IdComparator<>();
48   -
  53 +
49 54 private Tenant savedTenant;
50 55 private User tenantAdmin;
51   -
  56 +
52 57 @Before
53 58 public void beforeTest() throws Exception {
54 59 loginSysAdmin();
55   -
  60 +
56 61 Tenant tenant = new Tenant();
57 62 tenant.setTitle("My tenant");
58 63 savedTenant = doPost("/api/tenant", tenant, Tenant.class);
59 64 Assert.assertNotNull(savedTenant);
60   -
  65 +
61 66 tenantAdmin = new User();
62 67 tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
63 68 tenantAdmin.setTenantId(savedTenant.getId());
64 69 tenantAdmin.setEmail("tenant2@thingsboard.org");
65 70 tenantAdmin.setFirstName("Joe");
66 71 tenantAdmin.setLastName("Downs");
67   -
  72 +
68 73 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
69 74 }
70   -
  75 +
71 76 @After
72 77 public void afterTest() throws Exception {
73 78 loginSysAdmin();
74   -
75   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
76   - .andExpect(status().isOk());
  79 +
  80 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  81 + .andExpect(status().isOk());
77 82 }
78   -
  83 +
79 84 @Test
80 85 public void testSaveDevice() throws Exception {
81 86 Device device = new Device();
82 87 device.setName("My device");
83 88 device.setType("default");
84 89 Device savedDevice = doPost("/api/device", device, Device.class);
85   -
  90 +
86 91 Assert.assertNotNull(savedDevice);
87 92 Assert.assertNotNull(savedDevice.getId());
88 93 Assert.assertTrue(savedDevice.getCreatedTime() > 0);
... ... @@ -90,9 +95,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
90 95 Assert.assertNotNull(savedDevice.getCustomerId());
91 96 Assert.assertEquals(NULL_UUID, savedDevice.getCustomerId().getId());
92 97 Assert.assertEquals(device.getName(), savedDevice.getName());
93   -
94   - DeviceCredentials deviceCredentials =
95   - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  98 +
  99 + DeviceCredentials deviceCredentials =
  100 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
96 101
97 102 Assert.assertNotNull(deviceCredentials);
98 103 Assert.assertNotNull(deviceCredentials.getId());
... ... @@ -100,10 +105,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
100 105 Assert.assertEquals(DeviceCredentialsType.ACCESS_TOKEN, deviceCredentials.getCredentialsType());
101 106 Assert.assertNotNull(deviceCredentials.getCredentialsId());
102 107 Assert.assertEquals(20, deviceCredentials.getCredentialsId().length());
103   -
  108 +
104 109 savedDevice.setName("My new device");
105 110 doPost("/api/device", savedDevice, Device.class);
106   -
  111 +
107 112 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
108 113 Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
109 114 }
... ... @@ -115,10 +120,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
115 120 device.setType("default");
116 121 Device savedDevice = doPost("/api/device", device, Device.class);
117 122 loginDifferentTenant();
118   - doPost("/api/device", savedDevice, Device.class, status().isForbidden());
  123 + doPost("/api/device", savedDevice, Device.class, status().isNotFound());
119 124 deleteDifferentTenant();
120 125 }
121   -
  126 +
122 127 @Test
123 128 public void testFindDeviceById() throws Exception {
124 129 Device device = new Device();
... ... @@ -133,26 +138,27 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
133 138 @Test
134 139 public void testFindDeviceTypesByTenantId() throws Exception {
135 140 List<Device> devices = new ArrayList<>();
136   - for (int i=0;i<3;i++) {
  141 + for (int i = 0; i < 3; i++) {
137 142 Device device = new Device();
138   - device.setName("My device B"+i);
  143 + device.setName("My device B" + i);
139 144 device.setType("typeB");
140 145 devices.add(doPost("/api/device", device, Device.class));
141 146 }
142   - for (int i=0;i<7;i++) {
  147 + for (int i = 0; i < 7; i++) {
143 148 Device device = new Device();
144   - device.setName("My device C"+i);
  149 + device.setName("My device C" + i);
145 150 device.setType("typeC");
146 151 devices.add(doPost("/api/device", device, Device.class));
147 152 }
148   - for (int i=0;i<9;i++) {
  153 + for (int i = 0; i < 9; i++) {
149 154 Device device = new Device();
150   - device.setName("My device A"+i);
  155 + device.setName("My device A" + i);
151 156 device.setType("typeA");
152 157 devices.add(doPost("/api/device", device, Device.class));
153 158 }
154 159 List<EntitySubtype> deviceTypes = doGetTyped("/api/device/types",
155   - new TypeReference<List<EntitySubtype>>(){});
  160 + new TypeReference<List<EntitySubtype>>() {
  161 + });
156 162
157 163 Assert.assertNotNull(deviceTypes);
158 164 Assert.assertEquals(3, deviceTypes.size());
... ... @@ -160,19 +166,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
160 166 Assert.assertEquals("typeB", deviceTypes.get(1).getType());
161 167 Assert.assertEquals("typeC", deviceTypes.get(2).getType());
162 168 }
163   -
  169 +
164 170 @Test
165 171 public void testDeleteDevice() throws Exception {
166 172 Device device = new Device();
167 173 device.setName("My device");
168 174 device.setType("default");
169 175 Device savedDevice = doPost("/api/device", device, Device.class);
170   -
171   - doDelete("/api/device/"+savedDevice.getId().getId().toString())
172   - .andExpect(status().isOk());
173 176
174   - doGet("/api/device/"+savedDevice.getId().getId().toString())
175   - .andExpect(status().isNotFound());
  177 + doDelete("/api/device/" + savedDevice.getId().getId().toString())
  178 + .andExpect(status().isOk());
  179 +
  180 + doGet("/api/device/" + savedDevice.getId().getId().toString())
  181 + .andExpect(status().isNotFound());
176 182 }
177 183
178 184 @Test
... ... @@ -189,52 +195,51 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
189 195 Device device = new Device();
190 196 device.setType("default");
191 197 doPost("/api/device", device)
192   - .andExpect(status().isBadRequest())
193   - .andExpect(statusReason(containsString("Device name should be specified")));
  198 + .andExpect(status().isBadRequest())
  199 + .andExpect(statusReason(containsString("Device name should be specified")));
194 200 }
195   -
  201 +
196 202 @Test
197 203 public void testAssignUnassignDeviceToCustomer() throws Exception {
198 204 Device device = new Device();
199 205 device.setName("My device");
200 206 device.setType("default");
201 207 Device savedDevice = doPost("/api/device", device, Device.class);
202   -
  208 +
203 209 Customer customer = new Customer();
204 210 customer.setTitle("My customer");
205 211 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
206   -
207   - Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
  212 +
  213 + Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
208 214 + "/device/" + savedDevice.getId().getId().toString(), Device.class);
209 215 Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId());
210   -
  216 +
211 217 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
212 218 Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId());
213 219
214   - Device unassignedDevice =
  220 + Device unassignedDevice =
215 221 doDelete("/api/customer/device/" + savedDevice.getId().getId().toString(), Device.class);
216 222 Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId());
217   -
  223 +
218 224 foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
219 225 Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId());
220 226 }
221   -
  227 +
222 228 @Test
223 229 public void testAssignDeviceToNonExistentCustomer() throws Exception {
224 230 Device device = new Device();
225 231 device.setName("My device");
226 232 device.setType("default");
227 233 Device savedDevice = doPost("/api/device", device, Device.class);
228   -
229 234 doPost("/api/customer/" + Uuids.timeBased().toString()
230 235 + "/device/" + savedDevice.getId().getId().toString())
231   - .andExpect(status().isNotFound());
  236 + .andExpect(status().isNotFound());
232 237 }
233   -
  238 +
234 239 @Test
235 240 public void testAssignDeviceToCustomerFromDifferentTenant() throws Exception {
236 241 loginSysAdmin();
237   -
  242 +
238 243 Tenant tenant2 = new Tenant();
239 244 tenant2.setTitle("Different tenant");
240 245 Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class);
... ... @@ -246,103 +251,103 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
246 251 tenantAdmin2.setEmail("tenant3@thingsboard.org");
247 252 tenantAdmin2.setFirstName("Joe");
248 253 tenantAdmin2.setLastName("Downs");
249   -
  254 +
250 255 tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1");
251   -
  256 +
252 257 Customer customer = new Customer();
253 258 customer.setTitle("Different customer");
254 259 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
255 260
256 261 login(tenantAdmin.getEmail(), "testPassword1");
257   -
  262 +
258 263 Device device = new Device();
259 264 device.setName("My device");
260 265 device.setType("default");
261 266 Device savedDevice = doPost("/api/device", device, Device.class);
262   -
  267 +
263 268 doPost("/api/customer/" + savedCustomer.getId().getId().toString()
264 269 + "/device/" + savedDevice.getId().getId().toString())
265   - .andExpect(status().isForbidden());
266   -
  270 + .andExpect(status().isForbidden());
  271 +
267 272 loginSysAdmin();
268   -
269   - doDelete("/api/tenant/"+savedTenant2.getId().getId().toString())
270   - .andExpect(status().isOk());
  273 +
  274 + doDelete("/api/tenant/" + savedTenant2.getId().getId().toString())
  275 + .andExpect(status().isOk());
271 276 }
272   -
  277 +
273 278 @Test
274 279 public void testFindDeviceCredentialsByDeviceId() throws Exception {
275 280 Device device = new Device();
276 281 device.setName("My device");
277 282 device.setType("default");
278 283 Device savedDevice = doPost("/api/device", device, Device.class);
279   - DeviceCredentials deviceCredentials =
280   - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  284 + DeviceCredentials deviceCredentials =
  285 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
281 286 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
282 287 }
283   -
  288 +
284 289 @Test
285 290 public void testSaveDeviceCredentials() throws Exception {
286 291 Device device = new Device();
287 292 device.setName("My device");
288 293 device.setType("default");
289 294 Device savedDevice = doPost("/api/device", device, Device.class);
290   - DeviceCredentials deviceCredentials =
291   - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  295 + DeviceCredentials deviceCredentials =
  296 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
292 297 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
293 298 deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
294 299 deviceCredentials.setCredentialsId("access_token");
295 300 doPost("/api/device/credentials", deviceCredentials)
296   - .andExpect(status().isOk());
297   -
298   - DeviceCredentials foundDeviceCredentials =
  301 + .andExpect(status().isOk());
  302 +
  303 + DeviceCredentials foundDeviceCredentials =
299 304 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
300   -
  305 +
301 306 Assert.assertEquals(deviceCredentials, foundDeviceCredentials);
302 307 }
303   -
  308 +
304 309 @Test
305 310 public void testSaveDeviceCredentialsWithEmptyDevice() throws Exception {
306 311 DeviceCredentials deviceCredentials = new DeviceCredentials();
307 312 doPost("/api/device/credentials", deviceCredentials)
308   - .andExpect(status().isBadRequest());
  313 + .andExpect(status().isBadRequest());
309 314 }
310   -
  315 +
311 316 @Test
312 317 public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception {
313 318 Device device = new Device();
314 319 device.setName("My device");
315 320 device.setType("default");
316 321 Device savedDevice = doPost("/api/device", device, Device.class);
317   - DeviceCredentials deviceCredentials =
  322 + DeviceCredentials deviceCredentials =
318 323 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
319 324 deviceCredentials.setCredentialsType(null);
320 325 doPost("/api/device/credentials", deviceCredentials)
321   - .andExpect(status().isBadRequest())
322   - .andExpect(statusReason(containsString("Device credentials type should be specified")));
  326 + .andExpect(status().isBadRequest())
  327 + .andExpect(statusReason(containsString("Device credentials type should be specified")));
323 328 }
324   -
  329 +
325 330 @Test
326 331 public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception {
327 332 Device device = new Device();
328 333 device.setName("My device");
329 334 device.setType("default");
330 335 Device savedDevice = doPost("/api/device", device, Device.class);
331   - DeviceCredentials deviceCredentials =
  336 + DeviceCredentials deviceCredentials =
332 337 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
333 338 deviceCredentials.setCredentialsId(null);
334 339 doPost("/api/device/credentials", deviceCredentials)
335   - .andExpect(status().isBadRequest())
336   - .andExpect(statusReason(containsString("Device credentials id should be specified")));
  340 + .andExpect(status().isBadRequest())
  341 + .andExpect(statusReason(containsString("Device credentials id should be specified")));
337 342 }
338   -
  343 +
339 344 @Test
340 345 public void testSaveNonExistentDeviceCredentials() throws Exception {
341 346 Device device = new Device();
342 347 device.setName("My device");
343 348 device.setType("default");
344 349 Device savedDevice = doPost("/api/device", device, Device.class);
345   - DeviceCredentials deviceCredentials =
  350 + DeviceCredentials deviceCredentials =
346 351 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
347 352 DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(Uuids.timeBased()));
348 353 newDeviceCredentials.setCreatedTime(deviceCredentials.getCreatedTime());
... ... @@ -350,29 +355,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
350 355 newDeviceCredentials.setCredentialsType(deviceCredentials.getCredentialsType());
351 356 newDeviceCredentials.setCredentialsId(deviceCredentials.getCredentialsId());
352 357 doPost("/api/device/credentials", newDeviceCredentials)
353   - .andExpect(status().isBadRequest())
354   - .andExpect(statusReason(containsString("Unable to update non-existent device credentials")));
  358 + .andExpect(status().isBadRequest())
  359 + .andExpect(statusReason(containsString("Unable to update non-existent device credentials")));
355 360 }
356   -
  361 +
357 362 @Test
358 363 public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception {
359 364 Device device = new Device();
360 365 device.setName("My device");
361 366 device.setType("default");
362 367 Device savedDevice = doPost("/api/device", device, Device.class);
363   - DeviceCredentials deviceCredentials =
  368 + DeviceCredentials deviceCredentials =
364 369 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
365 370 deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased()));
366 371 doPost("/api/device/credentials", deviceCredentials)
367   - .andExpect(status().isNotFound());
  372 + .andExpect(status().isNotFound());
368 373 }
369 374
370 375 @Test
371 376 public void testFindTenantDevices() throws Exception {
372 377 List<Device> devices = new ArrayList<>();
373   - for (int i=0;i<178;i++) {
  378 + for (int i = 0; i < 178; i++) {
374 379 Device device = new Device();
375   - device.setName("Device"+i);
  380 + device.setName("Device" + i);
376 381 device.setType("default");
377 382 devices.add(doPost("/api/device", device, Device.class));
378 383 }
... ... @@ -380,28 +385,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
380 385 PageLink pageLink = new PageLink(23);
381 386 PageData<Device> pageData = null;
382 387 do {
383   - pageData = doGetTypedWithPageLink("/api/tenant/devices?",
  388 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
384 389 new TypeReference<PageData<Device>>(){}, pageLink);
  390 +
385 391 loadedDevices.addAll(pageData.getData());
386 392 if (pageData.hasNext()) {
387 393 pageLink = pageLink.nextPageLink();
388 394 }
389 395 } while (pageData.hasNext());
390   -
  396 +
391 397 Collections.sort(devices, idComparator);
392 398 Collections.sort(loadedDevices, idComparator);
393   -
  399 +
394 400 Assert.assertEquals(devices, loadedDevices);
395 401 }
396   -
  402 +
397 403 @Test
398 404 public void testFindTenantDevicesByName() throws Exception {
399 405 String title1 = "Device title 1";
400 406 List<Device> devicesTitle1 = new ArrayList<>();
401   - for (int i=0;i<143;i++) {
  407 + for (int i = 0; i < 143; i++) {
402 408 Device device = new Device();
403 409 String suffix = RandomStringUtils.randomAlphanumeric(15);
404   - String name = title1+suffix;
  410 + String name = title1 + suffix;
405 411 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
406 412 device.setName(name);
407 413 device.setType("default");
... ... @@ -409,37 +415,37 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
409 415 }
410 416 String title2 = "Device title 2";
411 417 List<Device> devicesTitle2 = new ArrayList<>();
412   - for (int i=0;i<75;i++) {
  418 + for (int i = 0; i < 75; i++) {
413 419 Device device = new Device();
414 420 String suffix = RandomStringUtils.randomAlphanumeric(15);
415   - String name = title2+suffix;
  421 + String name = title2 + suffix;
416 422 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
417 423 device.setName(name);
418 424 device.setType("default");
419 425 devicesTitle2.add(doPost("/api/device", device, Device.class));
420 426 }
421   -
  427 +
422 428 List<Device> loadedDevicesTitle1 = new ArrayList<>();
423 429 PageLink pageLink = new PageLink(15, 0, title1);
424 430 PageData<Device> pageData = null;
425 431 do {
426   - pageData = doGetTypedWithPageLink("/api/tenant/devices?",
  432 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
427 433 new TypeReference<PageData<Device>>(){}, pageLink);
428 434 loadedDevicesTitle1.addAll(pageData.getData());
429 435 if (pageData.hasNext()) {
430 436 pageLink = pageLink.nextPageLink();
431 437 }
432 438 } while (pageData.hasNext());
433   -
  439 +
434 440 Collections.sort(devicesTitle1, idComparator);
435 441 Collections.sort(loadedDevicesTitle1, idComparator);
436   -
  442 +
437 443 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
438   -
  444 +
439 445 List<Device> loadedDevicesTitle2 = new ArrayList<>();
440 446 pageLink = new PageLink(4, 0, title2);
441 447 do {
442   - pageData = doGetTypedWithPageLink("/api/tenant/devices?",
  448 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
443 449 new TypeReference<PageData<Device>>(){}, pageLink);
444 450 loadedDevicesTitle2.addAll(pageData.getData());
445 451 if (pageData.hasNext()) {
... ... @@ -449,25 +455,23 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
449 455
450 456 Collections.sort(devicesTitle2, idComparator);
451 457 Collections.sort(loadedDevicesTitle2, idComparator);
452   -
  458 +
453 459 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
454   -
  460 +
455 461 for (Device device : loadedDevicesTitle1) {
456   - doDelete("/api/device/"+device.getId().getId().toString())
457   - .andExpect(status().isOk());
  462 + doDelete("/api/device/" + device.getId().getId().toString())
  463 + .andExpect(status().isOk());
458 464 }
459   -
460 465 pageLink = new PageLink(4, 0, title1);
461 466 pageData = doGetTypedWithPageLink("/api/tenant/devices?",
462 467 new TypeReference<PageData<Device>>(){}, pageLink);
463 468 Assert.assertFalse(pageData.hasNext());
464 469 Assert.assertEquals(0, pageData.getData().size());
465   -
  470 +
466 471 for (Device device : loadedDevicesTitle2) {
467   - doDelete("/api/device/"+device.getId().getId().toString())
468   - .andExpect(status().isOk());
  472 + doDelete("/api/device/" + device.getId().getId().toString())
  473 + .andExpect(status().isOk());
469 474 }
470   -
471 475 pageLink = new PageLink(4, 0, title2);
472 476 pageData = doGetTypedWithPageLink("/api/tenant/devices?",
473 477 new TypeReference<PageData<Device>>(){}, pageLink);
... ... @@ -480,10 +484,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
480 484 String title1 = "Device title 1";
481 485 String type1 = "typeA";
482 486 List<Device> devicesType1 = new ArrayList<>();
483   - for (int i=0;i<143;i++) {
  487 + for (int i = 0; i < 143; i++) {
484 488 Device device = new Device();
485 489 String suffix = RandomStringUtils.randomAlphanumeric(15);
486   - String name = title1+suffix;
  490 + String name = title1 + suffix;
487 491 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
488 492 device.setName(name);
489 493 device.setType(type1);
... ... @@ -492,10 +496,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
492 496 String title2 = "Device title 2";
493 497 String type2 = "typeB";
494 498 List<Device> devicesType2 = new ArrayList<>();
495   - for (int i=0;i<75;i++) {
  499 + for (int i = 0; i < 75; i++) {
496 500 Device device = new Device();
497 501 String suffix = RandomStringUtils.randomAlphanumeric(15);
498   - String name = title2+suffix;
  502 + String name = title2 + suffix;
499 503 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
500 504 device.setName(name);
501 505 device.setType(type2);
... ... @@ -536,7 +540,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
536 540 Assert.assertEquals(devicesType2, loadedDevicesType2);
537 541
538 542 for (Device device : loadedDevicesType1) {
539   - doDelete("/api/device/"+device.getId().getId().toString())
  543 + doDelete("/api/device/" + device.getId().getId().toString())
540 544 .andExpect(status().isOk());
541 545 }
542 546
... ... @@ -547,7 +551,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
547 551 Assert.assertEquals(0, pageData.getData().size());
548 552
549 553 for (Device device : loadedDevicesType2) {
550   - doDelete("/api/device/"+device.getId().getId().toString())
  554 + doDelete("/api/device/" + device.getId().getId().toString())
551 555 .andExpect(status().isOk());
552 556 }
553 557
... ... @@ -557,42 +561,42 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
557 561 Assert.assertFalse(pageData.hasNext());
558 562 Assert.assertEquals(0, pageData.getData().size());
559 563 }
560   -
  564 +
561 565 @Test
562 566 public void testFindCustomerDevices() throws Exception {
563 567 Customer customer = new Customer();
564 568 customer.setTitle("Test customer");
565 569 customer = doPost("/api/customer", customer, Customer.class);
566 570 CustomerId customerId = customer.getId();
567   -
  571 +
568 572 List<Device> devices = new ArrayList<>();
569   - for (int i=0;i<128;i++) {
  573 + for (int i = 0; i < 128; i++) {
570 574 Device device = new Device();
571   - device.setName("Device"+i);
  575 + device.setName("Device" + i);
572 576 device.setType("default");
573 577 device = doPost("/api/device", device, Device.class);
574   - devices.add(doPost("/api/customer/" + customerId.getId().toString()
575   - + "/device/" + device.getId().getId().toString(), Device.class));
  578 + devices.add(doPost("/api/customer/" + customerId.getId().toString()
  579 + + "/device/" + device.getId().getId().toString(), Device.class));
576 580 }
577   -
  581 +
578 582 List<Device> loadedDevices = new ArrayList<>();
579 583 PageLink pageLink = new PageLink(23);
580 584 PageData<Device> pageData = null;
581 585 do {
582   - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
  586 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
583 587 new TypeReference<PageData<Device>>(){}, pageLink);
584 588 loadedDevices.addAll(pageData.getData());
585 589 if (pageData.hasNext()) {
586 590 pageLink = pageLink.nextPageLink();
587 591 }
588 592 } while (pageData.hasNext());
589   -
  593 +
590 594 Collections.sort(devices, idComparator);
591 595 Collections.sort(loadedDevices, idComparator);
592   -
  596 +
593 597 Assert.assertEquals(devices, loadedDevices);
594 598 }
595   -
  599 +
596 600 @Test
597 601 public void testFindCustomerDevicesByName() throws Exception {
598 602 Customer customer = new Customer();
... ... @@ -602,52 +606,52 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
602 606
603 607 String title1 = "Device title 1";
604 608 List<Device> devicesTitle1 = new ArrayList<>();
605   - for (int i=0;i<125;i++) {
  609 + for (int i = 0; i < 125; i++) {
606 610 Device device = new Device();
607 611 String suffix = RandomStringUtils.randomAlphanumeric(15);
608   - String name = title1+suffix;
  612 + String name = title1 + suffix;
609 613 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
610 614 device.setName(name);
611 615 device.setType("default");
612 616 device = doPost("/api/device", device, Device.class);
613   - devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
  617 + devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
614 618 + "/device/" + device.getId().getId().toString(), Device.class));
615 619 }
616 620 String title2 = "Device title 2";
617 621 List<Device> devicesTitle2 = new ArrayList<>();
618   - for (int i=0;i<143;i++) {
  622 + for (int i = 0; i < 143; i++) {
619 623 Device device = new Device();
620 624 String suffix = RandomStringUtils.randomAlphanumeric(15);
621   - String name = title2+suffix;
  625 + String name = title2 + suffix;
622 626 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
623 627 device.setName(name);
624 628 device.setType("default");
625 629 device = doPost("/api/device", device, Device.class);
626   - devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
  630 + devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
627 631 + "/device/" + device.getId().getId().toString(), Device.class));
628 632 }
629   -
  633 +
630 634 List<Device> loadedDevicesTitle1 = new ArrayList<>();
631 635 PageLink pageLink = new PageLink(15, 0, title1);
632 636 PageData<Device> pageData = null;
633 637 do {
634   - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
  638 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
635 639 new TypeReference<PageData<Device>>(){}, pageLink);
636 640 loadedDevicesTitle1.addAll(pageData.getData());
637 641 if (pageData.hasNext()) {
638 642 pageLink = pageLink.nextPageLink();
639 643 }
640 644 } while (pageData.hasNext());
641   -
  645 +
642 646 Collections.sort(devicesTitle1, idComparator);
643 647 Collections.sort(loadedDevicesTitle1, idComparator);
644   -
  648 +
645 649 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
646   -
  650 +
647 651 List<Device> loadedDevicesTitle2 = new ArrayList<>();
648 652 pageLink = new PageLink(4, 0, title2);
649 653 do {
650   - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
  654 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
651 655 new TypeReference<PageData<Device>>(){}, pageLink);
652 656 loadedDevicesTitle2.addAll(pageData.getData());
653 657 if (pageData.hasNext()) {
... ... @@ -657,25 +661,23 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
657 661
658 662 Collections.sort(devicesTitle2, idComparator);
659 663 Collections.sort(loadedDevicesTitle2, idComparator);
660   -
  664 +
661 665 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
662   -
  666 +
663 667 for (Device device : loadedDevicesTitle1) {
664 668 doDelete("/api/customer/device/" + device.getId().getId().toString())
665   - .andExpect(status().isOk());
  669 + .andExpect(status().isOk());
666 670 }
667   -
668 671 pageLink = new PageLink(4, 0, title1);
669 672 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
670 673 new TypeReference<PageData<Device>>(){}, pageLink);
671 674 Assert.assertFalse(pageData.hasNext());
672 675 Assert.assertEquals(0, pageData.getData().size());
673   -
  676 +
674 677 for (Device device : loadedDevicesTitle2) {
675 678 doDelete("/api/customer/device/" + device.getId().getId().toString())
676   - .andExpect(status().isOk());
  679 + .andExpect(status().isOk());
677 680 }
678   -
679 681 pageLink = new PageLink(4, 0, title2);
680 682 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
681 683 new TypeReference<PageData<Device>>(){}, pageLink);
... ... @@ -693,10 +695,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
693 695 String title1 = "Device title 1";
694 696 String type1 = "typeC";
695 697 List<Device> devicesType1 = new ArrayList<>();
696   - for (int i=0;i<125;i++) {
  698 + for (int i = 0; i < 125; i++) {
697 699 Device device = new Device();
698 700 String suffix = RandomStringUtils.randomAlphanumeric(15);
699   - String name = title1+suffix;
  701 + String name = title1 + suffix;
700 702 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
701 703 device.setName(name);
702 704 device.setType(type1);
... ... @@ -707,10 +709,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
707 709 String title2 = "Device title 2";
708 710 String type2 = "typeD";
709 711 List<Device> devicesType2 = new ArrayList<>();
710   - for (int i=0;i<143;i++) {
  712 + for (int i = 0; i < 143; i++) {
711 713 Device device = new Device();
712 714 String suffix = RandomStringUtils.randomAlphanumeric(15);
713   - String name = title2+suffix;
  715 + String name = title2 + suffix;
714 716 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
715 717 device.setName(name);
716 718 device.setType(type2);
... ... @@ -775,4 +777,54 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
775 777 Assert.assertEquals(0, pageData.getData().size());
776 778 }
777 779
  780 + @Test
  781 + public void testAssignDeviceToTenant() throws Exception {
  782 + Device device = new Device();
  783 + device.setName("My device");
  784 + device.setType("default");
  785 + Device savedDevice = doPost("/api/device", device, Device.class);
  786 +
  787 + Device anotherDevice = new Device();
  788 + anotherDevice.setName("My device1");
  789 + anotherDevice.setType("default");
  790 + Device savedAnotherDevice = doPost("/api/device", anotherDevice, Device.class);
  791 +
  792 + EntityRelation relation = new EntityRelation();
  793 + relation.setFrom(savedDevice.getId());
  794 + relation.setTo(savedAnotherDevice.getId());
  795 + relation.setTypeGroup(RelationTypeGroup.COMMON);
  796 + relation.setType("Contains");
  797 + doPost("/api/relation", relation).andExpect(status().isOk());
  798 +
  799 + loginSysAdmin();
  800 + Tenant tenant = new Tenant();
  801 + tenant.setTitle("Different tenant");
  802 + Tenant savedDifferentTenant = doPost("/api/tenant", tenant, Tenant.class);
  803 + Assert.assertNotNull(savedDifferentTenant);
  804 +
  805 + User user = new User();
  806 + user.setAuthority(Authority.TENANT_ADMIN);
  807 + user.setTenantId(savedDifferentTenant.getId());
  808 + user.setEmail("tenant9@thingsboard.org");
  809 + user.setFirstName("Sam");
  810 + user.setLastName("Downs");
  811 +
  812 + createUserAndLogin(user, "testPassword1");
  813 +
  814 + login("tenant2@thingsboard.org", "testPassword1");
  815 + Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class);
  816 +
  817 + doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class, status().isNotFound());
  818 +
  819 + login("tenant9@thingsboard.org", "testPassword1");
  820 +
  821 + Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class);
  822 + Assert.assertNotNull(foundDevice1);
  823 +
  824 + doGet("/api/relation?fromId=" + savedDevice.getId().getId() + "&fromType=DEVICE&relationType=Contains&toId=" + savedAnotherDevice.getId().getId() + "&toType=DEVICE", EntityRelation.class, status().isNotFound());
  825 +
  826 + loginSysAdmin();
  827 + doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
  828 + .andExpect(status().isOk());
  829 + }
778 830 }
... ...
... ... @@ -41,7 +41,9 @@ public class MqttNoSqlTestSuite {
41 41 public static CustomCassandraCQLUnit cassandraUnit =
42 42 new CustomCassandraCQLUnit(
43 43 Arrays.asList(
44   - new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false)),
  44 + new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
  45 + new ClassPathCQLDataSet("cassandra/schema-ts-latest.cql", false, false)
  46 + ),
45 47 "cassandra-test.yaml", 30000l);
46 48
47 49 @BeforeClass
... ...
... ... @@ -16,14 +16,12 @@
16 16 package org.thingsboard.server.dao.audit;
17 17
18 18 import com.google.common.util.concurrent.ListenableFuture;
19   -import org.thingsboard.server.common.data.BaseData;
20 19 import org.thingsboard.server.common.data.HasName;
21 20 import org.thingsboard.server.common.data.audit.ActionType;
22 21 import org.thingsboard.server.common.data.audit.AuditLog;
23 22 import org.thingsboard.server.common.data.id.CustomerId;
24 23 import org.thingsboard.server.common.data.id.EntityId;
25 24 import org.thingsboard.server.common.data.id.TenantId;
26   -import org.thingsboard.server.common.data.id.UUIDBased;
27 25 import org.thingsboard.server.common.data.id.UserId;
28 26 import org.thingsboard.server.common.data.page.PageData;
29 27 import org.thingsboard.server.common.data.page.TimePageLink;
... ... @@ -49,5 +47,4 @@ public interface AuditLogService {
49 47 E entity,
50 48 ActionType actionType,
51 49 Exception e, Object... additionalInfo);
52   -
53 50 }
... ...
... ... @@ -76,4 +76,6 @@ public interface DeviceService {
76 76
77 77 ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId);
78 78
  79 + Device assignDeviceToTenant(TenantId tenantId, Device device);
  80 +
79 81 }
... ...
... ... @@ -41,4 +41,6 @@ public interface EventService {
41 41
42 42 List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit);
43 43
  44 + void removeEvents(TenantId tenantId, EntityId entityId);
  45 +
44 46 }
... ...
... ... @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
24 24 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
25 25
26 26 import java.util.List;
27   -import java.util.concurrent.ExecutionException;
28 27
29 28 /**
30 29 * Created by ashvayka on 27.04.17.
... ... @@ -77,6 +76,8 @@ public interface RelationService {
77 76
78 77 ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query);
79 78
  79 + void removeRelations(TenantId tenantId, EntityId entityId);
  80 +
80 81 // TODO: This method may be useful for some validations in the future
81 82 // ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);
82 83
... ...
... ... @@ -21,6 +21,6 @@ import java.lang.annotation.Retention;
21 21 import java.lang.annotation.RetentionPolicy;
22 22
23 23 @Retention(RetentionPolicy.RUNTIME)
24   -@ConditionalOnExpression("'${database.ts.type}'=='cassandra' || '${database.entities.type}'=='cassandra'")
  24 +@ConditionalOnExpression("'${database.ts.type}'=='cassandra' || '${database.ts_latest.type}'=='cassandra'")
25 25 public @interface NoSqlAnyDao {
26 26 }
... ...
common/dao-api/src/main/java/org/thingsboard/server/dao/util/NoSqlTsLatestDao.java renamed from common/dao-api/src/main/java/org/thingsboard/server/dao/util/SqlDao.java
... ... @@ -15,9 +15,12 @@
15 15 */
16 16 package org.thingsboard.server.dao.util;
17 17
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  19 +
18 20 import java.lang.annotation.Retention;
19 21 import java.lang.annotation.RetentionPolicy;
20 22
21 23 @Retention(RetentionPolicy.RUNTIME)
22   -public @interface SqlDao {
  24 +@ConditionalOnProperty(prefix = "database.ts_latest", value = "type", havingValue = "cassandra")
  25 +public @interface NoSqlTsLatestDao {
23 26 }
... ...
common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsLatestAnyDao.java renamed from common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsAnyDao.java
... ... @@ -21,7 +21,7 @@ import java.lang.annotation.Retention;
21 21 import java.lang.annotation.RetentionPolicy;
22 22
23 23 @Retention(RetentionPolicy.RUNTIME)
24   -@ConditionalOnExpression("('${database.ts.type}'=='sql' || '${database.ts.type}'=='timescale') " +
  24 +@ConditionalOnExpression("('${database.ts_latest.type}'=='sql' || '${database.ts_latest.type}'=='timescale') " +
25 25 "&& '${spring.jpa.database-platform}'=='org.hibernate.dialect.PostgreSQLDialect'")
26   -public @interface PsqlTsAnyDao {
  26 +public @interface PsqlTsLatestAnyDao {
27 27 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.util;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +
  20 +import java.lang.annotation.Retention;
  21 +import java.lang.annotation.RetentionPolicy;
  22 +
  23 +@Retention(RetentionPolicy.RUNTIME)
  24 +@ConditionalOnExpression("'${database.ts_latest.type}'=='sql' || '${database.ts_latest.type}'=='timescale'")
  25 +public @interface SqlTsLatestAnyDao {
  26 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.util;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  19 +
  20 +import java.lang.annotation.Retention;
  21 +import java.lang.annotation.RetentionPolicy;
  22 +
  23 +@Retention(RetentionPolicy.RUNTIME)
  24 +@ConditionalOnProperty(prefix = "database.ts_latest", value = "type", havingValue = "sql")
  25 +public @interface SqlTsLatestDao {
  26 +}
... ...
common/dao-api/src/main/java/org/thingsboard/server/dao/util/SqlTsOrTsLatestAnyDao.java renamed from common/dao-api/src/main/java/org/thingsboard/server/dao/util/SqlTsAnyDao.java
... ... @@ -21,6 +21,6 @@ import java.lang.annotation.Retention;
21 21 import java.lang.annotation.RetentionPolicy;
22 22
23 23 @Retention(RetentionPolicy.RUNTIME)
24   -@ConditionalOnExpression("'${database.ts.type}'=='sql' || '${database.ts.type}'=='timescale'")
25   -public @interface SqlTsAnyDao {
  24 +@ConditionalOnExpression("'${database.ts.type}'=='sql' || '${database.ts.type}'=='timescale' || '${database.ts_latest.type}'=='sql' || '${database.ts_latest.type}'=='timescale'")
  25 +public @interface SqlTsOrTsLatestAnyDao {
26 26 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.util;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  19 +
  20 +import java.lang.annotation.Retention;
  21 +import java.lang.annotation.RetentionPolicy;
  22 +
  23 +@Retention(RetentionPolicy.RUNTIME)
  24 +@ConditionalOnProperty(prefix = "database.ts_latest", value = "type", havingValue = "timescale")
  25 +public @interface TimescaleDBTsLatestDao {
  26 +}
... ...
common/dao-api/src/main/java/org/thingsboard/server/dao/util/TimescaleDBTsOrTsLatestDao.java renamed from common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsDao.java
... ... @@ -21,5 +21,6 @@ import java.lang.annotation.Retention;
21 21 import java.lang.annotation.RetentionPolicy;
22 22
23 23 @Retention(RetentionPolicy.RUNTIME)
24   -@ConditionalOnExpression("'${database.ts.type}'=='sql' && '${spring.jpa.database-platform}'=='org.hibernate.dialect.PostgreSQLDialect'")
25   -public @interface PsqlTsDao { }
\ No newline at end of file
  24 +@ConditionalOnExpression("'${database.ts.type}'=='timescale' || '${database.ts_latest.type}'=='timescale'")
  25 +public @interface TimescaleDBTsOrTsLatestDao {
  26 +}
... ...
... ... @@ -57,6 +57,8 @@ public class DataConstants {
57 57 public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED";
58 58 public static final String ALARM_ACK = "ALARM_ACK";
59 59 public static final String ALARM_CLEAR = "ALARM_CLEAR";
  60 + public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT";
  61 + public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT";
60 62
61 63 public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE";
62 64
... ...
... ... @@ -40,7 +40,9 @@ public enum ActionType {
40 40 ALARM_CLEAR(false),
41 41 LOGIN(false),
42 42 LOGOUT(false),
43   - LOCKOUT(false);
  43 + LOCKOUT(false),
  44 + ASSIGNED_FROM_TENANT(false),
  45 + ASSIGNED_TO_TENANT(false);
44 46
45 47 private final boolean isRead;
46 48
... ...
... ... @@ -27,8 +27,8 @@ import org.thingsboard.server.dao.util.SqlTsDao;
27 27 @Configuration
28 28 @EnableAutoConfiguration
29 29 @ComponentScan({"org.thingsboard.server.dao.sqlts.hsql"})
30   -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.hsql", "org.thingsboard.server.dao.sqlts.insert.latest.hsql", "org.thingsboard.server.dao.sqlts.latest", "org.thingsboard.server.dao.sqlts.dictionary"})
31   -@EntityScan({"org.thingsboard.server.dao.model.sqlts.ts", "org.thingsboard.server.dao.model.sqlts.latest", "org.thingsboard.server.dao.model.sqlts.dictionary"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.hsql"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.ts"})
32 32 @EnableTransactionManagement
33 33 @SqlTsDao
34 34 @HsqlDao
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.domain.EntityScan;
  20 +import org.springframework.context.annotation.ComponentScan;
  21 +import org.springframework.context.annotation.Configuration;
  22 +import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  23 +import org.springframework.transaction.annotation.EnableTransactionManagement;
  24 +import org.thingsboard.server.dao.util.HsqlDao;
  25 +import org.thingsboard.server.dao.util.SqlTsLatestDao;
  26 +
  27 +@Configuration
  28 +@EnableAutoConfiguration
  29 +@ComponentScan({"org.thingsboard.server.dao.sqlts.hsql"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.hsql", "org.thingsboard.server.dao.sqlts.latest"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.latest"})
  32 +@EnableTransactionManagement
  33 +@SqlTsLatestDao
  34 +@HsqlDao
  35 +public class HsqlTsLatestDaoConfig {
  36 +
  37 +}
... ...
... ... @@ -21,7 +21,6 @@ import org.springframework.context.annotation.ComponentScan;
21 21 import org.springframework.context.annotation.Configuration;
22 22 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
23 23 import org.springframework.transaction.annotation.EnableTransactionManagement;
24   -import org.thingsboard.server.dao.util.SqlDao;
25 24
26 25 /**
27 26 * @author Valerii Sosliuk
... ... @@ -32,7 +31,6 @@ import org.thingsboard.server.dao.util.SqlDao;
32 31 @EnableJpaRepositories("org.thingsboard.server.dao.sql")
33 32 @EntityScan("org.thingsboard.server.dao.model.sql")
34 33 @EnableTransactionManagement
35   -@SqlDao
36 34 public class JpaDaoConfig {
37 35
38 36 }
... ...
... ... @@ -27,11 +27,11 @@ import org.thingsboard.server.dao.util.SqlTsDao;
27 27 @Configuration
28 28 @EnableAutoConfiguration
29 29 @ComponentScan({"org.thingsboard.server.dao.sqlts.psql"})
30   -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.psql", "org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.latest", "org.thingsboard.server.dao.sqlts.dictionary"})
31   -@EntityScan({"org.thingsboard.server.dao.model.sqlts.ts", "org.thingsboard.server.dao.model.sqlts.latest", "org.thingsboard.server.dao.model.sqlts.dictionary"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.psql"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.ts"})
32 32 @EnableTransactionManagement
33   -@SqlTsDao
34 33 @PsqlDao
  34 +@SqlTsDao
35 35 public class PsqlTsDaoConfig {
36 36
37 37 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.domain.EntityScan;
  20 +import org.springframework.context.annotation.ComponentScan;
  21 +import org.springframework.context.annotation.Configuration;
  22 +import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  23 +import org.springframework.transaction.annotation.EnableTransactionManagement;
  24 +import org.thingsboard.server.dao.util.PsqlDao;
  25 +import org.thingsboard.server.dao.util.SqlTsLatestDao;
  26 +
  27 +@Configuration
  28 +@EnableAutoConfiguration
  29 +@ComponentScan({"org.thingsboard.server.dao.sqlts.psql"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.latest"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.latest"})
  32 +@EnableTransactionManagement
  33 +@SqlTsLatestDao
  34 +@PsqlDao
  35 +public class PsqlTsLatestDaoConfig {
  36 +
  37 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.domain.EntityScan;
  20 +import org.springframework.context.annotation.Configuration;
  21 +import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  22 +import org.springframework.transaction.annotation.EnableTransactionManagement;
  23 +import org.thingsboard.server.dao.util.PsqlDao;
  24 +import org.thingsboard.server.dao.util.SqlTsOrTsLatestAnyDao;
  25 +
  26 +@Configuration
  27 +@EnableAutoConfiguration
  28 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.dictionary"})
  29 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.dictionary"})
  30 +@EnableTransactionManagement
  31 +@SqlTsOrTsLatestAnyDao
  32 +public class SqlTimeseriesDaoConfig {
  33 +
  34 +}
... ...
... ... @@ -27,8 +27,8 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao;
27 27 @Configuration
28 28 @EnableAutoConfiguration
29 29 @ComponentScan({"org.thingsboard.server.dao.sqlts.timescale"})
30   -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.timescale", "org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.insert.timescale", "org.thingsboard.server.dao.sqlts.dictionary", "org.thingsboard.server.dao.sqlts.latest"})
31   -@EntityScan({"org.thingsboard.server.dao.model.sqlts.timescale", "org.thingsboard.server.dao.model.sqlts.dictionary", "org.thingsboard.server.dao.model.sqlts.latest"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.timescale", "org.thingsboard.server.dao.sqlts.insert.timescale"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.timescale"})
32 32 @EnableTransactionManagement
33 33 @TimescaleDBTsDao
34 34 @PsqlDao
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.domain.EntityScan;
  20 +import org.springframework.context.annotation.ComponentScan;
  21 +import org.springframework.context.annotation.Configuration;
  22 +import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  23 +import org.springframework.transaction.annotation.EnableTransactionManagement;
  24 +import org.thingsboard.server.dao.util.PsqlDao;
  25 +import org.thingsboard.server.dao.util.TimescaleDBTsLatestDao;
  26 +
  27 +@Configuration
  28 +@EnableAutoConfiguration
  29 +@ComponentScan({"org.thingsboard.server.dao.sqlts.timescale"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.latest"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.latest"})
  32 +@EnableTransactionManagement
  33 +@TimescaleDBTsLatestDao
  34 +@PsqlDao
  35 +public class TimescaleTsLatestDaoConfig {
  36 +
  37 +}
... ...
... ... @@ -163,7 +163,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
163 163 try {
164 164 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get();
165 165 if (entityViews != null && !entityViews.isEmpty()) {
166   - throw new DataValidationException("Can't delete asset that is assigned to entity views!");
  166 + throw new DataValidationException("Can't delete asset that has entity views!");
167 167 }
168 168 } catch (ExecutionException | InterruptedException e) {
169 169 log.error("Exception while finding entity views for assetId [{}]", assetId, e);
... ...
... ... @@ -23,11 +23,12 @@ import org.thingsboard.server.common.data.id.EntityId;
23 23 import org.thingsboard.server.common.data.id.UserId;
24 24 import org.thingsboard.server.common.data.page.PageData;
25 25 import org.thingsboard.server.common.data.page.TimePageLink;
  26 +import org.thingsboard.server.dao.Dao;
26 27
27 28 import java.util.List;
28 29 import java.util.UUID;
29 30
30   -public interface AuditLogDao {
  31 +public interface AuditLogDao extends Dao<AuditLog> {
31 32
32 33 ListenableFuture<Void> saveByTenantId(AuditLog auditLog);
33 34
... ...
... ... @@ -163,6 +163,7 @@ public class AuditLogServiceImpl implements AuditLogService {
163 163 case ALARM_ACK:
164 164 case ALARM_CLEAR:
165 165 case RELATIONS_DELETED:
  166 + case ASSIGNED_TO_TENANT:
166 167 if (entity != null) {
167 168 ObjectNode entityNode = objectMapper.valueToTree(entity);
168 169 if (entityId.getEntityType() == EntityType.DASHBOARD) {
... ...
... ... @@ -58,5 +58,4 @@ public class DummyAuditLogServiceImpl implements AuditLogService {
58 58 public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>> logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) {
59 59 return null;
60 60 }
61   -
62 61 }
... ...
... ... @@ -166,4 +166,21 @@ public interface DeviceDao extends Dao<Device> {
166 166 * @return the list of tenant device type objects
167 167 */
168 168 ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId);
  169 +
  170 + /**
  171 + * Find devices by tenantId and device id.
  172 + * @param tenantId the tenant Id
  173 + * @param id the device Id
  174 + * @return the device object
  175 + */
  176 + Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id);
  177 +
  178 + /**
  179 + * Find devices by tenantId and device id.
  180 + * @param tenantId tenantId the tenantId
  181 + * @param id the deviceId
  182 + * @return the device object
  183 + */
  184 + ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id);
  185 +
169 186 }
... ...
... ... @@ -28,6 +28,8 @@ import org.springframework.cache.CacheManager;
28 28 import org.springframework.cache.annotation.CacheEvict;
29 29 import org.springframework.cache.annotation.Cacheable;
30 30 import org.springframework.stereotype.Service;
  31 +import org.springframework.transaction.annotation.Transactional;
  32 +import org.springframework.util.CollectionUtils;
31 33 import org.springframework.util.StringUtils;
32 34 import org.thingsboard.server.common.data.Customer;
33 35 import org.thingsboard.server.common.data.Device;
... ... @@ -50,6 +52,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
50 52 import org.thingsboard.server.dao.customer.CustomerDao;
51 53 import org.thingsboard.server.dao.entity.AbstractEntityService;
52 54 import org.thingsboard.server.dao.entityview.EntityViewService;
  55 +import org.thingsboard.server.dao.event.EventService;
53 56 import org.thingsboard.server.dao.exception.DataValidationException;
54 57 import org.thingsboard.server.dao.service.DataValidator;
55 58 import org.thingsboard.server.dao.service.PaginatedRemover;
... ... @@ -98,6 +101,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
98 101 @Autowired
99 102 private CacheManager cacheManager;
100 103
  104 + @Autowired
  105 + private EventService eventService;
  106 +
101 107 @Override
102 108 public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) {
103 109 log.trace("Executing findDeviceInfoById [{}]", deviceId);
... ... @@ -109,14 +115,22 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
109 115 public Device findDeviceById(TenantId tenantId, DeviceId deviceId) {
110 116 log.trace("Executing findDeviceById [{}]", deviceId);
111 117 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
112   - return deviceDao.findById(tenantId, deviceId.getId());
  118 + if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
  119 + return deviceDao.findById(tenantId, deviceId.getId());
  120 + } else {
  121 + return deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId());
  122 + }
113 123 }
114 124
115 125 @Override
116 126 public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) {
117 127 log.trace("Executing findDeviceById [{}]", deviceId);
118 128 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
119   - return deviceDao.findByIdAsync(tenantId, deviceId.getId());
  129 + if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
  130 + return deviceDao.findByIdAsync(tenantId, deviceId.getId());
  131 + } else {
  132 + return deviceDao.findDeviceByTenantIdAndIdAsync(tenantId, deviceId.getId());
  133 + }
120 134 }
121 135
122 136 @Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}")
... ... @@ -187,7 +201,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
187 201 try {
188 202 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get();
189 203 if (entityViews != null && !entityViews.isEmpty()) {
190   - throw new DataValidationException("Can't delete device that is assigned to entity views!");
  204 + throw new DataValidationException("Can't delete device that has entity views!");
191 205 }
192 206 } catch (ExecutionException | InterruptedException e) {
193 207 log.error("Exception while finding entity views for deviceId [{}]", deviceId, e);
... ... @@ -353,6 +367,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
353 367 }, MoreExecutors.directExecutor());
354 368 }
355 369
  370 + @Transactional
  371 + @CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
  372 + @Override
  373 + public Device assignDeviceToTenant(TenantId tenantId, Device device) {
  374 + log.trace("Executing assignDeviceToTenant [{}][{}]", tenantId, device);
  375 +
  376 + try {
  377 + List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), device.getId()).get();
  378 + if (!CollectionUtils.isEmpty(entityViews)) {
  379 + throw new DataValidationException("Can't assign device that has entity views to another tenant!");
  380 + }
  381 + } catch (ExecutionException | InterruptedException e) {
  382 + log.error("Exception while finding entity views for deviceId [{}]", device.getId(), e);
  383 + throw new RuntimeException("Exception while finding entity views for deviceId [" + device.getId() + "]", e);
  384 + }
  385 +
  386 + eventService.removeEvents(device.getTenantId(), device.getId());
  387 +
  388 + relationService.removeRelations(device.getTenantId(), device.getId());
  389 +
  390 + device.setTenantId(tenantId);
  391 + device.setCustomerId(null);
  392 + return doSaveDevice(device, null);
  393 + }
  394 +
356 395 private DataValidator<Device> deviceValidator =
357 396 new DataValidator<Device>() {
358 397
... ...
... ... @@ -92,6 +92,21 @@ public class BaseEventService implements EventService {
92 92 return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit);
93 93 }
94 94
  95 + @Override
  96 + public void removeEvents(TenantId tenantId, EntityId entityId) {
  97 + PageData<Event> eventPageData;
  98 + TimePageLink eventPageLink = new TimePageLink(1000);
  99 + do {
  100 + eventPageData = findEvents(tenantId, entityId, eventPageLink);
  101 + for (Event event : eventPageData.getData()) {
  102 + eventDao.removeById(tenantId, event.getUuidId());
  103 + }
  104 + if (eventPageData.hasNext()) {
  105 + eventPageLink = eventPageLink.nextPageLink();
  106 + }
  107 + } while (eventPageData.hasNext());
  108 + }
  109 +
95 110 private DataValidator<Event> eventValidator =
96 111 new DataValidator<Event>() {
97 112 @Override
... ...
... ... @@ -506,6 +506,22 @@ public class BaseRelationService implements RelationService {
506 506 }, MoreExecutors.directExecutor());
507 507 }
508 508
  509 + @Override
  510 + public void removeRelations(TenantId tenantId, EntityId entityId) {
  511 + Cache cache = cacheManager.getCache(RELATIONS_CACHE);
  512 +
  513 + List<EntityRelation> relations = new ArrayList<>();
  514 + for (RelationTypeGroup relationTypeGroup : RelationTypeGroup.values()) {
  515 + relations.addAll(findByFrom(tenantId, entityId, relationTypeGroup));
  516 + relations.addAll(findByTo(tenantId, entityId, relationTypeGroup));
  517 + }
  518 +
  519 + for (EntityRelation relation : relations) {
  520 + cacheEviction(relation, cache);
  521 + deleteRelation(tenantId, relation);
  522 + }
  523 + }
  524 +
509 525 protected void validate(EntityRelation relation) {
510 526 if (relation == null) {
511 527 throw new DataValidationException("Relation type should be specified!");
... ...
... ... @@ -18,10 +18,8 @@ package org.thingsboard.server.dao.sql;
18 18 import org.springframework.beans.factory.annotation.Value;
19 19 import org.springframework.stereotype.Component;
20 20 import org.thingsboard.common.util.AbstractListeningExecutor;
21   -import org.thingsboard.server.dao.util.SqlDao;
22 21
23 22 @Component
24   -@SqlDao
25 23 public class JpaExecutorService extends AbstractListeningExecutor {
26 24
27 25 @Value("${spring.datasource.hikari.maximumPoolSize}")
... ...
... ... @@ -21,25 +21,15 @@ import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.CrudRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.common.data.alarm.AlarmStatus;
24   -import org.thingsboard.server.common.data.id.CustomerId;
25   -import org.thingsboard.server.common.data.id.EntityId;
26   -import org.thingsboard.server.common.data.id.TenantId;
27   -import org.thingsboard.server.common.data.page.PageData;
28   -import org.thingsboard.server.common.data.query.AlarmData;
29   -import org.thingsboard.server.common.data.query.AlarmDataQuery;
30 24 import org.thingsboard.server.dao.model.sql.AlarmEntity;
31 25 import org.thingsboard.server.dao.model.sql.AlarmInfoEntity;
32   -import org.thingsboard.server.dao.util.SqlDao;
33 26
34   -import java.util.Collection;
35 27 import java.util.List;
36   -import java.util.Set;
37 28 import java.util.UUID;
38 29
39 30 /**
40 31 * Created by Valerii Sosliuk on 5/21/2017.
41 32 */
42   -@SqlDao
43 33 public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> {
44 34
45 35 @Query("SELECT a FROM AlarmEntity a WHERE a.originatorId = :originatorId AND a.type = :alarmType ORDER BY a.startTs DESC")
... ...
... ... @@ -39,7 +39,6 @@ import org.thingsboard.server.dao.model.sql.AlarmEntity;
39 39 import org.thingsboard.server.dao.relation.RelationDao;
40 40 import org.thingsboard.server.dao.sql.JpaAbstractDao;
41 41 import org.thingsboard.server.dao.sql.query.AlarmQueryRepository;
42   -import org.thingsboard.server.dao.util.SqlDao;
43 42
44 43 import java.util.ArrayList;
45 44 import java.util.Collection;
... ... @@ -54,7 +53,6 @@ import java.util.UUID;
54 53 */
55 54 @Slf4j
56 55 @Component
57   -@SqlDao
58 56 public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements AlarmDao {
59 57
60 58 @Autowired
... ...
... ... @@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.dao.model.sql.AssetEntity;
24 24 import org.thingsboard.server.dao.model.sql.AssetInfoEntity;
25   -import org.thingsboard.server.dao.util.SqlDao;
26 25
27 26 import java.util.List;
28 27 import java.util.UUID;
... ... @@ -30,7 +29,6 @@ import java.util.UUID;
30 29 /**
31 30 * Created by Valerii Sosliuk on 5/21/2017.
32 31 */
33   -@SqlDao
34 32 public interface AssetRepository extends PagingAndSortingRepository<AssetEntity, UUID> {
35 33
36 34 @Query("SELECT new org.thingsboard.server.dao.model.sql.AssetInfoEntity(a, c.title, c.additionalInfo) " +
... ...
... ... @@ -31,7 +31,6 @@ import org.thingsboard.server.dao.asset.AssetDao;
31 31 import org.thingsboard.server.dao.model.sql.AssetEntity;
32 32 import org.thingsboard.server.dao.model.sql.AssetInfoEntity;
33 33 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
34   -import org.thingsboard.server.dao.util.SqlDao;
35 34
36 35 import java.util.ArrayList;
37 36 import java.util.Collections;
... ... @@ -44,7 +43,6 @@ import java.util.UUID;
44 43 * Created by Valerii Sosliuk on 5/19/2017.
45 44 */
46 45 @Component
47   -@SqlDao
48 46 public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> implements AssetDao {
49 47
50 48 @Autowired
... ...
... ... @@ -25,7 +25,6 @@ import org.springframework.transaction.TransactionStatus;
25 25 import org.springframework.transaction.support.TransactionCallbackWithoutResult;
26 26 import org.springframework.transaction.support.TransactionTemplate;
27 27 import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
28   -import org.thingsboard.server.dao.util.SqlDao;
29 28
30 29 import java.sql.PreparedStatement;
31 30 import java.sql.SQLException;
... ... @@ -35,7 +34,6 @@ import java.util.ArrayList;
35 34 import java.util.List;
36 35 import java.util.regex.Pattern;
37 36
38   -@SqlDao
39 37 @Repository
40 38 @Slf4j
41 39 public abstract class AttributeKvInsertRepository {
... ...
... ... @@ -23,12 +23,10 @@ import org.springframework.transaction.annotation.Transactional;
23 23 import org.thingsboard.server.common.data.EntityType;
24 24 import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey;
25 25 import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
26   -import org.thingsboard.server.dao.util.SqlDao;
27 26
28 27 import java.util.List;
29 28 import java.util.UUID;
30 29
31   -@SqlDao
32 30 public interface AttributeKvRepository extends CrudRepository<AttributeKvEntity, AttributeKvCompositeKey> {
33 31
34 32 @Query("SELECT a FROM AttributeKvEntity a WHERE a.id.entityType = :entityType " +
... ...
... ... @@ -19,12 +19,10 @@ import org.springframework.stereotype.Repository;
19 19 import org.springframework.transaction.annotation.Transactional;
20 20 import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
21 21 import org.thingsboard.server.dao.util.HsqlDao;
22   -import org.thingsboard.server.dao.util.SqlDao;
23 22
24 23 import java.sql.Types;
25 24 import java.util.List;
26 25
27   -@SqlDao
28 26 @HsqlDao
29 27 @Repository
30 28 @Transactional
... ...
... ... @@ -34,7 +34,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
34 34 import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
35 35 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
36 36 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;
37   -import org.thingsboard.server.dao.util.SqlDao;
38 37
39 38 import javax.annotation.PostConstruct;
40 39 import javax.annotation.PreDestroy;
... ... @@ -46,7 +45,6 @@ import java.util.stream.Collectors;
46 45
47 46 @Component
48 47 @Slf4j
49   -@SqlDao
50 48 public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService implements AttributesDao {
51 49
52 50 @Autowired
... ...
... ... @@ -18,9 +18,7 @@ package org.thingsboard.server.dao.sql.attributes;
18 18 import org.springframework.stereotype.Repository;
19 19 import org.springframework.transaction.annotation.Transactional;
20 20 import org.thingsboard.server.dao.util.PsqlDao;
21   -import org.thingsboard.server.dao.util.SqlDao;
22 21
23   -@SqlDao
24 22 @PsqlDao
25 23 @Repository
26 24 @Transactional
... ...
... ... @@ -30,14 +30,12 @@ import org.thingsboard.server.dao.DaoUtil;
30 30 import org.thingsboard.server.dao.audit.AuditLogDao;
31 31 import org.thingsboard.server.dao.model.sql.AuditLogEntity;
32 32 import org.thingsboard.server.dao.sql.JpaAbstractDao;
33   -import org.thingsboard.server.dao.util.SqlDao;
34 33
35 34 import java.util.List;
36 35 import java.util.Objects;
37 36 import java.util.UUID;
38 37
39 38 @Component
40   -@SqlDao
41 39 public class JpaAuditLogDao extends JpaAbstractDao<AuditLogEntity, AuditLog> implements AuditLogDao {
42 40
43 41 @Autowired
... ...
... ... @@ -23,14 +23,12 @@ import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.common.data.plugin.ComponentScope;
24 24 import org.thingsboard.server.common.data.plugin.ComponentType;
25 25 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity;
26   -import org.thingsboard.server.dao.util.SqlDao;
27 26
28 27 import java.util.UUID;
29 28
30 29 /**
31 30 * Created by Valerii Sosliuk on 5/6/2017.
32 31 */
33   -@SqlDao
34 32 public interface ComponentDescriptorRepository extends PagingAndSortingRepository<ComponentDescriptorEntity, UUID> {
35 33
36 34 ComponentDescriptorEntity findByClazz(String clazz);
... ...
... ... @@ -18,11 +18,9 @@ package org.thingsboard.server.dao.sql.component;
18 18 import org.springframework.stereotype.Repository;
19 19 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity;
20 20 import org.thingsboard.server.dao.util.HsqlDao;
21   -import org.thingsboard.server.dao.util.SqlDao;
22 21
23 22 import javax.persistence.Query;
24 23
25   -@SqlDao
26 24 @HsqlDao
27 25 @Repository
28 26 public class HsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository {
... ...
... ... @@ -31,7 +31,6 @@ import org.thingsboard.server.dao.DaoUtil;
31 31 import org.thingsboard.server.dao.component.ComponentDescriptorDao;
32 32 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity;
33 33 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
34   -import org.thingsboard.server.dao.util.SqlDao;
35 34
36 35 import java.util.Objects;
37 36 import java.util.Optional;
... ... @@ -41,7 +40,6 @@ import java.util.UUID;
41 40 * Created by Valerii Sosliuk on 5/6/2017.
42 41 */
43 42 @Component
44   -@SqlDao
45 43 public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao<ComponentDescriptorEntity, ComponentDescriptor>
46 44 implements ComponentDescriptorDao {
47 45
... ...
... ... @@ -18,9 +18,7 @@ package org.thingsboard.server.dao.sql.component;
18 18 import org.springframework.stereotype.Repository;
19 19 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity;
20 20 import org.thingsboard.server.dao.util.PsqlDao;
21   -import org.thingsboard.server.dao.util.SqlDao;
22 21
23   -@SqlDao
24 22 @PsqlDao
25 23 @Repository
26 24 public class PsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository {
... ...
... ... @@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.dao.model.sql.CustomerEntity;
24   -import org.thingsboard.server.dao.util.SqlDao;
25 24
26 25 import java.util.UUID;
27 26
28 27 /**
29 28 * Created by Valerii Sosliuk on 5/6/2017.
30 29 */
31   -@SqlDao
32 30 public interface CustomerRepository extends PagingAndSortingRepository<CustomerEntity, UUID> {
33 31
34 32 @Query("SELECT c FROM CustomerEntity c WHERE c.tenantId = :tenantId " +
... ...
... ... @@ -25,7 +25,6 @@ import org.thingsboard.server.dao.DaoUtil;
25 25 import org.thingsboard.server.dao.customer.CustomerDao;
26 26 import org.thingsboard.server.dao.model.sql.CustomerEntity;
27 27 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
28   -import org.thingsboard.server.dao.util.SqlDao;
29 28
30 29 import java.util.Objects;
31 30 import java.util.Optional;
... ... @@ -35,7 +34,6 @@ import java.util.UUID;
35 34 * Created by Valerii Sosliuk on 5/6/2017.
36 35 */
37 36 @Component
38   -@SqlDao
39 37 public class JpaCustomerDao extends JpaAbstractSearchTextDao<CustomerEntity, Customer> implements CustomerDao {
40 38
41 39 @Autowired
... ...
... ... @@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.dao.model.sql.DashboardInfoEntity;
24   -import org.thingsboard.server.dao.util.SqlDao;
25 24
26 25 import java.util.UUID;
27 26
28 27 /**
29 28 * Created by Valerii Sosliuk on 5/6/2017.
30 29 */
31   -@SqlDao
32 30 public interface DashboardInfoRepository extends PagingAndSortingRepository<DashboardInfoEntity, UUID> {
33 31
34 32 @Query("SELECT di FROM DashboardInfoEntity di WHERE di.tenantId = :tenantId " +
... ...
... ... @@ -17,13 +17,11 @@ package org.thingsboard.server.dao.sql.dashboard;
17 17
18 18 import org.springframework.data.repository.CrudRepository;
19 19 import org.thingsboard.server.dao.model.sql.DashboardEntity;
20   -import org.thingsboard.server.dao.util.SqlDao;
21 20
22 21 import java.util.UUID;
23 22
24 23 /**
25 24 * Created by Valerii Sosliuk on 5/6/2017.
26 25 */
27   -@SqlDao
28 26 public interface DashboardRepository extends CrudRepository<DashboardEntity, UUID> {
29 27 }
... ...
... ... @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.Dashboard;
22 22 import org.thingsboard.server.dao.dashboard.DashboardDao;
23 23 import org.thingsboard.server.dao.model.sql.DashboardEntity;
24 24 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
25   -import org.thingsboard.server.dao.util.SqlDao;
26 25
27 26 import java.util.UUID;
28 27
... ... @@ -30,7 +29,6 @@ import java.util.UUID;
30 29 * Created by Valerii Sosliuk on 5/6/2017.
31 30 */
32 31 @Component
33   -@SqlDao
34 32 public class JpaDashboardDao extends JpaAbstractSearchTextDao<DashboardEntity, Dashboard> implements DashboardDao {
35 33
36 34 @Autowired
... ...
... ... @@ -27,7 +27,6 @@ import org.thingsboard.server.dao.dashboard.DashboardInfoDao;
27 27 import org.thingsboard.server.dao.model.sql.DashboardInfoEntity;
28 28 import org.thingsboard.server.dao.relation.RelationDao;
29 29 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
30   -import org.thingsboard.server.dao.util.SqlDao;
31 30
32 31 import java.util.Objects;
33 32 import java.util.UUID;
... ... @@ -37,7 +36,6 @@ import java.util.UUID;
37 36 */
38 37 @Slf4j
39 38 @Component
40   -@SqlDao
41 39 public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao {
42 40
43 41 @Autowired
... ...
... ... @@ -17,14 +17,12 @@ package org.thingsboard.server.dao.sql.device;
17 17
18 18 import org.springframework.data.repository.CrudRepository;
19 19 import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity;
20   -import org.thingsboard.server.dao.util.SqlDao;
21 20
22 21 import java.util.UUID;
23 22
24 23 /**
25 24 * Created by Valerii Sosliuk on 5/6/2017.
26 25 */
27   -@SqlDao
28 26 public interface DeviceCredentialsRepository extends CrudRepository<DeviceCredentialsEntity, UUID> {
29 27
30 28 DeviceCredentialsEntity findByDeviceId(UUID deviceId);
... ...
... ... @@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.dao.model.sql.DeviceEntity;
24 24 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity;
25   -import org.thingsboard.server.dao.util.SqlDao;
26 25
27 26 import java.util.List;
28 27 import java.util.UUID;
... ... @@ -30,7 +29,6 @@ import java.util.UUID;
30 29 /**
31 30 * Created by Valerii Sosliuk on 5/6/2017.
32 31 */
33   -@SqlDao
34 32 public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntity, UUID> {
35 33
36 34 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo) " +
... ... @@ -127,4 +125,7 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
127 125 List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List<UUID> deviceIds);
128 126
129 127 List<DeviceEntity> findDevicesByTenantIdAndIdIn(UUID tenantId, List<UUID> deviceIds);
  128 +
  129 + DeviceEntity findByTenantIdAndId(UUID tenantId, UUID id);
  130 +
130 131 }
... ...
... ... @@ -24,7 +24,6 @@ import org.thingsboard.server.dao.DaoUtil;
24 24 import org.thingsboard.server.dao.device.DeviceCredentialsDao;
25 25 import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity;
26 26 import org.thingsboard.server.dao.sql.JpaAbstractDao;
27   -import org.thingsboard.server.dao.util.SqlDao;
28 27
29 28 import java.util.UUID;
30 29
... ... @@ -32,7 +31,6 @@ import java.util.UUID;
32 31 * Created by Valerii Sosliuk on 5/6/2017.
33 32 */
34 33 @Component
35   -@SqlDao
36 34 public class JpaDeviceCredentialsDao extends JpaAbstractDao<DeviceCredentialsEntity, DeviceCredentials> implements DeviceCredentialsDao {
37 35
38 36 @Autowired
... ...
... ... @@ -32,7 +32,6 @@ import org.thingsboard.server.dao.device.DeviceDao;
32 32 import org.thingsboard.server.dao.model.sql.DeviceEntity;
33 33 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity;
34 34 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
35   -import org.thingsboard.server.dao.util.SqlDao;
36 35
37 36 import java.util.ArrayList;
38 37 import java.util.Collections;
... ... @@ -45,7 +44,6 @@ import java.util.UUID;
45 44 * Created by Valerii Sosliuk on 5/6/2017.
46 45 */
47 46 @Component
48   -@SqlDao
49 47 public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> implements DeviceDao {
50 48
51 49 @Autowired
... ... @@ -175,6 +173,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
175 173 return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(tenantId)));
176 174 }
177 175
  176 + @Override
  177 + public Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id) {
  178 + return DaoUtil.getData(deviceRepository.findByTenantIdAndId(tenantId.getId(), id));
  179 + }
  180 +
  181 + @Override
  182 + public ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id) {
  183 + return service.submit(() -> DaoUtil.getData(deviceRepository.findByTenantIdAndId(tenantId.getId(), id)));
  184 + }
  185 +
178 186 private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) {
179 187 List<EntitySubtype> list = Collections.emptyList();
180 188 if (types != null && !types.isEmpty()) {
... ...
... ... @@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.dao.model.sql.EntityViewEntity;
24 24 import org.thingsboard.server.dao.model.sql.EntityViewInfoEntity;
25   -import org.thingsboard.server.dao.util.SqlDao;
26 25
27 26 import java.util.List;
28 27 import java.util.UUID;
... ... @@ -30,7 +29,6 @@ import java.util.UUID;
30 29 /**
31 30 * Created by Victor Basanets on 8/31/2017.
32 31 */
33   -@SqlDao
34 32 public interface EntityViewRepository extends PagingAndSortingRepository<EntityViewEntity, UUID> {
35 33
36 34 @Query("SELECT new org.thingsboard.server.dao.model.sql.EntityViewInfoEntity(e, c.title, c.additionalInfo) " +
... ...
... ... @@ -31,7 +31,6 @@ import org.thingsboard.server.dao.entityview.EntityViewDao;
31 31 import org.thingsboard.server.dao.model.sql.EntityViewEntity;
32 32 import org.thingsboard.server.dao.model.sql.EntityViewInfoEntity;
33 33 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
34   -import org.thingsboard.server.dao.util.SqlDao;
35 34
36 35 import java.util.ArrayList;
37 36 import java.util.Collections;
... ... @@ -44,7 +43,6 @@ import java.util.UUID;
44 43 * Created by Victor Basanets on 8/31/2017.
45 44 */
46 45 @Component
47   -@SqlDao
48 46 public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity, EntityView>
49 47 implements EntityViewDao {
50 48
... ...
... ... @@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.common.data.EntityType;
24 24 import org.thingsboard.server.dao.model.sql.EventEntity;
25   -import org.thingsboard.server.dao.util.SqlDao;
26 25
27 26 import java.util.List;
28 27 import java.util.UUID;
... ... @@ -30,7 +29,6 @@ import java.util.UUID;
30 29 /**
31 30 * Created by Valerii Sosliuk on 5/3/2017.
32 31 */
33   -@SqlDao
34 32 public interface EventRepository extends PagingAndSortingRepository<EventEntity, UUID> {
35 33
36 34 EventEntity findByTenantIdAndEntityTypeAndEntityIdAndEventTypeAndEventUid(UUID tenantId,
... ...
... ... @@ -18,11 +18,9 @@ package org.thingsboard.server.dao.sql.event;
18 18 import org.springframework.stereotype.Repository;
19 19 import org.thingsboard.server.dao.model.sql.EventEntity;
20 20 import org.thingsboard.server.dao.util.HsqlDao;
21   -import org.thingsboard.server.dao.util.SqlDao;
22 21
23 22 import javax.persistence.Query;
24 23
25   -@SqlDao
26 24 @HsqlDao
27 25 @Repository
28 26 public class HsqlEventInsertRepository extends AbstractEventInsertRepository {
... ...
... ... @@ -21,7 +21,6 @@ import lombok.extern.slf4j.Slf4j;
21 21 import org.apache.commons.lang3.StringUtils;
22 22 import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.data.domain.PageRequest;
24   -import org.springframework.data.jpa.domain.Specification;
25 24 import org.springframework.data.repository.CrudRepository;
26 25 import org.springframework.stereotype.Component;
27 26 import org.thingsboard.server.common.data.Event;
... ... @@ -34,10 +33,7 @@ import org.thingsboard.server.dao.DaoUtil;
34 33 import org.thingsboard.server.dao.event.EventDao;
35 34 import org.thingsboard.server.dao.model.sql.EventEntity;
36 35 import org.thingsboard.server.dao.sql.JpaAbstractDao;
37   -import org.thingsboard.server.dao.util.SqlDao;
38 36
39   -import javax.persistence.criteria.Predicate;
40   -import java.util.ArrayList;
41 37 import java.util.List;
42 38 import java.util.Objects;
43 39 import java.util.Optional;
... ... @@ -50,7 +46,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
50 46 */
51 47 @Slf4j
52 48 @Component
53   -@SqlDao
54 49 public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implements EventDao {
55 50
56 51 private final UUID systemTenantId = NULL_UUID;
... ...
... ... @@ -19,10 +19,8 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.stereotype.Repository;
20 20 import org.thingsboard.server.dao.model.sql.EventEntity;
21 21 import org.thingsboard.server.dao.util.PsqlDao;
22   -import org.thingsboard.server.dao.util.SqlDao;
23 22
24 23 @Slf4j
25   -@SqlDao
26 24 @PsqlDao
27 25 @Repository
28 26 public class PsqlEventInsertRepository extends AbstractEventInsertRepository {
... ...
... ... @@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder;
39 39 import org.thingsboard.server.common.data.query.EntityKey;
40 40 import org.thingsboard.server.common.data.query.EntityKeyType;
41 41 import org.thingsboard.server.dao.model.ModelConstants;
42   -import org.thingsboard.server.dao.util.SqlDao;
43 42
44 43 import java.util.ArrayList;
45 44 import java.util.Arrays;
... ... @@ -52,7 +51,6 @@ import java.util.Objects;
52 51 import java.util.Set;
53 52 import java.util.stream.Collectors;
54 53
55   -@SqlDao
56 54 @Repository
57 55 @Slf4j
58 56 public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
... ...
... ... @@ -50,7 +50,6 @@ import org.thingsboard.server.common.data.query.RelationsQueryFilter;
50 50 import org.thingsboard.server.common.data.query.SingleEntityFilter;
51 51 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
52 52 import org.thingsboard.server.common.data.relation.EntityTypeFilter;
53   -import org.thingsboard.server.dao.util.SqlDao;
54 53
55 54 import java.util.Arrays;
56 55 import java.util.Collections;
... ... @@ -61,7 +60,6 @@ import java.util.Optional;
61 60 import java.util.UUID;
62 61 import java.util.stream.Collectors;
63 62
64   -@SqlDao
65 63 @Repository
66 64 @Slf4j
67 65 public class DefaultEntityQueryRepository implements EntityQueryRepository {
... ...
... ... @@ -24,10 +24,8 @@ import org.thingsboard.server.common.data.query.EntityCountQuery;
24 24 import org.thingsboard.server.common.data.query.EntityData;
25 25 import org.thingsboard.server.common.data.query.EntityDataQuery;
26 26 import org.thingsboard.server.dao.entity.EntityQueryDao;
27   -import org.thingsboard.server.dao.util.SqlDao;
28 27
29 28 @Component
30   -@SqlDao
31 29 public class JpaEntityQueryDao implements EntityQueryDao {
32 30
33 31 @Autowired
... ...
... ... @@ -20,12 +20,10 @@ import org.springframework.transaction.annotation.Transactional;
20 20 import org.thingsboard.server.dao.model.sql.RelationCompositeKey;
21 21 import org.thingsboard.server.dao.model.sql.RelationEntity;
22 22 import org.thingsboard.server.dao.util.HsqlDao;
23   -import org.thingsboard.server.dao.util.SqlDao;
24 23
25 24 import javax.persistence.Query;
26 25
27 26 @HsqlDao
28   -@SqlDao
29 27 @Repository
30 28 @Transactional
31 29 public class HsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository {
... ...
... ... @@ -30,7 +30,6 @@ import org.thingsboard.server.dao.model.sql.RelationCompositeKey;
30 30 import org.thingsboard.server.dao.model.sql.RelationEntity;
31 31 import org.thingsboard.server.dao.relation.RelationDao;
32 32 import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
33   -import org.thingsboard.server.dao.util.SqlDao;
34 33
35 34 import javax.persistence.criteria.Predicate;
36 35 import java.util.ArrayList;
... ... @@ -41,7 +40,6 @@ import java.util.List;
41 40 */
42 41 @Slf4j
43 42 @Component
44   -@SqlDao
45 43 public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService implements RelationDao {
46 44
47 45 @Autowired
... ...
... ... @@ -19,10 +19,8 @@ import org.springframework.stereotype.Repository;
19 19 import org.springframework.transaction.annotation.Transactional;
20 20 import org.thingsboard.server.dao.model.sql.RelationEntity;
21 21 import org.thingsboard.server.dao.util.PsqlDao;
22   -import org.thingsboard.server.dao.util.SqlDao;
23 22
24 23 @PsqlDao
25   -@SqlDao
26 24 @Repository
27 25 @Transactional
28 26 public class PsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository {
... ...
... ... @@ -20,12 +20,10 @@ import org.springframework.data.repository.CrudRepository;
20 20 import org.springframework.transaction.annotation.Transactional;
21 21 import org.thingsboard.server.dao.model.sql.RelationCompositeKey;
22 22 import org.thingsboard.server.dao.model.sql.RelationEntity;
23   -import org.thingsboard.server.dao.util.SqlDao;
24 23
25 24 import java.util.List;
26 25 import java.util.UUID;
27 26
28   -@SqlDao
29 27 public interface RelationRepository
30 28 extends CrudRepository<RelationEntity, RelationCompositeKey>, JpaSpecificationExecutor<RelationEntity> {
31 29
... ...
... ... @@ -26,14 +26,12 @@ import org.thingsboard.server.dao.DaoUtil;
26 26 import org.thingsboard.server.dao.model.sql.RuleChainEntity;
27 27 import org.thingsboard.server.dao.rule.RuleChainDao;
28 28 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
29   -import org.thingsboard.server.dao.util.SqlDao;
30 29
31 30 import java.util.Objects;
32 31 import java.util.UUID;
33 32
34 33 @Slf4j
35 34 @Component
36   -@SqlDao
37 35 public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, RuleChain> implements RuleChainDao {
38 36
39 37 @Autowired
... ...
... ... @@ -23,11 +23,9 @@ import org.thingsboard.server.common.data.rule.RuleNode;
23 23 import org.thingsboard.server.dao.model.sql.RuleNodeEntity;
24 24 import org.thingsboard.server.dao.rule.RuleNodeDao;
25 25 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
26   -import org.thingsboard.server.dao.util.SqlDao;
27 26
28 27 @Slf4j
29 28 @Component
30   -@SqlDao
31 29 public class JpaRuleNodeDao extends JpaAbstractSearchTextDao<RuleNodeEntity, RuleNode> implements RuleNodeDao {
32 30
33 31 @Autowired
... ...
... ... @@ -21,11 +21,9 @@ import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.dao.model.sql.RuleChainEntity;
24   -import org.thingsboard.server.dao.util.SqlDao;
25 24
26 25 import java.util.UUID;
27 26
28   -@SqlDao
29 27 public interface RuleChainRepository extends PagingAndSortingRepository<RuleChainEntity, UUID> {
30 28
31 29 @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " +
... ...
... ... @@ -17,9 +17,7 @@ package org.thingsboard.server.dao.sql.rule;
17 17
18 18 import org.springframework.data.repository.CrudRepository;
19 19 import org.thingsboard.server.dao.model.sql.RuleNodeEntity;
20   -import org.thingsboard.server.dao.util.SqlDao;
21 20
22   -@SqlDao
23 21 public interface RuleNodeRepository extends CrudRepository<RuleNodeEntity, String> {
24 22
25 23 }
... ...
... ... @@ -25,13 +25,11 @@ import org.thingsboard.server.dao.DaoUtil;
25 25 import org.thingsboard.server.dao.model.sql.AdminSettingsEntity;
26 26 import org.thingsboard.server.dao.settings.AdminSettingsDao;
27 27 import org.thingsboard.server.dao.sql.JpaAbstractDao;
28   -import org.thingsboard.server.dao.util.SqlDao;
29 28
30 29 import java.util.UUID;
31 30
32 31 @Component
33 32 @Slf4j
34   -@SqlDao
35 33 public class JpaAdminSettingsDao extends JpaAbstractDao<AdminSettingsEntity, AdminSettings> implements AdminSettingsDao {
36 34
37 35 @Autowired
... ...
... ... @@ -26,7 +26,6 @@ import org.thingsboard.server.dao.DaoUtil;
26 26 import org.thingsboard.server.dao.model.sql.TenantEntity;
27 27 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
28 28 import org.thingsboard.server.dao.tenant.TenantDao;
29   -import org.thingsboard.server.dao.util.SqlDao;
30 29
31 30 import java.util.Objects;
32 31 import java.util.UUID;
... ... @@ -36,7 +35,6 @@ import java.util.UUID;
36 35 * Created by Valerii Sosliuk on 4/30/2017.
37 36 */
38 37 @Component
39   -@SqlDao
40 38 public class JpaTenantDao extends JpaAbstractSearchTextDao<TenantEntity, Tenant> implements TenantDao {
41 39
42 40 @Autowired
... ...
... ... @@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.dao.model.sql.TenantEntity;
24   -import org.thingsboard.server.dao.util.SqlDao;
25 24
26 25 import java.util.UUID;
27 26
28 27 /**
29 28 * Created by Valerii Sosliuk on 4/30/2017.
30 29 */
31   -@SqlDao
32 30 public interface TenantRepository extends PagingAndSortingRepository<TenantEntity, UUID> {
33 31
34 32 @Query("SELECT t FROM TenantEntity t WHERE t.region = :region " +
... ...
... ... @@ -24,7 +24,6 @@ import org.thingsboard.server.dao.DaoUtil;
24 24 import org.thingsboard.server.dao.model.sql.UserCredentialsEntity;
25 25 import org.thingsboard.server.dao.sql.JpaAbstractDao;
26 26 import org.thingsboard.server.dao.user.UserCredentialsDao;
27   -import org.thingsboard.server.dao.util.SqlDao;
28 27
29 28 import java.util.UUID;
30 29
... ... @@ -32,7 +31,6 @@ import java.util.UUID;
32 31 * Created by Valerii Sosliuk on 4/22/2017.
33 32 */
34 33 @Component
35   -@SqlDao
36 34 public class JpaUserCredentialsDao extends JpaAbstractDao<UserCredentialsEntity, UserCredentials> implements UserCredentialsDao {
37 35
38 36 @Autowired
... ...
... ... @@ -27,7 +27,6 @@ import org.thingsboard.server.dao.DaoUtil;
27 27 import org.thingsboard.server.dao.model.sql.UserEntity;
28 28 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
29 29 import org.thingsboard.server.dao.user.UserDao;
30   -import org.thingsboard.server.dao.util.SqlDao;
31 30
32 31 import java.util.Objects;
33 32 import java.util.UUID;
... ... @@ -38,7 +37,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
38 37 * @author Valerii Sosliuk
39 38 */
40 39 @Component
41   -@SqlDao
42 40 public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> implements UserDao {
43 41
44 42 @Autowired
... ...
... ... @@ -17,14 +17,12 @@ package org.thingsboard.server.dao.sql.user;
17 17
18 18 import org.springframework.data.repository.CrudRepository;
19 19 import org.thingsboard.server.dao.model.sql.UserCredentialsEntity;
20   -import org.thingsboard.server.dao.util.SqlDao;
21 20
22 21 import java.util.UUID;
23 22
24 23 /**
25 24 * Created by Valerii Sosliuk on 4/22/2017.
26 25 */
27   -@SqlDao
28 26 public interface UserCredentialsRepository extends CrudRepository<UserCredentialsEntity, UUID> {
29 27
30 28 UserCredentialsEntity findByUserId(UUID userId);
... ...
... ... @@ -22,14 +22,12 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.common.data.security.Authority;
24 24 import org.thingsboard.server.dao.model.sql.UserEntity;
25   -import org.thingsboard.server.dao.util.SqlDao;
26 25
27 26 import java.util.UUID;
28 27
29 28 /**
30 29 * @author Valerii Sosliuk
31 30 */
32   -@SqlDao
33 31 public interface UserRepository extends PagingAndSortingRepository<UserEntity, UUID> {
34 32
35 33 UserEntity findByEmail(String email);
... ...
... ... @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.widget.WidgetType;
22 22 import org.thingsboard.server.dao.DaoUtil;
23 23 import org.thingsboard.server.dao.model.sql.WidgetTypeEntity;
24 24 import org.thingsboard.server.dao.sql.JpaAbstractDao;
25   -import org.thingsboard.server.dao.util.SqlDao;
26 25 import org.thingsboard.server.dao.widget.WidgetTypeDao;
27 26
28 27 import java.util.List;
... ... @@ -32,7 +31,6 @@ import java.util.UUID;
32 31 * Created by Valerii Sosliuk on 4/29/2017.
33 32 */
34 33 @Component
35   -@SqlDao
36 34 public class JpaWidgetTypeDao extends JpaAbstractDao<WidgetTypeEntity, WidgetType> implements WidgetTypeDao {
37 35
38 36 @Autowired
... ...
... ... @@ -25,7 +25,6 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle;
25 25 import org.thingsboard.server.dao.DaoUtil;
26 26 import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity;
27 27 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
28   -import org.thingsboard.server.dao.util.SqlDao;
29 28 import org.thingsboard.server.dao.widget.WidgetsBundleDao;
30 29
31 30 import java.util.Objects;
... ... @@ -37,7 +36,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
37 36 * Created by Valerii Sosliuk on 4/23/2017.
38 37 */
39 38 @Component
40   -@SqlDao
41 39 public class JpaWidgetsBundleDao extends JpaAbstractSearchTextDao<WidgetsBundleEntity, WidgetsBundle> implements WidgetsBundleDao {
42 40
43 41 @Autowired
... ...
... ... @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sql.widget;
17 17
18 18 import org.springframework.data.repository.CrudRepository;
19 19 import org.thingsboard.server.dao.model.sql.WidgetTypeEntity;
20   -import org.thingsboard.server.dao.util.SqlDao;
21 20
22 21 import java.util.List;
23 22 import java.util.UUID;
... ... @@ -25,7 +24,6 @@ import java.util.UUID;
25 24 /**
26 25 * Created by Valerii Sosliuk on 4/29/2017.
27 26 */
28   -@SqlDao
29 27 public interface WidgetTypeRepository extends CrudRepository<WidgetTypeEntity, UUID> {
30 28
31 29 List<WidgetTypeEntity> findByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias);
... ...
... ... @@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23 23 import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity;
24   -import org.thingsboard.server.dao.util.SqlDao;
25 24
26 25 import java.util.UUID;
27 26
28 27 /**
29 28 * Created by Valerii Sosliuk on 4/23/2017.
30 29 */
31   -@SqlDao
32 30 public interface WidgetsBundleRepository extends PagingAndSortingRepository<WidgetsBundleEntity, UUID> {
33 31
34 32 WidgetsBundleEntity findWidgetsBundleByTenantIdAndAlias(UUID tenantId, String alias);
... ...
... ... @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sqlts;
17 17
18 18 import com.google.common.util.concurrent.Futures;
19 19 import com.google.common.util.concurrent.ListenableFuture;
20   -import com.google.common.util.concurrent.ListeningExecutorService;
21 20 import com.google.common.util.concurrent.MoreExecutors;
22 21 import com.google.common.util.concurrent.SettableFuture;
23 22 import lombok.extern.slf4j.Slf4j;
... ... @@ -33,7 +32,6 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
33 32 import org.thingsboard.server.common.stats.StatsFactory;
34 33 import org.thingsboard.server.dao.DaoUtil;
35 34 import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity;
36   -import org.thingsboard.server.dao.sql.TbSqlBlockingQueue;
37 35 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
38 36 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;
39 37 import org.thingsboard.server.dao.sqlts.insert.InsertTsRepository;
... ... @@ -64,7 +62,6 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
64 62
65 63 @PostConstruct
66 64 protected void init() {
67   - super.init();
68 65 TbSqlBlockingQueueParams tsParams = TbSqlBlockingQueueParams.builder()
69 66 .logName("TS")
70 67 .batchSize(tsBatchSize)
... ... @@ -80,7 +77,6 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
80 77
81 78 @PreDestroy
82 79 protected void destroy() {
83   - super.destroy();
84 80 if (tsQueue != null) {
85 81 tsQueue.destroy();
86 82 }
... ... @@ -99,26 +95,6 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
99 95 }
100 96
101 97 @Override
102   - public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
103   - return getSaveLatestFuture(entityId, tsKvEntry);
104   - }
105   -
106   - @Override
107   - public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
108   - return getRemoveLatestFuture(entityId, query);
109   - }
110   -
111   - @Override
112   - public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
113   - return getFindLatestFuture(entityId, key);
114   - }
115   -
116   - @Override
117   - public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
118   - return getFindAllLatestFuture(entityId);
119   - }
120   -
121   - @Override
122 98 public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
123 99 return Futures.immediateFuture(null);
124 100 }
... ... @@ -134,7 +110,7 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
134 110 }
135 111
136 112 @Override
137   - protected ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) {
  113 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
138 114 if (query.getAggregation() == Aggregation.NONE) {
139 115 return findAllAsyncWithLimit(entityId, query);
140 116 } else {
... ... @@ -151,8 +127,7 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
151 127 }
152 128 }
153 129
154   - @Override
155   - protected ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
  130 + private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
156 131 Integer keyId = getOrSaveKeyId(query.getKey());
157 132 List<TsKvEntity> tsKvEntities = tsKvRepository.findAllWithLimit(
158 133 entityId.getId(),
... ...
... ... @@ -16,96 +16,28 @@
16 16 package org.thingsboard.server.dao.sqlts;
17 17
18 18 import com.google.common.base.Function;
19   -import com.google.common.collect.Lists;
20   -import com.google.common.util.concurrent.FutureCallback;
21 19 import com.google.common.util.concurrent.Futures;
22 20 import com.google.common.util.concurrent.ListenableFuture;
23   -import com.google.common.util.concurrent.MoreExecutors;
24 21 import lombok.extern.slf4j.Slf4j;
25   -import org.hibernate.exception.ConstraintViolationException;
26 22 import org.springframework.beans.factory.annotation.Autowired;
27 23 import org.springframework.beans.factory.annotation.Value;
28 24 import org.thingsboard.server.common.data.id.EntityId;
29 25 import org.thingsboard.server.common.data.id.TenantId;
30   -import org.thingsboard.server.common.data.kv.Aggregation;
31   -import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
32   -import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
33   -import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
34 26 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
35   -import org.thingsboard.server.common.data.kv.StringDataEntry;
36 27 import org.thingsboard.server.common.data.kv.TsKvEntry;
37   -import org.thingsboard.server.common.stats.StatsFactory;
38   -import org.thingsboard.server.dao.DaoUtil;
39   -import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
40   -import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
41   -import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey;
42   -import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
43   -import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
44 28 import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
45   -import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
46   -import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;
47   -import org.thingsboard.server.dao.sqlts.dictionary.TsKvDictionaryRepository;
48   -import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;
49   -import org.thingsboard.server.dao.sqlts.latest.SearchTsKvLatestRepository;
50   -import org.thingsboard.server.dao.sqlts.latest.TsKvLatestRepository;
51   -import org.thingsboard.server.dao.timeseries.SimpleListenableFuture;
52 29
53 30 import javax.annotation.Nullable;
54   -import javax.annotation.PostConstruct;
55   -import javax.annotation.PreDestroy;
56   -import java.util.ArrayList;
57   -import java.util.HashMap;
58 31 import java.util.List;
59   -import java.util.Map;
60 32 import java.util.Objects;
61   -import java.util.Optional;
62   -import java.util.concurrent.ConcurrentHashMap;
63   -import java.util.concurrent.ConcurrentMap;
64   -import java.util.concurrent.ExecutionException;
65   -import java.util.concurrent.locks.ReentrantLock;
66 33 import java.util.stream.Collectors;
67 34
68 35 @Slf4j
69   -public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningExecutorService {
70   -
71   - private static final String DESC_ORDER = "DESC";
72   -
73   - private final ConcurrentMap<String, Integer> tsKvDictionaryMap = new ConcurrentHashMap<>();
74   -
75   - private static final ReentrantLock tsCreationLock = new ReentrantLock();
76   -
77   - @Autowired
78   - private TsKvLatestRepository tsKvLatestRepository;
79   -
80   - @Autowired
81   - private SearchTsKvLatestRepository searchTsKvLatestRepository;
82   -
83   - @Autowired
84   - private InsertLatestTsRepository insertLatestTsRepository;
85   -
86   - @Autowired
87   - private TsKvDictionaryRepository dictionaryRepository;
88   -
89   - private TbSqlBlockingQueueWrapper<TsKvLatestEntity> tsLatestQueue;
90   -
91   - @Value("${sql.ts_latest.batch_size:1000}")
92   - private int tsLatestBatchSize;
93   -
94   - @Value("${sql.ts_latest.batch_max_delay:100}")
95   - private long tsLatestMaxDelay;
96   -
97   - @Value("${sql.ts_latest.stats_print_interval_ms:1000}")
98   - private long tsLatestStatsPrintIntervalMs;
99   -
100   - @Value("${sql.ts_latest.batch_threads:4}")
101   - private int tsLatestBatchThreads;
  36 +public abstract class AbstractSqlTimeseriesDao extends BaseAbstractSqlTimeseriesDao implements AggregationTimeseriesDao {
102 37
103 38 @Autowired
104 39 protected ScheduledLogExecutorComponent logExecutor;
105 40
106   - @Autowired
107   - private StatsFactory statsFactory;
108   -
109 41 @Value("${sql.ts.batch_size:1000}")
110 42 protected int tsBatchSize;
111 43
... ... @@ -121,44 +53,10 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx
121 53 @Value("${sql.timescale.batch_threads:4}")
122 54 protected int timescaleBatchThreads;
123 55
124   - @PostConstruct
125   - protected void init() {
126   - TbSqlBlockingQueueParams tsLatestParams = TbSqlBlockingQueueParams.builder()
127   - .logName("TS Latest")
128   - .batchSize(tsLatestBatchSize)
129   - .maxDelay(tsLatestMaxDelay)
130   - .statsPrintIntervalMs(tsLatestStatsPrintIntervalMs)
131   - .statsNamePrefix("ts.latest")
132   - .build();
133   -
134   - java.util.function.Function<TsKvLatestEntity, Integer> hashcodeFunction = entity -> entity.getEntityId().hashCode();
135   - tsLatestQueue = new TbSqlBlockingQueueWrapper<>(tsLatestParams, hashcodeFunction, tsLatestBatchThreads, statsFactory);
136   -
137   - tsLatestQueue.init(logExecutor, v -> {
138   - Map<TsKey, TsKvLatestEntity> trueLatest = new HashMap<>();
139   - v.forEach(ts -> {
140   - TsKey key = new TsKey(ts.getEntityId(), ts.getKey());
141   - TsKvLatestEntity old = trueLatest.get(key);
142   - if (old == null || old.getTs() < ts.getTs()) {
143   - trueLatest.put(key, ts);
144   - }
145   - });
146   - List<TsKvLatestEntity> latestEntities = new ArrayList<>(trueLatest.values());
147   - insertLatestTsRepository.saveOrUpdate(latestEntities);
148   - });
149   - }
150   -
151   - @PreDestroy
152   - protected void destroy() {
153   - if (tsLatestQueue != null) {
154   - tsLatestQueue.destroy();
155   - }
156   - }
157   -
158 56 protected ListenableFuture<List<TsKvEntry>> processFindAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
159 57 List<ListenableFuture<List<TsKvEntry>>> futures = queries
160 58 .stream()
161   - .map(query -> findAllAsync(entityId, query))
  59 + .map(query -> findAllAsync(tenantId, entityId, query))
162 60 .collect(Collectors.toList());
163 61 return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() {
164 62 @Nullable
... ... @@ -174,168 +72,4 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx
174 72 }
175 73 }, service);
176 74 }
177   -
178   - protected abstract ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query);
179   -
180   - protected abstract ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query);
181   -
182   - protected ListenableFuture<List<TsKvEntry>> getTskvEntriesFuture(ListenableFuture<List<Optional<TsKvEntry>>> future) {
183   - return Futures.transform(future, new Function<List<Optional<TsKvEntry>>, List<TsKvEntry>>() {
184   - @Nullable
185   - @Override
186   - public List<TsKvEntry> apply(@Nullable List<Optional<TsKvEntry>> results) {
187   - if (results == null || results.isEmpty()) {
188   - return null;
189   - }
190   - return results.stream()
191   - .filter(Optional::isPresent)
192   - .map(Optional::get)
193   - .collect(Collectors.toList());
194   - }
195   - }, service);
196   - }
197   -
198   - protected ListenableFuture<List<TsKvEntry>> findNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {
199   - long startTs = 0;
200   - long endTs = query.getStartTs() - 1;
201   - ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
202   - Aggregation.NONE, DESC_ORDER);
203   - return findAllAsync(entityId, findNewLatestQuery);
204   - }
205   -
206   - protected ListenableFuture<TsKvEntry> getFindLatestFuture(EntityId entityId, String key) {
207   - TsKvLatestCompositeKey compositeKey =
208   - new TsKvLatestCompositeKey(
209   - entityId.getId(),
210   - getOrSaveKeyId(key));
211   - Optional<TsKvLatestEntity> entry = tsKvLatestRepository.findById(compositeKey);
212   - TsKvEntry result;
213   - if (entry.isPresent()) {
214   - TsKvLatestEntity tsKvLatestEntity = entry.get();
215   - tsKvLatestEntity.setStrKey(key);
216   - result = DaoUtil.getData(tsKvLatestEntity);
217   - } else {
218   - result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));
219   - }
220   - return Futures.immediateFuture(result);
221   - }
222   -
223   - protected ListenableFuture<Void> getRemoveLatestFuture(EntityId entityId, DeleteTsKvQuery query) {
224   - ListenableFuture<TsKvEntry> latestFuture = getFindLatestFuture(entityId, query.getKey());
225   -
226   - ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> {
227   - long ts = tsKvEntry.getTs();
228   - return ts > query.getStartTs() && ts <= query.getEndTs();
229   - }, service);
230   -
231   - ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
232   - if (isRemove) {
233   - TsKvLatestEntity latestEntity = new TsKvLatestEntity();
234   - latestEntity.setEntityId(entityId.getId());
235   - latestEntity.setKey(getOrSaveKeyId(query.getKey()));
236   - return service.submit(() -> {
237   - tsKvLatestRepository.delete(latestEntity);
238   - return null;
239   - });
240   - }
241   - return Futures.immediateFuture(null);
242   - }, service);
243   -
244   - final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
245   - Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {
246   - @Override
247   - public void onSuccess(@Nullable Void result) {
248   - if (query.getRewriteLatestIfDeleted()) {
249   - ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
250   - if (isRemove) {
251   - return getNewLatestEntryFuture(entityId, query);
252   - }
253   - return Futures.immediateFuture(null);
254   - }, service);
255   -
256   - try {
257   - resultFuture.set(savedLatestFuture.get());
258   - } catch (InterruptedException | ExecutionException e) {
259   - log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);
260   - }
261   - } else {
262   - resultFuture.set(null);
263   - }
264   - }
265   -
266   - @Override
267   - public void onFailure(Throwable t) {
268   - log.warn("[{}] Failed to process remove of the latest value", entityId, t);
269   - }
270   - }, MoreExecutors.directExecutor());
271   - return resultFuture;
272   - }
273   -
274   - protected ListenableFuture<List<TsKvEntry>> getFindAllLatestFuture(EntityId entityId) {
275   - return Futures.immediateFuture(
276   - DaoUtil.convertDataList(Lists.newArrayList(
277   - searchTsKvLatestRepository.findAllByEntityId(entityId.getId()))));
278   - }
279   -
280   - protected ListenableFuture<Void> getSaveLatestFuture(EntityId entityId, TsKvEntry tsKvEntry) {
281   - TsKvLatestEntity latestEntity = new TsKvLatestEntity();
282   - latestEntity.setEntityId(entityId.getId());
283   - latestEntity.setTs(tsKvEntry.getTs());
284   - latestEntity.setKey(getOrSaveKeyId(tsKvEntry.getKey()));
285   - latestEntity.setStrValue(tsKvEntry.getStrValue().orElse(null));
286   - latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
287   - latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null));
288   - latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
289   - latestEntity.setJsonValue(tsKvEntry.getJsonValue().orElse(null));
290   -
291   - return tsLatestQueue.add(latestEntity);
292   - }
293   -
294   - protected Integer getOrSaveKeyId(String strKey) {
295   - Integer keyId = tsKvDictionaryMap.get(strKey);
296   - if (keyId == null) {
297   - Optional<TsKvDictionary> tsKvDictionaryOptional;
298   - tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
299   - if (!tsKvDictionaryOptional.isPresent()) {
300   - tsCreationLock.lock();
301   - try {
302   - tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
303   - if (!tsKvDictionaryOptional.isPresent()) {
304   - TsKvDictionary tsKvDictionary = new TsKvDictionary();
305   - tsKvDictionary.setKey(strKey);
306   - try {
307   - TsKvDictionary saved = dictionaryRepository.save(tsKvDictionary);
308   - tsKvDictionaryMap.put(saved.getKey(), saved.getKeyId());
309   - keyId = saved.getKeyId();
310   - } catch (ConstraintViolationException e) {
311   - tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
312   - TsKvDictionary dictionary = tsKvDictionaryOptional.orElseThrow(() -> new RuntimeException("Failed to get TsKvDictionary entity from DB!"));
313   - tsKvDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId());
314   - keyId = dictionary.getKeyId();
315   - }
316   - } else {
317   - keyId = tsKvDictionaryOptional.get().getKeyId();
318   - }
319   - } finally {
320   - tsCreationLock.unlock();
321   - }
322   - } else {
323   - keyId = tsKvDictionaryOptional.get().getKeyId();
324   - tsKvDictionaryMap.put(strKey, keyId);
325   - }
326   - }
327   - return keyId;
328   - }
329   -
330   - private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {
331   - ListenableFuture<List<TsKvEntry>> future = findNewLatestEntryFuture(entityId, query);
332   - return Futures.transformAsync(future, entryList -> {
333   - if (entryList.size() == 1) {
334   - return getSaveLatestFuture(entityId, entryList.get(0));
335   - } else {
336   - log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
337   - }
338   - return Futures.immediateFuture(null);
339   - }, service);
340   - }
341 75 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  22 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  23 +
  24 +import java.util.List;
  25 +
  26 +public interface AggregationTimeseriesDao {
  27 +
  28 + ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query);
  29 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts;
  17 +
  18 +import com.google.common.base.Function;
  19 +import com.google.common.util.concurrent.Futures;
  20 +import com.google.common.util.concurrent.ListenableFuture;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.hibernate.exception.ConstraintViolationException;
  23 +import org.springframework.beans.factory.annotation.Autowired;
  24 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  25 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
  26 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
  27 +import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
  28 +import org.thingsboard.server.dao.sqlts.dictionary.TsKvDictionaryRepository;
  29 +
  30 +import javax.annotation.Nullable;
  31 +import java.util.List;
  32 +import java.util.Optional;
  33 +import java.util.concurrent.ConcurrentHashMap;
  34 +import java.util.concurrent.ConcurrentMap;
  35 +import java.util.concurrent.locks.ReentrantLock;
  36 +import java.util.stream.Collectors;
  37 +
  38 +@Slf4j
  39 +public abstract class BaseAbstractSqlTimeseriesDao extends JpaAbstractDaoListeningExecutorService {
  40 +
  41 + private final ConcurrentMap<String, Integer> tsKvDictionaryMap = new ConcurrentHashMap<>();
  42 +
  43 + protected static final ReentrantLock tsCreationLock = new ReentrantLock();
  44 +
  45 + @Autowired
  46 + protected TsKvDictionaryRepository dictionaryRepository;
  47 +
  48 + protected Integer getOrSaveKeyId(String strKey) {
  49 + Integer keyId = tsKvDictionaryMap.get(strKey);
  50 + if (keyId == null) {
  51 + Optional<TsKvDictionary> tsKvDictionaryOptional;
  52 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  53 + if (!tsKvDictionaryOptional.isPresent()) {
  54 + tsCreationLock.lock();
  55 + try {
  56 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  57 + if (!tsKvDictionaryOptional.isPresent()) {
  58 + TsKvDictionary tsKvDictionary = new TsKvDictionary();
  59 + tsKvDictionary.setKey(strKey);
  60 + try {
  61 + TsKvDictionary saved = dictionaryRepository.save(tsKvDictionary);
  62 + tsKvDictionaryMap.put(saved.getKey(), saved.getKeyId());
  63 + keyId = saved.getKeyId();
  64 + } catch (ConstraintViolationException e) {
  65 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  66 + TsKvDictionary dictionary = tsKvDictionaryOptional.orElseThrow(() -> new RuntimeException("Failed to get TsKvDictionary entity from DB!"));
  67 + tsKvDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId());
  68 + keyId = dictionary.getKeyId();
  69 + }
  70 + } else {
  71 + keyId = tsKvDictionaryOptional.get().getKeyId();
  72 + }
  73 + } finally {
  74 + tsCreationLock.unlock();
  75 + }
  76 + } else {
  77 + keyId = tsKvDictionaryOptional.get().getKeyId();
  78 + tsKvDictionaryMap.put(strKey, keyId);
  79 + }
  80 + }
  81 + return keyId;
  82 + }
  83 +
  84 + protected ListenableFuture<List<TsKvEntry>> getTskvEntriesFuture(ListenableFuture<List<Optional<TsKvEntry>>> future) {
  85 + return Futures.transform(future, new Function<List<Optional<TsKvEntry>>, List<TsKvEntry>>() {
  86 + @Nullable
  87 + @Override
  88 + public List<TsKvEntry> apply(@Nullable List<Optional<TsKvEntry>> results) {
  89 + if (results == null || results.isEmpty()) {
  90 + return null;
  91 + }
  92 + return results.stream()
  93 + .filter(Optional::isPresent)
  94 + .map(Optional::get)
  95 + .collect(Collectors.toList());
  96 + }
  97 + }, service);
  98 + }
  99 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts;
  17 +
  18 +import com.google.common.collect.Lists;
  19 +import com.google.common.util.concurrent.FutureCallback;
  20 +import com.google.common.util.concurrent.Futures;
  21 +import com.google.common.util.concurrent.ListenableFuture;
  22 +import com.google.common.util.concurrent.MoreExecutors;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.beans.factory.annotation.Value;
  26 +import org.springframework.stereotype.Component;
  27 +import org.thingsboard.server.common.data.id.EntityId;
  28 +import org.thingsboard.server.common.data.id.TenantId;
  29 +import org.thingsboard.server.common.data.kv.Aggregation;
  30 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
  31 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  32 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  33 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  34 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  35 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  36 +import org.thingsboard.server.common.stats.StatsFactory;
  37 +import org.thingsboard.server.dao.DaoUtil;
  38 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey;
  39 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
  40 +import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
  41 +import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
  42 +import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;
  43 +import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;
  44 +import org.thingsboard.server.dao.sqlts.latest.SearchTsKvLatestRepository;
  45 +import org.thingsboard.server.dao.sqlts.latest.TsKvLatestRepository;
  46 +import org.thingsboard.server.dao.timeseries.SimpleListenableFuture;
  47 +import org.thingsboard.server.dao.timeseries.TimeseriesLatestDao;
  48 +import org.thingsboard.server.dao.util.SqlTsLatestAnyDao;
  49 +
  50 +import javax.annotation.Nullable;
  51 +import javax.annotation.PostConstruct;
  52 +import javax.annotation.PreDestroy;
  53 +import java.util.ArrayList;
  54 +import java.util.HashMap;
  55 +import java.util.List;
  56 +import java.util.Map;
  57 +import java.util.Optional;
  58 +import java.util.concurrent.ExecutionException;
  59 +
  60 +@Slf4j
  61 +@Component
  62 +@SqlTsLatestAnyDao
  63 +public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao implements TimeseriesLatestDao {
  64 +
  65 + private static final String DESC_ORDER = "DESC";
  66 +
  67 + @Autowired
  68 + private TsKvLatestRepository tsKvLatestRepository;
  69 +
  70 + @Autowired
  71 + protected AggregationTimeseriesDao aggregationTimeseriesDao;
  72 +
  73 + @Autowired
  74 + private SearchTsKvLatestRepository searchTsKvLatestRepository;
  75 +
  76 + @Autowired
  77 + private InsertLatestTsRepository insertLatestTsRepository;
  78 +
  79 + private TbSqlBlockingQueueWrapper<TsKvLatestEntity> tsLatestQueue;
  80 +
  81 + @Value("${sql.ts_latest.batch_size:1000}")
  82 + private int tsLatestBatchSize;
  83 +
  84 + @Value("${sql.ts_latest.batch_max_delay:100}")
  85 + private long tsLatestMaxDelay;
  86 +
  87 + @Value("${sql.ts_latest.stats_print_interval_ms:1000}")
  88 + private long tsLatestStatsPrintIntervalMs;
  89 +
  90 + @Value("${sql.ts_latest.batch_threads:4}")
  91 + private int tsLatestBatchThreads;
  92 +
  93 + @Autowired
  94 + protected ScheduledLogExecutorComponent logExecutor;
  95 +
  96 + @Autowired
  97 + private StatsFactory statsFactory;
  98 +
  99 + @PostConstruct
  100 + protected void init() {
  101 + TbSqlBlockingQueueParams tsLatestParams = TbSqlBlockingQueueParams.builder()
  102 + .logName("TS Latest")
  103 + .batchSize(tsLatestBatchSize)
  104 + .maxDelay(tsLatestMaxDelay)
  105 + .statsPrintIntervalMs(tsLatestStatsPrintIntervalMs)
  106 + .statsNamePrefix("ts.latest")
  107 + .build();
  108 +
  109 + java.util.function.Function<TsKvLatestEntity, Integer> hashcodeFunction = entity -> entity.getEntityId().hashCode();
  110 + tsLatestQueue = new TbSqlBlockingQueueWrapper<>(tsLatestParams, hashcodeFunction, tsLatestBatchThreads, statsFactory);
  111 +
  112 + tsLatestQueue.init(logExecutor, v -> {
  113 + Map<TsKey, TsKvLatestEntity> trueLatest = new HashMap<>();
  114 + v.forEach(ts -> {
  115 + TsKey key = new TsKey(ts.getEntityId(), ts.getKey());
  116 + TsKvLatestEntity old = trueLatest.get(key);
  117 + if (old == null || old.getTs() < ts.getTs()) {
  118 + trueLatest.put(key, ts);
  119 + }
  120 + });
  121 + List<TsKvLatestEntity> latestEntities = new ArrayList<>(trueLatest.values());
  122 + insertLatestTsRepository.saveOrUpdate(latestEntities);
  123 + });
  124 + }
  125 +
  126 + @PreDestroy
  127 + protected void destroy() {
  128 + if (tsLatestQueue != null) {
  129 + tsLatestQueue.destroy();
  130 + }
  131 + }
  132 +
  133 + @Override
  134 + public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
  135 + return getSaveLatestFuture(entityId, tsKvEntry);
  136 + }
  137 +
  138 + @Override
  139 + public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  140 + return getRemoveLatestFuture(tenantId, entityId, query);
  141 + }
  142 +
  143 + @Override
  144 + public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
  145 + return getFindLatestFuture(entityId, key);
  146 + }
  147 +
  148 + @Override
  149 + public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
  150 + return getFindAllLatestFuture(entityId);
  151 + }
  152 +
  153 + private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  154 + ListenableFuture<List<TsKvEntry>> future = findNewLatestEntryFuture(tenantId, entityId, query);
  155 + return Futures.transformAsync(future, entryList -> {
  156 + if (entryList.size() == 1) {
  157 + return getSaveLatestFuture(entityId, entryList.get(0));
  158 + } else {
  159 + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
  160 + }
  161 + return Futures.immediateFuture(null);
  162 + }, service);
  163 + }
  164 +
  165 + private ListenableFuture<List<TsKvEntry>> findNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  166 + long startTs = 0;
  167 + long endTs = query.getStartTs() - 1;
  168 + ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
  169 + Aggregation.NONE, DESC_ORDER);
  170 + return aggregationTimeseriesDao.findAllAsync(tenantId, entityId, findNewLatestQuery);
  171 + }
  172 +
  173 + protected ListenableFuture<TsKvEntry> getFindLatestFuture(EntityId entityId, String key) {
  174 + TsKvLatestCompositeKey compositeKey =
  175 + new TsKvLatestCompositeKey(
  176 + entityId.getId(),
  177 + getOrSaveKeyId(key));
  178 + Optional<TsKvLatestEntity> entry = tsKvLatestRepository.findById(compositeKey);
  179 + TsKvEntry result;
  180 + if (entry.isPresent()) {
  181 + TsKvLatestEntity tsKvLatestEntity = entry.get();
  182 + tsKvLatestEntity.setStrKey(key);
  183 + result = DaoUtil.getData(tsKvLatestEntity);
  184 + } else {
  185 + result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));
  186 + }
  187 + return Futures.immediateFuture(result);
  188 + }
  189 +
  190 + protected ListenableFuture<Void> getRemoveLatestFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  191 + ListenableFuture<TsKvEntry> latestFuture = getFindLatestFuture(entityId, query.getKey());
  192 +
  193 + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> {
  194 + long ts = tsKvEntry.getTs();
  195 + return ts > query.getStartTs() && ts <= query.getEndTs();
  196 + }, service);
  197 +
  198 + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  199 + if (isRemove) {
  200 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  201 + latestEntity.setEntityId(entityId.getId());
  202 + latestEntity.setKey(getOrSaveKeyId(query.getKey()));
  203 + return service.submit(() -> {
  204 + tsKvLatestRepository.delete(latestEntity);
  205 + return null;
  206 + });
  207 + }
  208 + return Futures.immediateFuture(null);
  209 + }, service);
  210 +
  211 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  212 + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {
  213 + @Override
  214 + public void onSuccess(@Nullable Void result) {
  215 + if (query.getRewriteLatestIfDeleted()) {
  216 + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  217 + if (isRemove) {
  218 + return getNewLatestEntryFuture(tenantId, entityId, query);
  219 + }
  220 + return Futures.immediateFuture(null);
  221 + }, service);
  222 +
  223 + try {
  224 + resultFuture.set(savedLatestFuture.get());
  225 + } catch (InterruptedException | ExecutionException e) {
  226 + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);
  227 + }
  228 + } else {
  229 + resultFuture.set(null);
  230 + }
  231 + }
  232 +
  233 + @Override
  234 + public void onFailure(Throwable t) {
  235 + log.warn("[{}] Failed to process remove of the latest value", entityId, t);
  236 + }
  237 + }, MoreExecutors.directExecutor());
  238 + return resultFuture;
  239 + }
  240 +
  241 + protected ListenableFuture<List<TsKvEntry>> getFindAllLatestFuture(EntityId entityId) {
  242 + return Futures.immediateFuture(
  243 + DaoUtil.convertDataList(Lists.newArrayList(
  244 + searchTsKvLatestRepository.findAllByEntityId(entityId.getId()))));
  245 + }
  246 +
  247 + protected ListenableFuture<Void> getSaveLatestFuture(EntityId entityId, TsKvEntry tsKvEntry) {
  248 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  249 + latestEntity.setEntityId(entityId.getId());
  250 + latestEntity.setTs(tsKvEntry.getTs());
  251 + latestEntity.setKey(getOrSaveKeyId(tsKvEntry.getKey()));
  252 + latestEntity.setStrValue(tsKvEntry.getStrValue().orElse(null));
  253 + latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
  254 + latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null));
  255 + latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
  256 + latestEntity.setJsonValue(tsKvEntry.getJsonValue().orElse(null));
  257 +
  258 + return tsLatestQueue.add(latestEntity);
  259 + }
  260 +
  261 +}
... ...
... ... @@ -18,11 +18,11 @@ package org.thingsboard.server.dao.sqlts.dictionary;
18 18 import org.springframework.data.repository.CrudRepository;
19 19 import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
20 20 import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
21   -import org.thingsboard.server.dao.util.SqlTsAnyDao;
  21 +import org.thingsboard.server.dao.util.SqlTsOrTsLatestAnyDao;
22 22
23 23 import java.util.Optional;
24 24
25   -@SqlTsAnyDao
  25 +@SqlTsOrTsLatestAnyDao
26 26 public interface TsKvDictionaryRepository extends CrudRepository<TsKvDictionary, TsKvDictionaryCompositeKey> {
27 27
28 28 Optional<TsKvDictionary> findByKeyId(int keyId);
... ...
... ... @@ -22,14 +22,14 @@ import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
22 22 import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository;
23 23 import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;
24 24 import org.thingsboard.server.dao.util.HsqlDao;
25   -import org.thingsboard.server.dao.util.SqlTsDao;
  25 +import org.thingsboard.server.dao.util.SqlTsLatestDao;
26 26
27 27 import java.sql.PreparedStatement;
28 28 import java.sql.SQLException;
29 29 import java.sql.Types;
30 30 import java.util.List;
31 31
32   -@SqlTsDao
  32 +@SqlTsLatestDao
33 33 @HsqlDao
34 34 @Repository
35 35 @Transactional
... ...
... ... @@ -23,7 +23,7 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult;
23 23 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
24 24 import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository;
25 25 import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;
26   -import org.thingsboard.server.dao.util.PsqlTsAnyDao;
  26 +import org.thingsboard.server.dao.util.PsqlTsLatestAnyDao;
27 27
28 28 import java.sql.PreparedStatement;
29 29 import java.sql.SQLException;
... ... @@ -32,7 +32,7 @@ import java.util.ArrayList;
32 32 import java.util.List;
33 33
34 34
35   -@PsqlTsAnyDao
  35 +@PsqlTsLatestAnyDao
36 36 @Repository
37 37 @Transactional
38 38 public class PsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository {
... ...
... ... @@ -17,14 +17,14 @@ package org.thingsboard.server.dao.sqlts.latest;
17 17
18 18 import org.springframework.stereotype.Repository;
19 19 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
20   -import org.thingsboard.server.dao.util.SqlTsAnyDao;
  20 +import org.thingsboard.server.dao.util.SqlTsLatestAnyDao;
21 21
22 22 import javax.persistence.EntityManager;
23 23 import javax.persistence.PersistenceContext;
24 24 import java.util.List;
25 25 import java.util.UUID;
26 26
27   -@SqlTsAnyDao
  27 +@SqlTsLatestAnyDao
28 28 @Repository
29 29 public class SearchTsKvLatestRepository {
30 30
... ...
... ... @@ -18,9 +18,7 @@ package org.thingsboard.server.dao.sqlts.latest;
18 18 import org.springframework.data.repository.CrudRepository;
19 19 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey;
20 20 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
21   -import org.thingsboard.server.dao.util.SqlDao;
22 21
23   -@SqlDao
24 22 public interface TsKvLatestRepository extends CrudRepository<TsKvLatestEntity, TsKvLatestCompositeKey> {
25 23
26 24 }
... ...
... ... @@ -41,11 +41,10 @@ import java.util.Optional;
41 41 import java.util.concurrent.ConcurrentHashMap;
42 42 import java.util.concurrent.locks.ReentrantLock;
43 43
44   -
45 44 @Component
46 45 @Slf4j
47   -@SqlTsDao
48 46 @PsqlDao
  47 +@SqlTsDao
49 48 public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDao {
50 49
51 50 private final Map<Long, PsqlPartition> partitions = new ConcurrentHashMap<>();
... ...
... ... @@ -18,7 +18,7 @@ package org.thingsboard.server.dao.sqlts.timescale;
18 18 import org.springframework.scheduling.annotation.Async;
19 19 import org.springframework.stereotype.Repository;
20 20 import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity;
21   -import org.thingsboard.server.dao.util.TimescaleDBTsDao;
  21 +import org.thingsboard.server.dao.util.TimescaleDBTsOrTsLatestDao;
22 22
23 23 import javax.persistence.EntityManager;
24 24 import javax.persistence.PersistenceContext;
... ... @@ -27,7 +27,7 @@ import java.util.UUID;
27 27 import java.util.concurrent.CompletableFuture;
28 28
29 29 @Repository
30   -@TimescaleDBTsDao
  30 +@TimescaleDBTsOrTsLatestDao
31 31 public class AggregationRepository {
32 32
33 33 public static final String FIND_AVG = "findAvg";
... ...
... ... @@ -72,7 +72,6 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
72 72
73 73 @PostConstruct
74 74 protected void init() {
75   - super.init();
76 75 TbSqlBlockingQueueParams tsParams = TbSqlBlockingQueueParams.builder()
77 76 .logName("TS Timescale")
78 77 .batchSize(tsBatchSize)
... ... @@ -89,14 +88,60 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
89 88
90 89 @PreDestroy
91 90 protected void destroy() {
92   - super.destroy();
93 91 if (tsQueue != null) {
94 92 tsQueue.destroy();
95 93 }
96 94 }
97 95
98 96 @Override
99   - protected ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) {
  97 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
  98 + return processFindAllAsync(tenantId, entityId, queries);
  99 + }
  100 +
  101 + @Override
  102 + public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
  103 + String strKey = tsKvEntry.getKey();
  104 + Integer keyId = getOrSaveKeyId(strKey);
  105 + TimescaleTsKvEntity entity = new TimescaleTsKvEntity();
  106 + entity.setEntityId(entityId.getId());
  107 + entity.setTs(tsKvEntry.getTs());
  108 + entity.setKey(keyId);
  109 + entity.setStrValue(tsKvEntry.getStrValue().orElse(null));
  110 + entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
  111 + entity.setLongValue(tsKvEntry.getLongValue().orElse(null));
  112 + entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
  113 + entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null));
  114 +
  115 + log.trace("Saving entity to timescale db: {}", entity);
  116 + return tsQueue.add(entity);
  117 + }
  118 +
  119 + @Override
  120 + public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
  121 + return Futures.immediateFuture(null);
  122 + }
  123 +
  124 + @Override
  125 + public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  126 + String strKey = query.getKey();
  127 + Integer keyId = getOrSaveKeyId(strKey);
  128 + return service.submit(() -> {
  129 + tsKvRepository.delete(
  130 + entityId.getId(),
  131 + keyId,
  132 + query.getStartTs(),
  133 + query.getEndTs());
  134 + return null;
  135 + });
  136 + }
  137 +
  138 + @Override
  139 + public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  140 + return service.submit(() -> null);
  141 + }
  142 +
  143 + @Override
  144 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
100 145 if (query.getAggregation() == Aggregation.NONE) {
101 146 return findAllAsyncWithLimit(entityId, query);
102 147 } else {
... ... @@ -108,8 +153,7 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
108 153 }
109 154 }
110 155
111   - @Override
112   - protected ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
  156 + private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
113 157 String strKey = query.getKey();
114 158 Integer keyId = getOrSaveKeyId(strKey);
115 159 List<TimescaleTsKvEntity> timescaleTsKvEntities = tsKvRepository.findAllWithLimit(
... ... @@ -153,73 +197,6 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
153 197 }, MoreExecutors.directExecutor());
154 198 }
155 199
156   - @Override
157   - public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
158   - return processFindAllAsync(tenantId, entityId, queries);
159   - }
160   -
161   - @Override
162   - public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
163   - return getFindLatestFuture(entityId, key);
164   - }
165   -
166   - @Override
167   - public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
168   - return getFindAllLatestFuture(entityId);
169   - }
170   -
171   - @Override
172   - public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
173   - String strKey = tsKvEntry.getKey();
174   - Integer keyId = getOrSaveKeyId(strKey);
175   - TimescaleTsKvEntity entity = new TimescaleTsKvEntity();
176   - entity.setEntityId(entityId.getId());
177   - entity.setTs(tsKvEntry.getTs());
178   - entity.setKey(keyId);
179   - entity.setStrValue(tsKvEntry.getStrValue().orElse(null));
180   - entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
181   - entity.setLongValue(tsKvEntry.getLongValue().orElse(null));
182   - entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
183   - entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null));
184   -
185   - log.trace("Saving entity to timescale db: {}", entity);
186   - return tsQueue.add(entity);
187   - }
188   -
189   - @Override
190   - public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
191   - return Futures.immediateFuture(null);
192   - }
193   -
194   - @Override
195   - public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
196   - return getSaveLatestFuture(entityId, tsKvEntry);
197   - }
198   -
199   - @Override
200   - public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
201   - String strKey = query.getKey();
202   - Integer keyId = getOrSaveKeyId(strKey);
203   - return service.submit(() -> {
204   - tsKvRepository.delete(
205   - entityId.getId(),
206   - keyId,
207   - query.getStartTs(),
208   - query.getEndTs());
209   - return null;
210   - });
211   - }
212   -
213   - @Override
214   - public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
215   - return getRemoveLatestFuture(entityId, query);
216   - }
217   -
218   - @Override
219   - public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
220   - return service.submit(() -> null);
221   - }
222   -
223 200 private CompletableFuture<List<TimescaleTsKvEntity>> switchAggregation(String key, long startTs, long endTs, long timeBucket, Aggregation aggregation, UUID entityId) {
224 201 switch (aggregation) {
225 202 case AVG:
... ...
... ... @@ -23,12 +23,12 @@ import org.springframework.data.repository.query.Param;
23 23 import org.springframework.transaction.annotation.Transactional;
24 24 import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvCompositeKey;
25 25 import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity;
26   -import org.thingsboard.server.dao.util.TimescaleDBTsDao;
  26 +import org.thingsboard.server.dao.util.TimescaleDBTsOrTsLatestDao;
27 27
28 28 import java.util.List;
29 29 import java.util.UUID;
30 30
31   -@TimescaleDBTsDao
  31 +@TimescaleDBTsOrTsLatestDao
32 32 public interface TsKvTimescaleRepository extends CrudRepository<TimescaleTsKvEntity, TimescaleTsKvCompositeKey> {
33 33
34 34 @Query("SELECT tskv FROM TimescaleTsKvEntity tskv WHERE tskv.entityId = :entityId " +
... ...
... ... @@ -24,13 +24,11 @@ import org.springframework.scheduling.annotation.Async;
24 24 import org.springframework.transaction.annotation.Transactional;
25 25 import org.thingsboard.server.dao.model.sqlts.ts.TsKvCompositeKey;
26 26 import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity;
27   -import org.thingsboard.server.dao.util.SqlDao;
28 27
29 28 import java.util.List;
30 29 import java.util.UUID;
31 30 import java.util.concurrent.CompletableFuture;
32 31
33   -@SqlDao
34 32 public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvCompositeKey> {
35 33
36 34 @Query("SELECT tskv FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " +
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.timeseries;
  17 +
  18 +import com.datastax.oss.driver.api.core.cql.Row;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.apache.commons.lang3.StringUtils;
  21 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  22 +import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  23 +import org.thingsboard.server.common.data.kv.DoubleDataEntry;
  24 +import org.thingsboard.server.common.data.kv.JsonDataEntry;
  25 +import org.thingsboard.server.common.data.kv.KvEntry;
  26 +import org.thingsboard.server.common.data.kv.LongDataEntry;
  27 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  28 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  29 +import org.thingsboard.server.dao.model.ModelConstants;
  30 +import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao;
  31 +
  32 +import java.util.ArrayList;
  33 +import java.util.List;
  34 +
  35 +@Slf4j
  36 +public abstract class AbstractCassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao {
  37 + public static final String DESC_ORDER = "DESC";
  38 + public static final String GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID = "Generated query [{}] for entityType {} and entityId {}";
  39 + public static final String INSERT_INTO = "INSERT INTO ";
  40 + public static final String SELECT_PREFIX = "SELECT ";
  41 + public static final String EQUALS_PARAM = " = ? ";
  42 +
  43 + public static KvEntry toKvEntry(Row row, String key) {
  44 + KvEntry kvEntry = null;
  45 + String strV = row.get(ModelConstants.STRING_VALUE_COLUMN, String.class);
  46 + if (strV != null) {
  47 + kvEntry = new StringDataEntry(key, strV);
  48 + } else {
  49 + Long longV = row.get(ModelConstants.LONG_VALUE_COLUMN, Long.class);
  50 + if (longV != null) {
  51 + kvEntry = new LongDataEntry(key, longV);
  52 + } else {
  53 + Double doubleV = row.get(ModelConstants.DOUBLE_VALUE_COLUMN, Double.class);
  54 + if (doubleV != null) {
  55 + kvEntry = new DoubleDataEntry(key, doubleV);
  56 + } else {
  57 + Boolean boolV = row.get(ModelConstants.BOOLEAN_VALUE_COLUMN, Boolean.class);
  58 + if (boolV != null) {
  59 + kvEntry = new BooleanDataEntry(key, boolV);
  60 + } else {
  61 + String jsonV = row.get(ModelConstants.JSON_VALUE_COLUMN, String.class);
  62 + if (StringUtils.isNoneEmpty(jsonV)) {
  63 + kvEntry = new JsonDataEntry(key, jsonV);
  64 + } else {
  65 + log.warn("All values in key-value row are nullable ");
  66 + }
  67 + }
  68 + }
  69 + }
  70 + }
  71 + return kvEntry;
  72 + }
  73 +
  74 + protected List<TsKvEntry> convertResultToTsKvEntryList(List<Row> rows) {
  75 + List<TsKvEntry> entries = new ArrayList<>(rows.size());
  76 + if (!rows.isEmpty()) {
  77 + rows.forEach(row -> entries.add(convertResultToTsKvEntry(row)));
  78 + }
  79 + return entries;
  80 + }
  81 +
  82 + private TsKvEntry convertResultToTsKvEntry(Row row) {
  83 + String key = row.getString(ModelConstants.KEY_COLUMN);
  84 + long ts = row.getLong(ModelConstants.TS_COLUMN);
  85 + return new BasicTsKvEntry(ts, toKvEntry(row, key));
  86 + }
  87 +
  88 +}
... ...
... ... @@ -60,6 +60,9 @@ public class BaseTimeseriesService implements TimeseriesService {
60 60 private TimeseriesDao timeseriesDao;
61 61
62 62 @Autowired
  63 + private TimeseriesLatestDao timeseriesLatestDao;
  64 +
  65 + @Autowired
63 66 private EntityViewService entityViewService;
64 67
65 68 @Override
... ... @@ -103,7 +106,7 @@ public class BaseTimeseriesService implements TimeseriesService {
103 106 return Futures.immediateFuture(new ArrayList<>());
104 107 }
105 108 }
106   - keys.forEach(key -> futures.add(timeseriesDao.findLatest(tenantId, entityId, key)));
  109 + keys.forEach(key -> futures.add(timeseriesLatestDao.findLatest(tenantId, entityId, key)));
107 110 return Futures.allAsList(futures);
108 111 }
109 112
... ... @@ -119,7 +122,7 @@ public class BaseTimeseriesService implements TimeseriesService {
119 122 return Futures.immediateFuture(new ArrayList<>());
120 123 }
121 124 } else {
122   - return timeseriesDao.findAllLatest(tenantId, entityId);
  125 + return timeseriesLatestDao.findAllLatest(tenantId, entityId);
123 126 }
124 127 }
125 128
... ... @@ -151,7 +154,7 @@ public class BaseTimeseriesService implements TimeseriesService {
151 154 throw new IncorrectParameterException("Telemetry data can't be stored for entity view. Read only");
152 155 }
153 156 futures.add(timeseriesDao.savePartition(tenantId, entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl));
154   - futures.add(timeseriesDao.saveLatest(tenantId, entityId, tsKvEntry));
  157 + futures.add(timeseriesLatestDao.saveLatest(tenantId, entityId, tsKvEntry));
155 158 futures.add(timeseriesDao.save(tenantId, entityId, tsKvEntry, ttl));
156 159 }
157 160
... ... @@ -187,7 +190,7 @@ public class BaseTimeseriesService implements TimeseriesService {
187 190
188 191 private void deleteAndRegisterFutures(TenantId tenantId, List<ListenableFuture<Void>> futures, EntityId entityId, DeleteTsKvQuery query) {
189 192 futures.add(timeseriesDao.remove(tenantId, entityId, query));
190   - futures.add(timeseriesDao.removeLatest(tenantId, entityId, query));
  193 + futures.add(timeseriesLatestDao.removeLatest(tenantId, entityId, query));
191 194 futures.add(timeseriesDao.removePartition(tenantId, entityId, query));
192 195 }
193 196
... ...
... ... @@ -20,7 +20,6 @@ import com.datastax.oss.driver.api.core.cql.BoundStatement;
20 20 import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder;
21 21 import com.datastax.oss.driver.api.core.cql.PreparedStatement;
22 22 import com.datastax.oss.driver.api.core.cql.Row;
23   -import com.datastax.oss.driver.api.core.cql.Statement;
24 23 import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
25 24 import com.datastax.oss.driver.api.querybuilder.select.Select;
26 25 import com.google.common.base.Function;
... ... @@ -30,7 +29,6 @@ import com.google.common.util.concurrent.Futures;
30 29 import com.google.common.util.concurrent.ListenableFuture;
31 30 import com.google.common.util.concurrent.MoreExecutors;
32 31 import lombok.extern.slf4j.Slf4j;
33   -import org.apache.commons.lang3.StringUtils;
34 32 import org.springframework.beans.factory.annotation.Autowired;
35 33 import org.springframework.beans.factory.annotation.Value;
36 34 import org.springframework.core.env.Environment;
... ... @@ -39,21 +37,15 @@ import org.thingsboard.server.common.data.id.EntityId;
39 37 import org.thingsboard.server.common.data.id.TenantId;
40 38 import org.thingsboard.server.common.data.kv.Aggregation;
41 39 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
42   -import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
43   -import org.thingsboard.server.common.data.kv.BooleanDataEntry;
44 40 import org.thingsboard.server.common.data.kv.DataType;
45 41 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
46   -import org.thingsboard.server.common.data.kv.DoubleDataEntry;
47   -import org.thingsboard.server.common.data.kv.JsonDataEntry;
48 42 import org.thingsboard.server.common.data.kv.KvEntry;
49   -import org.thingsboard.server.common.data.kv.LongDataEntry;
50 43 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
51   -import org.thingsboard.server.common.data.kv.StringDataEntry;
52 44 import org.thingsboard.server.common.data.kv.TsKvEntry;
53 45 import org.thingsboard.server.dao.model.ModelConstants;
54   -import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao;
55 46 import org.thingsboard.server.dao.nosql.TbResultSet;
56 47 import org.thingsboard.server.dao.nosql.TbResultSetFuture;
  48 +import org.thingsboard.server.dao.sqlts.AggregationTimeseriesDao;
57 49 import org.thingsboard.server.dao.util.NoSqlTsDao;
58 50
59 51 import javax.annotation.Nullable;
... ... @@ -68,7 +60,6 @@ import java.util.Arrays;
68 60 import java.util.Collections;
69 61 import java.util.List;
70 62 import java.util.Optional;
71   -import java.util.concurrent.ExecutionException;
72 63 import java.util.stream.Collectors;
73 64
74 65 import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal;
... ... @@ -79,16 +70,11 @@ import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal;
79 70 @Component
80 71 @Slf4j
81 72 @NoSqlTsDao
82   -public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implements TimeseriesDao {
  73 +public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesDao implements TimeseriesDao, AggregationTimeseriesDao {
83 74
84   - private static final int MIN_AGGREGATION_STEP_MS = 1000;
85   - public static final String INSERT_INTO = "INSERT INTO ";
86   - public static final String GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID = "Generated query [{}] for entityType {} and entityId {}";
87   - public static final String SELECT_PREFIX = "SELECT ";
88   - public static final String EQUALS_PARAM = " = ? ";
  75 + protected static final int MIN_AGGREGATION_STEP_MS = 1000;
89 76 public static final String ASC_ORDER = "ASC";
90   - public static final String DESC_ORDER = "DESC";
91   - private static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L});
  77 + protected static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L});
92 78
93 79 @Autowired
94 80 private Environment environment;
... ... @@ -106,13 +92,10 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
106 92
107 93 private PreparedStatement partitionInsertStmt;
108 94 private PreparedStatement partitionInsertTtlStmt;
109   - private PreparedStatement latestInsertStmt;
110 95 private PreparedStatement[] saveStmts;
111 96 private PreparedStatement[] saveTtlStmts;
112 97 private PreparedStatement[] fetchStmtsAsc;
113 98 private PreparedStatement[] fetchStmtsDesc;
114   - private PreparedStatement findLatestStmt;
115   - private PreparedStatement findAllLatestStmt;
116 99 private PreparedStatement deleteStmt;
117 100 private PreparedStatement deletePartitionStmt;
118 101
... ... @@ -157,8 +140,113 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
157 140 }, readResultsProcessingExecutor);
158 141 }
159 142
  143 + @Override
  144 + public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
  145 + List<ListenableFuture<Void>> futures = new ArrayList<>();
  146 + ttl = computeTtl(ttl);
  147 + long partition = toPartitionTs(tsKvEntry.getTs());
  148 + DataType type = tsKvEntry.getDataType();
  149 + if (setNullValuesEnabled) {
  150 + processSetNullValues(tenantId, entityId, tsKvEntry, ttl, futures, partition, type);
  151 + }
  152 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getSaveStmt(type) : getSaveTtlStmt(type)).bind());
  153 + stmtBuilder.setString(0, entityId.getEntityType().name())
  154 + .setUuid(1, entityId.getId())
  155 + .setString(2, tsKvEntry.getKey())
  156 + .setLong(3, partition)
  157 + .setLong(4, tsKvEntry.getTs());
  158 + addValue(tsKvEntry, stmtBuilder, 5);
  159 + if (ttl > 0) {
  160 + stmtBuilder.setInt(6, (int) ttl);
  161 + }
  162 + BoundStatement stmt = stmtBuilder.build();
  163 + futures.add(getFuture(executeAsyncWrite(tenantId, stmt), rs -> null));
  164 + return Futures.transform(Futures.allAsList(futures), result -> null, MoreExecutors.directExecutor());
  165 + }
  166 +
  167 + @Override
  168 + public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
  169 + if (isFixedPartitioning()) {
  170 + return Futures.immediateFuture(null);
  171 + }
  172 + ttl = computeTtl(ttl);
  173 + long partition = toPartitionTs(tsKvEntryTs);
  174 + log.debug("Saving partition {} for the entity [{}-{}] and key {}", partition, entityId.getEntityType(), entityId.getId(), key);
  175 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getPartitionInsertStmt() : getPartitionInsertTtlStmt()).bind());
  176 + stmtBuilder.setString(0, entityId.getEntityType().name())
  177 + .setUuid(1, entityId.getId())
  178 + .setLong(2, partition)
  179 + .setString(3, key);
  180 + if (ttl > 0) {
  181 + stmtBuilder.setInt(4, (int) ttl);
  182 + }
  183 + BoundStatement stmt = stmtBuilder.build();
  184 + return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
  185 + }
160 186
161   - private ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
  187 + @Override
  188 + public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  189 + long minPartition = toPartitionTs(query.getStartTs());
  190 + long maxPartition = toPartitionTs(query.getEndTs());
  191 +
  192 + TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
  193 +
  194 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  195 + final ListenableFuture<List<Long>> partitionsListFuture = Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
  196 +
  197 + Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
  198 + @Override
  199 + public void onSuccess(@Nullable List<Long> partitions) {
  200 + QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitions);
  201 + deleteAsync(tenantId, cursor, resultFuture);
  202 + }
  203 +
  204 + @Override
  205 + public void onFailure(Throwable t) {
  206 + log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
  207 + }
  208 + }, readResultsProcessingExecutor);
  209 + return resultFuture;
  210 + }
  211 +
  212 + @Override
  213 + public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  214 + long minPartition = toPartitionTs(query.getStartTs());
  215 + long maxPartition = toPartitionTs(query.getEndTs());
  216 + if (minPartition == maxPartition) {
  217 + return Futures.immediateFuture(null);
  218 + } else {
  219 + TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
  220 +
  221 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  222 + final ListenableFuture<List<Long>> partitionsListFuture = Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
  223 +
  224 + Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
  225 + @Override
  226 + public void onSuccess(@Nullable List<Long> partitions) {
  227 + int index = 0;
  228 + if (minPartition != query.getStartTs()) {
  229 + index = 1;
  230 + }
  231 + List<Long> partitionsToDelete = new ArrayList<>();
  232 + for (int i = index; i < partitions.size() - 1; i++) {
  233 + partitionsToDelete.add(partitions.get(i));
  234 + }
  235 + QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitionsToDelete);
  236 + deletePartitionAsync(tenantId, cursor, resultFuture);
  237 + }
  238 +
  239 + @Override
  240 + public void onFailure(Throwable t) {
  241 + log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
  242 + }
  243 + }, readResultsProcessingExecutor);
  244 + return resultFuture;
  245 + }
  246 + }
  247 +
  248 + @Override
  249 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
162 250 if (query.getAggregation() == Aggregation.NONE) {
163 251 return findAllAsyncWithLimit(tenantId, entityId, query);
164 252 } else {
... ... @@ -183,18 +271,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
183 271 }
184 272 }
185 273
186   - public boolean isFixedPartitioning() {
187   - return tsFormat.getTruncateUnit().equals(ChronoUnit.FOREVER);
188   - }
189   -
190   - private ListenableFuture<List<Long>> getPartitionsFuture(TenantId tenantId, ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {
191   - if (isFixedPartitioning()) { //no need to fetch partitions from DB
192   - return Futures.immediateFuture(FIXED_PARTITION);
193   - }
194   - TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
195   - return Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
196   - }
197   -
198 274 private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
199 275 long minPartition = toPartitionTs(query.getStartTs());
200 276 long maxPartition = toPartitionTs(query.getEndTs());
... ... @@ -287,10 +363,18 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
287 363
288 364 private AsyncFunction<TbResultSet, List<Long>> getPartitionsArrayFunction() {
289 365 return rs ->
290   - Futures.transform(rs.allRows(readResultsProcessingExecutor), rows ->
291   - rows.stream()
292   - .map(row -> row.getLong(ModelConstants.PARTITION_COLUMN)).collect(Collectors.toList()),
293   - readResultsProcessingExecutor);
  366 + Futures.transform(rs.allRows(readResultsProcessingExecutor), rows ->
  367 + rows.stream()
  368 + .map(row -> row.getLong(ModelConstants.PARTITION_COLUMN)).collect(Collectors.toList()),
  369 + readResultsProcessingExecutor);
  370 + }
  371 +
  372 + private ListenableFuture<List<Long>> getPartitionsFuture(TenantId tenantId, ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {
  373 + if (isFixedPartitioning()) { //no need to fetch partitions from DB
  374 + return Futures.immediateFuture(FIXED_PARTITION);
  375 + }
  376 + TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
  377 + return Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
294 378 }
295 379
296 380 private AsyncFunction<List<Long>, List<TbResultSet>> getFetchChunksAsyncFunction(TenantId tenantId, EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) {
... ... @@ -319,49 +403,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
319 403 };
320 404 }
321 405
322   - @Override
323   - public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
324   - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getFindLatestStmt().bind());
325   - stmtBuilder.setString(0, entityId.getEntityType().name());
326   - stmtBuilder.setUuid(1, entityId.getId());
327   - stmtBuilder.setString(2, key);
328   - BoundStatement stmt = stmtBuilder.build();
329   - log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());
330   - return getFuture(executeAsyncRead(tenantId, stmt), rs -> convertResultToTsKvEntry(key, rs.one()));
331   - }
332   -
333   - @Override
334   - public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
335   - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getFindAllLatestStmt().bind());
336   - stmtBuilder.setString(0, entityId.getEntityType().name());
337   - stmtBuilder.setUuid(1, entityId.getId());
338   - BoundStatement stmt = stmtBuilder.build();
339   - log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());
340   - return getFutureAsync(executeAsyncRead(tenantId, stmt), rs -> convertAsyncResultSetToTsKvEntryList(rs));
341   - }
342   -
343   - @Override
344   - public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
345   - List<ListenableFuture<Void>> futures = new ArrayList<>();
346   - ttl = computeTtl(ttl);
347   - long partition = toPartitionTs(tsKvEntry.getTs());
348   - DataType type = tsKvEntry.getDataType();
349   - if (setNullValuesEnabled) {
350   - processSetNullValues(tenantId, entityId, tsKvEntry, ttl, futures, partition, type);
351   - }
352   - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getSaveStmt(type) : getSaveTtlStmt(type)).bind());
353   - stmtBuilder.setString(0, entityId.getEntityType().name())
354   - .setUuid(1, entityId.getId())
355   - .setString(2, tsKvEntry.getKey())
356   - .setLong(3, partition)
357   - .setLong(4, tsKvEntry.getTs());
358   - addValue(tsKvEntry, stmtBuilder, 5);
359   - if (ttl > 0) {
360   - stmtBuilder.setInt(6, (int) ttl);
361   - }
362   - BoundStatement stmt = stmtBuilder.build();
363   - futures.add(getFuture(executeAsyncWrite(tenantId, stmt), rs -> null));
364   - return Futures.transform(Futures.allAsList(futures), result -> null, MoreExecutors.directExecutor());
  406 + private boolean isFixedPartitioning() {
  407 + return tsFormat.getTruncateUnit().equals(ChronoUnit.FOREVER);
365 408 }
366 409
367 410 private void processSetNullValues(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl, List<ListenableFuture<Void>> futures, long partition, DataType type) {
... ... @@ -414,26 +457,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
414 457 return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
415 458 }
416 459
417   - @Override
418   - public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
419   - if (isFixedPartitioning()) {
420   - return Futures.immediateFuture(null);
421   - }
422   - ttl = computeTtl(ttl);
423   - long partition = toPartitionTs(tsKvEntryTs);
424   - log.debug("Saving partition {} for the entity [{}-{}] and key {}", partition, entityId.getEntityType(), entityId.getId(), key);
425   - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getPartitionInsertStmt() : getPartitionInsertTtlStmt()).bind());
426   - stmtBuilder.setString(0, entityId.getEntityType().name())
427   - .setUuid(1, entityId.getId())
428   - .setLong(2, partition)
429   - .setString(3, key);
430   - if (ttl > 0) {
431   - stmtBuilder.setInt(4, (int) ttl);
432   - }
433   - BoundStatement stmt = stmtBuilder.build();
434   - return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
435   - }
436   -
437 460 private long computeTtl(long ttl) {
438 461 if (systemTtl > 0) {
439 462 if (ttl == 0) {
... ... @@ -445,53 +468,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
445 468 return ttl;
446 469 }
447 470
448   - @Override
449   - public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
450   - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getLatestStmt().bind());
451   - stmtBuilder.setString(0, entityId.getEntityType().name())
452   - .setUuid(1, entityId.getId())
453   - .setString(2, tsKvEntry.getKey())
454   - .setLong(3, tsKvEntry.getTs())
455   - .set(4, tsKvEntry.getBooleanValue().orElse(null), Boolean.class)
456   - .set(5, tsKvEntry.getStrValue().orElse(null), String.class)
457   - .set(6, tsKvEntry.getLongValue().orElse(null), Long.class)
458   - .set(7, tsKvEntry.getDoubleValue().orElse(null), Double.class);
459   - Optional<String> jsonV = tsKvEntry.getJsonValue();
460   - if (jsonV.isPresent()) {
461   - stmtBuilder.setString(8, tsKvEntry.getJsonValue().get());
462   - } else {
463   - stmtBuilder.setToNull(8);
464   - }
465   - BoundStatement stmt = stmtBuilder.build();
466   -
467   - return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
468   - }
469   -
470   - @Override
471   - public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
472   - long minPartition = toPartitionTs(query.getStartTs());
473   - long maxPartition = toPartitionTs(query.getEndTs());
474   -
475   - TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
476   -
477   - final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
478   - final ListenableFuture<List<Long>> partitionsListFuture = Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
479   -
480   - Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
481   - @Override
482   - public void onSuccess(@Nullable List<Long> partitions) {
483   - QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitions);
484   - deleteAsync(tenantId, cursor, resultFuture);
485   - }
486   -
487   - @Override
488   - public void onFailure(Throwable t) {
489   - log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
490   - }
491   - }, readResultsProcessingExecutor);
492   - return resultFuture;
493   - }
494   -
495 471 private void deleteAsync(TenantId tenantId, final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
496 472 if (!cursor.hasNextPartition()) {
497 473 resultFuture.set(null);
... ... @@ -534,119 +510,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
534 510 return deleteStmt;
535 511 }
536 512
537   - @Override
538   - public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
539   - ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(tenantId, entityId, query.getKey());
540   -
541   - ListenableFuture<Boolean> booleanFuture = Futures.transform(latestEntryFuture, latestEntry -> {
542   - long ts = latestEntry.getTs();
543   - if (ts > query.getStartTs() && ts <= query.getEndTs()) {
544   - return true;
545   - } else {
546   - log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey());
547   - }
548   - return false;
549   - }, readResultsProcessingExecutor);
550   -
551   - ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
552   - if (isRemove) {
553   - return deleteLatest(tenantId, entityId, query.getKey());
554   - }
555   - return Futures.immediateFuture(null);
556   - }, readResultsProcessingExecutor);
557   -
558   - final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
559   - Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {
560   - @Override
561   - public void onSuccess(@Nullable Void result) {
562   - if (query.getRewriteLatestIfDeleted()) {
563   - ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
564   - if (isRemove) {
565   - return getNewLatestEntryFuture(tenantId, entityId, query);
566   - }
567   - return Futures.immediateFuture(null);
568   - }, readResultsProcessingExecutor);
569   -
570   - try {
571   - resultFuture.set(savedLatestFuture.get());
572   - } catch (InterruptedException | ExecutionException e) {
573   - log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);
574   - }
575   - } else {
576   - resultFuture.set(null);
577   - }
578   - }
579   -
580   - @Override
581   - public void onFailure(Throwable t) {
582   - log.warn("[{}] Failed to process remove of the latest value", entityId, t);
583   - }
584   - }, MoreExecutors.directExecutor());
585   - return resultFuture;
586   - }
587   -
588   - private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
589   - long startTs = 0;
590   - long endTs = query.getStartTs() - 1;
591   - ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
592   - Aggregation.NONE, DESC_ORDER);
593   - ListenableFuture<List<TsKvEntry>> future = findAllAsync(tenantId, entityId, findNewLatestQuery);
594   -
595   - return Futures.transformAsync(future, entryList -> {
596   - if (entryList.size() == 1) {
597   - return saveLatest(tenantId, entityId, entryList.get(0));
598   - } else {
599   - log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
600   - }
601   - return Futures.immediateFuture(null);
602   - }, readResultsProcessingExecutor);
603   - }
604   -
605   - private ListenableFuture<Void> deleteLatest(TenantId tenantId, EntityId entityId, String key) {
606   - Statement delete = QueryBuilder.deleteFrom(ModelConstants.TS_KV_LATEST_CF)
607   - .whereColumn(ModelConstants.ENTITY_TYPE_COLUMN).isEqualTo(literal(entityId.getEntityType().name()))
608   - .whereColumn(ModelConstants.ENTITY_ID_COLUMN).isEqualTo(literal(entityId.getId()))
609   - .whereColumn(ModelConstants.KEY_COLUMN).isEqualTo(literal(key)).build();
610   - log.debug("Remove request: {}", delete.toString());
611   - return getFuture(executeAsyncWrite(tenantId, delete), rs -> null);
612   - }
613   -
614   - @Override
615   - public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
616   - long minPartition = toPartitionTs(query.getStartTs());
617   - long maxPartition = toPartitionTs(query.getEndTs());
618   - if (minPartition == maxPartition) {
619   - return Futures.immediateFuture(null);
620   - } else {
621   - TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
622   -
623   - final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
624   - final ListenableFuture<List<Long>> partitionsListFuture = Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
625   -
626   - Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
627   - @Override
628   - public void onSuccess(@Nullable List<Long> partitions) {
629   - int index = 0;
630   - if (minPartition != query.getStartTs()) {
631   - index = 1;
632   - }
633   - List<Long> partitionsToDelete = new ArrayList<>();
634   - for (int i = index; i < partitions.size() - 1; i++) {
635   - partitionsToDelete.add(partitions.get(i));
636   - }
637   - QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitionsToDelete);
638   - deletePartitionAsync(tenantId, cursor, resultFuture);
639   - }
640   -
641   - @Override
642   - public void onFailure(Throwable t) {
643   - log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
644   - }
645   - }, readResultsProcessingExecutor);
646   - return resultFuture;
647   - }
648   - }
649   -
650 513 private void deletePartitionAsync(TenantId tenantId, final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
651 514 if (!cursor.hasNextPartition()) {
652 515 resultFuture.set(null);
... ... @@ -685,79 +548,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
685 548 return deletePartitionStmt;
686 549 }
687 550
688   - private ListenableFuture<List<TsKvEntry>> convertAsyncResultSetToTsKvEntryList(TbResultSet rs) {
689   - return Futures.transform(rs.allRows(readResultsProcessingExecutor),
690   - rows -> this.convertResultToTsKvEntryList(rows), readResultsProcessingExecutor);
691   - }
692   -
693   - private List<TsKvEntry> convertResultToTsKvEntryList(List<Row> rows) {
694   - List<TsKvEntry> entries = new ArrayList<>(rows.size());
695   - if (!rows.isEmpty()) {
696   - rows.forEach(row -> entries.add(convertResultToTsKvEntry(row)));
697   - }
698   - return entries;
699   - }
700   -
701   - private TsKvEntry convertResultToTsKvEntry(String key, Row row) {
702   - if (row != null) {
703   - long ts = row.getLong(ModelConstants.TS_COLUMN);
704   - return new BasicTsKvEntry(ts, toKvEntry(row, key));
705   - } else {
706   - return new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));
707   - }
708   - }
709   -
710   - private TsKvEntry convertResultToTsKvEntry(Row row) {
711   - String key = row.getString(ModelConstants.KEY_COLUMN);
712   - long ts = row.getLong(ModelConstants.TS_COLUMN);
713   - return new BasicTsKvEntry(ts, toKvEntry(row, key));
714   - }
715   -
716   - public static KvEntry toKvEntry(Row row, String key) {
717   - KvEntry kvEntry = null;
718   - String strV = row.get(ModelConstants.STRING_VALUE_COLUMN, String.class);
719   - if (strV != null) {
720   - kvEntry = new StringDataEntry(key, strV);
721   - } else {
722   - Long longV = row.get(ModelConstants.LONG_VALUE_COLUMN, Long.class);
723   - if (longV != null) {
724   - kvEntry = new LongDataEntry(key, longV);
725   - } else {
726   - Double doubleV = row.get(ModelConstants.DOUBLE_VALUE_COLUMN, Double.class);
727   - if (doubleV != null) {
728   - kvEntry = new DoubleDataEntry(key, doubleV);
729   - } else {
730   - Boolean boolV = row.get(ModelConstants.BOOLEAN_VALUE_COLUMN, Boolean.class);
731   - if (boolV != null) {
732   - kvEntry = new BooleanDataEntry(key, boolV);
733   - } else {
734   - String jsonV = row.get(ModelConstants.JSON_VALUE_COLUMN, String.class);
735   - if (StringUtils.isNoneEmpty(jsonV)) {
736   - kvEntry = new JsonDataEntry(key, jsonV);
737   - } else {
738   - log.warn("All values in key-value row are nullable ");
739   - }
740   - }
741   - }
742   - }
743   - }
744   - return kvEntry;
745   - }
746   -
747   - /**
748   - * Select existing partitions from the table
749   - * <code>{@link ModelConstants#TS_KV_PARTITIONS_CF}</code> for the given entity
750   - */
751   - private TbResultSetFuture fetchPartitions(TenantId tenantId, EntityId entityId, String key, long minPartition, long maxPartition) {
752   - Select select = QueryBuilder.selectFrom(ModelConstants.TS_KV_PARTITIONS_CF).column(ModelConstants.PARTITION_COLUMN)
753   - .whereColumn(ModelConstants.ENTITY_TYPE_COLUMN).isEqualTo(literal(entityId.getEntityType().name()))
754   - .whereColumn(ModelConstants.ENTITY_ID_COLUMN).isEqualTo(literal(entityId.getId()))
755   - .whereColumn(ModelConstants.KEY_COLUMN).isEqualTo(literal(key))
756   - .whereColumn(ModelConstants.PARTITION_COLUMN).isGreaterThanOrEqualTo(literal(minPartition))
757   - .whereColumn(ModelConstants.PARTITION_COLUMN).isLessThanOrEqualTo(literal(maxPartition));
758   - return executeAsyncRead(tenantId, select.build());
759   - }
760   -
761 551 private PreparedStatement getSaveStmt(DataType dataType) {
762 552 if (saveStmts == null) {
763 553 saveStmts = new PreparedStatement[DataType.values().length];
... ... @@ -792,63 +582,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
792 582 return saveTtlStmts[dataType.ordinal()];
793 583 }
794 584
795   - private PreparedStatement getFetchStmt(Aggregation aggType, String orderBy) {
796   - switch (orderBy) {
797   - case ASC_ORDER:
798   - if (fetchStmtsAsc == null) {
799   - fetchStmtsAsc = initFetchStmt(orderBy);
800   - }
801   - return fetchStmtsAsc[aggType.ordinal()];
802   - case DESC_ORDER:
803   - if (fetchStmtsDesc == null) {
804   - fetchStmtsDesc = initFetchStmt(orderBy);
805   - }
806   - return fetchStmtsDesc[aggType.ordinal()];
807   - default:
808   - throw new RuntimeException("Not supported" + orderBy + "order!");
809   - }
810   - }
811   -
812   - private PreparedStatement[] initFetchStmt(String orderBy) {
813   - PreparedStatement[] fetchStmts = new PreparedStatement[Aggregation.values().length];
814   - for (Aggregation type : Aggregation.values()) {
815   - if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) {
816   - fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()];
817   - } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) {
818   - fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()];
819   - } else {
820   - fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX +
821   - String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF
822   - + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
823   - + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
824   - + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM
825   - + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
826   - + "AND " + ModelConstants.TS_COLUMN + " > ? "
827   - + "AND " + ModelConstants.TS_COLUMN + " <= ?"
828   - + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " " + orderBy + " LIMIT ?" : ""));
829   - }
830   - }
831   - return fetchStmts;
832   - }
833   -
834   - private PreparedStatement getLatestStmt() {
835   - if (latestInsertStmt == null) {
836   - latestInsertStmt = prepare(INSERT_INTO + ModelConstants.TS_KV_LATEST_CF +
837   - "(" + ModelConstants.ENTITY_TYPE_COLUMN +
838   - "," + ModelConstants.ENTITY_ID_COLUMN +
839   - "," + ModelConstants.KEY_COLUMN +
840   - "," + ModelConstants.TS_COLUMN +
841   - "," + ModelConstants.BOOLEAN_VALUE_COLUMN +
842   - "," + ModelConstants.STRING_VALUE_COLUMN +
843   - "," + ModelConstants.LONG_VALUE_COLUMN +
844   - "," + ModelConstants.DOUBLE_VALUE_COLUMN +
845   - "," + ModelConstants.JSON_VALUE_COLUMN + ")" +
846   - " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)");
847   - }
848   - return latestInsertStmt;
849   - }
850   -
851   -
852 585 private PreparedStatement getPartitionInsertStmt() {
853 586 if (partitionInsertStmt == null) {
854 587 partitionInsertStmt = prepare(INSERT_INTO + ModelConstants.TS_KV_PARTITIONS_CF +
... ... @@ -873,42 +606,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
873 606 return partitionInsertTtlStmt;
874 607 }
875 608
876   -
877   - private PreparedStatement getFindLatestStmt() {
878   - if (findLatestStmt == null) {
879   - findLatestStmt = prepare(SELECT_PREFIX +
880   - ModelConstants.KEY_COLUMN + "," +
881   - ModelConstants.TS_COLUMN + "," +
882   - ModelConstants.STRING_VALUE_COLUMN + "," +
883   - ModelConstants.BOOLEAN_VALUE_COLUMN + "," +
884   - ModelConstants.LONG_VALUE_COLUMN + "," +
885   - ModelConstants.DOUBLE_VALUE_COLUMN + "," +
886   - ModelConstants.JSON_VALUE_COLUMN + " " +
887   - "FROM " + ModelConstants.TS_KV_LATEST_CF + " " +
888   - "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM +
889   - "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM +
890   - "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM);
891   - }
892   - return findLatestStmt;
893   - }
894   -
895   - private PreparedStatement getFindAllLatestStmt() {
896   - if (findAllLatestStmt == null) {
897   - findAllLatestStmt = prepare(SELECT_PREFIX +
898   - ModelConstants.KEY_COLUMN + "," +
899   - ModelConstants.TS_COLUMN + "," +
900   - ModelConstants.STRING_VALUE_COLUMN + "," +
901   - ModelConstants.BOOLEAN_VALUE_COLUMN + "," +
902   - ModelConstants.LONG_VALUE_COLUMN + "," +
903   - ModelConstants.DOUBLE_VALUE_COLUMN + "," +
904   - ModelConstants.JSON_VALUE_COLUMN + " " +
905   - "FROM " + ModelConstants.TS_KV_LATEST_CF + " " +
906   - "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM +
907   - "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM);
908   - }
909   - return findAllLatestStmt;
910   - }
911   -
912 609 private static String getColumnName(DataType type) {
913 610 switch (type) {
914 611 case BOOLEAN:
... ... @@ -951,4 +648,56 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
951 648 }
952 649 }
953 650
  651 + /**
  652 + // * Select existing partitions from the table
  653 + // * <code>{@link ModelConstants#TS_KV_PARTITIONS_CF}</code> for the given entity
  654 + // */
  655 + private TbResultSetFuture fetchPartitions(TenantId tenantId, EntityId entityId, String key, long minPartition, long maxPartition) {
  656 + Select select = QueryBuilder.selectFrom(ModelConstants.TS_KV_PARTITIONS_CF).column(ModelConstants.PARTITION_COLUMN)
  657 + .whereColumn(ModelConstants.ENTITY_TYPE_COLUMN).isEqualTo(literal(entityId.getEntityType().name()))
  658 + .whereColumn(ModelConstants.ENTITY_ID_COLUMN).isEqualTo(literal(entityId.getId()))
  659 + .whereColumn(ModelConstants.KEY_COLUMN).isEqualTo(literal(key))
  660 + .whereColumn(ModelConstants.PARTITION_COLUMN).isGreaterThanOrEqualTo(literal(minPartition))
  661 + .whereColumn(ModelConstants.PARTITION_COLUMN).isLessThanOrEqualTo(literal(maxPartition));
  662 + return executeAsyncRead(tenantId, select.build());
  663 + }
  664 +
  665 + private PreparedStatement getFetchStmt(Aggregation aggType, String orderBy) {
  666 + switch (orderBy) {
  667 + case ASC_ORDER:
  668 + if (fetchStmtsAsc == null) {
  669 + fetchStmtsAsc = initFetchStmt(orderBy);
  670 + }
  671 + return fetchStmtsAsc[aggType.ordinal()];
  672 + case DESC_ORDER:
  673 + if (fetchStmtsDesc == null) {
  674 + fetchStmtsDesc = initFetchStmt(orderBy);
  675 + }
  676 + return fetchStmtsDesc[aggType.ordinal()];
  677 + default:
  678 + throw new RuntimeException("Not supported" + orderBy + "order!");
  679 + }
  680 + }
  681 +
  682 + private PreparedStatement[] initFetchStmt(String orderBy) {
  683 + PreparedStatement[] fetchStmts = new PreparedStatement[Aggregation.values().length];
  684 + for (Aggregation type : Aggregation.values()) {
  685 + if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) {
  686 + fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()];
  687 + } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) {
  688 + fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()];
  689 + } else {
  690 + fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX +
  691 + String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF
  692 + + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
  693 + + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
  694 + + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM
  695 + + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
  696 + + "AND " + ModelConstants.TS_COLUMN + " > ? "
  697 + + "AND " + ModelConstants.TS_COLUMN + " <= ?"
  698 + + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " " + orderBy + " LIMIT ?" : ""));
  699 + }
  700 + }
  701 + return fetchStmts;
  702 + }
954 703 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.timeseries;
  17 +
  18 +import com.datastax.oss.driver.api.core.cql.BoundStatement;
  19 +import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder;
  20 +import com.datastax.oss.driver.api.core.cql.PreparedStatement;
  21 +import com.datastax.oss.driver.api.core.cql.Row;
  22 +import com.datastax.oss.driver.api.core.cql.Statement;
  23 +import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
  24 +import com.google.common.util.concurrent.FutureCallback;
  25 +import com.google.common.util.concurrent.Futures;
  26 +import com.google.common.util.concurrent.ListenableFuture;
  27 +import com.google.common.util.concurrent.MoreExecutors;
  28 +import lombok.extern.slf4j.Slf4j;
  29 +import org.springframework.beans.factory.annotation.Autowired;
  30 +import org.springframework.stereotype.Component;
  31 +import org.thingsboard.server.common.data.id.EntityId;
  32 +import org.thingsboard.server.common.data.id.TenantId;
  33 +import org.thingsboard.server.common.data.kv.Aggregation;
  34 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
  35 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  36 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  37 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  38 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  39 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  40 +import org.thingsboard.server.dao.model.ModelConstants;
  41 +import org.thingsboard.server.dao.nosql.TbResultSet;
  42 +import org.thingsboard.server.dao.sqlts.AggregationTimeseriesDao;
  43 +import org.thingsboard.server.dao.util.NoSqlTsLatestDao;
  44 +
  45 +import javax.annotation.Nullable;
  46 +import java.util.List;
  47 +import java.util.Optional;
  48 +import java.util.concurrent.ExecutionException;
  49 +
  50 +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal;
  51 +
  52 +@Component
  53 +@Slf4j
  54 +@NoSqlTsLatestDao
  55 +public class CassandraBaseTimeseriesLatestDao extends AbstractCassandraBaseTimeseriesDao implements TimeseriesLatestDao {
  56 +
  57 + @Autowired
  58 + protected AggregationTimeseriesDao aggregationTimeseriesDao;
  59 +
  60 + private PreparedStatement latestInsertStmt;
  61 + private PreparedStatement findLatestStmt;
  62 + private PreparedStatement findAllLatestStmt;
  63 +
  64 + @Override
  65 + public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
  66 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getFindLatestStmt().bind());
  67 + stmtBuilder.setString(0, entityId.getEntityType().name());
  68 + stmtBuilder.setUuid(1, entityId.getId());
  69 + stmtBuilder.setString(2, key);
  70 + BoundStatement stmt = stmtBuilder.build();
  71 + log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());
  72 + return getFuture(executeAsyncRead(tenantId, stmt), rs -> convertResultToTsKvEntry(key, rs.one()));
  73 + }
  74 +
  75 + @Override
  76 + public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
  77 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getFindAllLatestStmt().bind());
  78 + stmtBuilder.setString(0, entityId.getEntityType().name());
  79 + stmtBuilder.setUuid(1, entityId.getId());
  80 + BoundStatement stmt = stmtBuilder.build();
  81 + log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());
  82 + return getFutureAsync(executeAsyncRead(tenantId, stmt), rs -> convertAsyncResultSetToTsKvEntryList(rs));
  83 + }
  84 +
  85 + @Override
  86 + public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
  87 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getLatestStmt().bind());
  88 + stmtBuilder.setString(0, entityId.getEntityType().name())
  89 + .setUuid(1, entityId.getId())
  90 + .setString(2, tsKvEntry.getKey())
  91 + .setLong(3, tsKvEntry.getTs())
  92 + .set(4, tsKvEntry.getBooleanValue().orElse(null), Boolean.class)
  93 + .set(5, tsKvEntry.getStrValue().orElse(null), String.class)
  94 + .set(6, tsKvEntry.getLongValue().orElse(null), Long.class)
  95 + .set(7, tsKvEntry.getDoubleValue().orElse(null), Double.class);
  96 + Optional<String> jsonV = tsKvEntry.getJsonValue();
  97 + if (jsonV.isPresent()) {
  98 + stmtBuilder.setString(8, tsKvEntry.getJsonValue().get());
  99 + } else {
  100 + stmtBuilder.setToNull(8);
  101 + }
  102 + BoundStatement stmt = stmtBuilder.build();
  103 +
  104 + return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
  105 + }
  106 +
  107 + @Override
  108 + public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  109 + ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(tenantId, entityId, query.getKey());
  110 +
  111 + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestEntryFuture, latestEntry -> {
  112 + long ts = latestEntry.getTs();
  113 + if (ts > query.getStartTs() && ts <= query.getEndTs()) {
  114 + return true;
  115 + } else {
  116 + log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey());
  117 + }
  118 + return false;
  119 + }, readResultsProcessingExecutor);
  120 +
  121 + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  122 + if (isRemove) {
  123 + return deleteLatest(tenantId, entityId, query.getKey());
  124 + }
  125 + return Futures.immediateFuture(null);
  126 + }, readResultsProcessingExecutor);
  127 +
  128 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  129 + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {
  130 + @Override
  131 + public void onSuccess(@Nullable Void result) {
  132 + if (query.getRewriteLatestIfDeleted()) {
  133 + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  134 + if (isRemove) {
  135 + return getNewLatestEntryFuture(tenantId, entityId, query);
  136 + }
  137 + return Futures.immediateFuture(null);
  138 + }, readResultsProcessingExecutor);
  139 +
  140 + try {
  141 + resultFuture.set(savedLatestFuture.get());
  142 + } catch (InterruptedException | ExecutionException e) {
  143 + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);
  144 + }
  145 + } else {
  146 + resultFuture.set(null);
  147 + }
  148 + }
  149 +
  150 + @Override
  151 + public void onFailure(Throwable t) {
  152 + log.warn("[{}] Failed to process remove of the latest value", entityId, t);
  153 + }
  154 + }, MoreExecutors.directExecutor());
  155 + return resultFuture;
  156 + }
  157 +
  158 + private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  159 + long startTs = 0;
  160 + long endTs = query.getStartTs() - 1;
  161 + ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
  162 + Aggregation.NONE, DESC_ORDER);
  163 + ListenableFuture<List<TsKvEntry>> future = aggregationTimeseriesDao.findAllAsync(tenantId, entityId, findNewLatestQuery);
  164 +
  165 + return Futures.transformAsync(future, entryList -> {
  166 + if (entryList.size() == 1) {
  167 + return saveLatest(tenantId, entityId, entryList.get(0));
  168 + } else {
  169 + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
  170 + }
  171 + return Futures.immediateFuture(null);
  172 + }, readResultsProcessingExecutor);
  173 + }
  174 +
  175 + private ListenableFuture<Void> deleteLatest(TenantId tenantId, EntityId entityId, String key) {
  176 + Statement delete = QueryBuilder.deleteFrom(ModelConstants.TS_KV_LATEST_CF)
  177 + .whereColumn(ModelConstants.ENTITY_TYPE_COLUMN).isEqualTo(literal(entityId.getEntityType().name()))
  178 + .whereColumn(ModelConstants.ENTITY_ID_COLUMN).isEqualTo(literal(entityId.getId()))
  179 + .whereColumn(ModelConstants.KEY_COLUMN).isEqualTo(literal(key)).build();
  180 + log.debug("Remove request: {}", delete.toString());
  181 + return getFuture(executeAsyncWrite(tenantId, delete), rs -> null);
  182 + }
  183 +
  184 + private ListenableFuture<List<TsKvEntry>> convertAsyncResultSetToTsKvEntryList(TbResultSet rs) {
  185 + return Futures.transform(rs.allRows(readResultsProcessingExecutor),
  186 + rows -> this.convertResultToTsKvEntryList(rows), readResultsProcessingExecutor);
  187 + }
  188 +
  189 + private TsKvEntry convertResultToTsKvEntry(String key, Row row) {
  190 + if (row != null) {
  191 + long ts = row.getLong(ModelConstants.TS_COLUMN);
  192 + return new BasicTsKvEntry(ts, toKvEntry(row, key));
  193 + } else {
  194 + return new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));
  195 + }
  196 + }
  197 +
  198 + private PreparedStatement getLatestStmt() {
  199 + if (latestInsertStmt == null) {
  200 + latestInsertStmt = prepare(INSERT_INTO + ModelConstants.TS_KV_LATEST_CF +
  201 + "(" + ModelConstants.ENTITY_TYPE_COLUMN +
  202 + "," + ModelConstants.ENTITY_ID_COLUMN +
  203 + "," + ModelConstants.KEY_COLUMN +
  204 + "," + ModelConstants.TS_COLUMN +
  205 + "," + ModelConstants.BOOLEAN_VALUE_COLUMN +
  206 + "," + ModelConstants.STRING_VALUE_COLUMN +
  207 + "," + ModelConstants.LONG_VALUE_COLUMN +
  208 + "," + ModelConstants.DOUBLE_VALUE_COLUMN +
  209 + "," + ModelConstants.JSON_VALUE_COLUMN + ")" +
  210 + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)");
  211 + }
  212 + return latestInsertStmt;
  213 + }
  214 +
  215 + private PreparedStatement getFindLatestStmt() {
  216 + if (findLatestStmt == null) {
  217 + findLatestStmt = prepare(SELECT_PREFIX +
  218 + ModelConstants.KEY_COLUMN + "," +
  219 + ModelConstants.TS_COLUMN + "," +
  220 + ModelConstants.STRING_VALUE_COLUMN + "," +
  221 + ModelConstants.BOOLEAN_VALUE_COLUMN + "," +
  222 + ModelConstants.LONG_VALUE_COLUMN + "," +
  223 + ModelConstants.DOUBLE_VALUE_COLUMN + "," +
  224 + ModelConstants.JSON_VALUE_COLUMN + " " +
  225 + "FROM " + ModelConstants.TS_KV_LATEST_CF + " " +
  226 + "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM +
  227 + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM +
  228 + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM);
  229 + }
  230 + return findLatestStmt;
  231 + }
  232 +
  233 + private PreparedStatement getFindAllLatestStmt() {
  234 + if (findAllLatestStmt == null) {
  235 + findAllLatestStmt = prepare(SELECT_PREFIX +
  236 + ModelConstants.KEY_COLUMN + "," +
  237 + ModelConstants.TS_COLUMN + "," +
  238 + ModelConstants.STRING_VALUE_COLUMN + "," +
  239 + ModelConstants.BOOLEAN_VALUE_COLUMN + "," +
  240 + ModelConstants.LONG_VALUE_COLUMN + "," +
  241 + ModelConstants.DOUBLE_VALUE_COLUMN + "," +
  242 + ModelConstants.JSON_VALUE_COLUMN + " " +
  243 + "FROM " + ModelConstants.TS_KV_LATEST_CF + " " +
  244 + "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM +
  245 + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM);
  246 + }
  247 + return findAllLatestStmt;
  248 + }
  249 +}
... ...
... ... @@ -31,19 +31,11 @@ public interface TimeseriesDao {
31 31
32 32 ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries);
33 33
34   - ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key);
35   -
36   - ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId);
37   -
38 34 ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl);
39 35
40 36 ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl);
41 37
42   - ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry);
43   -
44 38 ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
45 39
46   - ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
47   -
48 40 ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
49 41 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.timeseries;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  22 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  23 +
  24 +import java.util.List;
  25 +
  26 +public interface TimeseriesLatestDao {
  27 +
  28 + ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key);
  29 +
  30 + ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId);
  31 +
  32 + ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry);
  33 +
  34 + ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
  35 +
  36 +}
... ...
... ... @@ -113,7 +113,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
113 113 public User saveUser(User user) {
114 114 log.trace("Executing saveUser [{}]", user);
115 115 userValidator.validate(user, User::getTenantId);
116   - if (user.getId() == null && !userLoginCaseSensitive) {
  116 + if (!userLoginCaseSensitive) {
117 117 user.setEmail(user.getEmail().toLowerCase());
118 118 }
119 119 User savedUser = userDao.save(user.getTenantId(), user);
... ...
  1 +--
  2 +-- Copyright © 2016-2020 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +CREATE KEYSPACE IF NOT EXISTS thingsboard
  18 +WITH replication = {
  19 + 'class' : 'SimpleStrategy',
  20 + 'replication_factor' : 1
  21 +};
  22 +
  23 +CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_latest_cf (
  24 + entity_type text, // (DEVICE, CUSTOMER, TENANT)
  25 + entity_id timeuuid,
  26 + key text,
  27 + ts bigint,
  28 + bool_v boolean,
  29 + str_v text,
  30 + long_v bigint,
  31 + dbl_v double,
  32 + json_v text,
  33 + PRIMARY KEY (( entity_type, entity_id ), key)
  34 +) WITH compaction = { 'class' : 'LeveledCompactionStrategy' };
... ...
... ... @@ -42,16 +42,3 @@ CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_partitions_cf (
42 42 PRIMARY KEY (( entity_type, entity_id, key ), partition)
43 43 ) WITH CLUSTERING ORDER BY ( partition ASC )
44 44 AND compaction = { 'class' : 'LeveledCompactionStrategy' };
45   -
46   -CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_latest_cf (
47   - entity_type text, // (DEVICE, CUSTOMER, TENANT)
48   - entity_id timeuuid,
49   - key text,
50   - ts bigint,
51   - bool_v boolean,
52   - str_v text,
53   - long_v bigint,
54   - dbl_v double,
55   - json_v text,
56   - PRIMARY KEY (( entity_type, entity_id ), key)
57   -) WITH compaction = { 'class' : 'LeveledCompactionStrategy' };
... ...
... ... @@ -272,3 +272,20 @@ CREATE TABLE IF NOT EXISTS entity_view (
272 272 additional_info varchar
273 273 );
274 274
  275 +CREATE TABLE IF NOT EXISTS ts_kv_latest (
  276 + entity_id uuid NOT NULL,
  277 + key int NOT NULL,
  278 + ts bigint NOT NULL,
  279 + bool_v boolean,
  280 + str_v varchar(10000000),
  281 + long_v bigint,
  282 + dbl_v double precision,
  283 + json_v varchar(10000000),
  284 + CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
  285 +);
  286 +
  287 +CREATE TABLE IF NOT EXISTS ts_kv_dictionary (
  288 + key varchar(255) NOT NULL,
  289 + key_id int GENERATED BY DEFAULT AS IDENTITY(start with 0 increment by 1) UNIQUE,
  290 + CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
  291 +);
... ...
... ... @@ -14,6 +14,13 @@
14 14 -- limitations under the License.
15 15 --
16 16
  17 +CREATE TABLE IF NOT EXISTS tb_schema_settings
  18 +(
  19 + schema_version bigint NOT NULL,
  20 + CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
  21 +);
  22 +
  23 +INSERT INTO tb_schema_settings (schema_version) VALUES (3001000) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 3001000;
17 24
18 25 CREATE TABLE IF NOT EXISTS admin_settings (
19 26 id uuid NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY,
... ... @@ -331,3 +338,4 @@ BEGIN
331 338 '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
332 339 END;
333 340 $$ LANGUAGE plpgsql;
  341 +
... ...
... ... @@ -46,14 +46,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest (
46 46 CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
47 47 );
48 48
49   -CREATE TABLE IF NOT EXISTS tb_schema_settings
50   -(
51   - schema_version bigint NOT NULL,
52   - CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
53   -);
54   -
55   -INSERT INTO tb_schema_settings (schema_version) VALUES (2005001) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005001;
56   -
57 49 CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
58 50 $$
59 51 BEGIN
... ...
... ... @@ -28,18 +28,6 @@ CREATE TABLE IF NOT EXISTS ts_kv (
28 28 CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts)
29 29 );
30 30
31   -CREATE TABLE IF NOT EXISTS ts_kv_latest (
32   - entity_id uuid NOT NULL,
33   - key int NOT NULL,
34   - ts bigint NOT NULL,
35   - bool_v boolean,
36   - str_v varchar(10000000),
37   - long_v bigint,
38   - dbl_v double precision,
39   - json_v varchar(10000000),
40   - CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
41   -);
42   -
43 31 CREATE TABLE IF NOT EXISTS ts_kv_dictionary (
44 32 key varchar(255) NOT NULL,
45 33 key_id int GENERATED BY DEFAULT AS IDENTITY(start with 0 increment by 1) UNIQUE,
... ...
... ... @@ -27,19 +27,6 @@ CREATE TABLE IF NOT EXISTS ts_kv
27 27 CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts)
28 28 ) PARTITION BY RANGE (ts);
29 29
30   -CREATE TABLE IF NOT EXISTS ts_kv_latest
31   -(
32   - entity_id uuid NOT NULL,
33   - key int NOT NULL,
34   - ts bigint NOT NULL,
35   - bool_v boolean,
36   - str_v varchar(10000000),
37   - long_v bigint,
38   - dbl_v double precision,
39   - json_v json,
40   - CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
41   -);
42   -
43 30 CREATE TABLE IF NOT EXISTS ts_kv_dictionary
44 31 (
45 32 key varchar(255) NOT NULL,
... ... @@ -47,14 +34,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary
47 34 CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
48 35 );
49 36
50   -CREATE TABLE IF NOT EXISTS tb_schema_settings
51   -(
52   - schema_version bigint NOT NULL,
53   - CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
54   -);
55   -
56   -INSERT INTO tb_schema_settings (schema_version) VALUES (2005001) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005001;
57   -
58 37 CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar, IN system_ttl bigint, INOUT deleted bigint)
59 38 LANGUAGE plpgsql AS
60 39 $$
... ...
... ... @@ -22,7 +22,6 @@ import org.dbunit.ext.hsqldb.HsqldbDataTypeFactory;
22 22 import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.context.annotation.Bean;
24 24 import org.springframework.context.annotation.Configuration;
25   -import org.thingsboard.server.dao.util.SqlDao;
26 25
27 26 import javax.sql.DataSource;
28 27 import java.io.IOException;
... ... @@ -32,7 +31,6 @@ import java.sql.SQLException;
32 31 * Created by Valerii Sosliuk on 5/6/2017.
33 32 */
34 33 @Configuration
35   -@SqlDao
36 34 public class JpaDbunitTestConfig {
37 35
38 36 @Autowired
... ...
... ... @@ -40,7 +40,8 @@ public class NoSqlDaoServiceTestSuite {
40 40 public static CustomCassandraCQLUnit cassandraUnit =
41 41 new CustomCassandraCQLUnit(
42 42 Arrays.asList(
43   - new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false)
  43 + new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
  44 + new ClassPathCQLDataSet("cassandra/schema-ts-latest.cql", false, false)
44 45 ),
45 46 "cassandra-test.yaml", 30000L);
46 47
... ...
1 1 database.ts.type=cassandra
  2 +database.ts_latest.type=cassandra
2 3
3 4 sql.ts_inserts_executor_type=fixed
4 5 sql.ts_inserts_fixed_thread_pool_size=10
... ...
1 1 database.ts.type=sql
  2 +database.ts_latest.type=sql
2 3
3 4 sql.ts_inserts_executor_type=fixed
4 5 sql.ts_inserts_fixed_thread_pool_size=200
... ...
... ... @@ -25,8 +25,10 @@ import org.thingsboard.rule.engine.api.TbContext;
25 25 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
26 26 import org.thingsboard.rule.engine.api.TbNodeException;
27 27 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  28 +import org.thingsboard.server.common.data.EntityType;
28 29 import org.thingsboard.server.common.data.alarm.Alarm;
29 30 import org.thingsboard.server.common.data.alarm.AlarmStatus;
  31 +import org.thingsboard.server.common.data.id.AlarmId;
30 32 import org.thingsboard.server.common.data.plugin.ComponentType;
31 33 import org.thingsboard.server.common.msg.TbMsg;
32 34
... ... @@ -56,8 +58,13 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig
56 58 @Override
57 59 protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) {
58 60 String alarmType = TbNodeUtils.processPattern(this.config.getAlarmType(), msg.getMetaData());
59   - ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType);
60   - return Futures.transformAsync(latest, a -> {
  61 + ListenableFuture<Alarm> alarmFuture;
  62 + if(msg.getOriginator().getEntityType().equals(EntityType.ALARM)){
  63 + alarmFuture = ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), new AlarmId(msg.getOriginator().getId()));
  64 + } else {
  65 + alarmFuture = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType);
  66 + }
  67 + return Futures.transformAsync(alarmFuture, a -> {
61 68 if (a != null && !a.getStatus().isCleared()) {
62 69 return clearAlarm(ctx, msg, a);
63 70 }
... ...
... ... @@ -16,8 +16,13 @@
16 16 package org.thingsboard.rule.engine.filter;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
  20 +import org.thingsboard.rule.engine.api.RuleNode;
  21 +import org.thingsboard.rule.engine.api.TbContext;
  22 +import org.thingsboard.rule.engine.api.TbNode;
  23 +import org.thingsboard.rule.engine.api.TbNodeConfiguration;
  24 +import org.thingsboard.rule.engine.api.TbNodeException;
19 25 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
20   -import org.thingsboard.rule.engine.api.*;
21 26 import org.thingsboard.server.common.data.DataConstants;
22 27 import org.thingsboard.server.common.data.plugin.ComponentType;
23 28 import org.thingsboard.server.common.msg.TbMsg;
... ... @@ -30,7 +35,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType;
30 35 configClazz = EmptyNodeConfiguration.class,
31 36 relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event",
32 37 "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned",
33   - "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other"},
  38 + "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other", "Entity Assigned From Tenant", "Entity Assigned To Tenant"},
34 39 nodeDescription = "Route incoming messages by Message Type",
35 40 nodeDetails = "Sends messages with message types <b>\"Post attributes\", \"Post telemetry\", \"RPC Request\"</b> etc. via corresponding chain, otherwise <b>Other</b> chain is used.",
36 41 uiResources = {"static/rulenode/rulenode-core-config.js"},
... ... @@ -81,6 +86,10 @@ public class TbMsgTypeSwitchNode implements TbNode {
81 86 relationType = "Alarm Cleared";
82 87 } else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) {
83 88 relationType = "RPC Request to Device";
  89 + } else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_FROM_TENANT)) {
  90 + relationType = "Entity Assigned From Tenant";
  91 + } else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_TO_TENANT)) {
  92 + relationType = "Entity Assigned To Tenant";
84 93 } else {
85 94 relationType = "Other";
86 95 }
... ...
... ... @@ -74,6 +74,7 @@ export interface EntityDataSubscriptionOptions {
74 74
75 75 export class EntityDataSubscription {
76 76
  77 + private entityDataSubscriptionOptions = this.listener.subscriptionOptions;
77 78 private datasourceType: DatasourceType = this.entityDataSubscriptionOptions.datasourceType;
78 79 private history: boolean;
79 80 private realtime: boolean;
... ... @@ -103,8 +104,7 @@ export class EntityDataSubscription {
103 104 private dataResolved = false;
104 105 private started = false;
105 106
106   - constructor(public entityDataSubscriptionOptions: EntityDataSubscriptionOptions,
107   - private listener: EntityDataListener,
  107 + constructor(private listener: EntityDataListener,
108 108 private telemetryService: TelemetryService,
109 109 private utils: UtilsService) {
110 110 this.initializeSubscription();
... ...
... ... @@ -17,7 +17,7 @@
17 17 import { DataSetHolder, Datasource, DatasourceType, widgetType } from '@shared/models/widget.models';
18 18 import { SubscriptionTimewindow } from '@shared/models/time/time.models';
19 19 import { EntityData, EntityDataPageLink, KeyFilter } from '@shared/models/query/query.models';
20   -import { PageData } from '@shared/models/page/page-data';
  20 +import { emptyPageData, PageData } from '@shared/models/page/page-data';
21 21 import { Injectable } from '@angular/core';
22 22 import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service';
23 23 import { UtilsService } from '@core/services/utils.service';
... ... @@ -41,6 +41,7 @@ export interface EntityDataListener {
41 41 initialPageDataChanged?: (nextPageData: PageData<EntityData>) => void;
42 42 updateRealtimeSubscription?: () => SubscriptionTimewindow;
43 43 setRealtimeSubscription?: (subscriptionTimewindow: SubscriptionTimewindow) => void;
  44 + subscriptionOptions?: EntityDataSubscriptionOptions;
44 45 subscription?: EntityDataSubscription;
45 46 }
46 47
... ... @@ -61,19 +62,24 @@ export class EntityDataService {
61 62
62 63 public prepareSubscription(listener: EntityDataListener): Observable<EntityDataLoadResult> {
63 64 const datasource = listener.configDatasource;
  65 + listener.subscriptionOptions = this.createSubscriptionOptions(
  66 + datasource,
  67 + listener.subscriptionType,
  68 + datasource.pageLink,
  69 + datasource.keyFilters,
  70 + null,
  71 + false);
64 72 if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !datasource.pageLink)) {
65 73 return of(null);
66 74 }
67   - listener.subscription = this.createSubscription(listener,
68   - datasource.pageLink, datasource.keyFilters, null,
69   - false);
  75 + listener.subscription = new EntityDataSubscription(listener, this.telemetryService, this.utils);
70 76 return listener.subscription.subscribe();
71 77 }
72 78
73 79 public startSubscription(listener: EntityDataListener) {
74 80 if (listener.subscription) {
75 81 if (listener.subscriptionType === widgetType.timeseries) {
76   - listener.subscription.entityDataSubscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow);
  82 + listener.subscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow);
77 83 }
78 84 listener.subscription.start();
79 85 }
... ... @@ -83,13 +89,21 @@ export class EntityDataService {
83 89 pageLink: EntityDataPageLink,
84 90 keyFilters: KeyFilter[]): Observable<EntityDataLoadResult> {
85 91 const datasource = listener.configDatasource;
  92 + listener.subscriptionOptions = this.createSubscriptionOptions(
  93 + datasource,
  94 + listener.subscriptionType,
  95 + pageLink,
  96 + datasource.keyFilters,
  97 + keyFilters,
  98 + true);
86 99 if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !pageLink)) {
  100 + listener.dataLoaded(emptyPageData<EntityData>(), [],
  101 + listener.configDatasourceIndex, listener.subscriptionOptions.pageLink);
87 102 return of(null);
88 103 }
89   - listener.subscription = this.createSubscription(listener,
90   - pageLink, datasource.keyFilters, keyFilters,true);
  104 + listener.subscription = new EntityDataSubscription(listener, this.telemetryService, this.utils);
91 105 if (listener.subscriptionType === widgetType.timeseries) {
92   - listener.subscription.entityDataSubscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow);
  106 + listener.subscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow);
93 107 }
94 108 return listener.subscription.subscribe();
95 109 }
... ... @@ -100,12 +114,12 @@ export class EntityDataService {
100 114 }
101 115 }
102 116
103   - private createSubscription(listener: EntityDataListener,
104   - pageLink: EntityDataPageLink,
105   - keyFilters: KeyFilter[],
106   - additionalKeyFilters: KeyFilter[],
107   - isPaginatedDataSubscription: boolean): EntityDataSubscription {
108   - const datasource = listener.configDatasource;
  117 + private createSubscriptionOptions(datasource: Datasource,
  118 + subscriptionType: widgetType,
  119 + pageLink: EntityDataPageLink,
  120 + keyFilters: KeyFilter[],
  121 + additionalKeyFilters: KeyFilter[],
  122 + isPaginatedDataSubscription: boolean): EntityDataSubscriptionOptions {
109 123 const subscriptionDataKeys: Array<SubscriptionDataKey> = [];
110 124 datasource.dataKeys.forEach((dataKey) => {
111 125 const subscriptionDataKey: SubscriptionDataKey = {
... ... @@ -119,7 +133,7 @@ export class EntityDataService {
119 133 const entityDataSubscriptionOptions: EntityDataSubscriptionOptions = {
120 134 datasourceType: datasource.type,
121 135 dataKeys: subscriptionDataKeys,
122   - type: listener.subscriptionType
  136 + type: subscriptionType
123 137 };
124 138 if (entityDataSubscriptionOptions.datasourceType === DatasourceType.entity) {
125 139 entityDataSubscriptionOptions.entityFilter = datasource.entityFilter;
... ... @@ -128,8 +142,6 @@ export class EntityDataService {
128 142 entityDataSubscriptionOptions.additionalKeyFilters = additionalKeyFilters;
129 143 }
130 144 entityDataSubscriptionOptions.isPaginatedDataSubscription = isPaginatedDataSubscription;
131   - return new EntityDataSubscription(entityDataSubscriptionOptions,
132   - listener, this.telemetryService, this.utils);
  145 + return entityDataSubscriptionOptions;
133 146 }
134   -
135 147 }
... ...
... ... @@ -735,8 +735,12 @@ export class WidgetSubscription implements IWidgetSubscription {
735 735 if (this.type === widgetType.alarm) {
736 736 this.updateAlarmDataSubscription();
737 737 } else {
738   - this.notifyDataLoading();
739   - this.dataSubscribe();
  738 + if (this.hasDataPageLink) {
  739 + this.updateDataSubscriptions();
  740 + } else {
  741 + this.notifyDataLoading();
  742 + this.dataSubscribe();
  743 + }
740 744 }
741 745 }
742 746 }
... ... @@ -1017,8 +1021,8 @@ export class WidgetSubscription implements IWidgetSubscription {
1017 1021 for (let datasourceIndex = 0; datasourceIndex < this.entityDataListeners.length; datasourceIndex++) {
1018 1022 const entityDataListener = this.entityDataListeners[datasourceIndex];
1019 1023 if (entityDataListener) {
1020   - const pageLink = entityDataListener.subscription.entityDataSubscriptionOptions.pageLink;
1021   - const keyFilters = entityDataListener.subscription.entityDataSubscriptionOptions.additionalKeyFilters;
  1024 + const pageLink = entityDataListener.subscriptionOptions.pageLink;
  1025 + const keyFilters = entityDataListener.subscriptionOptions.additionalKeyFilters;
1022 1026 this.subscribeForPaginatedData(datasourceIndex, pageLink, keyFilters);
1023 1027 }
1024 1028 }
... ...
... ... @@ -376,6 +376,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
376 376 }
377 377 this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings);
378 378 this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx');
  379 + this.contentsInfo[dataKey.def].units = dataKey.units;
  380 + this.contentsInfo[dataKey.def].decimals = dataKey.decimals;
379 381 this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
380 382 this.columns.push(dataKey);
381 383
... ... @@ -932,7 +934,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
932 934 }
933 935 }
934 936 }
935   - alarm[dataKey.label] = value;
  937 + alarm[dataKey.name] = value;
936 938 });
937 939 return alarm;
938 940 }
... ...
... ... @@ -87,7 +87,6 @@ import {
87 87 import { sortItems } from '@shared/models/page/page-link';
88 88 import { entityFields } from '@shared/models/entity.models';
89 89 import { DatePipe } from '@angular/common';
90   -import { alarmFields } from '@shared/models/alarm.models';
91 90
92 91 interface EntitiesTableWidgetSettings extends TableWidgetSettings {
93 92 entitiesTitle: string;
... ... @@ -659,7 +658,9 @@ class EntityDatasource implements DataSource<EntityData> {
659 658 this.dataKeys.forEach((dataKey, index) => {
660 659 const keyData = data[index].data;
661 660 if (keyData && keyData.length && keyData[0].length > 1) {
662   - entity[dataKey.label] = keyData[0][1];
  661 + if (data[index].dataKey.type !== DataKeyType.entityField || !entity.hasOwnProperty(dataKey.label)) {
  662 + entity[dataKey.label] = keyData[0][1];
  663 + }
663 664 } else {
664 665 entity[dataKey.label] = '';
665 666 }
... ...
... ... @@ -14,8 +14,6 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { DEFAULT_MAP_PAGE_SIZE } from '@home/components/widget/lib/maps/map-models';
18   -
19 17 export const googleMapSettingsSchema =
20 18 {
21 19 schema: {
... ... @@ -247,7 +245,7 @@ export const commonMapSettingsSchema =
247 245 mapPageSize: {
248 246 title: 'Map page size load entities',
249 247 type: 'number',
250   - default: DEFAULT_MAP_PAGE_SIZE
  248 + default: 16384
251 249 },
252 250 defaultCenterPosition: {
253 251 title: 'Default map center position (0,0)',
... ...
... ... @@ -189,7 +189,7 @@ export function getAlarmValue(alarm: AlarmDataInfo, key: EntityColumn) {
189 189 if (alarmField) {
190 190 return getDescendantProp(alarm, alarmField.value);
191 191 } else {
192   - return getDescendantProp(alarm, key.label);
  192 + return getDescendantProp(alarm, key.name);
193 193 }
194 194 }
195 195
... ...
... ... @@ -49,7 +49,9 @@ export enum ActionType {
49 49 ALARM_CLEAR = 'ALARM_CLEAR',
50 50 LOGIN = 'LOGIN',
51 51 LOGOUT = 'LOGOUT',
52   - LOCKOUT = 'LOCKOUT'
  52 + LOCKOUT = 'LOCKOUT',
  53 + ASSIGNED_FROM_TENANT = 'ASSIGNED_FROM_TENANT',
  54 + ASSIGNED_TO_TENANT = 'ASSIGNED_TO_TENANT'
53 55 }
54 56
55 57 export enum ActionStatus {
... ... @@ -79,7 +81,9 @@ export const actionTypeTranslations = new Map<ActionType, string>(
79 81 [ActionType.ALARM_CLEAR, 'audit-log.type-alarm-clear'],
80 82 [ActionType.LOGIN, 'audit-log.type-login'],
81 83 [ActionType.LOGOUT, 'audit-log.type-logout'],
82   - [ActionType.LOCKOUT, 'audit-log.type-lockout']
  84 + [ActionType.LOCKOUT, 'audit-log.type-lockout'],
  85 + [ActionType.ASSIGNED_FROM_TENANT, 'audit-log.type-assigned-from-tenant'],
  86 + [ActionType.ASSIGNED_TO_TENANT, 'audit-log.type-assigned-to-tenant']
83 87 ]
84 88 );
85 89
... ...
... ... @@ -100,6 +100,7 @@ export const HelpLinks = {
100 100 ruleNodeAwsSqs: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#aws-sqs-node',
101 101 ruleNodeKafka: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#kafka-node',
102 102 ruleNodeMqtt: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#mqtt-node',
  103 + ruleNodeAzureIotHub: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#azure-iot-hub-node',
103 104 ruleNodeRabbitMq: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#rabbitmq-node',
104 105 ruleNodeRestApiCall: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#rest-api-call-node',
105 106 ruleNodeSendEmail: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#send-email-node',
... ...
... ... @@ -412,6 +412,7 @@ const ruleNodeClazzHelpLinkMap = {
412 412 'org.thingsboard.rule.engine.aws.sqs.TbSqsNode': 'ruleNodeAwsSqs',
413 413 'org.thingsboard.rule.engine.kafka.TbKafkaNode': 'ruleNodeKafka',
414 414 'org.thingsboard.rule.engine.mqtt.TbMqttNode': 'ruleNodeMqtt',
  415 + 'org.thingsboard.rule.engine.mqtt.azure.TbAzureIotHubNode': 'ruleNodeAzureIotHub',
415 416 'org.thingsboard.rule.engine.rabbitmq.TbRabbitMqNode': 'ruleNodeRabbitMq',
416 417 'org.thingsboard.rule.engine.rest.TbRestApiCallNode': 'ruleNodeRestApiCall',
417 418 'org.thingsboard.rule.engine.mail.TbSendEmailNode': 'ruleNodeSendEmail'
... ...
... ... @@ -371,7 +371,9 @@
371 371 "action-data": "Action data",
372 372 "failure-details": "Failure details",
373 373 "search": "Search audit logs",
374   - "clear-search": "Clear search"
  374 + "clear-search": "Clear search",
  375 + "type-assigned-from-tenant": "Assigned from Tenant",
  376 + "type-assigned-to-tenant": "Assigned to Tenant"
375 377 },
376 378 "confirm-on-exit": {
377 379 "message": "You have unsaved changes. Are you sure you want to leave this page?",
... ...