Skip to content

Method: evict(URI)

1: /*
2: * JOPA
3: * Copyright (C) 2023 Czech Technical University in Prague
4: *
5: * This library is free software; you can redistribute it and/or
6: * modify it under the terms of the GNU Lesser General Public
7: * License as published by the Free Software Foundation; either
8: * version 3.0 of the License, or (at your option) any later version.
9: *
10: * This library is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: * Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public
16: * License along with this library.
17: */
18: package cz.cvut.kbss.jopa.sessions.cache;
19:
20: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
21:
22: import java.net.URI;
23: import java.util.*;
24: import java.util.function.Consumer;
25:
26: class EntityCache {
27:
28: // TODO Think about locking on context level, so that the whole cache doesn't have to be locked when being accessed
29:
30: private static final String DEFAULT_CONTEXT_BASE = "http://defaultContext";
31:
32: final Map<URI, Map<Object, Map<Class<?>, Object>>> repoCache;
33: final Map<Object, Descriptor> descriptors;
34: final URI defaultContext;
35:
36: EntityCache() {
37: repoCache = new HashMap<>();
38: this.descriptors =
39: new IdentityHashMap<>(); // Need to use identity to cope with entities overriding equals/hashcode
40: this.defaultContext = URI.create(DEFAULT_CONTEXT_BASE + System.currentTimeMillis());
41: }
42:
43: void put(Object identifier, Object entity, Descriptor descriptor) {
44: assert identifier != null;
45: assert entity != null;
46: assert isCacheable(descriptor);
47:
48: final Class<?> cls = entity.getClass();
49: final URI ctx = descriptor.getSingleContext().orElse(defaultContext);
50:
51: Map<Object, Map<Class<?>, Object>> ctxMap;
52: if (!repoCache.containsKey(ctx)) {
53: ctxMap = new HashMap<>();
54: repoCache.put(ctx, ctxMap);
55: } else {
56: ctxMap = repoCache.get(ctx);
57: }
58: Map<Class<?>, Object> individualMap;
59: if (!ctxMap.containsKey(identifier)) {
60: individualMap = new HashMap<>();
61: ctxMap.put(identifier, individualMap);
62: } else {
63: individualMap = ctxMap.get(identifier);
64: }
65: if (individualMap.containsKey(cls)) {
66: descriptors.remove(individualMap.get(cls));
67: }
68: individualMap.put(cls, entity);
69: descriptors.put(entity, descriptor);
70: }
71:
72: boolean isCacheable(Descriptor descriptor) {
73: return descriptor.getContexts().size() <= 1;
74: }
75:
76: <T> T get(Class<T> cls, Object identifier, Descriptor descriptor) {
77: return getInternal(cls, identifier, descriptor, u -> {});
78: }
79:
80: <T> T getInternal(Class<T> cls, Object identifier, Descriptor descriptor, Consumer<URI> contextHandler) {
81: assert cls != null;
82: assert identifier != null;
83:
84: final Set<URI> contexts =
85: descriptor.getContexts().isEmpty() ? Collections.singleton(defaultContext) : descriptor.getContexts();
86: for (URI ctx : contexts) {
87: final Map<Class<?>, Object> m = getMapForId(ctx, identifier);
88: final Object result = m.get(cls);
89: if (result != null && descriptors.get(result).equals(descriptor)) {
90: contextHandler.accept(ctx);
91: return cls.cast(result);
92: }
93: }
94: return null;
95: }
96:
97: boolean contains(Class<?> cls, Object identifier, Descriptor descriptor) {
98: assert cls != null;
99: assert identifier != null;
100: assert descriptor != null;
101:
102: final Set<URI> contexts =
103: descriptor.getContexts().isEmpty() ? Collections.singleton(defaultContext) : descriptor.getContexts();
104: for (URI ctx : contexts) {
105: final Map<Class<?>, Object> m = getMapForId(ctx, identifier);
106: if (!m.containsKey(cls)) {
107: continue;
108: }
109: final Object result = m.get(cls);
110: assert descriptors.containsKey(result);
111:
112: if (descriptors.get(result).equals(descriptor)) {
113: return true;
114: }
115: }
116: return false;
117: }
118:
119: void evict(Class<?> cls, Object identifier, URI context) {
120: assert cls != null;
121: assert identifier != null;
122:
123: final Map<Class<?>, Object> m = getMapForId(context, identifier);
124: if (m.containsKey(cls)) {
125: descriptors.remove(m.get(cls));
126: }
127: m.remove(cls);
128: }
129:
130: void evict(URI context) {
131:• if (context == null) {
132: context = defaultContext;
133: }
134:• if (!repoCache.containsKey(context)) {
135: return;
136: }
137: final Map<Object, Map<Class<?>, Object>> contextCache = repoCache.remove(context);
138: contextCache.values().forEach(instances -> instances.values().forEach(descriptors::remove));
139: }
140:
141: void evict(Class<?> cls) {
142: for (Map.Entry<URI, Map<Object, Map<Class<?>, Object>>> e : repoCache.entrySet()) {
143: final Map<Object, Map<Class<?>, Object>> m = e.getValue();
144: m.forEach((key, value) -> {
145: if (value.containsKey(cls)) {
146: descriptors.remove(value.get(cls));
147: value.remove(cls);
148: }
149: });
150: }
151: }
152:
153: private Map<Class<?>, Object> getMapForId(URI context, Object identifier) {
154: assert identifier != null;
155:
156: final URI ctx = context != null ? context : defaultContext;
157:
158: if (!repoCache.containsKey(ctx)) {
159: return Collections.emptyMap();
160: }
161: final Map<Object, Map<Class<?>, Object>> ctxMap = repoCache.get(ctx);
162: return ctxMap.getOrDefault(identifier, Collections.emptyMap());
163: }
164: }