Skip to content

Package: LruCacheManager$LruEntityCache

LruCacheManager$LruEntityCache

nameinstructionbranchcomplexitylinemethod
LruCacheManager.LruEntityCache(int)
M: 0 C: 10
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
accept(LruCache.CacheNode)
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
evict(Class)
M: 0 C: 26
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
evict(Class, Object, URI)
M: 0 C: 23
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
evict(URI)
M: 0 C: 42
100%
M: 0 C: 6
100%
M: 0 C: 4
100%
M: 0 C: 9
100%
M: 0 C: 1
100%
get(Class, Object, Descriptor)
M: 0 C: 29
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
lambda$evict$0(URI, Map.Entry, Class, Object)
M: 0 C: 17
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
lambda$evict$1(Class, URI, Object, Map)
M: 0 C: 22
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
put(Object, Object, Descriptor)
M: 0 C: 27
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
static {...}
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: /**
2: * Copyright (C) 2020 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.sessions.cache;
16:
17: import cz.cvut.kbss.jopa.model.JOPAPersistenceProperties;
18: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
19: import cz.cvut.kbss.jopa.sessions.CacheManager;
20: import cz.cvut.kbss.jopa.utils.ErrorUtils;
21: import org.slf4j.Logger;
22: import org.slf4j.LoggerFactory;
23:
24: import java.net.URI;
25: import java.util.Collections;
26: import java.util.Map;
27: import java.util.Objects;
28: import java.util.Set;
29: import java.util.concurrent.locks.Lock;
30: import java.util.concurrent.locks.ReadWriteLock;
31: import java.util.concurrent.locks.ReentrantReadWriteLock;
32: import java.util.function.Consumer;
33:
34: /**
35: * This is a fixed-size second level cache implementation with LRU eviction policy.
36: * <p>
37: * When the capacity is reached, the least recently used entry is removed from the cache.
38: */
39: public class LruCacheManager implements CacheManager {
40:
41: private static final Logger LOG = LoggerFactory.getLogger(LruCacheManager.class);
42:
43: /**
44: * Default cache size limit in number of entries.
45: */
46: public static final int DEFAULT_CAPACITY = 512;
47:
48: private final int capacity;
49:
50: private final Lock readLock;
51: private final Lock writeLock;
52:
53: private LruEntityCache entityCache;
54:
55: private Set<Class<?>> inferredClasses;
56:
57: LruCacheManager() {
58: this(Collections.emptyMap());
59: }
60:
61: LruCacheManager(Map<String, String> properties) {
62: Objects.requireNonNull(properties);
63: this.capacity = properties.containsKey(JOPAPersistenceProperties.LRU_CACHE_CAPACITY) ?
64: resolveCapacitySetting(properties) : DEFAULT_CAPACITY;
65: final ReadWriteLock rwLock = new ReentrantReadWriteLock();
66: this.readLock = rwLock.readLock();
67: this.writeLock = rwLock.writeLock();
68: this.entityCache = new LruEntityCache(capacity);
69: }
70:
71: private static int resolveCapacitySetting(Map<String, String> properties) {
72: int capacitySetting = DEFAULT_CAPACITY;
73: try {
74: capacitySetting = Integer.parseInt(properties.get(JOPAPersistenceProperties.LRU_CACHE_CAPACITY));
75: if (capacitySetting <= 0) {
76: LOG.warn("Invalid LRU cache capacity value {}. Using default value.", capacitySetting);
77: capacitySetting = DEFAULT_CAPACITY;
78: }
79: } catch (NumberFormatException e) {
80: LOG.error("Unable to parse LRU cache capacity setting. Using default capacity {}.", DEFAULT_CAPACITY);
81: }
82: return capacitySetting;
83: }
84:
85: int getCapacity() {
86: return capacity;
87: }
88:
89: @Override
90: public void add(Object primaryKey, Object entity, Descriptor descriptor) {
91: Objects.requireNonNull(primaryKey, ErrorUtils.getNPXMessageSupplier("primaryKey"));
92: Objects.requireNonNull(entity, ErrorUtils.getNPXMessageSupplier("entity"));
93: Objects.requireNonNull(descriptor, ErrorUtils.getNPXMessageSupplier("descriptor"));
94:
95: writeLock.lock();
96: try {
97: entityCache.put(primaryKey, entity, descriptor);
98: } finally {
99: writeLock.unlock();
100: }
101: }
102:
103: @Override
104: public <T> T get(Class<T> cls, Object primaryKey, Descriptor descriptor) {
105: if (cls == null || primaryKey == null || descriptor == null) {
106: return null;
107: }
108: readLock.lock();
109: try {
110: return entityCache.get(cls, primaryKey, descriptor);
111: } finally {
112: readLock.unlock();
113: }
114: }
115:
116: @Override
117: public void evictInferredObjects() {
118: writeLock.lock();
119: try {
120: getInferredClasses().forEach(this::evict);
121: } finally {
122: writeLock.unlock();
123: }
124: }
125:
126: private Set<Class<?>> getInferredClasses() {
127: if (inferredClasses == null) {
128: return Collections.emptySet();
129: }
130: return inferredClasses;
131: }
132:
133: @Override
134: public void setInferredClasses(Set<Class<?>> inferredClasses) {
135: this.inferredClasses = inferredClasses;
136: }
137:
138: @Override
139: public void close() {
140: // No-op
141: }
142:
143: @Override
144: public boolean contains(Class<?> cls, Object identifier, Descriptor descriptor) {
145: if (cls == null || identifier == null || descriptor == null) {
146: return false;
147: }
148: readLock.lock();
149: try {
150: return entityCache.contains(cls, identifier, descriptor);
151: } finally {
152: readLock.unlock();
153: }
154: }
155:
156: @Override
157: public void evict(Class<?> cls, Object identifier, URI context) {
158: Objects.requireNonNull(cls, ErrorUtils.getNPXMessageSupplier("cls"));
159: Objects.requireNonNull(identifier, ErrorUtils.getNPXMessageSupplier("primaryKey"));
160:
161: writeLock.lock();
162: try {
163: entityCache.evict(cls, identifier, context);
164: } finally {
165: writeLock.unlock();
166: }
167: }
168:
169: @Override
170: public void evict(Class<?> cls) {
171: Objects.requireNonNull(cls);
172:
173: writeLock.lock();
174: try {
175: entityCache.evict(cls);
176: } finally {
177: writeLock.unlock();
178: }
179: }
180:
181: @Override
182: public void evict(URI context) {
183: writeLock.lock();
184: try {
185: entityCache.evict(context);
186: } finally {
187: writeLock.unlock();
188: }
189: }
190:
191: @Override
192: public void evictAll() {
193: writeLock.lock();
194: try {
195: this.entityCache = new LruEntityCache(capacity);
196: } finally {
197: writeLock.unlock();
198: }
199: }
200:
201: static final class LruEntityCache extends EntityCache implements Consumer<LruCache.CacheNode> {
202:
203: private static final Object NULL_VALUE = null;
204:
205: private final LruCache cache;
206:
207: LruEntityCache(int capacity) {
208: this.cache = new LruCache(capacity, this);
209: }
210:
211: @Override
212: public void accept(LruCache.CacheNode cacheNode) {
213: super.evict(cacheNode.getCls(), cacheNode.getIdentifier(), cacheNode.getContext());
214: }
215:
216: @Override
217: void put(Object identifier, Object entity, Descriptor descriptor) {
218:• final URI ctx = descriptor.getContext() != null ? descriptor.getContext() : defaultContext;
219: super.put(identifier, entity, descriptor);
220: cache.put(new LruCache.CacheNode(ctx, entity.getClass(), identifier), NULL_VALUE);
221: }
222:
223: @Override
224: <T> T get(Class<T> cls, Object identifier, Descriptor descriptor) {
225:• final URI ctx = descriptor.getContext() != null ? descriptor.getContext() : defaultContext;
226: T result = super.get(cls, identifier, descriptor);
227:• if (result != null) {
228: cache.get(new LruCache.CacheNode(ctx, cls, identifier));
229: }
230: return result;
231: }
232:
233: @Override
234: void evict(Class<?> cls, Object identifier, URI context) {
235:• final URI ctx = context != null ? context : defaultContext;
236: super.evict(cls, identifier, ctx);
237: cache.remove(new LruCache.CacheNode(ctx, cls, identifier));
238: }
239:
240: @Override
241: void evict(URI context) {
242:• final URI ctx = context != null ? context : defaultContext;
243:• if (!repoCache.containsKey(ctx)) {
244: return;
245: }
246: final Map<Object, Map<Class<?>, Object>> ctxContent = repoCache.get(ctx);
247:• for (Map.Entry<Object, Map<Class<?>, Object>> e : ctxContent.entrySet()) {
248: e.getValue().forEach((cls, instance) -> {
249: descriptors.remove(instance);
250: cache.remove(new LruCache.CacheNode(ctx, cls, e.getKey()));
251: });
252: }
253: ctxContent.clear();
254: }
255:
256: @Override
257: void evict(Class<?> cls) {
258:• for (Map.Entry<URI, Map<Object, Map<Class<?>, Object>>> e : repoCache.entrySet()) {
259: final URI ctx = e.getKey();
260: e.getValue().forEach((id, indNode) -> {
261: final Object instance = indNode.remove(cls);
262:• if (instance != null) {
263: descriptors.remove(instance);
264: }
265: cache.remove(new LruCache.CacheNode(ctx, cls, id));
266: });
267: }
268: }
269: }
270: }