Skip to content

Method: evictPossiblyUpdatedReferencesFromCache()

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