Skip to content

Method: getContexts()

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;
16:
17: import cz.cvut.kbss.jopa.adapters.IndirectCollection;
18: import cz.cvut.kbss.jopa.exceptions.EntityNotFoundException;
19: import cz.cvut.kbss.jopa.exceptions.OWLEntityExistsException;
20: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
21: import cz.cvut.kbss.jopa.model.AbstractEntityManager;
22: import cz.cvut.kbss.jopa.model.BeanListenerAspect;
23: import cz.cvut.kbss.jopa.model.EntityManagerImpl.State;
24: import cz.cvut.kbss.jopa.model.LoadState;
25: import cz.cvut.kbss.jopa.model.MetamodelImpl;
26: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
27: import cz.cvut.kbss.jopa.model.lifecycle.PostLoadInvoker;
28: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
29: import cz.cvut.kbss.jopa.model.metamodel.EntityTypeImpl;
30: import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
31: import cz.cvut.kbss.jopa.query.NamedQueryManager;
32: import cz.cvut.kbss.jopa.query.ResultSetMappingManager;
33: import cz.cvut.kbss.jopa.query.sparql.SparqlQueryFactory;
34: import cz.cvut.kbss.jopa.sessions.change.ChangeManagerImpl;
35: import cz.cvut.kbss.jopa.sessions.change.ChangeRecordImpl;
36: import cz.cvut.kbss.jopa.sessions.change.ChangeSetFactory;
37: import cz.cvut.kbss.jopa.sessions.descriptor.InstanceDescriptor;
38: import cz.cvut.kbss.jopa.sessions.descriptor.InstanceDescriptorFactory;
39: import cz.cvut.kbss.jopa.sessions.validator.AttributeModificationValidator;
40: import cz.cvut.kbss.jopa.sessions.validator.IntegrityConstraintsValidator;
41: import cz.cvut.kbss.jopa.utils.CollectionFactory;
42: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
43: import cz.cvut.kbss.jopa.utils.ErrorUtils;
44: import cz.cvut.kbss.jopa.utils.Wrapper;
45: import org.aspectj.lang.Aspects;
46:
47: import java.lang.reflect.Field;
48: import java.net.URI;
49: import java.util.*;
50: import java.util.Map.Entry;
51: import java.util.function.Consumer;
52:
53: import static cz.cvut.kbss.jopa.exceptions.OWLEntityExistsException.individualAlreadyManaged;
54: import static cz.cvut.kbss.jopa.utils.EntityPropertiesUtils.getValueAsURI;
55:
56: public class UnitOfWorkImpl extends AbstractSession implements UnitOfWork, ConfigurationHolder, Wrapper {
57:
58: // Read-only!!! It is just the keyset of cloneToOriginals
59: private final Set<Object> cloneMapping;
60: private final Map<Object, Object> cloneToOriginals;
61: private final Map<Object, Object> keysToClones = new HashMap<>();
62: private final Map<Object, Object> deletedObjects;
63: private final Map<Object, Object> newObjectsCloneToOriginal;
64: private final Map<Object, Object> newObjectsKeyToClone = new HashMap<>();
65: private final Map<Object, InstanceDescriptor> instanceDescriptors;
66: private RepositoryMap repoMap;
67:
68: private boolean hasChanges;
69: private boolean hasNew;
70: private boolean hasDeleted;
71: private boolean shouldReleaseAfterCommit;
72: private boolean shouldClearCacheAfterCommit;
73: private boolean useTransactionalOntology;
74:
75: private boolean isActive;
76: private boolean inCommit;
77:
78: private UnitOfWorkChangeSet uowChangeSet = ChangeSetFactory.createUoWChangeSet();
79:
80: private AbstractSession parent;
81: private AbstractEntityManager entityManager;
82: private final ConnectionWrapper storage;
83:
84: private final MergeManager mergeManager;
85: private final CloneBuilder cloneBuilder;
86: private final ChangeManager changeManager;
87: private final SparqlQueryFactory queryFactory;
88: private final CollectionFactory collectionFactory;
89: /**
90: * This is a shortcut for the second level cache.
91: */
92: private final CacheManager cacheManager;
93:
94: public UnitOfWorkImpl(AbstractSession parent) {
95: super(parent.getConfiguration());
96: this.parent = Objects.requireNonNull(parent);
97: this.cloneToOriginals = createMap();
98: this.cloneMapping = cloneToOriginals.keySet();
99: this.deletedObjects = createMap();
100: this.newObjectsCloneToOriginal = createMap();
101: this.instanceDescriptors = new IdentityHashMap<>();
102: this.repoMap = new RepositoryMap();
103: repoMap.initDescriptors();
104: this.cloneBuilder = new CloneBuilderImpl(this);
105: this.collectionFactory = new CollectionFactory(this);
106: this.cacheManager = parent.getLiveObjectCache();
107: this.storage = acquireConnection();
108: this.queryFactory = new SparqlQueryFactory(this, storage);
109: this.mergeManager = new MergeManagerImpl(this);
110: this.changeManager = new ChangeManagerImpl(this);
111: this.useTransactionalOntology = true;
112: this.isActive = true;
113: }
114:
115: CloneBuilder getCloneBuilder() {
116: return cloneBuilder;
117: }
118:
119: /**
120: * This method returns null, since we don't support nested Units of Work yet.
121: */
122: @Override
123: public UnitOfWork acquireUnitOfWork() {
124: return null;
125: }
126:
127: @Override
128: protected ConnectionWrapper acquireConnection() {
129: final ConnectionWrapper conn = parent.acquireConnection();
130: conn.setUnitOfWork(this);
131: return conn;
132: }
133:
134: @Override
135: public <T> T readObject(Class<T> cls, Object identifier, Descriptor descriptor) {
136: Objects.requireNonNull(cls, ErrorUtils.getNPXMessageSupplier("cls"));
137: Objects.requireNonNull(identifier, ErrorUtils.getNPXMessageSupplier("primaryKey"));
138: Objects.requireNonNull(descriptor, ErrorUtils.getNPXMessageSupplier("descriptor"));
139:
140: return readObjectInternal(cls, identifier, descriptor);
141: }
142:
143: private <T> T readObjectInternal(Class<T> cls, Object identifier, Descriptor descriptor) {
144: assert cls != null;
145: assert identifier != null;
146: assert descriptor != null;
147: T result = readManagedObject(cls, identifier, descriptor);
148: if (result != null) {
149: return result;
150: }
151: result = storage.find(new LoadingParameters<>(cls, getValueAsURI(identifier), descriptor));
152:
153: if (result == null) {
154: return null;
155: }
156: final Object clone = registerExistingObject(result, descriptor,
157: Collections.singletonList(new PostLoadInvoker(getMetamodel())));
158: checkForCollections(clone);
159: return cls.cast(clone);
160: }
161:
162: private <T> T readManagedObject(Class<T> cls, Object identifier, Descriptor descriptor) {
163: // First try to find the object among new uncommitted objects
164: Object result = newObjectsKeyToClone.get(identifier);
165: if (result != null && (isInRepository(descriptor, result))) {
166: // The result can be returned, since it is already registered in this UOW
167: return cls.cast(result);
168: }
169: // Object is already managed
170: return getManagedClone(cls, identifier, descriptor);
171: }
172:
173: private <T> T getManagedClone(Class<T> cls, Object identifier, Descriptor descriptor) {
174: if (!keysToClones.containsKey(identifier)) {
175: return null;
176: }
177: final Object clone = keysToClones.get(identifier);
178: if (!cls.isAssignableFrom(clone.getClass())) {
179: throw individualAlreadyManaged(identifier);
180: }
181: return isInRepository(descriptor, clone) && !deletedObjects.containsKey(clone) ? cls.cast(clone) : null;
182: }
183:
184: @Override
185: public <T> T getReference(Class<T> cls, Object identifier, Descriptor descriptor) {
186: Objects.requireNonNull(cls);
187: Objects.requireNonNull(identifier);
188: Objects.requireNonNull(descriptor);
189:
190: final T managedResult = readManagedObject(cls, identifier, descriptor);
191: if (managedResult != null) {
192: return managedResult;
193: }
194: final T result = storage.getReference(new LoadingParameters<>(cls, getValueAsURI(identifier), descriptor));
195: if (result == null) {
196: return null;
197: }
198: instanceDescriptors.put(result, InstanceDescriptorFactory.createNotLoaded(result, entityType(cls)));
199: registerEntityWithPersistenceContext(result);
200: registerEntityWithOntologyContext(result, descriptor);
201: if (getLiveObjectCache().contains(cls, identifier, descriptor)) {
202: cloneToOriginals.put(result, getLiveObjectCache().get(cls, identifier, descriptor));
203: } else {
204: cloneToOriginals.put(result, null);
205: }
206: keysToClones.put(identifier, result);
207: return result;
208: }
209:
210: /**
211: * This method calculates the changes that were to the registered entities and adds these changes into the given
212: * change set for future commit to the ontology.
213: */
214: private void calculateChanges() {
215: if (hasNew) {
216: calculateNewObjects(uowChangeSet);
217: }
218: if (hasDeleted) {
219: calculateDeletedObjects(uowChangeSet);
220: }
221: }
222:
223: /**
224: * Create object change sets for the new objects and adds them into our UnitOfWorkChangeSet.
225: *
226: * @param changeSet UnitOfWorkChangeSet
227: */
228: private void calculateNewObjects(UnitOfWorkChangeSet changeSet) {
229: for (Object clone : newObjectsCloneToOriginal.keySet()) {
230: final Descriptor c = getDescriptor(clone);
231: Object original = newObjectsCloneToOriginal
232: .computeIfAbsent(clone, key -> cloneBuilder.buildClone(key, new CloneConfiguration(c)));
233: if (original == null) {
234: throw new OWLPersistenceException(
235: "Error while calculating changes for new objects. Original not found.");
236: }
237: newObjectsCloneToOriginal.put(clone, original);
238: changeSet.addNewObjectChangeSet(ChangeSetFactory.createObjectChangeSet(original, clone,
239: c));
240: }
241: }
242:
243: private void calculateDeletedObjects(final UnitOfWorkChangeSet changeSet) {
244: for (Object clone : deletedObjects.keySet()) {
245: Descriptor descriptor = getDescriptor(clone);
246: changeSet.addDeletedObjectChangeSet(ChangeSetFactory.createDeleteObjectChangeSet(clone, descriptor));
247: changeSet.cancelObjectChanges(getOriginal(clone));
248: }
249: }
250:
251: @Override
252: public void clear() {
253: detachAllManagedInstances();
254: cloneToOriginals.clear();
255: keysToClones.clear();
256: deletedObjects.clear();
257: newObjectsCloneToOriginal.clear();
258: newObjectsKeyToClone.clear();
259: instanceDescriptors.clear();
260: this.hasChanges = false;
261: this.hasDeleted = false;
262: this.hasNew = false;
263: cloneBuilder.reset();
264: this.repoMap = new RepositoryMap();
265: repoMap.initDescriptors();
266: this.uowChangeSet = ChangeSetFactory.createUoWChangeSet();
267: }
268:
269: private void detachAllManagedInstances() {
270: cloneMapping.forEach(instance -> {
271: removeIndirectCollections(instance);
272: deregisterEntityFromPersistenceContext(instance);
273: });
274: newObjectsCloneToOriginal.keySet().forEach(instance -> {
275: removeIndirectCollections(instance);
276: deregisterEntityFromPersistenceContext(instance);
277: });
278: }
279:
280: @Override
281: public boolean contains(Object entity) {
282: Objects.requireNonNull(entity);
283: return isObjectManaged(entity);
284: }
285:
286: @Override
287: public void commit() {
288: LOG.trace("UnitOfWork commit started.");
289: if (!isActive()) {
290: throw new IllegalStateException("Cannot commit inactive Unit of Work!");
291: }
292: this.inCommit = true;
293: commitUnitOfWork();
294: LOG.trace("UnitOfWork commit finished.");
295: }
296:
297: @Override
298: public void rollback() {
299: LOG.trace("UnitOfWork rollback started.");
300: if (!isActive()) {
301: throw new IllegalStateException("Cannot rollback inactive Unit of Work!");
302: }
303: storage.rollback();
304: clear();
305: }
306:
307: /**
308: * Commit this Unit of Work.
309: */
310: private void commitUnitOfWork() {
311: commitToOntology();
312: mergeChangesIntoParent();
313: postCommit();
314: }
315:
316: /**
317: * Clean up after the commit.
318: */
319: private void postCommit() {
320: final boolean changes = hasChanges();
321: clear();
322: this.inCommit = false;
323: if (changes) {
324: if (shouldClearCacheAfterCommit) {
325: cacheManager.evictAll();
326: this.shouldReleaseAfterCommit = true;
327: } else {
328: cacheManager.evictInferredObjects();
329: }
330: }
331:
332: }
333:
334: /**
335: * If there are any changes, commit them to the ontology.
336: */
337: private void commitToOntology() {
338: if (this.hasNew || this.hasChanges || this.hasDeleted) {
339: calculateChanges();
340: }
341: validateIntegrityConstraints();
342: storageCommit();
343: }
344:
345: private void validateIntegrityConstraints() {
346: final IntegrityConstraintsValidator validator = IntegrityConstraintsValidator.getValidator();
347: for (ObjectChangeSet changeSet : uowChangeSet.getNewObjects()) {
348: validator.validate(changeSet.getCloneObject(),
349: entityType((Class<Object>) changeSet.getObjectClass()), false);
350: }
351: uowChangeSet.getExistingObjectsChanges().forEach(changeSet -> validator.validate(changeSet, getMetamodel()));
352: }
353:
354: private static Map<Object, Object> createMap() {
355: return new IdentityHashMap<>();
356: }
357:
358: /**
359: * Gets current state of the specified entity.
360: * <p>
361: * Note that since no repository is specified we can only determine if the entity is managed or removed. Therefore
362: * if the case is different this method returns State#NOT_MANAGED.
363: *
364: * @param entity The entity to check
365: * @return State of the entity
366: */
367: public State getState(Object entity) {
368: Objects.requireNonNull(entity);
369:
370: if (deletedObjects.containsKey(entity)) {
371: return State.REMOVED;
372: } else if (newObjectsCloneToOriginal.containsKey(entity)) {
373: return State.MANAGED_NEW;
374: } else if (cloneMapping.contains(entity)) {
375: return State.MANAGED;
376: } else {
377: return State.NOT_MANAGED;
378: }
379: }
380:
381: /**
382: * Checks the state of the specified entity with regards to the specified repository.
383: *
384: * @param entity Object
385: * @param descriptor Entity descriptor
386: * @return The state of the specified entity
387: */
388: public State getState(Object entity, Descriptor descriptor) {
389: Objects.requireNonNull(entity, ErrorUtils.getNPXMessageSupplier("entity"));
390: Objects.requireNonNull(descriptor, ErrorUtils.getNPXMessageSupplier("descriptor"));
391:
392: if (deletedObjects.containsKey(entity)) {
393: return State.REMOVED;
394: } else if (newObjectsCloneToOriginal.containsKey(entity) && isInRepository(descriptor, entity)) {
395: return State.MANAGED_NEW;
396: } else if (cloneMapping.contains(entity) && isInRepository(descriptor, entity)) {
397: return State.MANAGED;
398: } else {
399: return State.NOT_MANAGED;
400: }
401: }
402:
403: /**
404: * Tries to find the original object for the given clone. It searches the existing objects, new objects and deleted
405: * objects.
406: *
407: * @param clone Object
408: * @return The original object for the given clone
409: */
410: public Object getOriginal(Object clone) {
411: if (clone == null) {
412: return null;
413: }
414: return cloneToOriginals.containsKey(clone) ? cloneToOriginals.get(clone) : newObjectsCloneToOriginal.get(clone);
415: }
416:
417: /**
418: * Gets managed original with the specified identifier or {@code null} if there is none matching.
419: * <p>
420: * Descriptor is used to check repository context validity.
421: *
422: * @param cls Return type of the original
423: * @param identifier Instance identifier
424: * @param descriptor Repository descriptor
425: * @return Original object managed by this UoW or {@code null} if this UoW doesn't contain a matching instance
426: */
427: public <T> T getManagedOriginal(Class<T> cls, Object identifier, Descriptor descriptor) {
428: final T clone = getManagedClone(cls, identifier, descriptor);
429: return clone != null ? cls.cast(cloneToOriginals.get(clone)) : null;
430: }
431:
432: /**
433: * Check if this UnitOfWork contains this original entity. This method is used by the CloneBuilder so it does not
434: * have to clone already managed referenced objects.
435: *
436: * @param entity The original entity.
437: * @return True if the original is managed in this UnitOfWork.
438: */
439: boolean containsOriginal(Object entity) {
440: return entity != null && cloneToOriginals.containsValue(entity);
441: }
442:
443: /**
444: * Finds clone of the specified original.
445: *
446: * @param original The original object whose clone we are looking for
447: * @return The clone or null, if there is none
448: */
449: public Object getCloneForOriginal(Object original) {
450: for (Entry<Object, Object> entry : cloneToOriginals.entrySet()) {
451: // We use IdentityMap, so we can use ==
452: if (entry.getValue() == original) {
453: return entry.getKey();
454: }
455: }
456: return null;
457: }
458:
459: public boolean hasChanges() {
460: return hasChanges || hasDeleted || hasNew;
461: }
462:
463: void setHasChanges() {
464: this.hasChanges = true;
465: }
466:
467: @Override
468: public CacheManager getLiveObjectCache() {
469: return parent.getLiveObjectCache();
470: }
471:
472: UnitOfWorkChangeSet getUowChangeSet() {
473: return uowChangeSet;
474: }
475:
476: @Override
477: public boolean isActive() {
478: return this.isActive;
479: }
480:
481: /**
482: * Returns true if the given clone represents a newly created object. Otherwise returns false.
483: *
484: * @param clone Object
485: * @return boolean
486: */
487: public boolean isObjectNew(Object clone) {
488: return clone != null && newObjectsCloneToOriginal.containsKey(clone);
489: }
490:
491: /**
492: * Returns true if the given object is already managed.
493: *
494: * @param entity Object
495: * @return boolean
496: */
497: @Override
498: public boolean isObjectManaged(Object entity) {
499: Objects.requireNonNull(entity);
500:
501: return cloneMapping.contains(entity) && !deletedObjects.containsKey(entity) ||
502: newObjectsCloneToOriginal.containsKey(entity);
503: }
504:
505: /**
506: * Persists changed value of the specified field.
507: *
508: * @param entity Entity with changes (the clone)
509: * @param f The field whose value has changed
510: * @throws IllegalStateException If this UoW is not in transaction
511: */
512: public void attributeChanged(Object entity, Field f) {
513: if (!isInTransaction()) {
514: throw new IllegalStateException("This unit of work is not in a transaction.");
515: }
516: final Descriptor descriptor = getDescriptor(entity);
517: final EntityTypeImpl<Object> et = entityType((Class<Object>) entity.getClass());
518: et.getLifecycleListenerManager().invokePreUpdateCallbacks(entity);
519: storage.merge(entity, f, descriptor);
520: createAndRegisterChangeRecord(entity, et.getFieldSpecification(f.getName()), descriptor);
521: setHasChanges();
522: setIndirectCollectionIfPresent(entity, f);
523: et.getLifecycleListenerManager().invokePostUpdateCallbacks(entity);
524: instanceDescriptors.get(entity).setLoaded(et.getFieldSpecification(f.getName()), LoadState.LOADED);
525: }
526:
527: private void createAndRegisterChangeRecord(Object clone, FieldSpecification<?, ?> fieldSpec,
528: Descriptor descriptor) {
529: final Object orig = getOriginal(clone);
530: if (orig == null) {
531: return;
532: }
533: final ChangeRecord record = new ChangeRecordImpl(fieldSpec,
534: EntityPropertiesUtils.getFieldValue(fieldSpec.getJavaField(), clone));
535: preventCachingIfReferenceIsNotLoaded(record);
536: registerChangeRecord(clone, orig, descriptor, record);
537: }
538:
539: private void preventCachingIfReferenceIsNotLoaded(ChangeRecord changeRecord) {
540: final Object newValue = changeRecord.getNewValue();
541: if (newValue != null && contains(newValue) && isLoaded(newValue) != LoadState.LOADED) {
542: changeRecord.preventCaching();
543: }
544: }
545:
546: private void registerChangeRecord(Object clone, Object orig, Descriptor descriptor, ChangeRecord record) {
547: ObjectChangeSet chSet = uowChangeSet.getExistingObjectChanges(orig);
548: if (chSet == null) {
549: chSet = ChangeSetFactory.createObjectChangeSet(orig, clone, descriptor);
550: uowChangeSet.addObjectChangeSet(chSet);
551: }
552: chSet.addChangeRecord(record);
553: }
554:
555: /**
556: * Merge the changes from this Unit of Work's change set into the server session.
557: */
558: private void mergeChangesIntoParent() {
559: if (hasChanges()) {
560: mergeManager.mergeChangesFromChangeSet(uowChangeSet);
561: }
562: evictPossiblyUpdatedReferencesFromCache();
563: }
564:
565: private void evictPossiblyUpdatedReferencesFromCache() {
566: cloneToOriginals.forEach((clone, orig) -> {
567: if (orig == null && !deletedObjects.containsKey(clone)) {
568: removeObjectFromCache(clone, getDescriptor(clone).getContext());
569: }
570: });
571: }
572:
573: @Override
574: public <T> T mergeDetached(T entity, Descriptor descriptor) {
575: Objects.requireNonNull(entity, ErrorUtils.getNPXMessageSupplier("entity"));
576: Objects.requireNonNull(descriptor, ErrorUtils.getNPXMessageSupplier("descriptor"));
577:
578: final Object id = getIdentifier(entity);
579: if (!storage.contains(id, entity.getClass(), descriptor)) {
580: registerNewObject(entity, descriptor);
581: return entity;
582: } else {
583: if (isIndividualManaged(id, entity) && !isSameType(id, entity)) {
584: throw individualAlreadyManaged(id);
585: }
586: return mergeDetachedInternal(entity, descriptor);
587: }
588: }
589:
590: private boolean isSameType(Object id, Object entity) {
591: final Class<?> mergedType = entity.getClass();
592: final Object managed = keysToClones.containsKey(id) ? keysToClones.get(id) : newObjectsKeyToClone.get(id);
593: return managed != null && managed.getClass().isAssignableFrom(mergedType);
594: }
595:
596: private <T> T mergeDetachedInternal(T entity, Descriptor descriptor) {
597: assert entity != null;
598: final EntityTypeImpl<T> et = (EntityTypeImpl<T>) entityType(entity.getClass());
599: final URI idUri = EntityPropertiesUtils.getIdentifier(entity, et);
600:
601: final Object clone = getInstanceForMerge(idUri, et, descriptor);
602: try {
603: // Merge only the changed attributes
604: final ObjectChangeSet chSet = ChangeSetFactory.createObjectChangeSet(clone, entity, descriptor);
605: changeManager.calculateChanges(chSet);
606: if (chSet.hasChanges()) {
607: et.getLifecycleListenerManager().invokePreUpdateCallbacks(clone);
608: final DetachedInstanceMerger merger = new DetachedInstanceMerger(this);
609: merger.mergeChangesFromDetachedToManagedInstance(chSet, descriptor);
610: for (ChangeRecord record : chSet.getChanges()) {
611: AttributeModificationValidator.verifyCanModify(record.getAttribute());
612: preventCachingIfReferenceIsNotLoaded(record);
613: final Field field = record.getAttribute().getJavaField();
614: storage.merge(clone, field, descriptor);
615: }
616: et.getLifecycleListenerManager().invokePostUpdateCallbacks(clone);
617: uowChangeSet.addObjectChangeSet(copyChangeSet(chSet, getOriginal(clone), clone, descriptor));
618: }
619: } catch (OWLEntityExistsException e) {
620: unregisterObject(clone);
621: throw e;
622: }
623: if (cacheManager.contains(et.getJavaType(), idUri, descriptor)) {
624: cacheManager.evict(et.getJavaType(), idUri, descriptor.getContext());
625: }
626: setHasChanges();
627: checkForCollections(clone);
628: return et.getJavaType().cast(clone);
629: }
630:
631: private <T> Object getInstanceForMerge(URI identifier, EntityType<T> et, Descriptor descriptor) {
632: if (keysToClones.containsKey(identifier)) {
633: return keysToClones.get(identifier);
634: }
635: final LoadingParameters<T> params = new LoadingParameters<>(et.getJavaType(), identifier, descriptor, true);
636: T original = storage.find(params);
637: assert original != null;
638:
639: return registerExistingObject(original, descriptor);
640: }
641:
642: private static ObjectChangeSet copyChangeSet(ObjectChangeSet changeSet, Object original, Object clone,
643: Descriptor descriptor) {
644: final ObjectChangeSet newChangeSet = ChangeSetFactory.createObjectChangeSet(original, clone, descriptor);
645: changeSet.getChanges().forEach(newChangeSet::addChangeRecord);
646: return newChangeSet;
647: }
648:
649: private void registerEntityWithPersistenceContext(Object entity) {
650: Aspects.aspectOf(BeanListenerAspect.class).register(entity, this);
651: }
652:
653: private void deregisterEntityFromPersistenceContext(Object entity) {
654: Aspects.aspectOf(BeanListenerAspect.class).deregister(entity);
655: }
656:
657: @Override
658: public NamedQueryManager getNamedQueryManager() {
659: return parent.getNamedQueryManager();
660: }
661:
662: @Override
663: public ResultSetMappingManager getResultSetMappingManager() {
664: return parent.getResultSetMappingManager();
665: }
666:
667: @Override
668: public Object registerExistingObject(Object entity, Descriptor descriptor) {
669: return registerExistingObject(entity, descriptor, Collections.emptyList());
670: }
671:
672: @Override
673: public Object registerExistingObject(Object entity, Descriptor descriptor, List<Consumer<Object>> postClone) {
674: if (entity == null) {
675: return null;
676: }
677: if (cloneToOriginals.containsValue(entity)) {
678: return getCloneForOriginal(entity);
679: }
680: final CloneConfiguration cloneConfig = new CloneConfiguration(descriptor);
681: postClone.forEach(cloneConfig::addPostRegisterHandler);
682: Object clone = cloneBuilder.buildClone(entity, cloneConfig);
683: assert clone != null;
684: registerClone(clone, entity, descriptor);
685: postClone.forEach(c -> c.accept(clone));
686: return clone;
687: }
688:
689: private void registerClone(Object clone, Object original, Descriptor descriptor) {
690: cloneToOriginals.put(clone, original);
691: final Object identifier = EntityPropertiesUtils.getIdentifier(clone, getMetamodel());
692: keysToClones.put(identifier, clone);
693: instanceDescriptors
694: .put(clone, InstanceDescriptorFactory.create(clone, (EntityType<Object>) entityType(clone.getClass())));
695: registerEntityWithPersistenceContext(clone);
696: registerEntityWithOntologyContext(clone, descriptor);
697: }
698:
699: /**
700: * Release this Unit of Work. Releasing an active Unit of Work with uncommitted changes causes all pending changes
701: * to be discarded.
702: */
703: @Override
704: public void release() {
705: clear();
706: storage.close();
707: this.isActive = false;
708: LOG.debug("UnitOfWork released.");
709: }
710:
711: @Override
712: public <T> void refreshObject(T object) {
713: Objects.requireNonNull(object);
714: if (!isObjectManaged(object)) {
715: throw new IllegalArgumentException(
716: "Cannot call refresh on an instance not managed by this persistence context.");
717: }
718: final EntityTypeImpl<T> et = entityType((Class<T>) object.getClass());
719: final URI idUri = EntityPropertiesUtils.getIdentifier(object, et);
720: final Descriptor descriptor = getDescriptor(object);
721:
722: final LoadingParameters<T> params = new LoadingParameters<>(et.getJavaType(), idUri, descriptor, true);
723: params.bypassCache();
724: final ConnectionWrapper connection = acquireConnection();
725: try {
726: uowChangeSet.cancelObjectChanges(getOriginal(object));
727: T original = connection.find(params);
728: if (original == null) {
729: throw new EntityNotFoundException("Entity " + object + " no longer exists in the repository.");
730: }
731: T source = (T) cloneBuilder.buildClone(original, new CloneConfiguration(descriptor));
732: final ObjectChangeSet chSet = ChangeSetFactory.createObjectChangeSet(source, object, descriptor);
733: changeManager.calculateChanges(chSet);
734: new RefreshInstanceMerger(collectionFactory).mergeChanges(chSet);
735: revertTransactionalChanges(object, descriptor, chSet);
736: registerClone(object, original, descriptor);
737: et.getLifecycleListenerManager().invokePostLoadCallbacks(object);
738: } finally {
739: connection.close();
740: }
741: }
742:
743: private <T> void revertTransactionalChanges(T object, Descriptor descriptor, ObjectChangeSet chSet) {
744: for (ChangeRecord change : chSet.getChanges()) {
745: storage.merge(object, change.getAttribute().getJavaField(),
746: descriptor.getAttributeDescriptor(change.getAttribute()));
747: }
748: }
749:
750: @Override
751: public void registerNewObject(Object entity, Descriptor descriptor) {
752: Objects.requireNonNull(entity, ErrorUtils.getNPXMessageSupplier("entity"));
753: Objects.requireNonNull(descriptor, ErrorUtils.getNPXMessageSupplier("descriptor"));
754:
755: registerNewObjectInternal(entity, descriptor);
756: }
757:
758: /**
759: * Registers the specified entity for persist in this Unit of Work.
760: *
761: * @param entity The entity to register
762: * @param descriptor Entity descriptor, specifying optionally contexts into which the entity will be persisted
763: */
764: private void registerNewObjectInternal(Object entity, Descriptor descriptor) {
765: final EntityTypeImpl<?> eType = entityType(entity.getClass());
766: eType.getLifecycleListenerManager().invokePrePersistCallbacks(entity);
767: Object id = getIdentifier(entity);
768: if (id == null) {
769: EntityPropertiesUtils.verifyIdentifierIsGenerated(entity, eType);
770: }
771: verifyCanPersist(id, entity, eType, descriptor);
772: storage.persist(id, entity, descriptor);
773: if (id == null) {
774: // If the ID was null, extract it from the entity. It is present now
775: id = getIdentifier(entity);
776: }
777: assert id != null;
778: // Original is null until commit
779: newObjectsCloneToOriginal.put(entity, null);
780: registerEntityWithPersistenceContext(entity);
781: registerEntityWithOntologyContext(entity, descriptor);
782: instanceDescriptors.put(entity, InstanceDescriptorFactory.createAllLoaded(entity, (EntityType<Object>) eType));
783: newObjectsKeyToClone.put(id, entity);
784: checkForCollections(entity);
785: this.hasNew = true;
786: eType.getLifecycleListenerManager().invokePostPersistCallbacks(entity);
787: }
788:
789: private void verifyCanPersist(Object id, Object instance, EntityType<?> et, Descriptor descriptor) {
790: if (isIndividualManaged(id, instance) && !instance.getClass().isEnum()) {
791: throw individualAlreadyManaged(id);
792: }
793: if (storage.contains(id, instance.getClass(), descriptor)) {
794: throw new OWLEntityExistsException(
795: "Individual " + id + " of type " + et.getIRI() + " already exists in storage.");
796: }
797: }
798:
799: private boolean isIndividualManaged(Object identifier, Object entity) {
800: return keysToClones.containsKey(identifier) ||
801: newObjectsKeyToClone.containsKey(identifier) && !cloneMapping.contains(entity);
802: }
803:
804: @Override
805: public void removeObject(Object entity) {
806: assert entity != null;
807: if (!isObjectManaged(entity)) {
808: throw new IllegalArgumentException(
809: "Cannot remove entity which is not managed in the current persistence context.");
810: }
811: final EntityTypeImpl<?> et = entityType(entity.getClass());
812: et.getLifecycleListenerManager().invokePreRemoveCallbacks(entity);
813: final Object primaryKey = getIdentifier(entity);
814: final Descriptor descriptor = getDescriptor(entity);
815:
816: if (hasNew && newObjectsCloneToOriginal.containsKey(entity)) {
817: unregisterObject(entity);
818: newObjectsKeyToClone.remove(primaryKey);
819: } else {
820: deletedObjects.put(entity, entity);
821: this.hasDeleted = true;
822: }
823: storage.remove(primaryKey, et.getJavaType(), descriptor);
824: et.getLifecycleListenerManager().invokePostRemoveCallbacks(entity);
825: }
826:
827: @Override
828: public void restoreRemovedObject(Object entity) {
829: assert deletedObjects.containsKey(entity);
830:
831: deletedObjects.remove(entity);
832: final Object id = getIdentifier(entity);
833: storage.persist(id, entity, getDescriptor(entity));
834: }
835:
836: /**
837: * Remove the registered object from this Unit of Work.
838: *
839: * @param object Clone of the original object
840: */
841: public void unregisterObject(Object object) {
842: if (object == null) {
843: return;
844: }
845: final Object original = cloneToOriginals.remove(object);
846: keysToClones.remove(EntityPropertiesUtils.getIdentifier(object, getMetamodel()));
847:
848: deletedObjects.remove(object);
849: if (hasNew) {
850: newObjectsCloneToOriginal.remove(object);
851: }
852: if (original != null) {
853: cloneBuilder.removeVisited(original, repoMap.getEntityDescriptor(object));
854: }
855: removeIndirectCollections(object);
856: deregisterEntityFromPersistenceContext(object);
857: unregisterEntityFromOntologyContext(object);
858: }
859:
860: @Override
861: public boolean shouldReleaseAfterCommit() {
862: return shouldReleaseAfterCommit;
863: }
864:
865: public void setShouldClearAfterCommit(boolean shouldClearCache) {
866: this.shouldClearCacheAfterCommit = shouldClearCache;
867: }
868:
869: public void setEntityManager(AbstractEntityManager entityManager) {
870: this.entityManager = entityManager;
871: }
872:
873: @Override
874: public void writeUncommittedChanges() {
875: if (hasChanges()) {
876: commitUnitOfWork();
877: }
878: }
879:
880: @Override
881: public MetamodelImpl getMetamodel() {
882: return parent.getMetamodel();
883: }
884:
885: private <T> EntityTypeImpl<T> entityType(Class<T> cls) {
886: return getMetamodel().entity(cls);
887: }
888:
889: @Override
890: public boolean isEntityType(Class<?> cls) {
891: return parent.isEntityType(cls);
892: }
893:
894: @Override
895: public boolean isInTransaction() {
896: return entityManager != null && entityManager.getTransaction().isActive();
897: }
898:
899: /**
900: * Returns {@code true} if this UoW is currently committing changes.
901: *
902: * @return Whether this UoW is in the commit phase
903: */
904: public boolean isInCommit() {
905: return inCommit;
906: }
907:
908: @Override
909: public <T> void loadEntityField(T entity, Field field) {
910: Objects.requireNonNull(entity, ErrorUtils.getNPXMessageSupplier("entity"));
911: Objects.requireNonNull(field, ErrorUtils.getNPXMessageSupplier("field"));
912: assert field.getDeclaringClass().isAssignableFrom(entity.getClass());
913:
914: final Descriptor entityDescriptor = getDescriptor(entity);
915: if (!instanceDescriptors.containsKey(entity)) {
916: throw new OWLPersistenceException(
917: "Unable to find repository identifier for entity " + entity + ". Is it managed by this UoW?");
918: }
919: final InstanceDescriptor<?> instanceDescriptor = instanceDescriptors.get(entity);
920: final FieldSpecification<?, ?> fieldSpec = entityType((Class<Object>) entity.getClass())
921: .getFieldSpecification(field.getName());
922: if (instanceDescriptor.isLoaded(fieldSpec) == LoadState.LOADED) {
923: return;
924: }
925:
926: storage.loadFieldValue(entity, field, entityDescriptor);
927: final Object orig = EntityPropertiesUtils.getFieldValue(field, entity);
928: final Object entityOriginal = getOriginal(entity);
929: if (entityOriginal != null) {
930: EntityPropertiesUtils.setFieldValue(field, entityOriginal, orig);
931: }
932: final Descriptor fieldDescriptor = getFieldDescriptor(entity, field, entityDescriptor);
933: final Object clone = cloneLoadedFieldValue(entity, field, fieldDescriptor, orig);
934: EntityPropertiesUtils.setFieldValue(field, entity, clone);
935: instanceDescriptors.get(entity).setLoaded(fieldSpec, LoadState.LOADED);
936: }
937:
938: private <T> Descriptor getFieldDescriptor(T entity, Field field, Descriptor entityDescriptor) {
939: final EntityType<?> et = entityType(entity.getClass());
940: final FieldSpecification<?, ?> fieldSpec = et.getFieldSpecification(field.getName());
941: return entityDescriptor.getAttributeDescriptor(fieldSpec);
942: }
943:
944: private <T> Object cloneLoadedFieldValue(T entity, Field field, final Descriptor fieldDescriptor,
945: final Object fieldValueOrig) {
946: Object clone;
947: if (fieldValueOrig == null) {
948: clone = null;
949: } else {
950: if (isEntityType(field.getType())) {
951: clone = registerExistingObject(fieldValueOrig, fieldDescriptor);
952: putObjectIntoCache(getIdentifier(clone), fieldValueOrig, fieldDescriptor);
953: } else {
954: clone = cloneBuilder.buildClone(entity, field, fieldValueOrig, fieldDescriptor);
955: }
956: }
957: return clone;
958: }
959:
960: @Override
961: public void removeObjectFromCache(Object toRemove, URI context) {
962: Objects.requireNonNull(toRemove, ErrorUtils.getNPXMessageSupplier("toRemove"));
963:
964: cacheManager.evict(toRemove.getClass(), getIdentifier(toRemove), context);
965: }
966:
967: @Override
968: public boolean isConsistent(URI context) {
969: return storage.isConsistent(context);
970: }
971:
972: @Override
973: public List<URI> getContexts() {
974: return storage.getContexts();
975: }
976:
977: @Override
978: public LoadState isLoaded(Object entity, String attributeName) {
979: Objects.requireNonNull(entity);
980: final FieldSpecification<?, ?> fs = entityType(entity.getClass()).getFieldSpecification(attributeName);
981: return instanceDescriptors.containsKey(entity) ? instanceDescriptors.get(entity).isLoaded(fs) :
982: LoadState.UNKNOWN;
983: }
984:
985: @Override
986: public LoadState isLoaded(Object entity) {
987: Objects.requireNonNull(entity);
988: return instanceDescriptors.containsKey(entity) ? instanceDescriptors.get(entity).isLoaded() : LoadState.UNKNOWN;
989: }
990:
991: @Override
992: public void setUseTransactionalOntologyForQueryProcessing() {
993: this.useTransactionalOntology = true;
994: }
995:
996: @Override
997: public boolean useTransactionalOntologyForQueryProcessing() {
998: return useTransactionalOntology;
999: }
1000:
1001: @Override
1002: public void setUseBackupOntologyForQueryProcessing() {
1003: this.useTransactionalOntology = false;
1004: }
1005:
1006: @Override
1007: public boolean useBackupOntologyForQueryProcessing() {
1008: return !useTransactionalOntology;
1009: }
1010:
1011: public SparqlQueryFactory sparqlQueryFactory() {
1012: return queryFactory;
1013: }
1014:
1015: /**
1016: * Check if the specified entity contains a collection. If so, replace it with its indirect representation so that
1017: * changes in that collection can be tracked.
1018: *
1019: * @param entity The entity to check
1020: */
1021: private void checkForCollections(Object entity) {
1022: assert entity != null;
1023: final EntityType<?> et = entityType(entity.getClass());
1024: for (FieldSpecification<?, ?> fieldSpec : et.getFieldSpecifications()) {
1025: setIndirectCollectionIfPresent(entity, fieldSpec.getJavaField());
1026: }
1027: }
1028:
1029: /**
1030: * Create and set indirect collection on the specified entity field.
1031: * <p>
1032: * If the specified field is of Collection type and it is not already an indirect collection, create new one and set
1033: * it as the value of the specified field on the specified entity.
1034: *
1035: * @param entity The entity collection will be set on
1036: * @param field The field to set
1037: * @throws IllegalArgumentException Reflection
1038: */
1039: private void setIndirectCollectionIfPresent(Object entity, Field field) {
1040: assert entity != null;
1041: assert field != null;
1042:
1043: final Object value = EntityPropertiesUtils.getFieldValue(field, entity);
1044: if (value instanceof IndirectCollection) {
1045: return;
1046: }
1047: if (value instanceof Collection || value instanceof Map) {
1048: EntityPropertiesUtils.setFieldValue(field, entity, createIndirectCollection(value, entity, field));
1049: }
1050: }
1051:
1052: /**
1053: * Creates an indirect collection, which wraps the specified collection instance and propagates changes to the
1054: * persistence context.
1055: *
1056: * @param collection Collection to be proxied
1057: * @param owner Collection owner instance
1058: * @param field Field filled with the collection
1059: * @return Indirect collection
1060: */
1061: public IndirectCollection<?> createIndirectCollection(Object collection, Object owner, Field field) {
1062: return collectionFactory.createIndirectCollection(collection, owner, field);
1063: }
1064:
1065: /**
1066: * Remove indirect collection implementations from the specified entity (if present).
1067: *
1068: * @param entity The entity to remove indirect collections from
1069: */
1070: private void removeIndirectCollections(Object entity) {
1071: assert entity != null;
1072: final EntityType<?> et = entityType(entity.getClass());
1073: for (FieldSpecification<?, ?> fs : et.getFieldSpecifications()) {
1074: final Object value = EntityPropertiesUtils.getFieldValue(fs.getJavaField(), entity);
1075: if (value instanceof IndirectCollection) {
1076: IndirectCollection<?> indCol = (IndirectCollection<?>) value;
1077: EntityPropertiesUtils.setFieldValue(fs.getJavaField(), entity, indCol.getReferencedCollection());
1078: }
1079: }
1080: }
1081:
1082: void putObjectIntoCache(Object identifier, Object entity, Descriptor descriptor) {
1083: cacheManager.add(identifier, entity, descriptor);
1084: }
1085:
1086: private Object getIdentifier(Object entity) {
1087: return EntityPropertiesUtils.getIdentifier(entity, getMetamodel());
1088: }
1089:
1090: private void unregisterEntityFromOntologyContext(Object entity) {
1091: assert entity != null;
1092:
1093: final Descriptor descriptor = repoMap.getEntityDescriptor(entity);
1094: if (descriptor == null) {
1095: throw new OWLPersistenceException("Fatal error, unable to find descriptor for entity " + entity);
1096: }
1097:
1098: repoMap.remove(descriptor, entity);
1099: repoMap.removeEntityToRepository(entity);
1100: }
1101:
1102: private void registerEntityWithOntologyContext(Object entity, Descriptor descriptor) {
1103: assert descriptor != null;
1104: assert entity != null;
1105:
1106: repoMap.add(descriptor, entity, null);
1107: repoMap.addEntityToRepository(entity, descriptor);
1108: }
1109:
1110: private boolean isInRepository(Descriptor descriptor, Object entity) {
1111: assert descriptor != null;
1112: assert entity != null;
1113:
1114: return repoMap.contains(descriptor, entity);
1115: }
1116:
1117: private Descriptor getDescriptor(Object entity) {
1118: assert entity != null;
1119:
1120: final Descriptor descriptor = repoMap.getEntityDescriptor(entity);
1121: if (descriptor == null) {
1122: throw new OWLPersistenceException("Unable to find descriptor of entity " + entity + " in this UoW!");
1123: }
1124: return descriptor;
1125: }
1126:
1127: private void storageCommit() {
1128: try {
1129: storage.commit();
1130: } catch (OWLPersistenceException e) {
1131: entityManager.removeCurrentPersistenceContext();
1132: throw e;
1133: }
1134: }
1135:
1136: @Override
1137: public <T> T unwrap(Class<T> cls) {
1138: if (cls.isAssignableFrom(getClass())) {
1139: return cls.cast(this);
1140: }
1141: return storage.unwrap(cls);
1142: }
1143: }