Skip to content

Method: entityBuilder(EntityConstructor)

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.oom;
19:
20: import cz.cvut.kbss.jopa.datatype.util.Pair;
21: import cz.cvut.kbss.jopa.exceptions.StorageAccessException;
22: import cz.cvut.kbss.jopa.model.MetamodelImpl;
23: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
24: import cz.cvut.kbss.jopa.model.metamodel.Attribute;
25: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
26: import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
27: import cz.cvut.kbss.jopa.model.metamodel.PluralAttribute;
28: import cz.cvut.kbss.jopa.oom.exception.EntityReconstructionException;
29: import cz.cvut.kbss.jopa.sessions.cache.CacheManager;
30: import cz.cvut.kbss.jopa.sessions.descriptor.LoadStateDescriptor;
31: import cz.cvut.kbss.jopa.sessions.descriptor.LoadStateDescriptorFactory;
32: import cz.cvut.kbss.jopa.sessions.util.LoadStateDescriptorRegistry;
33: import cz.cvut.kbss.jopa.sessions.util.LoadingParameters;
34: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
35: import cz.cvut.kbss.ontodriver.Connection;
36: import cz.cvut.kbss.ontodriver.descriptor.AxiomDescriptor;
37: import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
38: import cz.cvut.kbss.ontodriver.model.Axiom;
39:
40: import java.net.URI;
41: import java.util.Collection;
42: import java.util.IdentityHashMap;
43: import java.util.List;
44: import java.util.Map;
45: import java.util.Objects;
46: import java.util.function.Consumer;
47:
48: /**
49: * Root of the entity loading strategies.
50: */
51: abstract class EntityInstanceLoader {
52:
53: final Connection storageConnection;
54: final MetamodelImpl metamodel;
55:
56: final CacheManager cache;
57: private final AxiomDescriptorFactory descriptorFactory;
58: final EntityConstructor entityBuilder;
59:
60: final LoadStateDescriptorRegistry loadStateRegistry;
61:
62: EntityInstanceLoader(EntityInstanceLoaderBuilder builder) {
63: assert builder.storageConnection != null;
64: assert builder.metamodel != null;
65: assert builder.cache != null;
66: assert builder.descriptorFactory != null;
67: assert builder.entityBuilder != null;
68:
69: this.storageConnection = builder.storageConnection;
70: this.metamodel = builder.metamodel;
71: this.cache = builder.cache;
72: this.descriptorFactory = builder.descriptorFactory;
73: this.entityBuilder = builder.entityBuilder;
74: this.loadStateRegistry = builder.loadStateRegistry;
75: }
76:
77: /**
78: * Loads entity based on the specified loading parameters.
79: *
80: * @param loadingParameters Instance loading parameters
81: * @return The loaded instance (possibly {@code null})
82: */
83: abstract <T> T loadEntity(LoadingParameters<T> loadingParameters);
84:
85: <U extends T, T> U loadInstance(LoadingParameters<T> loadingParameters, IdentifiableEntityType<U> et) {
86: final URI identifier = loadingParameters.getIdentifier();
87: final Descriptor descriptor = loadingParameters.getDescriptor();
88: if (isCached(loadingParameters, et)) {
89: return loadCached(et, identifier, descriptor);
90: }
91: final AxiomDescriptor axiomDescriptor = descriptorFactory.createForEntityLoading(loadingParameters, et);
92: try {
93: final Collection<Axiom<?>> axioms = storageConnection.find(axiomDescriptor);
94: return axioms.isEmpty() ? null : entityBuilder.reconstructEntity(
95: new EntityConstructor.EntityConstructionParameters<>(identifier, et, descriptor, loadingParameters.isForceEager()),
96: axioms);
97: } catch (OntoDriverException e) {
98: throw new StorageAccessException(e);
99: } catch (cz.cvut.kbss.jopa.exception.InstantiationException e) {
100: throw new EntityReconstructionException(e);
101: }
102: }
103:
104: <T> boolean isCached(LoadingParameters<T> loadingParameters, EntityType<? extends T> et) {
105: return !loadingParameters.shouldBypassCache() &&
106: cache.contains(et.getJavaType(), loadingParameters.getIdentifier(), loadingParameters.getDescriptor());
107: }
108:
109: <T> T loadCached(EntityType<T> et, URI identifier, Descriptor descriptor) {
110: final T cached = cache.get(et.getJavaType(), identifier, descriptor);
111: recursivelyProcessCachedEntityReferences(cached, et, new IdentityHashMap<>(), List.of(
112: pair -> loadStateRegistry.put(pair.first(), getLoadStatDescriptor(pair.first(), pair.second())),
113: pair -> entityBuilder.populateQueryAttributes(pair.first(), (EntityType<Object>) pair.second())
114: ));
115: return cached;
116: }
117:
118: private LoadStateDescriptor<?> getLoadStatDescriptor(Object instance, EntityType<?> et) {
119: final LoadStateDescriptor<?> cached = cache.getLoadStateDescriptor(instance);
120: if (cached != null) {
121: return cached;
122: }
123: return LoadStateDescriptorFactory.createAllUnknown(instance, (EntityType<Object>) et);
124: }
125:
126: private void recursivelyProcessCachedEntityReferences(Object instance, EntityType<?> et,
127: Map<Object, Object> visited,
128: List<Consumer<Pair<Object, EntityType<?>>>> handlers) {
129: if (visited.containsKey(instance)) {
130: return;
131: }
132: visited.put(instance, null);
133: handlers.forEach(h -> h.accept(new Pair<>(instance, et)));
134: et.getAttributes().stream().filter(Attribute::isAssociation).forEach(att -> {
135: final Class<?> cls = att.isCollection() ? ((PluralAttribute) att).getElementType()
136: .getJavaType() : att.getJavaType();
137: if (!metamodel.isEntityType(cls)) {
138: return;
139: }
140: final Object value = EntityPropertiesUtils.getAttributeValue(att, instance);
141: if (value != null) {
142: // Resolve the value class instead of using the attribute type, as it may be a subclass at runtime
143: if (att.isCollection()) {
144: ((Collection<?>) value).forEach(el -> recursivelyProcessCachedEntityReferences(el, metamodel.entity(el.getClass()), visited, handlers));
145: } else {
146: recursivelyProcessCachedEntityReferences(value, metamodel.entity(value.getClass()), visited, handlers);
147: }
148: }
149: });
150: }
151:
152: abstract static class EntityInstanceLoaderBuilder {
153: private Connection storageConnection;
154: private MetamodelImpl metamodel;
155: private CacheManager cache;
156:
157: private AxiomDescriptorFactory descriptorFactory;
158: private EntityConstructor entityBuilder;
159:
160: private LoadStateDescriptorRegistry loadStateRegistry;
161:
162: EntityInstanceLoaderBuilder connection(Connection connection) {
163: this.storageConnection = Objects.requireNonNull(connection);
164: return this;
165: }
166:
167: EntityInstanceLoaderBuilder metamodel(MetamodelImpl metamodel) {
168: this.metamodel = metamodel;
169: return this;
170: }
171:
172: EntityInstanceLoaderBuilder descriptorFactory(AxiomDescriptorFactory factory) {
173: this.descriptorFactory = factory;
174: return this;
175: }
176:
177: EntityInstanceLoaderBuilder entityBuilder(EntityConstructor builder) {
178: this.entityBuilder = builder;
179: return this;
180: }
181:
182: EntityInstanceLoaderBuilder cache(CacheManager cache) {
183: this.cache = cache;
184: return this;
185: }
186:
187: EntityInstanceLoaderBuilder loadStateRegistry(LoadStateDescriptorRegistry loadStateRegistry) {
188: this.loadStateRegistry = loadStateRegistry;
189: return this;
190: }
191:
192: abstract EntityInstanceLoader build();
193: }
194: }