Skip to content

Method: isCacheable(Descriptor)

1: /*
2: * JOPA
3: * Copyright (C) 2024 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: import cz.cvut.kbss.jopa.sessions.descriptor.LoadStateDescriptor;
22:
23: import java.net.URI;
24: import java.util.Collections;
25: import java.util.HashMap;
26: import java.util.IdentityHashMap;
27: import java.util.Map;
28: import java.util.Set;
29: import java.util.function.Consumer;
30:
31: class EntityCache {
32:
33: // TODO Think about locking on context level, so that the whole cache doesn't have to be locked when being accessed
34:
35: private static final String DEFAULT_CONTEXT_BASE = "http://defaultContext";
36:
37: final Map<URI, Map<Object, Map<Class<?>, Object>>> repoCache;
38: final Map<Object, Descriptors> descriptors;
39: final URI defaultContext;
40:
41: EntityCache() {
42: repoCache = new HashMap<>();
43: this.descriptors =
44: new IdentityHashMap<>(); // Need to use identity to cope with entities overriding equals/hashcode
45: this.defaultContext = URI.create(DEFAULT_CONTEXT_BASE + System.currentTimeMillis());
46: }
47:
48: void put(Object identifier, Object entity, Descriptors descriptors) {
49: assert identifier != null;
50: assert entity != null;
51: assert isCacheable(descriptors.repositoryDescriptor());
52:
53: final Class<?> cls = entity.getClass();
54: final URI ctx = descriptors.repositoryDescriptor().getSingleContext().orElse(defaultContext);
55:
56: Map<Object, Map<Class<?>, Object>> ctxMap;
57: if (!repoCache.containsKey(ctx)) {
58: ctxMap = new HashMap<>();
59: repoCache.put(ctx, ctxMap);
60: } else {
61: ctxMap = repoCache.get(ctx);
62: }
63: Map<Class<?>, Object> individualMap;
64: if (!ctxMap.containsKey(identifier)) {
65: individualMap = new HashMap<>();
66: ctxMap.put(identifier, individualMap);
67: } else {
68: individualMap = ctxMap.get(identifier);
69: }
70: if (individualMap.containsKey(cls)) {
71: this.descriptors.remove(individualMap.get(cls));
72: }
73: individualMap.put(cls, entity);
74: this.descriptors.put(entity, descriptors);
75: }
76:
77: boolean isCacheable(Descriptor descriptor) {
78:• return descriptor.getContexts().size() <= 1;
79: }
80:
81: <T> T get(Class<T> cls, Object identifier, Descriptor descriptor) {
82: return getInternal(cls, identifier, descriptor, u -> {});
83: }
84:
85: <T> T getInternal(Class<T> cls, Object identifier, Descriptor descriptor, Consumer<URI> contextHandler) {
86: assert cls != null;
87: assert identifier != null;
88:
89: final Set<URI> contexts =
90: descriptor.getContexts().isEmpty() ? Collections.singleton(defaultContext) : descriptor.getContexts();
91: for (URI ctx : contexts) {
92: final Map<Class<?>, Object> m = getMapForId(ctx, identifier);
93: final Object result = m.get(cls);
94: if (result != null && descriptors.get(result).repositoryDescriptor().equals(descriptor)) {
95: contextHandler.accept(ctx);
96: return cls.cast(result);
97: }
98: }
99: return null;
100: }
101:
102: <T> LoadStateDescriptor<T> getLoadStateDescriptor(T instance) {
103: return descriptors.containsKey(instance) ?
104: (LoadStateDescriptor<T>) descriptors.get(instance).loadStateDescriptor() : null;
105: }
106:
107: boolean contains(Class<?> cls, Object identifier, Descriptor descriptor) {
108: assert cls != null;
109: assert identifier != null;
110: assert descriptor != null;
111:
112: final Set<URI> contexts =
113: descriptor.getContexts().isEmpty() ? Collections.singleton(defaultContext) : descriptor.getContexts();
114: for (URI ctx : contexts) {
115: final Map<Class<?>, Object> m = getMapForId(ctx, identifier);
116: if (!m.containsKey(cls)) {
117: continue;
118: }
119: final Object result = m.get(cls);
120: assert descriptors.containsKey(result);
121:
122: if (descriptors.get(result).repositoryDescriptor().equals(descriptor)) {
123: return true;
124: }
125: }
126: return false;
127: }
128:
129: void evict(Class<?> cls, Object identifier, URI context) {
130: assert cls != null;
131: assert identifier != null;
132:
133: final Map<Class<?>, Object> m = getMapForId(context, identifier);
134: if (m.containsKey(cls)) {
135: descriptors.remove(m.get(cls));
136: }
137: m.remove(cls);
138: }
139:
140: void evict(URI context) {
141: if (context == null) {
142: context = defaultContext;
143: }
144: if (!repoCache.containsKey(context)) {
145: return;
146: }
147: final Map<Object, Map<Class<?>, Object>> contextCache = repoCache.remove(context);
148: contextCache.values().forEach(instances -> instances.values().forEach(descriptors::remove));
149: }
150:
151: void evict(Class<?> cls) {
152: for (Map.Entry<URI, Map<Object, Map<Class<?>, Object>>> e : repoCache.entrySet()) {
153: final Map<Object, Map<Class<?>, Object>> m = e.getValue();
154: m.forEach((key, value) -> {
155: if (value.containsKey(cls)) {
156: descriptors.remove(value.get(cls));
157: value.remove(cls);
158: }
159: });
160: }
161: }
162:
163: private Map<Class<?>, Object> getMapForId(URI context, Object identifier) {
164: assert identifier != null;
165:
166: final URI ctx = context != null ? context : defaultContext;
167:
168: if (!repoCache.containsKey(ctx)) {
169: return Collections.emptyMap();
170: }
171: final Map<Object, Map<Class<?>, Object>> ctxMap = repoCache.get(ctx);
172: return ctxMap.getOrDefault(identifier, Collections.emptyMap());
173: }
174: }