diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 4f8bd25d4c..69ccf2dc01 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -372,6 +372,12 @@ caffeine: securitySettings: timeToLiveInMinutes: 1440 maxSize: 0 + tenantProfiles: + timeToLiveInMinutes: 1440 + maxSize: 0 + deviceProfiles: + timeToLiveInMinutes: 1440 + maxSize: 0 redis: # standalone or cluster @@ -793,4 +799,4 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' \ No newline at end of file + include: '${METRICS_ENDPOINTS_EXPOSE:info}' diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index 1890e7ba37..fe9f3df3ae 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -24,4 +24,6 @@ public class CacheConstants { public static final String ENTITY_VIEW_CACHE = "entityViews"; public static final String CLAIM_DEVICES_CACHE = "claimDevices"; public static final String SECURITY_SETTINGS_CACHE = "securitySettings"; + public static final String TENANT_PROFILE_CACHE = "tenantProfiles"; + public static final String DEVICE_PROFILE_CACHE = "deviceProfiles"; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index b16e3cb907..5fd4c4d336 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -19,6 +19,9 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityInfo; @@ -35,6 +38,10 @@ import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.util.mapping.JacksonUtil; +import java.util.Arrays; +import java.util.Collections; + +import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE; import static org.thingsboard.server.dao.service.Validator.validateId; @Service @@ -50,6 +57,10 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @Autowired private TenantDao tenantDao; + @Autowired + private CacheManager cacheManager; + + @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId}") @Override public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId) { log.trace("Executing findDeviceProfileById [{}]", deviceProfileId); @@ -57,6 +68,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D return deviceProfileDao.findById(tenantId, deviceProfileId.getId()); } + @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{'info', #deviceProfileId}") @Override public EntityInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId) { log.trace("Executing findDeviceProfileById [{}]", deviceProfileId); @@ -79,6 +91,13 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D throw t; } } + Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE); + cache.evict(Collections.singletonList(savedDeviceProfile.getId())); + cache.evict(Arrays.asList("info", savedDeviceProfile.getId())); + if (savedDeviceProfile.isDefault()) { + cache.evict(Arrays.asList("default", savedDeviceProfile.getTenantId())); + cache.evict(Arrays.asList("default", "info", savedDeviceProfile.getTenantId())); + } return savedDeviceProfile; } @@ -86,8 +105,19 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D public void deleteDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) { log.trace("Executing deleteDeviceProfile [{}]", deviceProfileId); Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId); + DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId()); + if (deviceProfile != null && deviceProfile.isDefault()) { + throw new DataValidationException("Deletion of Default Device Profile is prohibited!"); + } + this.removeDeviceProfile(tenantId, deviceProfileId); + } + + private void removeDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) { deleteEntityRelations(tenantId, deviceProfileId); deviceProfileDao.removeById(tenantId, deviceProfileId.getId()); + Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE); + cache.evict(Collections.singletonList(deviceProfileId.getId())); + cache.evict(Arrays.asList("info", deviceProfileId.getId())); } @Override @@ -116,9 +146,10 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D deviceProfile.setName("Default"); deviceProfile.setDescription("Default device profile"); deviceProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode()); - return deviceProfileDao.save(tenantId, deviceProfile); + return saveDeviceProfile(deviceProfile); } + @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{'default', #tenantId}") @Override public DeviceProfile findDefaultDeviceProfile(TenantId tenantId) { log.trace("Executing findDefaultDeviceProfile tenantId [{}]", tenantId); @@ -126,6 +157,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D return deviceProfileDao.findDefaultDeviceProfile(tenantId); } + @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{'default', 'info', #tenantId}") @Override public EntityInfo findDefaultDeviceProfileInfo(TenantId tenantId) { log.trace("Executing findDefaultDeviceProfileInfo tenantId [{}]", tenantId); @@ -139,17 +171,28 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D Validator.validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId); DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId()); if (!deviceProfile.isDefault()) { + Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE); deviceProfile.setDefault(true); DeviceProfile previousDefaultDeviceProfile = findDefaultDeviceProfile(tenantId); + boolean changed = false; if (previousDefaultDeviceProfile == null) { deviceProfileDao.save(tenantId, deviceProfile); - return true; + changed = true; } else if (!previousDefaultDeviceProfile.getId().equals(deviceProfile.getId())) { previousDefaultDeviceProfile.setDefault(false); deviceProfileDao.save(tenantId, previousDefaultDeviceProfile); deviceProfileDao.save(tenantId, deviceProfile); - return true; + cache.evict(Collections.singletonList(previousDefaultDeviceProfile.getId())); + cache.evict(Arrays.asList("info", previousDefaultDeviceProfile.getId())); + changed = true; + } + if (changed) { + cache.evict(Collections.singletonList(deviceProfile.getId())); + cache.evict(Arrays.asList("info", deviceProfile.getId())); + cache.evict(Arrays.asList("default", tenantId)); + cache.evict(Arrays.asList("default", "info", tenantId)); } + return changed; } return false; } @@ -195,7 +238,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @Override protected void removeEntity(TenantId tenantId, DeviceProfile entity) { - deleteDeviceProfile(tenantId, new DeviceProfileId(entity.getUuidId())); + removeDeviceProfile(tenantId, entity.getId()); } }; diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java index 8fb703a969..39008eeb0d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java @@ -19,6 +19,9 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.TenantProfile; @@ -33,6 +36,10 @@ import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.util.mapping.JacksonUtil; +import java.util.Arrays; +import java.util.Collections; + +import static org.thingsboard.server.common.data.CacheConstants.TENANT_PROFILE_CACHE; import static org.thingsboard.server.dao.service.Validator.validateId; @Service @@ -44,6 +51,10 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T @Autowired private TenantProfileDao tenantProfileDao; + @Autowired + private CacheManager cacheManager; + + @Cacheable(cacheNames = TENANT_PROFILE_CACHE, key = "{#tenantProfileId}") @Override public TenantProfile findTenantProfileById(TenantId tenantId, TenantProfileId tenantProfileId) { log.trace("Executing findTenantProfileById [{}]", tenantProfileId); @@ -51,6 +62,7 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T return tenantProfileDao.findById(tenantId, tenantProfileId.getId()); } + @Cacheable(cacheNames = TENANT_PROFILE_CACHE, key = "{'info', #tenantProfileId}") @Override public EntityInfo findTenantProfileInfoById(TenantId tenantId, TenantProfileId tenantProfileId) { log.trace("Executing findTenantProfileInfoById [{}]", tenantProfileId); @@ -73,6 +85,13 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T throw t; } } + Cache cache = cacheManager.getCache(TENANT_PROFILE_CACHE); + cache.evict(Collections.singletonList(savedTenantProfile.getId())); + cache.evict(Arrays.asList("info", savedTenantProfile.getId())); + if (savedTenantProfile.isDefault()) { + cache.evict(Collections.singletonList("default")); + cache.evict(Arrays.asList("default", "info")); + } return savedTenantProfile; } @@ -80,8 +99,19 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T public void deleteTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId) { log.trace("Executing deleteTenantProfile [{}]", tenantProfileId); validateId(tenantId, INCORRECT_TENANT_PROFILE_ID + tenantProfileId); + TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenantProfileId.getId()); + if (tenantProfile != null && tenantProfile.isDefault()) { + throw new DataValidationException("Deletion of Default Tenant Profile is prohibited!"); + } + this.removeTenantProfile(tenantId, tenantProfileId); + } + + private void removeTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId) { deleteEntityRelations(tenantId, tenantProfileId); tenantProfileDao.removeById(tenantId, tenantProfileId.getId()); + Cache cache = cacheManager.getCache(TENANT_PROFILE_CACHE); + cache.evict(Collections.singletonList(tenantProfileId.getId())); + cache.evict(Arrays.asList("info", tenantProfileId.getId())); } @Override @@ -115,12 +145,14 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T return defaultTenantProfile; } + @Cacheable(cacheNames = TENANT_PROFILE_CACHE, key = "{'default'}") @Override public TenantProfile findDefaultTenantProfile(TenantId tenantId) { log.trace("Executing findDefaultTenantProfile"); return tenantProfileDao.findDefaultTenantProfile(tenantId); } + @Cacheable(cacheNames = TENANT_PROFILE_CACHE, key = "{'default', 'info'}") @Override public EntityInfo findDefaultTenantProfileInfo(TenantId tenantId) { log.trace("Executing findDefaultTenantProfileInfo"); @@ -133,17 +165,28 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T validateId(tenantId, INCORRECT_TENANT_PROFILE_ID + tenantProfileId); TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenantProfileId.getId()); if (!tenantProfile.isDefault()) { + Cache cache = cacheManager.getCache(TENANT_PROFILE_CACHE); tenantProfile.setDefault(true); TenantProfile previousDefaultTenantProfile = findDefaultTenantProfile(tenantId); + boolean changed = false; if (previousDefaultTenantProfile == null) { tenantProfileDao.save(tenantId, tenantProfile); - return true; + changed = true; } else if (!previousDefaultTenantProfile.getId().equals(tenantProfile.getId())) { previousDefaultTenantProfile.setDefault(false); tenantProfileDao.save(tenantId, previousDefaultTenantProfile); tenantProfileDao.save(tenantId, tenantProfile); - return true; + cache.evict(Collections.singletonList(previousDefaultTenantProfile.getId())); + cache.evict(Arrays.asList("info", previousDefaultTenantProfile.getId())); + changed = true; + } + if (changed) { + cache.evict(Collections.singletonList(tenantProfile.getId())); + cache.evict(Arrays.asList("info", tenantProfile.getId())); + cache.evict(Collections.singletonList("default")); + cache.evict(Arrays.asList("default", "info")); } + return changed; } return false; } @@ -180,7 +223,7 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T @Override protected void removeEntity(TenantId tenantId, TenantProfile entity) { - deleteTenantProfile(tenantId, new TenantProfileId(entity.getUuidId())); + removeTenantProfile(tenantId, entity.getId()); } }; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java index ffbcd7b603..f8c09306c1 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java @@ -71,9 +71,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { savedDeviceProfile.setName("New device profile"); deviceProfileService.saveDeviceProfile(savedDeviceProfile); DeviceProfile foundDeviceProfile = deviceProfileService.findDeviceProfileById(tenantId, savedDeviceProfile.getId()); - Assert.assertEquals(foundDeviceProfile.getName(), savedDeviceProfile.getName()); - - deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); + Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfile.getName()); } @Test @@ -83,8 +81,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { DeviceProfile foundDeviceProfile = deviceProfileService.findDeviceProfileById(tenantId, savedDeviceProfile.getId()); Assert.assertNotNull(foundDeviceProfile); Assert.assertEquals(savedDeviceProfile, foundDeviceProfile); - deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); - } + } @Test public void testFindDeviceProfileInfoById() { @@ -94,7 +91,6 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { Assert.assertNotNull(foundDeviceProfileInfo); Assert.assertEquals(savedDeviceProfile.getId(), foundDeviceProfileInfo.getId()); Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfileInfo.getName()); - deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); } @Test @@ -131,8 +127,6 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { defaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId); Assert.assertNotNull(defaultDeviceProfile); Assert.assertEquals(savedDeviceProfile2.getId(), defaultDeviceProfile.getId()); - deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile1.getId()); - deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile2.getId()); } @Test(expected = DataValidationException.class) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java index d3f1b7b9d7..b8a038b72d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java @@ -61,7 +61,7 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest { TenantProfile foundTenantProfile = tenantProfileService.findTenantProfileById(TenantId.SYS_TENANT_ID, savedTenantProfile.getId()); Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName()); - tenantProfileService.deleteTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile.getId()); + tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); } @Test @@ -71,7 +71,7 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest { TenantProfile foundTenantProfile = tenantProfileService.findTenantProfileById(TenantId.SYS_TENANT_ID, savedTenantProfile.getId()); Assert.assertNotNull(foundTenantProfile); Assert.assertEquals(savedTenantProfile, foundTenantProfile); - tenantProfileService.deleteTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile.getId()); + tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); } @Test @@ -82,7 +82,7 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest { Assert.assertNotNull(foundTenantProfileInfo); Assert.assertEquals(savedTenantProfile.getId(), foundTenantProfileInfo.getId()); Assert.assertEquals(savedTenantProfile.getName(), foundTenantProfileInfo.getName()); - tenantProfileService.deleteTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile.getId()); + tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); } @Test @@ -93,7 +93,7 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest { TenantProfile foundDefaultTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); Assert.assertNotNull(foundDefaultTenantProfile); Assert.assertEquals(savedTenantProfile, foundDefaultTenantProfile); - tenantProfileService.deleteTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile.getId()); + tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); } @Test @@ -105,7 +105,7 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest { Assert.assertNotNull(foundDefaultTenantProfileInfo); Assert.assertEquals(savedTenantProfile.getId(), foundDefaultTenantProfileInfo.getId()); Assert.assertEquals(savedTenantProfile.getName(), foundDefaultTenantProfileInfo.getName()); - tenantProfileService.deleteTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile.getId()); + tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); } @Test @@ -126,8 +126,7 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest { defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); Assert.assertNotNull(defaultTenantProfile); Assert.assertEquals(savedTenantProfile2.getId(), defaultTenantProfile.getId()); - tenantProfileService.deleteTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile1.getId()); - tenantProfileService.deleteTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile2.getId()); + tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); } @Test(expected = DataValidationException.class) diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index caec9e0e66..36d73a96ca 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -30,6 +30,12 @@ caffeine.specs.entityViews.maxSize=100000 caffeine.specs.claimDevices.timeToLiveInMinutes=1440 caffeine.specs.claimDevices.maxSize=100000 +caffeine.specs.tenantProfiles.timeToLiveInMinutes=1440 +caffeine.specs.tenantProfiles.maxSize=100000 + +caffeine.specs.deviceProfiles.timeToLiveInMinutes=1440 +caffeine.specs.deviceProfiles.maxSize=100000 + redis.connection.host=localhost redis.connection.port=6379 redis.connection.db=0