Browse Source

Fix tenants and User Update Time cache

pull/6845/head
Andrii Shvaika 4 years ago
parent
commit
18893d9cf3
  1. 16
      application/src/main/java/org/thingsboard/server/service/security/auth/TokenOutdatingService.java
  2. 30
      application/src/main/resources/thingsboard.yml
  3. 15
      application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java
  4. 3
      common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java
  5. 7
      common/data/src/main/java/org/thingsboard/server/common/data/security/event/UserAuthDataChangedEvent.java
  6. 45
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantCacheKey.java
  7. 3
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantCaffeineCache.java
  8. 3
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantEvictEvent.java
  9. 6
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantExistsCaffeineCache.java
  10. 5
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantExistsRedisCache.java
  11. 3
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantRedisCache.java
  12. 19
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  13. 99
      dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantServiceTest.java
  14. 3
      dao/src/test/resources/application-test.properties

16
application/src/main/java/org/thingsboard/server/service/security/auth/TokenOutdatingService.java

@ -40,23 +40,23 @@ public class TokenOutdatingService {
private final CacheManager cacheManager;
private final JwtTokenFactory tokenFactory;
private final JwtSettings jwtSettings;
private Cache tokenOutdatageTimeCache;
private Cache usersUpdateTimeCache;
@PostConstruct
protected void initCache() {
tokenOutdatageTimeCache = cacheManager.getCache(CacheConstants.TOKEN_OUTDATAGE_TIME_CACHE);
usersUpdateTimeCache = cacheManager.getCache(CacheConstants.USERS_UPDATE_TIME_CACHE);
}
@EventListener(classes = UserAuthDataChangedEvent.class)
public void onUserAuthDataChanged(UserAuthDataChangedEvent userAuthDataChangedEvent) {
outdateOldUserTokens(userAuthDataChangedEvent.getUserId());
public void onUserAuthDataChanged(UserAuthDataChangedEvent event) {
usersUpdateTimeCache.put(toKey(event.getUserId()), event.getTs());
}
public boolean isOutdated(JwtToken token, UserId userId) {
Claims claims = tokenFactory.parseTokenClaims(token).getBody();
long issueTime = claims.getIssuedAt().getTime();
return Optional.ofNullable(tokenOutdatageTimeCache.get(toKey(userId), Long.class))
return Optional.ofNullable(usersUpdateTimeCache.get(toKey(userId), Long.class))
.map(outdatageTime -> {
if (System.currentTimeMillis() - outdatageTime <= SECONDS.toMillis(jwtSettings.getRefreshTokenExpTime())) {
return MILLISECONDS.toSeconds(issueTime) < MILLISECONDS.toSeconds(outdatageTime);
@ -68,17 +68,13 @@ public class TokenOutdatingService {
* as all the tokens issued before the outdatage time
* are now expired by themselves
* */
tokenOutdatageTimeCache.evict(toKey(userId));
usersUpdateTimeCache.evict(toKey(userId));
return false;
}
})
.orElse(false);
}
public void outdateOldUserTokens(UserId userId) {
tokenOutdatageTimeCache.put(toKey(userId), System.currentTimeMillis());
}
private String toKey(UserId userId) {
return userId.getId().toString();
}

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

@ -28,7 +28,7 @@ server:
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${SSL_CREDENTIALS_TYPE:PEM}"
type: "${SSL_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
@ -397,15 +397,19 @@ cache:
tenants:
timeToLiveInMinutes: "${CACHE_SPECS_TENANTS_TTL:1440}"
maxSize: "${CACHE_SPECS_TENANTS_MAX_SIZE:10000}"
tenantsExist:
# environment variables are intentionally the same as in 'tenants' cache to be equal.
timeToLiveInMinutes: "${CACHE_SPECS_TENANTS_TTL:1440}"
maxSize: "${CACHE_SPECS_TENANTS_MAX_SIZE:10000}"
deviceProfiles:
timeToLiveInMinutes: "${CACHE_SPECS_DEVICE_PROFILES_TTL:1440}"
maxSize: "${CACHE_SPECS_DEVICE_PROFILES_MAX_SIZE:10000}"
attributes:
timeToLiveInMinutes: "${CACHE_SPECS_ATTRIBUTES_TTL:1440}"
maxSize: "${CACHE_SPECS_ATTRIBUTES_MAX_SIZE:100000}"
tokensOutdatageTime:
timeToLiveInMinutes: "${CACHE_SPECS_TOKENS_OUTDATAGE_TIME_TTL:20000}"
maxSize: "${CACHE_SPECS_TOKENS_OUTDATAGE_TIME_MAX_SIZE:10000}"
usersUpdateTime:
timeToLiveInMinutes: "${CACHE_SPECS_USERS_UPDATE_TIME_TTL:20000}"
maxSize: "${CACHE_SPECS_USERS_UPDATE_TIME_MAX_SIZE:10000}"
otaPackages:
timeToLiveInMinutes: "${CACHE_SPECS_OTA_PACKAGES_TTL:60}"
maxSize: "${CACHE_SPECS_OTA_PACKAGES_MAX_SIZE:10}"
@ -669,7 +673,7 @@ transport:
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${MQTT_SSL_CREDENTIALS_TYPE:PEM}"
type: "${MQTT_SSL_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
@ -712,7 +716,7 @@ transport:
# Server DTLS credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${COAP_DTLS_CREDENTIALS_TYPE:PEM}"
type: "${COAP_DTLS_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
@ -790,7 +794,7 @@ transport:
# Whether to enable LWM2M bootstrap server X509 Certificate/RPK support
enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:false}"
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${LWM2M_BS_CREDENTIALS_TYPE:PEM}"
type: "${LWM2M_BS_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
@ -817,7 +821,7 @@ transport:
# Whether to load X509 trust certificates
enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:false}"
# Trust certificates store type (PEM - pem certificates file; KEYSTORE - java keystore)
type: "${LWM2M_TRUST_CREDENTIALS_TYPE:PEM}"
type: "${LWM2M_TRUST_CREDENTIALS_TYPE:PEM}"
# PEM certificates
pem:
# Path to the certificates file (holds trust certificates)
@ -841,8 +845,8 @@ transport:
psm_activity_timer: "${LWM2M_PSM_ACTIVITY_TIMER:10000}"
paging_transmission_window: "${LWM2M_PAGING_TRANSMISSION_WINDOW:10000}"
network_config: # In this section you can specify custom parameters for LwM2M network configuration and expose the env variables to configure outside
# - key: "PROTOCOL_STAGE_THREAD_COUNT"
# value: "${LWM2M_PROTOCOL_STAGE_THREAD_COUNT:4}"
# - key: "PROTOCOL_STAGE_THREAD_COUNT"
# value: "${LWM2M_PROTOCOL_STAGE_THREAD_COUNT:4}"
snmp:
enabled: "${SNMP_ENABLED:true}"
response_processing:
@ -928,9 +932,9 @@ queue:
tb_version_control:
- key: max.poll.interval.ms
value: "${TB_QUEUE_KAFKA_VC_MAX_POLL_INTERVAL_MS:600000}"
# tb_rule_engine.sq:
# - key: max.poll.records
# value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}"
# tb_rule_engine.sq:
# - key: max.poll.records
# value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}"
other: # In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside
- key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms
value: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds)

15
application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.security.event.UserAuthDataChangedEvent;
import org.thingsboard.server.common.data.security.model.JwtToken;
import org.thingsboard.server.config.JwtSettings;
import org.thingsboard.server.dao.customer.CustomerService;
@ -99,7 +100,7 @@ public class TokenOutdatingTest {
JwtToken jwtToken = createAccessJwtToken(userId);
SECONDS.sleep(1); // need to wait before outdating so that outdatage time is strictly after token issue time
tokenOutdatingService.outdateOldUserTokens(userId);
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(userId));
assertTrue(tokenOutdatingService.isOutdated(jwtToken, userId));
SECONDS.sleep(1);
@ -117,7 +118,7 @@ public class TokenOutdatingTest {
});
SECONDS.sleep(1);
tokenOutdatingService.outdateOldUserTokens(userId);
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(userId));
assertThrows(JwtExpiredTokenException.class, () -> {
accessTokenAuthenticationProvider.authenticate(new JwtAuthenticationToken(accessJwtToken));
@ -133,7 +134,7 @@ public class TokenOutdatingTest {
});
SECONDS.sleep(1);
tokenOutdatingService.outdateOldUserTokens(userId);
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(userId));
assertThrows(CredentialsExpiredException.class, () -> {
refreshTokenAuthenticationProvider.authenticate(new RefreshAuthenticationToken(refreshJwtToken));
@ -145,20 +146,20 @@ public class TokenOutdatingTest {
JwtToken jwtToken = createAccessJwtToken(userId);
SECONDS.sleep(1);
tokenOutdatingService.outdateOldUserTokens(userId);
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(userId));
int refreshTokenExpirationTime = 5;
int refreshTokenExpirationTime = 3;
jwtSettings.setRefreshTokenExpTime(refreshTokenExpirationTime);
SECONDS.sleep(refreshTokenExpirationTime - 2);
assertTrue(tokenOutdatingService.isOutdated(jwtToken, userId));
assertNotNull(cacheManager.getCache(CacheConstants.TOKEN_OUTDATAGE_TIME_CACHE).get(userId.getId().toString()));
assertNotNull(cacheManager.getCache(CacheConstants.USERS_UPDATE_TIME_CACHE).get(userId.getId().toString()));
SECONDS.sleep(3);
assertFalse(tokenOutdatingService.isOutdated(jwtToken, userId));
assertNull(cacheManager.getCache(CacheConstants.TOKEN_OUTDATAGE_TIME_CACHE).get(userId.getId().toString()));
assertNull(cacheManager.getCache(CacheConstants.USERS_UPDATE_TIME_CACHE).get(userId.getId().toString()));
}
private JwtToken createAccessJwtToken(UserId userId) {

3
common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java

@ -27,9 +27,10 @@ public class CacheConstants {
public static final String SECURITY_SETTINGS_CACHE = "securitySettings";
public static final String TENANT_PROFILE_CACHE = "tenantProfiles";
public static final String TENANTS_CACHE = "tenants";
public static final String TENANTS_EXIST_CACHE = "tenantsExist";
public static final String DEVICE_PROFILE_CACHE = "deviceProfiles";
public static final String ATTRIBUTES_CACHE = "attributes";
public static final String TOKEN_OUTDATAGE_TIME_CACHE = "tokensOutdatageTime";
public static final String USERS_UPDATE_TIME_CACHE = "usersUpdateTime";
public static final String OTA_PACKAGE_CACHE = "otaPackages";
public static final String OTA_PACKAGE_DATA_CACHE = "otaPackagesData";
public static final String REPOSITORY_SETTINGS_CACHE = "repositorySettings";

7
common/data/src/main/java/org/thingsboard/server/common/data/security/event/UserAuthDataChangedEvent.java

@ -15,16 +15,17 @@
*/
package org.thingsboard.server.common.data.security.event;
import lombok.Data;
import org.thingsboard.server.common.data.id.UserId;
@Data
public class UserAuthDataChangedEvent {
private final UserId userId;
private final long ts;
public UserAuthDataChangedEvent(UserId userId) {
this.userId = userId;
this.ts = System.currentTimeMillis();
}
public UserId getUserId() {
return userId;
}
}

45
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantCacheKey.java

@ -1,45 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.tenant;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class TenantCacheKey implements Serializable {
private static final long serialVersionUID = -121787454251592384L;
private final TenantId tenantId;
private final TenantCacheKeyPrefix keyPrefix;
public static TenantCacheKey fromId(TenantId tenantId) {
return new TenantCacheKey(tenantId, TenantCacheKeyPrefix.TENANT);
}
public static TenantCacheKey fromIdExists(TenantId tenantId) {
return new TenantCacheKey(tenantId, TenantCacheKeyPrefix.EXISTS);
}
public enum TenantCacheKeyPrefix {
TENANT, EXISTS
}
}

3
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantCaffeineCache.java

@ -21,10 +21,11 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.TenantId;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("TenantCache")
public class TenantCaffeineCache extends CaffeineTbTransactionalCache<TenantCacheKey, Tenant> {
public class TenantCaffeineCache extends CaffeineTbTransactionalCache<TenantId, Tenant> {
public TenantCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.TENANTS_CACHE);

3
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantEvictEvent.java

@ -21,6 +21,5 @@ import org.thingsboard.server.common.data.id.TenantId;
@Data
public class TenantEvictEvent {
private final TenantId tenantId;
// for exists tenant cache
private final boolean isExistsTenant;
private final boolean invalidateExists;
}

6
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantExistsCaffeineCache.java

@ -21,13 +21,15 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantInfo;
import org.thingsboard.server.common.data.id.TenantId;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("TenantExistsCache")
public class TenantExistsCaffeineCache extends CaffeineTbTransactionalCache<TenantCacheKey, Boolean> {
public class TenantExistsCaffeineCache extends CaffeineTbTransactionalCache<TenantId, Boolean> {
public TenantExistsCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.TENANTS_CACHE);
super(cacheManager, CacheConstants.TENANTS_EXIST_CACHE);
}
}

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

@ -23,12 +23,13 @@ import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbFSTRedisSerializer;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.id.TenantId;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("TenantExistsCache")
public class TenantExistsRedisCache extends RedisTbTransactionalCache<TenantCacheKey, Boolean> {
public class TenantExistsRedisCache extends RedisTbTransactionalCache<TenantId, Boolean> {
public TenantExistsRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.TENANTS_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>());
super(CacheConstants.TENANTS_EXIST_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>());
}
}

3
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantRedisCache.java

@ -24,10 +24,11 @@ import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbFSTRedisSerializer;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.TenantId;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("TenantCache")
public class TenantRedisCache extends RedisTbTransactionalCache<TenantCacheKey, Tenant> {
public class TenantRedisCache extends RedisTbTransactionalCache<TenantId, Tenant> {
public TenantRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.TENANTS_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>());

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

@ -55,7 +55,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
public class TenantServiceImpl extends AbstractCachedEntityService<TenantCacheKey, Tenant, TenantEvictEvent> implements TenantService {
public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Tenant, TenantEvictEvent> implements TenantService {
private static final String DEFAULT_TENANT_REGION = "Global";
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@ -115,15 +115,15 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantCacheKe
private AdminSettingsService adminSettingsService;
@Autowired
protected TbTransactionalCache<TenantCacheKey, Boolean> existsTenantCache;
protected TbTransactionalCache<TenantId, Boolean> existsTenantCache;
@TransactionalEventListener(classes = TenantEvictEvent.class)
@Override
public void handleEvictEvent(TenantEvictEvent event) {
TenantId tenantId = event.getTenantId();
cache.evict(TenantCacheKey.fromId(tenantId));
if (event.isExistsTenant()) {
existsTenantCache.evict(TenantCacheKey.fromIdExists(tenantId));
cache.evict(tenantId);
if (event.isInvalidateExists()) {
existsTenantCache.evict(tenantId);
}
}
@ -132,8 +132,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantCacheKe
log.trace("Executing findTenantById [{}]", tenantId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
return cache.getAndPutInTransaction(TenantCacheKey.fromId(tenantId),
() -> tenantDao.findById(tenantId, tenantId.getId()), true);
return cache.getAndPutInTransaction(tenantId, () -> tenantDao.findById(tenantId, tenantId.getId()), true);
}
@Override
@ -160,8 +159,9 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantCacheKe
tenant.setTenantProfileId(tenantProfile.getId());
}
tenantValidator.validate(tenant, Tenant::getId);
boolean create = tenant.getId() == null;
Tenant savedTenant = tenantDao.save(tenant.getId(), tenant);
publishEvictEvent(new TenantEvictEvent(savedTenant.getId(), false));
publishEvictEvent(new TenantEvictEvent(savedTenant.getId(), create));
if (tenant.getId() == null) {
deviceProfileService.createDefaultDeviceProfile(savedTenant.getId());
apiUsageStateService.createDefaultApiUsageState(savedTenant.getId(), null);
@ -230,8 +230,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantCacheKe
@Override
public boolean tenantExists(TenantId tenantId) {
return existsTenantCache.getAndPutInTransaction(TenantCacheKey.fromIdExists(tenantId),
() -> tenantDao.existsById(tenantId, tenantId.getId()), false);
return existsTenantCache.getAndPutInTransaction(tenantId, () -> tenantDao.existsById(tenantId, tenantId.getId()), false);
}
private PaginatedRemover<TenantId, Tenant> tenantsRemover = new PaginatedRemover<>() {

99
dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantServiceTest.java

@ -17,17 +17,12 @@ package org.thingsboard.server.dao.service;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cache.TbCacheValueWrapper;
import org.thingsboard.server.cache.TbTransactionalCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
@ -61,19 +56,17 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.tenant.TenantCacheKey;
import org.thingsboard.server.dao.tenant.TenantDao;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.assertj.core.api.Assertions.assertThat;
public abstract class BaseTenantServiceTest extends AbstractServiceTest {
@ -83,10 +76,10 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
protected TenantDao tenantDao;
@Autowired
protected TbTransactionalCache<TenantCacheKey, Tenant> cache;
protected TbTransactionalCache<TenantId, Tenant> cache;
@Autowired
protected TbTransactionalCache<TenantCacheKey, Boolean> existsTenantCache;
protected TbTransactionalCache<TenantId, Boolean> existsTenantCache;
@Test
public void testSaveTenant() {
@ -97,15 +90,15 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
Assert.assertNotNull(savedTenant.getId());
Assert.assertTrue(savedTenant.getCreatedTime() > 0);
Assert.assertEquals(tenant.getTitle(), savedTenant.getTitle());
savedTenant.setTitle("My new tenant");
tenantService.saveTenant(savedTenant);
Tenant foundTenant = tenantService.findTenantById(savedTenant.getId());
Assert.assertEquals(foundTenant.getTitle(), savedTenant.getTitle());
tenantService.deleteTenant(savedTenant.getId());
}
@Test
public void testFindTenantById() {
Tenant tenant = new Tenant();
@ -127,13 +120,13 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
Assert.assertEquals(new TenantInfo(savedTenant, "Default"), foundTenant);
tenantService.deleteTenant(savedTenant.getId());
}
@Test(expected = DataValidationException.class)
public void testSaveTenantWithEmptyTitle() {
Tenant tenant = new Tenant();
tenantService.saveTenant(tenant);
}
@Test(expected = DataValidationException.class)
public void testSaveTenantWithInvalidEmail() {
Tenant tenant = new Tenant();
@ -141,7 +134,7 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
tenant.setEmail("invalid@mail");
tenantService.saveTenant(tenant);
}
@Test
public void testDeleteTenant() {
Tenant tenant = new Tenant();
@ -151,7 +144,7 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
Tenant foundTenant = tenantService.findTenantById(savedTenant.getId());
Assert.assertNull(foundTenant);
}
@Test
public void testFindTenants() {
List<Tenant> tenants = new ArrayList<>();
@ -160,13 +153,13 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
Assert.assertFalse(pageData.hasNext());
Assert.assertTrue(pageData.getData().isEmpty());
tenants.addAll(pageData.getData());
for (int i=0;i<156;i++) {
for (int i = 0; i < 156; i++) {
Tenant tenant = new Tenant();
tenant.setTitle("Tenant"+i);
tenant.setTitle("Tenant" + i);
tenants.add(tenantService.saveTenant(tenant));
}
List<Tenant> loadedTenants = new ArrayList<>();
pageLink = new PageLink(17);
do {
@ -176,46 +169,46 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(tenants, idComparator);
Collections.sort(loadedTenants, idComparator);
Assert.assertEquals(tenants, loadedTenants);
for (Tenant tenant : loadedTenants) {
tenantService.deleteTenant(tenant.getId());
}
pageLink = new PageLink(17);
pageData = tenantService.findTenants(pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertTrue(pageData.getData().isEmpty());
}
@Test
public void testFindTenantsByTitle() {
String title1 = "Tenant title 1";
List<Tenant> tenantsTitle1 = new ArrayList<>();
for (int i=0;i<134;i++) {
for (int i = 0; i < 134; i++) {
Tenant tenant = new Tenant();
String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15));
String title = title1+suffix;
String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 15));
String title = title1 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
tenant.setTitle(title);
tenantsTitle1.add(tenantService.saveTenant(tenant));
}
String title2 = "Tenant title 2";
List<Tenant> tenantsTitle2 = new ArrayList<>();
for (int i=0;i<127;i++) {
for (int i = 0; i < 127; i++) {
Tenant tenant = new Tenant();
String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15));
String title = title2+suffix;
String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 15));
String title = title2 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
tenant.setTitle(title);
tenantsTitle2.add(tenantService.saveTenant(tenant));
}
List<Tenant> loadedTenantsTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Tenant> pageData = null;
@ -226,12 +219,12 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(tenantsTitle1, idComparator);
Collections.sort(loadedTenantsTitle1, idComparator);
Assert.assertEquals(tenantsTitle1, loadedTenantsTitle1);
List<Tenant> loadedTenantsTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
do {
@ -244,22 +237,22 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
Collections.sort(tenantsTitle2, idComparator);
Collections.sort(loadedTenantsTitle2, idComparator);
Assert.assertEquals(tenantsTitle2, loadedTenantsTitle2);
for (Tenant tenant : loadedTenantsTitle1) {
tenantService.deleteTenant(tenant.getId());
}
pageLink = new PageLink(4, 0, title1);
pageData = tenantService.findTenants(pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Tenant tenant : loadedTenantsTitle2) {
tenantService.deleteTenant(tenant.getId());
}
pageLink = new PageLink(4, 0, title2);
pageData = tenantService.findTenants(pageLink);
Assert.assertFalse(pageData.hasNext());
@ -276,9 +269,9 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
Assert.assertTrue(pageData.getData().isEmpty());
tenants.addAll(pageData.getData());
for (int i=0;i<156;i++) {
for (int i = 0; i < 156; i++) {
Tenant tenant = new Tenant();
tenant.setTitle("Tenant"+i);
tenant.setTitle("Tenant" + i);
tenants.add(new TenantInfo(tenantService.saveTenant(tenant), "Default"));
}
@ -338,7 +331,7 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
tenantService.findTenantById(savedTenant.getId());
verify(tenantDao, Mockito.times(1)).findById(eq(savedTenant.getId()), eq(savedTenant.getId().getId()));
var cachedTenant = cache.get(TenantCacheKey.fromId(savedTenant.getId()));
var cachedTenant = cache.get(savedTenant.getId());
Assert.assertNotNull("Getting an existing Tenant doesn't add it to the cache!", cachedTenant);
Assert.assertEquals(savedTenant, cachedTenant.get());
@ -358,13 +351,13 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
Mockito.reset(tenantDao);
//fromIdExists invoked from device profile validator
existsTenantCache.evict(TenantCacheKey.fromIdExists(savedTenant.getTenantId()));
existsTenantCache.evict(savedTenant.getTenantId());
verify(tenantDao, Mockito.times(0)).existsById(any(), any());
tenantService.tenantExists(savedTenant.getId());
verify(tenantDao, Mockito.times(1)).existsById(eq(savedTenant.getId()), eq(savedTenant.getId().getId()));
var isExists = existsTenantCache.get(TenantCacheKey.fromIdExists(savedTenant.getId()));
var isExists = existsTenantCache.get(savedTenant.getId());
Assert.assertNotNull("Getting an existing Tenant doesn't add it to the cache!", isExists);
for (int i = 0; i < 100; i++) {
@ -383,7 +376,7 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
tenantService.findTenantById(savedTenant.getId());
var cachedTenant = cache.get(TenantCacheKey.fromId(savedTenant.getId()));
var cachedTenant = cache.get(savedTenant.getId());
Assert.assertNotNull("Saving a Tenant doesn't add it to the cache!", cachedTenant);
Assert.assertEquals(savedTenant, cachedTenant.get());
@ -392,7 +385,7 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
Mockito.reset(tenantDao);
cachedTenant = cache.get(TenantCacheKey.fromId(savedTenant.getId()));
cachedTenant = cache.get(savedTenant.getId());
Assert.assertNull("Updating a Tenant doesn't evict the cache!", cachedTenant);
verify(tenantDao, Mockito.times(0)).findById(any(), any());
@ -411,18 +404,18 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
tenantService.findTenantById(savedTenant.getId());
tenantService.tenantExists(savedTenant.getId());
var cachedTenant =
cache.get(TenantCacheKey.fromId(savedTenant.getId()));
var cachedTenant =
cache.get(savedTenant.getId());
var cachedExists =
existsTenantCache.get(TenantCacheKey.fromIdExists(savedTenant.getId()));
existsTenantCache.get(savedTenant.getId());
Assert.assertNotNull("Saving a Tenant doesn't add it to the cache!", cachedTenant);
Assert.assertNotNull("Saving a Tenant doesn't add it to the cache!", cachedExists);
tenantService.deleteTenant(savedTenant.getId());
cachedTenant =
cache.get(TenantCacheKey.fromId(savedTenant.getId()));
cache.get(savedTenant.getId());
cachedExists =
existsTenantCache.get(TenantCacheKey.fromIdExists(savedTenant.getId()));
existsTenantCache.get(savedTenant.getId());
Assert.assertNull("Removing a Tenant doesn't evict the cache!", cachedTenant);

3
dao/src/test/resources/application-test.properties

@ -38,6 +38,9 @@ cache.specs.claimDevices.maxSize=100000
cache.specs.tenants.timeToLiveInMinutes=1440
cache.specs.tenants.maxSize=100000
cache.specs.tenantsExist.timeToLiveInMinutes=1440
cache.specs.tenantsExist.maxSize=100000
cache.specs.securitySettings.timeToLiveInMinutes=1440
cache.specs.securitySettings.maxSize=100000

Loading…
Cancel
Save