Skip to content

Method: lambda$evictPossiblyUpdatedReferencesFromCache$4(Object, Object)

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