Commit d727a8a3b4770a47557b087ac87dd824db202240
Committed by
GitHub
1 parent
f6da85e9
Caching of main requests related to relations and GWs API (#567)
* added enable/disable condition for transport protocols * fix coap tests * configured relations cache * minor fix * Improvements * delete devices caching
Showing
18 changed files
with
431 additions
and
127 deletions
... | ... | @@ -77,6 +77,8 @@ http: |
77 | 77 | |
78 | 78 | # MQTT server parameters |
79 | 79 | mqtt: |
80 | + # Enable/disable mqtt transport protocol. | |
81 | + enabled: "${MQTT_ENABLED:true}" | |
80 | 82 | bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" |
81 | 83 | bind_port: "${MQTT_BIND_PORT:1883}" |
82 | 84 | adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}" |
... | ... | @@ -102,6 +104,8 @@ mqtt: |
102 | 104 | |
103 | 105 | # CoAP server parameters |
104 | 106 | coap: |
107 | + # Enable/disable coap transport protocol. | |
108 | + enabled: "${COAP_ENABLED:true}" | |
105 | 109 | bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" |
106 | 110 | bind_port: "${COAP_BIND_PORT:5683}" |
107 | 111 | adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}" |
... | ... | @@ -208,6 +212,18 @@ cache: |
208 | 212 | policy: "${CACHE_DEVICE_CREDENTIAL_MAX_SIZE_POLICY:PER_NODE}" |
209 | 213 | size: "${CACHE_DEVICE_CREDENTIAL_MAX_SIZE_SIZE:1000000}" |
210 | 214 | |
215 | +caching: | |
216 | + specs: | |
217 | + relations: | |
218 | + timeToLiveInMinutes: 1440 | |
219 | + maxSize: 100000 | |
220 | + deviceCredentials: | |
221 | + timeToLiveInMinutes: 1440 | |
222 | + maxSize: 100000 | |
223 | + devices: | |
224 | + timeToLiveInMinutes: 1440 | |
225 | + maxSize: 100000 | |
226 | + | |
211 | 227 | # Check new version updates parameters |
212 | 228 | updates: |
213 | 229 | # Enable/disable updates checking. | ... | ... |
... | ... | @@ -17,4 +17,6 @@ package org.thingsboard.server.common.data; |
17 | 17 | |
18 | 18 | public class CacheConstants { |
19 | 19 | public static final String DEVICE_CREDENTIALS_CACHE = "deviceCredentials"; |
20 | + public static final String RELATIONS_CACHE = "relations"; | |
21 | + public static final String DEVICE_CACHE = "devices"; | |
20 | 22 | } | ... | ... |
... | ... | @@ -149,6 +149,10 @@ |
149 | 149 | <artifactId>hazelcast</artifactId> |
150 | 150 | </dependency> |
151 | 151 | <dependency> |
152 | + <groupId>com.github.ben-manes.caffeine</groupId> | |
153 | + <artifactId>caffeine</artifactId> | |
154 | + </dependency> | |
155 | + <dependency> | |
152 | 156 | <groupId>com.hazelcast</groupId> |
153 | 157 | <artifactId>hazelcast-spring</artifactId> |
154 | 158 | </dependency> |
... | ... | @@ -174,6 +178,10 @@ |
174 | 178 | <artifactId>hsqldb</artifactId> |
175 | 179 | <scope>test</scope> |
176 | 180 | </dependency> |
181 | + <dependency> | |
182 | + <groupId>org.springframework</groupId> | |
183 | + <artifactId>spring-context-support</artifactId> | |
184 | + </dependency> | |
177 | 185 | </dependencies> |
178 | 186 | <build> |
179 | 187 | <plugins> | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2017 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.cache; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +@Data | |
21 | +public class CacheSpecs { | |
22 | + private Integer timeToLiveInMinutes; | |
23 | + private Integer maxSize; | |
24 | +} | ... | ... |
... | ... | @@ -23,6 +23,8 @@ import java.lang.reflect.Method; |
23 | 23 | |
24 | 24 | public class PreviousDeviceCredentialsIdKeyGenerator implements KeyGenerator { |
25 | 25 | |
26 | + private static final String NOT_VALID_DEVICE = "notValidDeviceCredentialsId"; | |
27 | + | |
26 | 28 | @Override |
27 | 29 | public Object generate(Object o, Method method, Object... objects) { |
28 | 30 | DeviceCredentialsService deviceCredentialsService = (DeviceCredentialsService) o; |
... | ... | @@ -33,6 +35,6 @@ public class PreviousDeviceCredentialsIdKeyGenerator implements KeyGenerator { |
33 | 35 | return oldDeviceCredentials.getCredentialsId(); |
34 | 36 | } |
35 | 37 | } |
36 | - return null; | |
38 | + return NOT_VALID_DEVICE; | |
37 | 39 | } |
38 | 40 | } | ... | ... |
... | ... | @@ -15,76 +15,57 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.cache; |
17 | 17 | |
18 | -import com.hazelcast.config.*; | |
19 | -import com.hazelcast.core.Hazelcast; | |
20 | -import com.hazelcast.core.HazelcastInstance; | |
21 | -import com.hazelcast.instance.GroupProperty; | |
22 | -import com.hazelcast.spring.cache.HazelcastCacheManager; | |
23 | -import com.hazelcast.zookeeper.ZookeeperDiscoveryProperties; | |
24 | -import com.hazelcast.zookeeper.ZookeeperDiscoveryStrategyFactory; | |
25 | -import org.springframework.beans.factory.annotation.Value; | |
26 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
18 | +import com.github.benmanes.caffeine.cache.Caffeine; | |
19 | +import com.github.benmanes.caffeine.cache.Ticker; | |
20 | +import lombok.Data; | |
21 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
27 | 22 | import org.springframework.cache.CacheManager; |
28 | 23 | import org.springframework.cache.annotation.EnableCaching; |
24 | +import org.springframework.cache.caffeine.CaffeineCache; | |
29 | 25 | import org.springframework.cache.interceptor.KeyGenerator; |
26 | +import org.springframework.cache.support.SimpleCacheManager; | |
30 | 27 | import org.springframework.context.annotation.Bean; |
31 | 28 | import org.springframework.context.annotation.Configuration; |
32 | -import org.thingsboard.server.common.data.CacheConstants; | |
29 | + | |
30 | +import java.util.List; | |
31 | +import java.util.Map; | |
32 | +import java.util.concurrent.TimeUnit; | |
33 | +import java.util.stream.Collectors; | |
33 | 34 | |
34 | 35 | @Configuration |
36 | +@ConfigurationProperties(prefix = "caching") | |
35 | 37 | @EnableCaching |
36 | -@ConditionalOnProperty(prefix = "cache", value = "enabled", havingValue = "true") | |
38 | +@Data | |
37 | 39 | public class ServiceCacheConfiguration { |
38 | 40 | |
39 | - private static final String HAZELCAST_CLUSTER_NAME = "hazelcast"; | |
40 | - | |
41 | - @Value("${cache.device_credentials.max_size.size}") | |
42 | - private Integer cacheDeviceCredentialsMaxSizeSize; | |
43 | - @Value("${cache.device_credentials.max_size.policy}") | |
44 | - private String cacheDeviceCredentialsMaxSizePolicy; | |
45 | - @Value("${cache.device_credentials.time_to_live}") | |
46 | - private Integer cacheDeviceCredentialsTTL; | |
47 | - | |
48 | - @Value("${zk.enabled}") | |
49 | - private boolean zkEnabled; | |
50 | - @Value("${zk.url}") | |
51 | - private String zkUrl; | |
52 | - @Value("${zk.zk_dir}") | |
53 | - private String zkDir; | |
41 | + private Map<String, CacheSpecs> specs; | |
54 | 42 | |
55 | 43 | @Bean |
56 | - public HazelcastInstance hazelcastInstance() { | |
57 | - Config config = new Config(); | |
58 | - | |
59 | - if (zkEnabled) { | |
60 | - addZkConfig(config); | |
44 | + public CacheManager cacheManager() { | |
45 | + SimpleCacheManager manager = new SimpleCacheManager(); | |
46 | + if (specs != null) { | |
47 | + List<CaffeineCache> caches = | |
48 | + specs.entrySet().stream() | |
49 | + .map(entry -> buildCache(entry.getKey(), | |
50 | + entry.getValue())) | |
51 | + .collect(Collectors.toList()); | |
52 | + manager.setCaches(caches); | |
61 | 53 | } |
62 | - | |
63 | - config.addMapConfig(createDeviceCredentialsCacheConfig()); | |
64 | - | |
65 | - return Hazelcast.newHazelcastInstance(config); | |
54 | + return manager; | |
66 | 55 | } |
67 | 56 | |
68 | - private void addZkConfig(Config config) { | |
69 | - config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false); | |
70 | - config.setProperty(GroupProperty.DISCOVERY_SPI_ENABLED.getName(), Boolean.TRUE.toString()); | |
71 | - DiscoveryStrategyConfig discoveryStrategyConfig = new DiscoveryStrategyConfig(new ZookeeperDiscoveryStrategyFactory()); | |
72 | - discoveryStrategyConfig.addProperty(ZookeeperDiscoveryProperties.ZOOKEEPER_URL.key(), zkUrl); | |
73 | - discoveryStrategyConfig.addProperty(ZookeeperDiscoveryProperties.ZOOKEEPER_PATH.key(), zkDir); | |
74 | - discoveryStrategyConfig.addProperty(ZookeeperDiscoveryProperties.GROUP.key(), HAZELCAST_CLUSTER_NAME); | |
75 | - config.getNetworkConfig().getJoin().getDiscoveryConfig().addDiscoveryStrategyConfig(discoveryStrategyConfig); | |
57 | + private CaffeineCache buildCache(String name, CacheSpecs cacheSpec) { | |
58 | + final Caffeine<Object, Object> caffeineBuilder | |
59 | + = Caffeine.newBuilder() | |
60 | + .expireAfterWrite(cacheSpec.getTimeToLiveInMinutes(), TimeUnit.MINUTES) | |
61 | + .maximumSize(cacheSpec.getMaxSize()) | |
62 | + .ticker(ticker()); | |
63 | + return new CaffeineCache(name, caffeineBuilder.build()); | |
76 | 64 | } |
77 | 65 | |
78 | - private MapConfig createDeviceCredentialsCacheConfig() { | |
79 | - MapConfig deviceCredentialsCacheConfig = new MapConfig(CacheConstants.DEVICE_CREDENTIALS_CACHE); | |
80 | - deviceCredentialsCacheConfig.setTimeToLiveSeconds(cacheDeviceCredentialsTTL); | |
81 | - deviceCredentialsCacheConfig.setEvictionPolicy(EvictionPolicy.LRU); | |
82 | - deviceCredentialsCacheConfig.setMaxSizeConfig( | |
83 | - new MaxSizeConfig( | |
84 | - cacheDeviceCredentialsMaxSizeSize, | |
85 | - MaxSizeConfig.MaxSizePolicy.valueOf(cacheDeviceCredentialsMaxSizePolicy)) | |
86 | - ); | |
87 | - return deviceCredentialsCacheConfig; | |
66 | + @Bean | |
67 | + public Ticker ticker() { | |
68 | + return Ticker.systemTicker(); | |
88 | 69 | } |
89 | 70 | |
90 | 71 | @Bean |
... | ... | @@ -92,8 +73,4 @@ public class ServiceCacheConfiguration { |
92 | 73 | return new PreviousDeviceCredentialsIdKeyGenerator(); |
93 | 74 | } |
94 | 75 | |
95 | - @Bean | |
96 | - public CacheManager cacheManager() { | |
97 | - return new HazelcastCacheManager(hazelcastInstance()); | |
98 | - } | |
99 | 76 | } | ... | ... |
... | ... | @@ -34,7 +34,7 @@ public interface DeviceService { |
34 | 34 | |
35 | 35 | ListenableFuture<Device> findDeviceByIdAsync(DeviceId deviceId); |
36 | 36 | |
37 | - Optional<Device> findDeviceByTenantIdAndName(TenantId tenantId, String name); | |
37 | + Device findDeviceByTenantIdAndName(TenantId tenantId, String name); | |
38 | 38 | |
39 | 39 | Device saveDevice(Device device); |
40 | 40 | ... | ... |
... | ... | @@ -22,6 +22,10 @@ import com.google.common.util.concurrent.ListenableFuture; |
22 | 22 | import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.apache.commons.lang3.RandomStringUtils; |
24 | 24 | import org.springframework.beans.factory.annotation.Autowired; |
25 | +import org.springframework.cache.Cache; | |
26 | +import org.springframework.cache.CacheManager; | |
27 | +import org.springframework.cache.annotation.CacheEvict; | |
28 | +import org.springframework.cache.annotation.Cacheable; | |
25 | 29 | import org.springframework.stereotype.Service; |
26 | 30 | import org.springframework.util.StringUtils; |
27 | 31 | import org.thingsboard.server.common.data.*; |
... | ... | @@ -33,12 +37,12 @@ import org.thingsboard.server.common.data.id.TenantId; |
33 | 37 | import org.thingsboard.server.common.data.page.TextPageData; |
34 | 38 | import org.thingsboard.server.common.data.page.TextPageLink; |
35 | 39 | import org.thingsboard.server.common.data.relation.EntityRelation; |
40 | +import org.thingsboard.server.common.data.relation.EntitySearchDirection; | |
36 | 41 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
37 | 42 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
38 | 43 | import org.thingsboard.server.dao.customer.CustomerDao; |
39 | 44 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
40 | 45 | import org.thingsboard.server.dao.exception.DataValidationException; |
41 | -import org.thingsboard.server.common.data.relation.EntitySearchDirection; | |
42 | 46 | import org.thingsboard.server.dao.service.DataValidator; |
43 | 47 | import org.thingsboard.server.dao.service.PaginatedRemover; |
44 | 48 | import org.thingsboard.server.dao.tenant.TenantDao; |
... | ... | @@ -47,6 +51,7 @@ import javax.annotation.Nullable; |
47 | 51 | import java.util.*; |
48 | 52 | import java.util.stream.Collectors; |
49 | 53 | |
54 | +import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE; | |
50 | 55 | import static org.thingsboard.server.dao.DaoUtil.toUUIDs; |
51 | 56 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
52 | 57 | import static org.thingsboard.server.dao.service.Validator.*; |
... | ... | @@ -71,6 +76,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
71 | 76 | @Autowired |
72 | 77 | private DeviceCredentialsService deviceCredentialsService; |
73 | 78 | |
79 | + @Autowired | |
80 | + private CacheManager cacheManager; | |
81 | + | |
74 | 82 | @Override |
75 | 83 | public Device findDeviceById(DeviceId deviceId) { |
76 | 84 | log.trace("Executing findDeviceById [{}]", deviceId); |
... | ... | @@ -85,18 +93,16 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
85 | 93 | return deviceDao.findByIdAsync(deviceId.getId()); |
86 | 94 | } |
87 | 95 | |
96 | + @Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}") | |
88 | 97 | @Override |
89 | - public Optional<Device> findDeviceByTenantIdAndName(TenantId tenantId, String name) { | |
98 | + public Device findDeviceByTenantIdAndName(TenantId tenantId, String name) { | |
90 | 99 | log.trace("Executing findDeviceByTenantIdAndName [{}][{}]", tenantId, name); |
91 | 100 | validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
92 | 101 | Optional<Device> deviceOpt = deviceDao.findDeviceByTenantIdAndName(tenantId.getId(), name); |
93 | - if (deviceOpt.isPresent()) { | |
94 | - return Optional.of(deviceOpt.get()); | |
95 | - } else { | |
96 | - return Optional.empty(); | |
97 | - } | |
102 | + return deviceOpt.orElse(null); | |
98 | 103 | } |
99 | 104 | |
105 | + @CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}") | |
100 | 106 | @Override |
101 | 107 | public Device saveDevice(Device device) { |
102 | 108 | log.trace("Executing saveDevice [{}]", device); |
... | ... | @@ -129,12 +135,18 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
129 | 135 | @Override |
130 | 136 | public void deleteDevice(DeviceId deviceId) { |
131 | 137 | log.trace("Executing deleteDevice [{}]", deviceId); |
138 | + Cache cache = cacheManager.getCache(DEVICE_CACHE); | |
132 | 139 | validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); |
133 | 140 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId); |
134 | 141 | if (deviceCredentials != null) { |
135 | 142 | deviceCredentialsService.deleteDeviceCredentials(deviceCredentials); |
136 | 143 | } |
137 | 144 | deleteEntityRelations(deviceId); |
145 | + Device device = deviceDao.findById(deviceId.getId()); | |
146 | + List<Object> list = new ArrayList<>(); | |
147 | + list.add(device.getTenantId()); | |
148 | + list.add(device.getName()); | |
149 | + cache.evict(list); | |
138 | 150 | deviceDao.removeById(deviceId.getId()); |
139 | 151 | } |
140 | 152 | |
... | ... | @@ -190,7 +202,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
190 | 202 | validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); |
191 | 203 | validateString(type, "Incorrect type " + type); |
192 | 204 | validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); |
193 | - List<Device> devices = deviceDao.findDevicesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); | |
205 | + List<Device> devices = deviceDao.findDevicesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); | |
194 | 206 | return new TextPageData<>(devices, pageLink); |
195 | 207 | } |
196 | 208 | |
... | ... | @@ -244,10 +256,10 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
244 | 256 | validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
245 | 257 | ListenableFuture<List<EntitySubtype>> tenantDeviceTypes = deviceDao.findTenantDeviceTypesAsync(tenantId.getId()); |
246 | 258 | return Futures.transform(tenantDeviceTypes, |
247 | - (Function<List<EntitySubtype>, List<EntitySubtype>>) deviceTypes -> { | |
248 | - deviceTypes.sort(Comparator.comparing(EntitySubtype::getType)); | |
249 | - return deviceTypes; | |
250 | - }); | |
259 | + (Function<List<EntitySubtype>, List<EntitySubtype>>) deviceTypes -> { | |
260 | + deviceTypes.sort(Comparator.comparing(EntitySubtype::getType)); | |
261 | + return deviceTypes; | |
262 | + }); | |
251 | 263 | } |
252 | 264 | |
253 | 265 | private DataValidator<Device> deviceValidator = | ... | ... |
... | ... | @@ -21,6 +21,11 @@ import com.google.common.util.concurrent.Futures; |
21 | 21 | import com.google.common.util.concurrent.ListenableFuture; |
22 | 22 | import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.springframework.beans.factory.annotation.Autowired; |
24 | +import org.springframework.cache.Cache; | |
25 | +import org.springframework.cache.CacheManager; | |
26 | +import org.springframework.cache.annotation.CacheEvict; | |
27 | +import org.springframework.cache.annotation.Cacheable; | |
28 | +import org.springframework.cache.annotation.Caching; | |
24 | 29 | import org.springframework.stereotype.Service; |
25 | 30 | import org.springframework.util.StringUtils; |
26 | 31 | import org.thingsboard.server.common.data.id.EntityId; |
... | ... | @@ -34,6 +39,8 @@ import java.util.concurrent.ConcurrentHashMap; |
34 | 39 | import java.util.concurrent.ExecutionException; |
35 | 40 | import java.util.function.BiConsumer; |
36 | 41 | |
42 | +import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; | |
43 | + | |
37 | 44 | /** |
38 | 45 | * Created by ashvayka on 28.04.17. |
39 | 46 | */ |
... | ... | @@ -47,6 +54,9 @@ public class BaseRelationService implements RelationService { |
47 | 54 | @Autowired |
48 | 55 | private EntityService entityService; |
49 | 56 | |
57 | + @Autowired | |
58 | + private CacheManager cacheManager; | |
59 | + | |
50 | 60 | @Override |
51 | 61 | public ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
52 | 62 | log.trace("Executing checkRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); |
... | ... | @@ -54,6 +64,7 @@ public class BaseRelationService implements RelationService { |
54 | 64 | return relationDao.checkRelation(from, to, relationType, typeGroup); |
55 | 65 | } |
56 | 66 | |
67 | + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType}") | |
57 | 68 | @Override |
58 | 69 | public ListenableFuture<EntityRelation> getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
59 | 70 | log.trace("Executing EntityRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); |
... | ... | @@ -61,6 +72,12 @@ public class BaseRelationService implements RelationService { |
61 | 72 | return relationDao.getRelation(from, to, relationType, typeGroup); |
62 | 73 | } |
63 | 74 | |
75 | + @Caching(evict = { | |
76 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), | |
77 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), | |
78 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), | |
79 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}") | |
80 | + }) | |
64 | 81 | @Override |
65 | 82 | public boolean saveRelation(EntityRelation relation) { |
66 | 83 | log.trace("Executing saveRelation [{}]", relation); |
... | ... | @@ -68,6 +85,12 @@ public class BaseRelationService implements RelationService { |
68 | 85 | return relationDao.saveRelation(relation); |
69 | 86 | } |
70 | 87 | |
88 | + @Caching(evict = { | |
89 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), | |
90 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), | |
91 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), | |
92 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}") | |
93 | + }) | |
71 | 94 | @Override |
72 | 95 | public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) { |
73 | 96 | log.trace("Executing saveRelationAsync [{}]", relation); |
... | ... | @@ -75,6 +98,13 @@ public class BaseRelationService implements RelationService { |
75 | 98 | return relationDao.saveRelationAsync(relation); |
76 | 99 | } |
77 | 100 | |
101 | + @Caching(evict = { | |
102 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), | |
103 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), | |
104 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), | |
105 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}"), | |
106 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type}") | |
107 | + }) | |
78 | 108 | @Override |
79 | 109 | public boolean deleteRelation(EntityRelation relation) { |
80 | 110 | log.trace("Executing deleteRelation [{}]", relation); |
... | ... | @@ -82,6 +112,13 @@ public class BaseRelationService implements RelationService { |
82 | 112 | return relationDao.deleteRelation(relation); |
83 | 113 | } |
84 | 114 | |
115 | + @Caching(evict = { | |
116 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), | |
117 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), | |
118 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), | |
119 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}"), | |
120 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type}") | |
121 | + }) | |
85 | 122 | @Override |
86 | 123 | public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) { |
87 | 124 | log.trace("Executing deleteRelationAsync [{}]", relation); |
... | ... | @@ -89,6 +126,13 @@ public class BaseRelationService implements RelationService { |
89 | 126 | return relationDao.deleteRelationAsync(relation); |
90 | 127 | } |
91 | 128 | |
129 | + @Caching(evict = { | |
130 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#from"), | |
131 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType}"), | |
132 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#to"), | |
133 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType}"), | |
134 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType}") | |
135 | + }) | |
92 | 136 | @Override |
93 | 137 | public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
94 | 138 | log.trace("Executing deleteRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); |
... | ... | @@ -96,6 +140,13 @@ public class BaseRelationService implements RelationService { |
96 | 140 | return relationDao.deleteRelation(from, to, relationType, typeGroup); |
97 | 141 | } |
98 | 142 | |
143 | + @Caching(evict = { | |
144 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#from"), | |
145 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType}"), | |
146 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#to"), | |
147 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType}"), | |
148 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType}") | |
149 | + }) | |
99 | 150 | @Override |
100 | 151 | public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
101 | 152 | log.trace("Executing deleteRelationAsync [{}][{}][{}][{}]", from, to, relationType, typeGroup); |
... | ... | @@ -105,23 +156,17 @@ public class BaseRelationService implements RelationService { |
105 | 156 | |
106 | 157 | @Override |
107 | 158 | public boolean deleteEntityRelations(EntityId entity) { |
159 | + Cache cache = cacheManager.getCache(RELATIONS_CACHE); | |
108 | 160 | log.trace("Executing deleteEntityRelations [{}]", entity); |
109 | 161 | validate(entity); |
110 | - List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>(); | |
162 | + List<ListenableFuture<List<EntityRelation>>> inboundRelationsListTo = new ArrayList<>(); | |
111 | 163 | for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { |
112 | - inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); | |
164 | + inboundRelationsListTo.add(relationDao.findAllByTo(entity, typeGroup)); | |
113 | 165 | } |
114 | - ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList); | |
115 | - ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, new Function<List<List<EntityRelation>>, List<Boolean>>() { | |
116 | - @Override | |
117 | - public List<Boolean> apply(List<List<EntityRelation>> relations) { | |
118 | - List<Boolean> results = new ArrayList<>(); | |
119 | - for (List<EntityRelation> relationList : relations) { | |
120 | - relationList.stream().forEach(relation -> results.add(relationDao.deleteRelation(relation))); | |
121 | - } | |
122 | - return results; | |
123 | - } | |
124 | - }); | |
166 | + ListenableFuture<List<List<EntityRelation>>> inboundRelationsTo = Futures.allAsList(inboundRelationsListTo); | |
167 | + ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelationsTo, (List<List<EntityRelation>> relations) -> | |
168 | + getBooleans(relations, cache, true)); | |
169 | + | |
125 | 170 | ListenableFuture<Boolean> inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); |
126 | 171 | boolean inboundDeleteResult = false; |
127 | 172 | try { |
... | ... | @@ -129,12 +174,39 @@ public class BaseRelationService implements RelationService { |
129 | 174 | } catch (InterruptedException | ExecutionException e) { |
130 | 175 | log.error("Error deleting entity inbound relations", e); |
131 | 176 | } |
177 | + | |
178 | + List<ListenableFuture<List<EntityRelation>>> inboundRelationsListFrom = new ArrayList<>(); | |
179 | + for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { | |
180 | + inboundRelationsListFrom.add(relationDao.findAllByFrom(entity, typeGroup)); | |
181 | + } | |
182 | + ListenableFuture<List<List<EntityRelation>>> inboundRelationsFrom = Futures.allAsList(inboundRelationsListFrom); | |
183 | + Futures.transform(inboundRelationsFrom, (Function<List<List<EntityRelation>>, List<Boolean>>) relations -> | |
184 | + getBooleans(relations, cache, false)); | |
185 | + | |
132 | 186 | boolean outboundDeleteResult = relationDao.deleteOutboundRelations(entity); |
133 | 187 | return inboundDeleteResult && outboundDeleteResult; |
134 | 188 | } |
135 | 189 | |
190 | + private List<Boolean> getBooleans(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) { | |
191 | + List<Boolean> results = new ArrayList<>(); | |
192 | + for (List<EntityRelation> relationList : relations) { | |
193 | + relationList.stream().forEach(relation -> { | |
194 | + checkFromDeleteSync(cache, results, relation, isRemove); | |
195 | + }); | |
196 | + } | |
197 | + return results; | |
198 | + } | |
199 | + | |
200 | + private void checkFromDeleteSync(Cache cache, List<Boolean> results, EntityRelation relation, boolean isRemove) { | |
201 | + if (isRemove) { | |
202 | + results.add(relationDao.deleteRelation(relation)); | |
203 | + } | |
204 | + cacheEviction(relation, relation.getTo(), cache); | |
205 | + } | |
206 | + | |
136 | 207 | @Override |
137 | 208 | public ListenableFuture<Boolean> deleteEntityRelationsAsync(EntityId entity) { |
209 | + Cache cache = cacheManager.getCache(RELATIONS_CACHE); | |
138 | 210 | log.trace("Executing deleteEntityRelationsAsync [{}]", entity); |
139 | 211 | validate(entity); |
140 | 212 | List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>(); |
... | ... | @@ -142,24 +214,61 @@ public class BaseRelationService implements RelationService { |
142 | 214 | inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); |
143 | 215 | } |
144 | 216 | ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList); |
145 | - ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, new AsyncFunction<List<List<EntityRelation>>, List<Boolean>>() { | |
146 | - @Override | |
147 | - public ListenableFuture<List<Boolean>> apply(List<List<EntityRelation>> relations) throws Exception { | |
148 | - List<ListenableFuture<Boolean>> results = new ArrayList<>(); | |
149 | - for (List<EntityRelation> relationList : relations) { | |
150 | - relationList.stream().forEach(relation -> results.add(relationDao.deleteRelationAsync(relation))); | |
151 | - } | |
152 | - return Futures.allAsList(results); | |
153 | - } | |
217 | + ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, | |
218 | + (AsyncFunction<List<List<EntityRelation>>, List<Boolean>>) relations -> { | |
219 | + List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, true); | |
220 | + return Futures.allAsList(results); | |
154 | 221 | }); |
155 | 222 | |
156 | 223 | ListenableFuture<Boolean> inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); |
157 | 224 | |
158 | - ListenableFuture<Boolean> outboundFuture = relationDao.deleteOutboundRelationsAsync(entity); | |
225 | + List<ListenableFuture<List<EntityRelation>>> inboundRelationsList1 = new ArrayList<>(); | |
226 | + for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { | |
227 | + inboundRelationsList1.add(relationDao.findAllByTo(entity, typeGroup)); | |
228 | + } | |
229 | + ListenableFuture<List<List<EntityRelation>>> inboundRelations1 = Futures.allAsList(inboundRelationsList1); | |
230 | + Futures.transform(inboundRelations1, (AsyncFunction<List<List<EntityRelation>>, List<Boolean>>) relations -> { | |
231 | + List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, false); | |
232 | + return Futures.allAsList(results); | |
233 | + }); | |
159 | 234 | |
235 | + ListenableFuture<Boolean> outboundFuture = relationDao.deleteOutboundRelationsAsync(entity); | |
160 | 236 | return Futures.transform(Futures.allAsList(Arrays.asList(inboundFuture, outboundFuture)), getListToBooleanFunction()); |
161 | 237 | } |
162 | 238 | |
239 | + private List<ListenableFuture<Boolean>> getListenableFutures(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) { | |
240 | + List<ListenableFuture<Boolean>> results = new ArrayList<>(); | |
241 | + for (List<EntityRelation> relationList : relations) { | |
242 | + relationList.stream().forEach(relation -> { | |
243 | + checkFromDeleteAsync(cache, results, relation, isRemove); | |
244 | + }); | |
245 | + } | |
246 | + return results; | |
247 | + } | |
248 | + | |
249 | + private void checkFromDeleteAsync(Cache cache, List<ListenableFuture<Boolean>> results, EntityRelation relation, boolean isRemove) { | |
250 | + if (isRemove) { | |
251 | + results.add(relationDao.deleteRelationAsync(relation)); | |
252 | + } | |
253 | + cacheEviction(relation, relation.getTo(), cache); | |
254 | + } | |
255 | + | |
256 | + private void cacheEviction(EntityRelation relation, EntityId entityId, Cache cache) { | |
257 | + cache.evict(entityId); | |
258 | + | |
259 | + List<Object> toAndType = new ArrayList<>(); | |
260 | + toAndType.add(entityId); | |
261 | + toAndType.add(relation.getType()); | |
262 | + cache.evict(toAndType); | |
263 | + | |
264 | + List<Object> fromToAndType = new ArrayList<>(); | |
265 | + fromToAndType.add(relation.getFrom()); | |
266 | + fromToAndType.add(relation.getTo()); | |
267 | + fromToAndType.add(relation.getType()); | |
268 | + cache.evict(fromToAndType); | |
269 | + } | |
270 | + | |
271 | + @Cacheable(cacheNames = RELATIONS_CACHE, key = "#from") | |
163 | 272 | @Override |
164 | 273 | public ListenableFuture<List<EntityRelation>> findByFrom(EntityId from, RelationTypeGroup typeGroup) { |
165 | 274 | log.trace("Executing findByFrom [{}][{}]", from, typeGroup); |
... | ... | @@ -176,17 +285,18 @@ public class BaseRelationService implements RelationService { |
176 | 285 | ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(from, typeGroup); |
177 | 286 | ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, |
178 | 287 | (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { |
179 | - List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); | |
288 | + List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); | |
180 | 289 | relations1.stream().forEach(relation -> |
181 | 290 | futures.add(fetchRelationInfoAsync(relation, |
182 | 291 | relation2 -> relation2.getTo(), |
183 | 292 | (EntityRelationInfo relationInfo, String entityName) -> relationInfo.setToName(entityName))) |
184 | 293 | ); |
185 | 294 | return Futures.successfulAsList(futures); |
186 | - }); | |
295 | + }); | |
187 | 296 | return relationsInfo; |
188 | 297 | } |
189 | 298 | |
299 | + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType}") | |
190 | 300 | @Override |
191 | 301 | public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { |
192 | 302 | log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup); |
... | ... | @@ -196,6 +306,7 @@ public class BaseRelationService implements RelationService { |
196 | 306 | return relationDao.findAllByFromAndType(from, relationType, typeGroup); |
197 | 307 | } |
198 | 308 | |
309 | + @Cacheable(cacheNames = RELATIONS_CACHE, key = "#to") | |
199 | 310 | @Override |
200 | 311 | public ListenableFuture<List<EntityRelation>> findByTo(EntityId to, RelationTypeGroup typeGroup) { |
201 | 312 | log.trace("Executing findByTo [{}][{}]", to, typeGroup); |
... | ... | @@ -214,9 +325,9 @@ public class BaseRelationService implements RelationService { |
214 | 325 | (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { |
215 | 326 | List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); |
216 | 327 | relations1.stream().forEach(relation -> |
217 | - futures.add(fetchRelationInfoAsync(relation, | |
218 | - relation2 -> relation2.getFrom(), | |
219 | - (EntityRelationInfo relationInfo, String entityName) -> relationInfo.setFromName(entityName))) | |
328 | + futures.add(fetchRelationInfoAsync(relation, | |
329 | + relation2 -> relation2.getFrom(), | |
330 | + (EntityRelationInfo relationInfo, String entityName) -> relationInfo.setFromName(entityName))) | |
220 | 331 | ); |
221 | 332 | return Futures.successfulAsList(futures); |
222 | 333 | }); |
... | ... | @@ -236,6 +347,7 @@ public class BaseRelationService implements RelationService { |
236 | 347 | return entityRelationInfo; |
237 | 348 | } |
238 | 349 | |
350 | + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType}") | |
239 | 351 | @Override |
240 | 352 | public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { |
241 | 353 | log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup); |
... | ... | @@ -417,5 +529,4 @@ public class BaseRelationService implements RelationService { |
417 | 529 | } |
418 | 530 | return relations; |
419 | 531 | } |
420 | - | |
421 | 532 | } | ... | ... |
... | ... | @@ -15,16 +15,14 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.service; |
17 | 17 | |
18 | -import com.hazelcast.core.HazelcastInstance; | |
19 | 18 | import org.apache.commons.lang3.RandomStringUtils; |
20 | 19 | import org.junit.After; |
21 | -import org.junit.Assert; | |
22 | 20 | import org.junit.Before; |
23 | 21 | import org.junit.Test; |
24 | 22 | import org.springframework.aop.framework.Advised; |
25 | 23 | import org.springframework.aop.support.AopUtils; |
26 | 24 | import org.springframework.beans.factory.annotation.Autowired; |
27 | -import org.springframework.test.context.TestPropertySource; | |
25 | +import org.springframework.cache.CacheManager; | |
28 | 26 | import org.springframework.test.util.ReflectionTestUtils; |
29 | 27 | import org.thingsboard.server.common.data.CacheConstants; |
30 | 28 | import org.thingsboard.server.common.data.Device; |
... | ... | @@ -40,7 +38,6 @@ import java.util.UUID; |
40 | 38 | |
41 | 39 | import static org.mockito.Mockito.*; |
42 | 40 | |
43 | -@TestPropertySource(properties = {"cache.enabled = true"}) | |
44 | 41 | public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest { |
45 | 42 | |
46 | 43 | private static final String CREDENTIALS_ID_1 = RandomStringUtils.randomAlphanumeric(20); |
... | ... | @@ -53,7 +50,7 @@ public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest |
53 | 50 | private DeviceService deviceService; |
54 | 51 | |
55 | 52 | @Autowired |
56 | - private HazelcastInstance hazelcastInstance; | |
53 | + private CacheManager cacheManager; | |
57 | 54 | |
58 | 55 | private UUID deviceId = UUID.randomUUID(); |
59 | 56 | |
... | ... | @@ -67,7 +64,7 @@ public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest |
67 | 64 | |
68 | 65 | @After |
69 | 66 | public void cleanup() { |
70 | - hazelcastInstance.getMap(CacheConstants.DEVICE_CREDENTIALS_CACHE).evictAll(); | |
67 | + cacheManager.getCache(CacheConstants.DEVICE_CREDENTIALS_CACHE).clear(); | |
71 | 68 | } |
72 | 69 | |
73 | 70 | @Test |
... | ... | @@ -77,7 +74,6 @@ public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest |
77 | 74 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
78 | 75 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
79 | 76 | |
80 | - Assert.assertEquals(1, hazelcastInstance.getMap(CacheConstants.DEVICE_CREDENTIALS_CACHE).size()); | |
81 | 77 | verify(deviceCredentialsDao, times(1)).findByCredentialsId(CREDENTIALS_ID_1); |
82 | 78 | } |
83 | 79 | |
... | ... | @@ -88,17 +84,13 @@ public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest |
88 | 84 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
89 | 85 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
90 | 86 | |
91 | - Assert.assertEquals(1, hazelcastInstance.getMap(CacheConstants.DEVICE_CREDENTIALS_CACHE).size()); | |
92 | 87 | verify(deviceCredentialsDao, times(1)).findByCredentialsId(CREDENTIALS_ID_1); |
93 | 88 | |
94 | 89 | deviceCredentialsService.deleteDeviceCredentials(createDummyDeviceCredentials(CREDENTIALS_ID_1, deviceId)); |
95 | 90 | |
96 | - Assert.assertEquals(0, hazelcastInstance.getMap(CacheConstants.DEVICE_CREDENTIALS_CACHE).size()); | |
97 | - | |
98 | 91 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
99 | 92 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
100 | 93 | |
101 | - Assert.assertEquals(1, hazelcastInstance.getMap(CacheConstants.DEVICE_CREDENTIALS_CACHE).size()); | |
102 | 94 | verify(deviceCredentialsDao, times(2)).findByCredentialsId(CREDENTIALS_ID_1); |
103 | 95 | } |
104 | 96 | |
... | ... | @@ -109,7 +101,6 @@ public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest |
109 | 101 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
110 | 102 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
111 | 103 | |
112 | - Assert.assertEquals(1, hazelcastInstance.getMap(CacheConstants.DEVICE_CREDENTIALS_CACHE).size()); | |
113 | 104 | verify(deviceCredentialsDao, times(1)).findByCredentialsId(CREDENTIALS_ID_1); |
114 | 105 | |
115 | 106 | when(deviceCredentialsDao.findByDeviceId(deviceId)).thenReturn(createDummyDeviceCredentialsEntity(CREDENTIALS_ID_1)); |
... | ... | @@ -119,13 +110,11 @@ public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest |
119 | 110 | when(deviceService.findDeviceById(new DeviceId(deviceId))).thenReturn(new Device()); |
120 | 111 | |
121 | 112 | deviceCredentialsService.updateDeviceCredentials(createDummyDeviceCredentials(deviceCredentialsId, CREDENTIALS_ID_2, deviceId)); |
122 | - Assert.assertEquals(0, hazelcastInstance.getMap(CacheConstants.DEVICE_CREDENTIALS_CACHE).size()); | |
123 | 113 | |
124 | 114 | when(deviceCredentialsDao.findByCredentialsId(CREDENTIALS_ID_1)).thenReturn(null); |
125 | 115 | |
126 | 116 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
127 | 117 | deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); |
128 | - Assert.assertEquals(0, hazelcastInstance.getMap(CacheConstants.DEVICE_CREDENTIALS_CACHE).size()); | |
129 | 118 | |
130 | 119 | verify(deviceCredentialsDao, times(3)).findByCredentialsId(CREDENTIALS_ID_1); |
131 | 120 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2017 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.service; | |
17 | + | |
18 | +import com.google.common.util.concurrent.Futures; | |
19 | +import org.junit.After; | |
20 | +import org.junit.Before; | |
21 | +import org.junit.Test; | |
22 | +import org.springframework.aop.framework.Advised; | |
23 | +import org.springframework.aop.support.AopUtils; | |
24 | +import org.springframework.beans.factory.annotation.Autowired; | |
25 | +import org.springframework.cache.CacheManager; | |
26 | +import org.springframework.test.util.ReflectionTestUtils; | |
27 | +import org.thingsboard.server.common.data.id.DeviceId; | |
28 | +import org.thingsboard.server.common.data.id.EntityId; | |
29 | +import org.thingsboard.server.common.data.relation.EntityRelation; | |
30 | +import org.thingsboard.server.common.data.relation.RelationTypeGroup; | |
31 | +import org.thingsboard.server.dao.relation.RelationDao; | |
32 | +import org.thingsboard.server.dao.relation.RelationService; | |
33 | + | |
34 | +import java.util.UUID; | |
35 | +import java.util.concurrent.ExecutionException; | |
36 | + | |
37 | +import static org.mockito.Mockito.*; | |
38 | +import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; | |
39 | + | |
40 | +public abstract class BaseRelationCacheTest extends AbstractServiceTest { | |
41 | + | |
42 | + private static final EntityId ENTITY_ID_FROM = new DeviceId(UUID.randomUUID()); | |
43 | + private static final EntityId ENTITY_ID_TO = new DeviceId(UUID.randomUUID()); | |
44 | + private static final String RELATION_TYPE = "Contains"; | |
45 | + | |
46 | + @Autowired | |
47 | + private RelationService relationService; | |
48 | + @Autowired | |
49 | + private CacheManager cacheManager; | |
50 | + | |
51 | + private RelationDao relationDao; | |
52 | + | |
53 | + @Before | |
54 | + public void setup() throws Exception { | |
55 | + relationDao = mock(RelationDao.class); | |
56 | + ReflectionTestUtils.setField(unwrapRelationService(), "relationDao", relationDao); | |
57 | + } | |
58 | + | |
59 | + @After | |
60 | + public void cleanup() { | |
61 | + cacheManager.getCache(RELATIONS_CACHE).clear(); | |
62 | + } | |
63 | + | |
64 | + private RelationService unwrapRelationService() throws Exception { | |
65 | + if (AopUtils.isAopProxy(relationService) && relationService instanceof Advised) { | |
66 | + Object target = ((Advised) relationService).getTargetSource().getTarget(); | |
67 | + return (RelationService) target; | |
68 | + } | |
69 | + return null; | |
70 | + } | |
71 | + | |
72 | + @Test | |
73 | + public void testFindRelationByFrom_Cached() throws ExecutionException, InterruptedException { | |
74 | + when(relationDao.getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON)) | |
75 | + .thenReturn(Futures.immediateFuture(new EntityRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE))); | |
76 | + | |
77 | + relationService.getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
78 | + relationService.getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
79 | + | |
80 | + verify(relationDao, times(1)).getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
81 | + } | |
82 | + | |
83 | + @Test | |
84 | + public void testDeleteRelations_EvictsCache() { | |
85 | + when(relationDao.getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON)) | |
86 | + .thenReturn(Futures.immediateFuture(new EntityRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE))); | |
87 | + | |
88 | + relationService.getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
89 | + relationService.getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
90 | + | |
91 | + verify(relationDao, times(1)).getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
92 | + | |
93 | + relationService.deleteRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
94 | + | |
95 | + relationService.getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
96 | + relationService.getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
97 | + | |
98 | + verify(relationDao, times(2)).getRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON); | |
99 | + | |
100 | + } | |
101 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2017 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.service.nosql; | |
17 | + | |
18 | +import org.thingsboard.server.dao.service.BaseRelationCacheTest; | |
19 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | |
20 | + | |
21 | +@DaoNoSqlTest | |
22 | +public class RelationCacheNoSqlTest extends BaseRelationCacheTest { | |
23 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2017 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.service.sql; | |
17 | + | |
18 | +import org.thingsboard.server.dao.service.BaseRelationCacheTest; | |
19 | +import org.thingsboard.server.dao.service.DaoSqlTest; | |
20 | + | |
21 | +@DaoSqlTest | |
22 | +public class RelationCacheSqlTest extends BaseRelationCacheTest { | |
23 | +} | ... | ... |
... | ... | @@ -7,4 +7,13 @@ zk.enabled=false |
7 | 7 | zk.url=localhost:2181 |
8 | 8 | zk.zk_dir=/thingsboard |
9 | 9 | |
10 | -updates.enabled=false | |
\ No newline at end of file | ||
10 | +updates.enabled=false | |
11 | + | |
12 | +caching.specs.relations.timeToLiveInMinutes=1440 | |
13 | +caching.specs.relations.maxSize=100000 | |
14 | + | |
15 | +caching.specs.deviceCredentials.timeToLiveInMinutes=1440 | |
16 | +caching.specs.deviceCredentials.maxSize=100000 | |
17 | + | |
18 | +caching.specs.devices.timeToLiveInMinutes=1440 | |
19 | +caching.specs.devices.maxSize=100000 | |
\ No newline at end of file | ... | ... |
... | ... | @@ -43,6 +43,7 @@ |
43 | 43 | <cassandra-unit.version>3.0.0.1</cassandra-unit.version> |
44 | 44 | <takari-cpsuite.version>1.2.7</takari-cpsuite.version> |
45 | 45 | <guava.version>18.0</guava.version> |
46 | + <caffeine.version>2.6.1</caffeine.version> | |
46 | 47 | <commons-lang3.version>3.4</commons-lang3.version> |
47 | 48 | <commons-validator.version>1.5.0</commons-validator.version> |
48 | 49 | <commons-io.version>2.5</commons-io.version> |
... | ... | @@ -645,6 +646,11 @@ |
645 | 646 | <version>${guava.version}</version> |
646 | 647 | </dependency> |
647 | 648 | <dependency> |
649 | + <groupId>com.github.ben-manes.caffeine</groupId> | |
650 | + <artifactId>caffeine</artifactId> | |
651 | + <version>${caffeine.version}</version> | |
652 | + </dependency> | |
653 | + <dependency> | |
648 | 654 | <groupId>com.google.protobuf</groupId> |
649 | 655 | <artifactId>protobuf-java</artifactId> |
650 | 656 | <version>${protobuf.version}</version> | ... | ... |
... | ... | @@ -26,17 +26,17 @@ import lombok.extern.slf4j.Slf4j; |
26 | 26 | import org.eclipse.californium.core.CoapResource; |
27 | 27 | import org.eclipse.californium.core.CoapServer; |
28 | 28 | import org.eclipse.californium.core.network.CoapEndpoint; |
29 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
29 | 30 | import org.thingsboard.server.common.transport.SessionMsgProcessor; |
30 | 31 | import org.thingsboard.server.common.transport.auth.DeviceAuthService; |
31 | 32 | import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; |
32 | -import org.slf4j.Logger; | |
33 | -import org.slf4j.LoggerFactory; | |
34 | 33 | import org.springframework.beans.factory.annotation.Autowired; |
35 | 34 | import org.springframework.beans.factory.annotation.Value; |
36 | 35 | import org.springframework.context.ApplicationContext; |
37 | 36 | import org.springframework.stereotype.Service; |
38 | 37 | |
39 | 38 | @Service("CoapTransportService") |
39 | +@ConditionalOnProperty(prefix = "coap", value = "enabled", havingValue = "true", matchIfMissing = true) | |
40 | 40 | @Slf4j |
41 | 41 | public class CoapTransportService { |
42 | 42 | ... | ... |
... | ... | @@ -24,6 +24,7 @@ import io.netty.util.ResourceLeakDetector; |
24 | 24 | import lombok.extern.slf4j.Slf4j; |
25 | 25 | import org.springframework.beans.factory.annotation.Autowired; |
26 | 26 | import org.springframework.beans.factory.annotation.Value; |
27 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
27 | 28 | import org.springframework.context.ApplicationContext; |
28 | 29 | import org.springframework.stereotype.Service; |
29 | 30 | import org.thingsboard.server.common.transport.SessionMsgProcessor; |
... | ... | @@ -39,6 +40,7 @@ import javax.annotation.PreDestroy; |
39 | 40 | * @author Andrew Shvayka |
40 | 41 | */ |
41 | 42 | @Service("MqttTransportService") |
43 | +@ConditionalOnProperty(prefix = "mqtt", value = "enabled", havingValue = "true", matchIfMissing = false) | |
42 | 44 | @Slf4j |
43 | 45 | public class MqttTransportService { |
44 | 46 | ... | ... |
... | ... | @@ -84,16 +84,15 @@ public class GatewaySessionCtx { |
84 | 84 | |
85 | 85 | private void onDeviceConnect(String deviceName, String deviceType) { |
86 | 86 | if (!devices.containsKey(deviceName)) { |
87 | - Optional<Device> deviceOpt = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), deviceName); | |
88 | - Device device = deviceOpt.orElseGet(() -> { | |
89 | - Device newDevice = new Device(); | |
90 | - newDevice.setTenantId(gateway.getTenantId()); | |
91 | - newDevice.setName(deviceName); | |
92 | - newDevice.setType(deviceType); | |
93 | - newDevice = deviceService.saveDevice(newDevice); | |
94 | - relationService.saveRelationAsync(new EntityRelation(gateway.getId(), newDevice.getId(), "Created")); | |
95 | - return newDevice; | |
96 | - }); | |
87 | + Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), deviceName); | |
88 | + if (device == null) { | |
89 | + device = new Device(); | |
90 | + device.setTenantId(gateway.getTenantId()); | |
91 | + device.setName(deviceName); | |
92 | + device.setType(deviceType); | |
93 | + device = deviceService.saveDevice(device); | |
94 | + relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created")); | |
95 | + } | |
97 | 96 | GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device); |
98 | 97 | devices.put(deviceName, ctx); |
99 | 98 | log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName); | ... | ... |